Skip to main content

thalir_core/
function.rs

1use crate::block::{BasicBlock, BlockId};
2use crate::contract::ModifierRef;
3use crate::types::Type;
4use cranelift::codegen::ir as clif_ir;
5use indexmap::IndexMap;
6use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct Function {
10    pub signature: FunctionSignature,
11    pub visibility: Visibility,
12    pub mutability: Mutability,
13    pub modifiers: Vec<ModifierRef>,
14    pub body: FunctionBody,
15    pub metadata: FunctionMetadata,
16}
17
18impl Function {
19    pub fn new(signature: FunctionSignature) -> Self {
20        Self {
21            signature,
22            visibility: Visibility::Private,
23            mutability: Mutability::NonPayable,
24            modifiers: Vec::new(),
25            body: FunctionBody::new(),
26            metadata: FunctionMetadata::default(),
27        }
28    }
29
30    pub fn name(&self) -> &str {
31        &self.signature.name
32    }
33
34    pub fn entry_block(&self) -> BlockId {
35        self.body.entry_block()
36    }
37
38    pub fn analyze_metadata(&mut self) {
39        let (calls_external, modifies_state) = self
40            .body
41            .blocks
42            .values()
43            .flat_map(|block| &block.instructions)
44            .fold((false, false), |(ext, state), inst| {
45                (
46                    ext || inst.is_external_call(),
47                    state || inst.is_state_changing(),
48                )
49            });
50
51        self.metadata.calls_external = calls_external;
52        self.metadata.modifies_state = modifies_state;
53        self.metadata.can_reenter = calls_external && modifies_state;
54    }
55}
56
57#[derive(Debug, Clone, Serialize, Deserialize)]
58pub struct FunctionSignature {
59    pub name: String,
60    pub params: Vec<Parameter>,
61    pub returns: Vec<Type>,
62    pub is_payable: bool,
63}
64
65#[derive(Debug, Clone, Serialize, Deserialize)]
66pub struct Parameter {
67    pub name: String,
68    pub param_type: Type,
69    pub location: DataLocation,
70}
71
72impl Parameter {
73    pub fn new(name: impl Into<String>, param_type: Type) -> Self {
74        Self {
75            name: name.into(),
76            param_type,
77            location: DataLocation::Memory,
78        }
79    }
80}
81
82#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
83pub enum DataLocation {
84    Storage,
85    Memory,
86    Calldata,
87}
88
89#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
90pub enum Visibility {
91    Public,
92    External,
93    Internal,
94    Private,
95}
96
97#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
98pub enum Mutability {
99    Pure,
100    View,
101    NonPayable,
102    Payable,
103}
104
105#[derive(Debug, Clone, Serialize, Deserialize)]
106pub struct FunctionBody {
107    pub entry_block: BlockId,
108    pub blocks: IndexMap<BlockId, BasicBlock>,
109    pub locals: Vec<LocalVariable>,
110    #[serde(skip)]
111    pub cranelift_func: Option<CraneliftFunction>,
112    next_block_id: u32,
113    next_local_id: u32,
114}
115
116impl FunctionBody {
117    pub fn new() -> Self {
118        let entry_block = BlockId(0);
119        let mut blocks = IndexMap::new();
120        blocks.insert(entry_block, BasicBlock::new(entry_block));
121
122        Self {
123            entry_block,
124            blocks,
125            locals: Vec::new(),
126            cranelift_func: None,
127            next_block_id: 1,
128            next_local_id: 0,
129        }
130    }
131
132    pub fn create_block(&mut self) -> BlockId {
133        let id = BlockId(self.next_block_id);
134        self.next_block_id += 1;
135        self.blocks.insert(id, BasicBlock::new(id));
136        id
137    }
138
139    pub fn get_block(&self, id: BlockId) -> Option<&BasicBlock> {
140        self.blocks.get(&id)
141    }
142
143    pub fn get_block_mut(&mut self, id: BlockId) -> Option<&mut BasicBlock> {
144        self.blocks.get_mut(&id)
145    }
146
147    pub fn entry_block(&self) -> BlockId {
148        self.entry_block
149    }
150
151    pub fn add_local(&mut self, var: LocalVariable) -> LocalId {
152        let id = LocalId(self.next_local_id);
153        self.next_local_id += 1;
154        self.locals.push(var);
155        id
156    }
157}
158
159impl Default for FunctionBody {
160    fn default() -> Self {
161        Self::new()
162    }
163}
164
165#[derive(Debug, Clone, Serialize, Deserialize)]
166pub struct LocalVariable {
167    pub id: LocalId,
168    pub name: String,
169    pub var_type: Type,
170    pub location: DataLocation,
171}
172
173#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
174pub struct LocalId(pub u32);
175
176pub struct CraneliftFunction {
177    pub func: clif_ir::Function,
178}
179
180impl std::fmt::Debug for CraneliftFunction {
181    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
182        f.debug_struct("CraneliftFunction")
183            .field("name", &self.func.name)
184            .finish()
185    }
186}
187
188impl Clone for CraneliftFunction {
189    fn clone(&self) -> Self {
190        Self {
191            func: self.func.clone(),
192        }
193    }
194}
195
196#[derive(Debug, Clone, Default, Serialize, Deserialize)]
197pub struct FunctionMetadata {
198    pub is_constructor: bool,
199    pub is_fallback: bool,
200    pub is_receive: bool,
201    pub estimated_gas: Option<u64>,
202    pub can_reenter: bool,
203    pub has_assembly: bool,
204    pub calls_external: bool,
205    pub modifies_state: bool,
206}