fe_codegen/db/queries/
abi.rs

1use fe_abi::{
2    contract::AbiContract,
3    event::{AbiEvent, AbiEventField},
4    function::{AbiFunction, AbiFunctionType, CtxParam, SelfParam, StateMutability},
5    types::{AbiTupleField, AbiType},
6};
7use fe_analyzer::{
8    constants::INDEXED,
9    namespace::{
10        items::{ContractId, ModuleId},
11        types::{CtxDecl, SelfDecl},
12    },
13};
14use fe_mir::ir::{self, FunctionId, TypeId};
15
16use crate::db::CodegenDb;
17
18pub fn abi_contract(db: &dyn CodegenDb, contract: ContractId) -> AbiContract {
19    let mut funcs = vec![];
20
21    if let Some(init) = contract.init_function(db.upcast()) {
22        let init_func = db.mir_lowered_func_signature(init);
23        let init_abi = db.codegen_abi_function(init_func);
24        funcs.push(init_abi);
25    }
26
27    for &func in contract.all_functions(db.upcast()).as_ref() {
28        let mir_func = db.mir_lowered_func_signature(func);
29        if mir_func.linkage(db.upcast()).is_exported() {
30            let func_abi = db.codegen_abi_function(mir_func);
31            funcs.push(func_abi);
32        }
33    }
34
35    let events = abi_module_events(db, contract.module(db.upcast()));
36
37    AbiContract::new(funcs, events)
38}
39
40pub fn abi_module_events(db: &dyn CodegenDb, module: ModuleId) -> Vec<AbiEvent> {
41    let mut events = vec![];
42    for &s in db.module_structs(module).as_ref() {
43        let struct_ty = s.as_type(db.upcast());
44        // TODO: This is a hack to avoid generating an ABI for non-`emittable` structs.
45        if struct_ty.is_emittable(db.upcast()) {
46            let mir_event = db.mir_lowered_type(struct_ty);
47            let event = db.codegen_abi_event(mir_event);
48            events.push(event);
49        }
50    }
51
52    events
53}
54
55pub fn abi_function(db: &dyn CodegenDb, function: FunctionId) -> AbiFunction {
56    // We use a legalized signature.
57    let sig = db.codegen_legalized_signature(function);
58
59    let name = function.name(db.upcast());
60    let args = sig
61        .params
62        .iter()
63        .map(|param| (param.name.to_string(), db.codegen_abi_type(param.ty)))
64        .collect();
65    let ret_ty = sig.return_type.map(|ty| db.codegen_abi_type(ty));
66
67    let func_type = if function.is_contract_init(db.upcast()) {
68        AbiFunctionType::Constructor
69    } else {
70        AbiFunctionType::Function
71    };
72
73    // The "stateMutability" field is derived from the presence & mutability of
74    // `self` and `ctx` params in the analyzer fn sig.
75    let analyzer_sig = sig.analyzer_func_id.signature(db.upcast());
76    let self_param = match analyzer_sig.self_decl {
77        None => SelfParam::None,
78        Some(SelfDecl { mut_: None, .. }) => SelfParam::Imm,
79        Some(SelfDecl { mut_: Some(_), .. }) => SelfParam::Mut,
80    };
81    let ctx_param = match analyzer_sig.ctx_decl {
82        None => CtxParam::None,
83        Some(CtxDecl { mut_: None, .. }) => CtxParam::Imm,
84        Some(CtxDecl { mut_: Some(_), .. }) => CtxParam::Mut,
85    };
86
87    let state_mutability = if name == "__init__" {
88        StateMutability::Payable
89    } else {
90        StateMutability::from_self_and_ctx_params(self_param, ctx_param)
91    };
92
93    AbiFunction::new(func_type, name.to_string(), args, ret_ty, state_mutability)
94}
95
96pub fn abi_function_argument_maximum_size(db: &dyn CodegenDb, function: FunctionId) -> usize {
97    let sig = db.codegen_legalized_signature(function);
98    sig.params.iter().fold(0, |acc, param| {
99        acc + db.codegen_abi_type_maximum_size(param.ty)
100    })
101}
102
103pub fn abi_function_return_maximum_size(db: &dyn CodegenDb, function: FunctionId) -> usize {
104    let sig = db.codegen_legalized_signature(function);
105    sig.return_type
106        .map(|ty| db.codegen_abi_type_maximum_size(ty))
107        .unwrap_or_default()
108}
109
110pub fn abi_type_maximum_size(db: &dyn CodegenDb, ty: TypeId) -> usize {
111    let abi_type = db.codegen_abi_type(ty);
112    if abi_type.is_static() {
113        abi_type.header_size()
114    } else {
115        match &ty.data(db.upcast()).kind {
116            ir::TypeKind::Array(def) if def.elem_ty.data(db.upcast()).kind == ir::TypeKind::U8 => {
117                debug_assert_eq!(abi_type, AbiType::Bytes);
118                64 + ceil_32(def.len)
119            }
120
121            ir::TypeKind::Array(def) => {
122                db.codegen_abi_type_maximum_size(def.elem_ty) * def.len + 32
123            }
124
125            ir::TypeKind::String(len) => abi_type.header_size() + 32 + ceil_32(*len),
126            _ if ty.is_aggregate(db.upcast()) => {
127                let mut maximum = 0;
128                for i in 0..ty.aggregate_field_num(db.upcast()) {
129                    let field_ty = ty.projection_ty_imm(db.upcast(), i);
130                    maximum += db.codegen_abi_type_maximum_size(field_ty)
131                }
132                maximum + 32
133            }
134            ir::TypeKind::MPtr(ty) => abi_type_maximum_size(db, ty.deref(db.upcast())),
135
136            _ => unreachable!(),
137        }
138    }
139}
140
141pub fn abi_type_minimum_size(db: &dyn CodegenDb, ty: TypeId) -> usize {
142    let abi_type = db.codegen_abi_type(ty);
143    if abi_type.is_static() {
144        abi_type.header_size()
145    } else {
146        match &ty.data(db.upcast()).kind {
147            ir::TypeKind::Array(def) if def.elem_ty.data(db.upcast()).kind == ir::TypeKind::U8 => {
148                debug_assert_eq!(abi_type, AbiType::Bytes);
149                64
150            }
151            ir::TypeKind::Array(def) => {
152                db.codegen_abi_type_minimum_size(def.elem_ty) * def.len + 32
153            }
154
155            ir::TypeKind::String(_) => abi_type.header_size() + 32,
156
157            _ if ty.is_aggregate(db.upcast()) => {
158                let mut minimum = 0;
159                for i in 0..ty.aggregate_field_num(db.upcast()) {
160                    let field_ty = ty.projection_ty_imm(db.upcast(), i);
161                    minimum += db.codegen_abi_type_minimum_size(field_ty)
162                }
163                minimum + 32
164            }
165            ir::TypeKind::MPtr(ty) => abi_type_minimum_size(db, ty.deref(db.upcast())),
166            _ => unreachable!(),
167        }
168    }
169}
170
171pub fn abi_type(db: &dyn CodegenDb, ty: TypeId) -> AbiType {
172    let legalized_ty = db.codegen_legalized_type(ty);
173
174    if legalized_ty.is_zero_sized(db.upcast()) {
175        unreachable!("zero-sized type must be removed in legalization");
176    }
177
178    let ty_data = legalized_ty.data(db.upcast());
179
180    match &ty_data.kind {
181        ir::TypeKind::I8 => AbiType::Int(8),
182        ir::TypeKind::I16 => AbiType::Int(16),
183        ir::TypeKind::I32 => AbiType::Int(32),
184        ir::TypeKind::I64 => AbiType::Int(64),
185        ir::TypeKind::I128 => AbiType::Int(128),
186        ir::TypeKind::I256 => AbiType::Int(256),
187        ir::TypeKind::U8 => AbiType::UInt(8),
188        ir::TypeKind::U16 => AbiType::UInt(16),
189        ir::TypeKind::U32 => AbiType::UInt(32),
190        ir::TypeKind::U64 => AbiType::UInt(64),
191        ir::TypeKind::U128 => AbiType::UInt(128),
192        ir::TypeKind::U256 => AbiType::UInt(256),
193        ir::TypeKind::Bool => AbiType::Bool,
194        ir::TypeKind::Address => AbiType::Address,
195        ir::TypeKind::String(_) => AbiType::String,
196        ir::TypeKind::Unit => unreachable!("zero-sized type must be removed in legalization"),
197        ir::TypeKind::Array(def) => {
198            let elem_ty_data = &def.elem_ty.data(db.upcast());
199            match &elem_ty_data.kind {
200                ir::TypeKind::U8 => AbiType::Bytes,
201                _ => {
202                    let elem_ty = db.codegen_abi_type(def.elem_ty);
203                    let len = def.len;
204                    AbiType::Array {
205                        elem_ty: elem_ty.into(),
206                        len,
207                    }
208                }
209            }
210        }
211        ir::TypeKind::Tuple(def) => {
212            let fields = def
213                .items
214                .iter()
215                .enumerate()
216                .map(|(i, item)| {
217                    let field_ty = db.codegen_abi_type(*item);
218                    AbiTupleField::new(format!("{i}"), field_ty)
219                })
220                .collect();
221
222            AbiType::Tuple(fields)
223        }
224        ir::TypeKind::Struct(def) => {
225            let fields = def
226                .fields
227                .iter()
228                .map(|(name, ty)| {
229                    let ty = db.codegen_abi_type(*ty);
230                    AbiTupleField::new(name.to_string(), ty)
231                })
232                .collect();
233
234            AbiType::Tuple(fields)
235        }
236        ir::TypeKind::MPtr(inner) => db.codegen_abi_type(*inner),
237
238        ir::TypeKind::Contract(_)
239        | ir::TypeKind::Map(_)
240        | ir::TypeKind::Enum(_)
241        | ir::TypeKind::SPtr(_) => unreachable!(),
242    }
243}
244
245pub fn abi_event(db: &dyn CodegenDb, ty: TypeId) -> AbiEvent {
246    debug_assert!(ty.is_struct(db.upcast()));
247
248    let legalized_ty = db.codegen_legalized_type(ty);
249    let analyzer_struct = ty
250        .analyzer_ty(db.upcast())
251        .and_then(|val| val.as_struct(db.upcast()))
252        .unwrap();
253    let legalized_ty_data = legalized_ty.data(db.upcast());
254    let event_def = match &legalized_ty_data.kind {
255        ir::TypeKind::Struct(def) => def,
256        _ => unreachable!(),
257    };
258
259    let fields = event_def
260        .fields
261        .iter()
262        .map(|(name, ty)| {
263            let attr = analyzer_struct
264                .field(db.upcast(), name)
265                .unwrap()
266                .attributes(db.upcast());
267
268            let ty = db.codegen_abi_type(*ty);
269            let indexed = attr.iter().any(|attr| attr == INDEXED);
270            AbiEventField::new(name.to_string(), ty, indexed)
271        })
272        .collect();
273
274    AbiEvent::new(event_def.name.to_string(), fields, false)
275}
276
277fn ceil_32(value: usize) -> usize {
278    ((value + 31) / 32) * 32
279}