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 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 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 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}