diff --git a/Cargo.lock b/Cargo.lock index bc7cbab66d..ba7a702548 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -673,6 +673,7 @@ dependencies = [ "num-bigint 0.4.0", "pretty_assertions", "salsa", + "smol_str", "wasm-bindgen-test", "yultsur", ] @@ -1590,6 +1591,15 @@ version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" +[[package]] +name = "smol_str" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61d15c83e300cce35b7c8cd39ff567c1ef42dde6d4a1a38dbdbf9a59902261bd" +dependencies = [ + "serde", +] + [[package]] name = "solc" version = "0.1.0" diff --git a/crates/analyzer/src/context.rs b/crates/analyzer/src/context.rs index 538b0c9394..9f2a470d61 100644 --- a/crates/analyzer/src/context.rs +++ b/crates/analyzer/src/context.rs @@ -263,7 +263,10 @@ impl fmt::Display for ExpressionAttributes { #[derive(Clone, Debug, PartialEq, Eq)] pub enum CallType { BuiltinFunction(GlobalFunction), - BuiltinValueMethod(ValueMethod), + BuiltinValueMethod { + method: ValueMethod, + typ: Type, + }, // create, create2 (will be methods of the context struct soon) BuiltinAssociatedFunction { @@ -294,7 +297,7 @@ impl CallType { use CallType::*; match self { BuiltinFunction(_) - | BuiltinValueMethod(_) + | BuiltinValueMethod { .. } | TypeConstructor(_) | BuiltinAssociatedFunction { .. } => None, AssociatedFunction { function: id, .. } @@ -307,7 +310,7 @@ impl CallType { pub fn function_name(&self, db: &dyn AnalyzerDb) -> String { match self { CallType::BuiltinFunction(f) => f.as_ref().to_string(), - CallType::BuiltinValueMethod(f) => f.as_ref().to_string(), + CallType::BuiltinValueMethod { method, .. } => method.as_ref().to_string(), CallType::BuiltinAssociatedFunction { function, .. } => function.as_ref().to_string(), CallType::AssociatedFunction { function: id, .. } diff --git a/crates/analyzer/src/db.rs b/crates/analyzer/src/db.rs index b2d8fb5169..c28d3da1bb 100644 --- a/crates/analyzer/src/db.rs +++ b/crates/analyzer/src/db.rs @@ -119,8 +119,10 @@ pub trait AnalyzerDb { &self, field: ContractFieldId, ) -> Analysis>; + #[salsa::cycle(queries::contracts::contract_dependency_graph_cycle)] #[salsa::invoke(queries::contracts::contract_dependency_graph)] fn contract_dependency_graph(&self, id: ContractId) -> DepGraphWrapper; + #[salsa::cycle(queries::contracts::contract_runtime_dependency_graph_cycle)] #[salsa::invoke(queries::contracts::contract_runtime_dependency_graph)] fn contract_runtime_dependency_graph(&self, id: ContractId) -> DepGraphWrapper; @@ -129,6 +131,7 @@ pub trait AnalyzerDb { fn function_signature(&self, id: FunctionId) -> Analysis>; #[salsa::invoke(queries::functions::function_body)] fn function_body(&self, id: FunctionId) -> Analysis>; + #[salsa::cycle(queries::functions::function_dependency_graph_cycle)] #[salsa::invoke(queries::functions::function_dependency_graph)] fn function_dependency_graph(&self, id: FunctionId) -> DepGraphWrapper; diff --git a/crates/analyzer/src/db/queries/contracts.rs b/crates/analyzer/src/db/queries/contracts.rs index 8876ea6c94..905b435a11 100644 --- a/crates/analyzer/src/db/queries/contracts.rs +++ b/crates/analyzer/src/db/queries/contracts.rs @@ -283,7 +283,7 @@ pub fn contract_field_type( pub fn contract_dependency_graph(db: &dyn AnalyzerDb, contract: ContractId) -> DepGraphWrapper { // A contract depends on the types of its fields, and the things those types depend on. // Note that this *does not* include the contract's public function graph. - // (See `contract_public_fn_dependency_graph` below) + // (See `contract_runtime_dependency_graph` below) let fields = contract.fields(db); let field_types = fields @@ -313,6 +313,14 @@ pub fn contract_dependency_graph(db: &dyn AnalyzerDb, contract: ContractId) -> D DepGraphWrapper(Rc::new(graph)) } +pub fn contract_dependency_graph_cycle( + _db: &dyn AnalyzerDb, + _cycle: &[String], + _contract: &ContractId, +) -> DepGraphWrapper { + DepGraphWrapper(Rc::new(DepGraph::new())) +} + pub fn contract_runtime_dependency_graph( db: &dyn AnalyzerDb, contract: ContractId, @@ -337,3 +345,11 @@ pub fn contract_runtime_dependency_graph( } DepGraphWrapper(Rc::new(graph)) } + +pub fn contract_runtime_dependency_graph_cycle( + _db: &dyn AnalyzerDb, + _cycle: &[String], + _contract: &ContractId, +) -> DepGraphWrapper { + DepGraphWrapper(Rc::new(DepGraph::new())) +} diff --git a/crates/analyzer/src/db/queries/functions.rs b/crates/analyzer/src/db/queries/functions.rs index 5c03e7537c..a431d26f10 100644 --- a/crates/analyzer/src/db/queries/functions.rs +++ b/crates/analyzer/src/db/queries/functions.rs @@ -294,7 +294,7 @@ pub fn function_dependency_graph(db: &dyn AnalyzerDb, function: FunctionId) -> D )); } // Builtin functions aren't part of the dependency graph yet. - CallType::BuiltinFunction(_) | CallType::BuiltinValueMethod(_) => {} + CallType::BuiltinFunction(_) | CallType::BuiltinValueMethod { .. } => {} } } @@ -323,3 +323,11 @@ pub fn function_dependency_graph(db: &dyn AnalyzerDb, function: FunctionId) -> D } DepGraphWrapper(Rc::new(graph)) } + +pub fn function_dependency_graph_cycle( + _db: &dyn AnalyzerDb, + _cycle: &[String], + _function: &FunctionId, +) -> DepGraphWrapper { + DepGraphWrapper(Rc::new(DepGraph::new())) +} diff --git a/crates/analyzer/src/namespace/items.rs b/crates/analyzer/src/namespace/items.rs index 679a289c51..6720f8cfbb 100644 --- a/crates/analyzer/src/namespace/items.rs +++ b/crates/analyzer/src/namespace/items.rs @@ -154,12 +154,19 @@ impl Item { } pub fn path(&self, db: &dyn AnalyzerDb) -> Rc> { - if let Some(parent) = self.parent(db) { - let mut path = parent.path(db).as_ref().clone(); - path.push(self.name(db)); - Rc::new(path) - } else { - Rc::new(vec![self.name(db)]) + // The path is used to generate a yul identifier, + // eg `foo::Bar::new` becomes `$$foo$Bar$new`. + // Right now, the ingot name is the os path, so it could + // be "my project/src". + // For now, we'll just leave the ingot out of the path, + // because we can only compile a single ingot anyway. + match self.parent(db) { + Some(Item::Ingot(_)) | None => Rc::new(vec![self.name(db)]), + Some(parent) => { + let mut path = parent.path(db).as_ref().clone(); + path.push(self.name(db)); + Rc::new(path) + } } } diff --git a/crates/analyzer/src/traversal/expressions.rs b/crates/analyzer/src/traversal/expressions.rs index 01bd1bd804..39d1e6d40b 100644 --- a/crates/analyzer/src/traversal/expressions.rs +++ b/crates/analyzer/src/traversal/expressions.rs @@ -1480,7 +1480,10 @@ fn expr_call_builtin_value_method( "argument", ); - let calltype = CallType::BuiltinValueMethod(method); + let calltype = CallType::BuiltinValueMethod { + method, + typ: value_attrs.typ.clone(), + }; match method { ValueMethod::Clone => { match value_attrs.location { diff --git a/crates/analyzer/tests/analysis.rs b/crates/analyzer/tests/analysis.rs index d89c93655b..bb8546d14e 100644 --- a/crates/analyzer/tests/analysis.rs +++ b/crates/analyzer/tests/analysis.rs @@ -89,13 +89,20 @@ macro_rules! test_analysis_ingot { .values() .into_iter() .map(|file| { - ( - file.id, - ( - file.clone(), - fe_parser::parse_file(file.id, &file.content).unwrap().0, - ), - ) + let ast = match fe_parser::parse_file(file.id, &file.content) { + Ok((ast, diags)) => { + if !diags.is_empty() { + print_diagnostics(&diags, &files); + panic!("non-fatal parsing error"); + } + ast + } + Err(diags) => { + print_diagnostics(&diags, &files); + panic!("parsing failed"); + } + }; + (file.id, (file.clone(), ast)) }) .collect(), }; diff --git a/crates/analyzer/tests/snapshots/analysis__abi_encoding_stress.snap b/crates/analyzer/tests/snapshots/analysis__abi_encoding_stress.snap index 38100c3e9e..88328c47ae 100644 --- a/crates/analyzer/tests/snapshots/analysis__abi_encoding_stress.snap +++ b/crates/analyzer/tests/snapshots/analysis__abi_encoding_stress.snap @@ -1,6 +1,6 @@ --- source: crates/analyzer/tests/analysis.rs -expression: "build_snapshot(&files, module, &db)" +expression: "build_snapshot(&files, module_id, &db)" --- note: @@ -136,7 +136,7 @@ note: ┌─ stress/abi_encoding_stress.fe:27:16 │ 27 │ return self.my_addrs.to_mem() - │ ^^^^^^^^^^^^^^^^^^^^ BuiltinValueMethod(ToMem) + │ ^^^^^^^^^^^^^^^^^^^^ BuiltinValueMethod { method: ToMem, typ: Array(Array { size: 5, inner: Address }) } note: ┌─ stress/abi_encoding_stress.fe:29:5 @@ -302,7 +302,7 @@ note: ┌─ stress/abi_encoding_stress.fe:39:16 │ 39 │ return self.my_string.to_mem() - │ ^^^^^^^^^^^^^^^^^^^^^ BuiltinValueMethod(ToMem) + │ ^^^^^^^^^^^^^^^^^^^^^ BuiltinValueMethod { method: ToMem, typ: String(FeString { max_size: 10 }) } note: ┌─ stress/abi_encoding_stress.fe:41:5 @@ -397,7 +397,7 @@ note: ┌─ stress/abi_encoding_stress.fe:45:16 │ 45 │ return self.my_u16s.to_mem() - │ ^^^^^^^^^^^^^^^^^^^ BuiltinValueMethod(ToMem) + │ ^^^^^^^^^^^^^^^^^^^ BuiltinValueMethod { method: ToMem, typ: Array(Array { size: 255, inner: Numeric(U16) }) } note: ┌─ stress/abi_encoding_stress.fe:47:5 @@ -565,7 +565,7 @@ note: ┌─ stress/abi_encoding_stress.fe:57:16 │ 57 │ return self.my_bytes.to_mem() - │ ^^^^^^^^^^^^^^^^^^^^ BuiltinValueMethod(ToMem) + │ ^^^^^^^^^^^^^^^^^^^^ BuiltinValueMethod { method: ToMem, typ: Array(Array { size: 100, inner: Numeric(U8) }) } note: ┌─ stress/abi_encoding_stress.fe:59:5 @@ -948,14 +948,14 @@ note: ┌─ stress/abi_encoding_stress.fe:76:22 │ 76 │ my_addrs=self.my_addrs.to_mem(), - │ ^^^^^^^^^^^^^^^^^^^^ BuiltinValueMethod(ToMem) + │ ^^^^^^^^^^^^^^^^^^^^ BuiltinValueMethod { method: ToMem, typ: Array(Array { size: 5, inner: Address }) } 77 │ my_u128=self.my_u128, 78 │ my_string=self.my_string.to_mem(), - │ ^^^^^^^^^^^^^^^^^^^^^ BuiltinValueMethod(ToMem) + │ ^^^^^^^^^^^^^^^^^^^^^ BuiltinValueMethod { method: ToMem, typ: String(FeString { max_size: 10 }) } 79 │ my_u16s=self.my_u16s.to_mem(), - │ ^^^^^^^^^^^^^^^^^^^ BuiltinValueMethod(ToMem) + │ ^^^^^^^^^^^^^^^^^^^ BuiltinValueMethod { method: ToMem, typ: Array(Array { size: 255, inner: Numeric(U16) }) } 80 │ my_bool=self.my_bool, 81 │ my_bytes=self.my_bytes.to_mem() - │ ^^^^^^^^^^^^^^^^^^^^ BuiltinValueMethod(ToMem) + │ ^^^^^^^^^^^^^^^^^^^^ BuiltinValueMethod { method: ToMem, typ: Array(Array { size: 100, inner: Numeric(U8) }) } diff --git a/crates/analyzer/tests/snapshots/analysis__address_bytes10_map.snap b/crates/analyzer/tests/snapshots/analysis__address_bytes10_map.snap index 08a95b14f7..21893c0d5f 100644 --- a/crates/analyzer/tests/snapshots/analysis__address_bytes10_map.snap +++ b/crates/analyzer/tests/snapshots/analysis__address_bytes10_map.snap @@ -1,6 +1,6 @@ --- source: crates/analyzer/tests/analysis.rs -expression: "build_snapshot(&files, module, &db)" +expression: "build_snapshot(&files, module_id, &db)" --- note: @@ -72,7 +72,7 @@ note: ┌─ features/address_bytes10_map.fe:5:16 │ 5 │ return self.bar[key].to_mem() - │ ^^^^^^^^^^^^^^^^^^^^ BuiltinValueMethod(ToMem) + │ ^^^^^^^^^^^^^^^^^^^^ BuiltinValueMethod { method: ToMem, typ: Array(Array { size: 10, inner: Numeric(U8) }) } note: ┌─ features/address_bytes10_map.fe:7:5 diff --git a/crates/analyzer/tests/snapshots/analysis__assert.snap b/crates/analyzer/tests/snapshots/analysis__assert.snap index 4a42247d25..8f48d06e63 100644 --- a/crates/analyzer/tests/snapshots/analysis__assert.snap +++ b/crates/analyzer/tests/snapshots/analysis__assert.snap @@ -1,6 +1,6 @@ --- source: crates/analyzer/tests/analysis.rs -expression: "build_snapshot(\"features/assert.fe\", &src, module, &db)" +expression: "build_snapshot(&files, module_id, &db)" --- note: @@ -247,6 +247,6 @@ note: ┌─ features/assert.fe:20:23 │ 20 │ assert false, self.my_string.to_mem() - │ ^^^^^^^^^^^^^^^^^^^^^ BuiltinValueMethod(ToMem) + │ ^^^^^^^^^^^^^^^^^^^^^ BuiltinValueMethod { method: ToMem, typ: String(FeString { max_size: 5 }) } diff --git a/crates/analyzer/tests/snapshots/analysis__basic_ingot.snap b/crates/analyzer/tests/snapshots/analysis__basic_ingot.snap index 99c88f32c5..ec069d9667 100644 --- a/crates/analyzer/tests/snapshots/analysis__basic_ingot.snap +++ b/crates/analyzer/tests/snapshots/analysis__basic_ingot.snap @@ -10,12 +10,118 @@ note: │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Array +note: + ┌─ ingots/basic_ingot/src/ding/dong.fe:2:3 + │ +2 │ my_address: address + │ ^^^^^^^^^^^^^^^^^^^ address +3 │ my_u256: u256 + │ ^^^^^^^^^^^^^ u256 +4 │ my_i8: i8 + │ ^^^^^^^^^ i8 + + +note: + ┌─ ingots/basic_ingot/src/bar/baz.fe:2:5 + │ +2 │ my_bool: bool + │ ^^^^^^^^^^^^^ bool +3 │ my_u256: u256 + │ ^^^^^^^^^^^^^ u256 + + +note: + ┌─ ingots/basic_ingot/src/bing.fe:2:5 + │ +2 │ my_address: address + │ ^^^^^^^^^^^^^^^^^^^ address + +note: + ┌─ ingots/basic_ingot/src/bing.fe:4:1 + │ +4 │ ╭ fn get_42_backend() -> u256: +5 │ │ return 42 + │ ╰─────────────^ attributes hash: 17979516652885443340 + │ + = FunctionSignature { + self_decl: None, + params: [], + return_type: Ok( + Base( + Numeric( + U256, + ), + ), + ), + } + +note: + ┌─ ingots/basic_ingot/src/bing.fe:5:12 + │ +5 │ return 42 + │ ^^ u256: Value + +note: + ┌─ ingots/basic_ingot/src/bing.fe:8:4 + │ +8 │ ╭ pub fn add(x: u256, y: u256) -> u256: +9 │ │ return x + y + │ ╰───────────────────^ attributes hash: 4022593831796629401 + │ + = FunctionSignature { + self_decl: None, + params: [ + FunctionParam { + name: "x", + typ: Ok( + Base( + Numeric( + U256, + ), + ), + ), + }, + FunctionParam { + name: "y", + typ: Ok( + Base( + Numeric( + U256, + ), + ), + ), + }, + ], + return_type: Ok( + Base( + Numeric( + U256, + ), + ), + ), + } + +note: + ┌─ ingots/basic_ingot/src/bing.fe:9:15 + │ +9 │ return x + y + │ ^ ^ u256: Value + │ │ + │ u256: Value + +note: + ┌─ ingots/basic_ingot/src/bing.fe:9:15 + │ +9 │ return x + y + │ ^^^^^ u256: Value + + note: ┌─ ingots/basic_ingot/src/main.fe:9:5 │ 9 │ ╭ pub fn get_my_baz() -> Baz: 10 │ │ return Baz(my_bool=true, my_u256=26) - │ ╰────────────────────────────────────────────^ attributes hash: 10853850528666400742 + │ ╰────────────────────────────────────────────^ attributes hash: 12775921899186886669 │ = FunctionSignature { self_decl: None, @@ -25,7 +131,7 @@ note: Struct { name: "Baz", id: StructId( - 0, + 1, ), field_count: 2, }, @@ -51,14 +157,14 @@ note: ┌─ ingots/basic_ingot/src/main.fe:10:16 │ 10 │ return Baz(my_bool=true, my_u256=26) - │ ^^^ TypeConstructor(Struct(Struct { name: "Baz", id: StructId(0), field_count: 2 })) + │ ^^^ TypeConstructor(Struct(Struct { name: "Baz", id: StructId(1), field_count: 2 })) note: ┌─ ingots/basic_ingot/src/main.fe:12:5 │ 12 │ ╭ pub fn get_my_bing() -> Bong: 13 │ │ return Bong(my_address=address(42)) - │ ╰───────────────────────────────────────────^ attributes hash: 14834639838018463348 + │ ╰───────────────────────────────────────────^ attributes hash: 9604670028259107253 │ = FunctionSignature { self_decl: None, @@ -68,7 +174,7 @@ note: Struct { name: "Bing", id: StructId( - 1, + 2, ), field_count: 1, }, @@ -104,7 +210,7 @@ note: ┌─ ingots/basic_ingot/src/main.fe:13:16 │ 13 │ return Bong(my_address=address(42)) - │ ^^^^ TypeConstructor(Struct(Struct { name: "Bing", id: StructId(1), field_count: 1 })) + │ ^^^^ TypeConstructor(Struct(Struct { name: "Bing", id: StructId(2), field_count: 1 })) note: ┌─ ingots/basic_ingot/src/main.fe:15:5 @@ -135,59 +241,136 @@ note: ┌─ ingots/basic_ingot/src/main.fe:16:16 │ 16 │ return get_42_backend() - │ ^^^^^^^^^^^^^^ Pure(FunctionId(3)) + │ ^^^^^^^^^^^^^^ Pure(FunctionId(0)) + +note: + ┌─ ingots/basic_ingot/src/main.fe:18:5 + │ +18 │ ╭ pub fn get_my_dyng() -> dong::Dyng: +19 │ │ return dong::Dyng( +20 │ │ my_address=address(8), +21 │ │ my_u256=42, +22 │ │ my_i8=-1 +23 │ │ ) + │ ╰─────────^ attributes hash: 12523642377619379671 + │ + = FunctionSignature { + self_decl: None, + params: [], + return_type: Ok( + Struct( + Struct { + name: "Dyng", + id: StructId( + 0, + ), + field_count: 3, + }, + ), + ), + } +note: + ┌─ ingots/basic_ingot/src/main.fe:20:32 + │ +20 │ my_address=address(8), + │ ^ u256: Value note: - ┌─ ingots/basic_ingot/src/ding/dong.fe:2:3 - │ -2 │ my_address: address - │ ^^^^^^^^^^^^^^^^^^^ address -3 │ my_u256: u256 - │ ^^^^^^^^^^^^^ u256 -4 │ my_i8: i8 - │ ^^^^^^^^^ i8 + ┌─ ingots/basic_ingot/src/main.fe:20:24 + │ +20 │ my_address=address(8), + │ ^^^^^^^^^^ address: Value +21 │ my_u256=42, + │ ^^ u256: Value +22 │ my_i8=-1 + │ ^ u256: Value +note: + ┌─ ingots/basic_ingot/src/main.fe:22:19 + │ +22 │ my_i8=-1 + │ ^^ i8: Value note: - ┌─ ingots/basic_ingot/src/bar/baz.fe:2:5 - │ -2 │ my_bool: bool - │ ^^^^^^^^^^^^^ bool -3 │ my_u256: u256 - │ ^^^^^^^^^^^^^ u256 + ┌─ ingots/basic_ingot/src/main.fe:19:16 + │ +19 │ return dong::Dyng( + │ ╭────────────────^ +20 │ │ my_address=address(8), +21 │ │ my_u256=42, +22 │ │ my_i8=-1 +23 │ │ ) + │ ╰─────────^ Dyng: Memory +note: + ┌─ ingots/basic_ingot/src/main.fe:20:24 + │ +20 │ my_address=address(8), + │ ^^^^^^^ TypeConstructor(Base(Address)) note: - ┌─ ingots/basic_ingot/src/bing.fe:2:5 - │ -2 │ my_address: address - │ ^^^^^^^^^^^^^^^^^^^ address + ┌─ ingots/basic_ingot/src/main.fe:19:16 + │ +19 │ return dong::Dyng( + │ ^^^^^^^^^^ TypeConstructor(Struct(Struct { name: "Dyng", id: StructId(0), field_count: 3 })) note: - ┌─ ingots/basic_ingot/src/bing.fe:4:1 - │ -4 │ ╭ fn get_42_backend() -> u256: -5 │ │ return 42 - │ ╰─────────────^ attributes hash: 17979516652885443340 - │ - = FunctionSignature { - self_decl: None, - params: [], - return_type: Ok( - Base( - Numeric( - U256, - ), - ), - ), - } + ┌─ ingots/basic_ingot/src/main.fe:25:5 + │ +25 │ ╭ pub fn create_bing_contract() -> u256: +26 │ │ let bing: BingContract = BingContract.create(0) +27 │ │ return bing.add(40, 50) + │ ╰───────────────────────────────^ attributes hash: 17979516652885443340 + │ + = FunctionSignature { + self_decl: None, + params: [], + return_type: Ok( + Base( + Numeric( + U256, + ), + ), + ), + } note: - ┌─ ingots/basic_ingot/src/bing.fe:5:12 - │ -5 │ return 42 - │ ^^ u256: Value + ┌─ ingots/basic_ingot/src/main.fe:26:19 + │ +26 │ let bing: BingContract = BingContract.create(0) + │ ^^^^^^^^^^^^ BingContract + +note: + ┌─ ingots/basic_ingot/src/main.fe:26:54 + │ +26 │ let bing: BingContract = BingContract.create(0) + │ ^ u256: Value + +note: + ┌─ ingots/basic_ingot/src/main.fe:26:34 + │ +26 │ let bing: BingContract = BingContract.create(0) + │ ^^^^^^^^^^^^^^^^^^^^^^ BingContract: Value +27 │ return bing.add(40, 50) + │ ^^^^ ^^ ^^ u256: Value + │ │ │ + │ │ u256: Value + │ BingContract: Value + +note: + ┌─ ingots/basic_ingot/src/main.fe:27:16 + │ +27 │ return bing.add(40, 50) + │ ^^^^^^^^^^^^^^^^ u256: Value + +note: + ┌─ ingots/basic_ingot/src/main.fe:26:34 + │ +26 │ let bing: BingContract = BingContract.create(0) + │ ^^^^^^^^^^^^^^^^^^^ BuiltinAssociatedFunction { contract: ContractId(0), function: Create } +27 │ return bing.add(40, 50) + │ ^^^^^^^^ External { contract: ContractId(0), function: FunctionId(1) } diff --git a/crates/analyzer/tests/snapshots/analysis__data_copying_stress.snap b/crates/analyzer/tests/snapshots/analysis__data_copying_stress.snap index 0f8adf8804..896144c649 100644 --- a/crates/analyzer/tests/snapshots/analysis__data_copying_stress.snap +++ b/crates/analyzer/tests/snapshots/analysis__data_copying_stress.snap @@ -1,6 +1,6 @@ --- source: crates/analyzer/tests/analysis.rs -expression: "build_snapshot(&files, module, &db)" +expression: "build_snapshot(&files, module_id, &db)" --- note: @@ -506,7 +506,7 @@ note: ┌─ stress/data_copying_stress.fe:52:16 │ 52 │ return my_array.clone() - │ ^^^^^^^^^^^^^^ BuiltinValueMethod(Clone) + │ ^^^^^^^^^^^^^^ BuiltinValueMethod { method: Clone, typ: Array(Array { size: 10, inner: Numeric(U256) }) } note: ┌─ stress/data_copying_stress.fe:54:5 @@ -573,7 +573,7 @@ note: ┌─ stress/data_copying_stress.fe:55:9 │ 55 │ my_array.clone()[3] = 5 - │ ^^^^^^^^^^^^^^ BuiltinValueMethod(Clone) + │ ^^^^^^^^^^^^^^ BuiltinValueMethod { method: Clone, typ: Array(Array { size: 10, inner: Numeric(U256) }) } note: ┌─ stress/data_copying_stress.fe:58:5 @@ -726,7 +726,7 @@ note: ┌─ stress/data_copying_stress.fe:65:23 │ 65 │ my_nums_mem = self.my_nums.to_mem() - │ ^^^^^^^^^^^^^^^^^^^ BuiltinValueMethod(ToMem) + │ ^^^^^^^^^^^^^^^^^^^ BuiltinValueMethod { method: ToMem, typ: Array(Array { size: 5, inner: Numeric(U256) }) } note: ┌─ stress/data_copying_stress.fe:68:5 @@ -795,9 +795,9 @@ note: ┌─ stress/data_copying_stress.fe:70:13 │ 70 │ self.my_string.to_mem(), - │ ^^^^^^^^^^^^^^^^^^^^^ BuiltinValueMethod(ToMem) + │ ^^^^^^^^^^^^^^^^^^^^^ BuiltinValueMethod { method: ToMem, typ: String(FeString { max_size: 42 }) } 71 │ self.my_u256.to_mem() - │ ^^^^^^^^^^^^^^^^^^^ BuiltinValueMethod(ToMem) + │ ^^^^^^^^^^^^^^^^^^^ BuiltinValueMethod { method: ToMem, typ: Base(Numeric(U256)) } note: ┌─ stress/data_copying_stress.fe:69:9 diff --git a/crates/analyzer/tests/snapshots/analysis__erc20_token.snap b/crates/analyzer/tests/snapshots/analysis__erc20_token.snap index 29b84b87ab..bd07045b67 100644 --- a/crates/analyzer/tests/snapshots/analysis__erc20_token.snap +++ b/crates/analyzer/tests/snapshots/analysis__erc20_token.snap @@ -1,6 +1,6 @@ --- source: crates/analyzer/tests/analysis.rs -expression: "build_snapshot(&files, module, &db)" +expression: "build_snapshot(&files, module_id, &db)" --- note: @@ -82,7 +82,7 @@ note: ┌─ demos/erc20_token.fe:26:16 │ 26 │ return self._name.to_mem() - │ ^^^^^^^^^^^^^^^^^ BuiltinValueMethod(ToMem) + │ ^^^^^^^^^^^^^^^^^ BuiltinValueMethod { method: ToMem, typ: String(FeString { max_size: 100 }) } note: ┌─ demos/erc20_token.fe:28:5 @@ -127,7 +127,7 @@ note: ┌─ demos/erc20_token.fe:29:16 │ 29 │ return self._symbol.to_mem() - │ ^^^^^^^^^^^^^^^^^^^ BuiltinValueMethod(ToMem) + │ ^^^^^^^^^^^^^^^^^^^ BuiltinValueMethod { method: ToMem, typ: String(FeString { max_size: 100 }) } note: ┌─ demos/erc20_token.fe:31:5 diff --git a/crates/analyzer/tests/snapshots/analysis__guest_book.snap b/crates/analyzer/tests/snapshots/analysis__guest_book.snap index da7471f01b..87939240f5 100644 --- a/crates/analyzer/tests/snapshots/analysis__guest_book.snap +++ b/crates/analyzer/tests/snapshots/analysis__guest_book.snap @@ -1,6 +1,6 @@ --- source: crates/analyzer/tests/analysis.rs -expression: "build_snapshot(\"demos/guest_book.fe\", &src, module, &db)" +expression: "build_snapshot(&files, module_id, &db)" --- note: @@ -159,6 +159,6 @@ note: ┌─ demos/guest_book.fe:21:16 │ 21 │ return self.messages[addr].to_mem() - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^ BuiltinValueMethod(ToMem) + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^ BuiltinValueMethod { method: ToMem, typ: String(FeString { max_size: 100 }) } diff --git a/crates/analyzer/tests/snapshots/analysis__revert.snap b/crates/analyzer/tests/snapshots/analysis__revert.snap index af681a7c4c..93d79d7902 100644 --- a/crates/analyzer/tests/snapshots/analysis__revert.snap +++ b/crates/analyzer/tests/snapshots/analysis__revert.snap @@ -1,6 +1,6 @@ --- source: crates/analyzer/tests/analysis.rs -expression: "build_snapshot(\"features/revert.fe\", &src, module, &db)" +expression: "build_snapshot(&files, module_id, &db)" --- note: @@ -179,6 +179,6 @@ note: 22 │ self.my_other_error = OtherError(msg=1, val=true) │ ^^^^^^^^^^ TypeConstructor(Struct(Struct { name: "OtherError", id: StructId(1), field_count: 2 })) 23 │ revert self.my_other_error.to_mem() - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^ BuiltinValueMethod(ToMem) + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^ BuiltinValueMethod { method: ToMem, typ: Struct(Struct { name: "OtherError", id: StructId(1), field_count: 2 }) } diff --git a/crates/analyzer/tests/snapshots/analysis__sized_vals_in_sto.snap b/crates/analyzer/tests/snapshots/analysis__sized_vals_in_sto.snap index de9fb17c0f..1dc8effc3f 100644 --- a/crates/analyzer/tests/snapshots/analysis__sized_vals_in_sto.snap +++ b/crates/analyzer/tests/snapshots/analysis__sized_vals_in_sto.snap @@ -1,6 +1,6 @@ --- source: crates/analyzer/tests/analysis.rs -expression: "build_snapshot(&files, module, &db)" +expression: "build_snapshot(&files, module_id, &db)" --- note: @@ -193,7 +193,7 @@ note: ┌─ features/sized_vals_in_sto.fe:21:16 │ 21 │ return self.nums.to_mem() - │ ^^^^^^^^^^^^^^^^ BuiltinValueMethod(ToMem) + │ ^^^^^^^^^^^^^^^^ BuiltinValueMethod { method: ToMem, typ: Array(Array { size: 42, inner: Numeric(U256) }) } note: ┌─ features/sized_vals_in_sto.fe:23:5 @@ -282,7 +282,7 @@ note: ┌─ features/sized_vals_in_sto.fe:27:16 │ 27 │ return self.str.to_mem() - │ ^^^^^^^^^^^^^^^ BuiltinValueMethod(ToMem) + │ ^^^^^^^^^^^^^^^ BuiltinValueMethod { method: ToMem, typ: String(FeString { max_size: 26 }) } note: ┌─ features/sized_vals_in_sto.fe:29:5 @@ -403,8 +403,8 @@ note: ┌─ features/sized_vals_in_sto.fe:32:18 │ 32 │ nums=self.nums.to_mem(), - │ ^^^^^^^^^^^^^^^^ BuiltinValueMethod(ToMem) + │ ^^^^^^^^^^^^^^^^ BuiltinValueMethod { method: ToMem, typ: Array(Array { size: 42, inner: Numeric(U256) }) } 33 │ str=self.str.to_mem() - │ ^^^^^^^^^^^^^^^ BuiltinValueMethod(ToMem) + │ ^^^^^^^^^^^^^^^ BuiltinValueMethod { method: ToMem, typ: String(FeString { max_size: 26 }) } diff --git a/crates/analyzer/tests/snapshots/analysis__structs.snap b/crates/analyzer/tests/snapshots/analysis__structs.snap index 6d9ad8cfff..ada9ad3b5c 100644 --- a/crates/analyzer/tests/snapshots/analysis__structs.snap +++ b/crates/analyzer/tests/snapshots/analysis__structs.snap @@ -1,6 +1,6 @@ --- source: crates/analyzer/tests/analysis.rs -expression: "build_snapshot(&files, module, &db)" +expression: "build_snapshot(&files, module_id, &db)" --- note: @@ -55,7 +55,7 @@ note: ┌─ features/structs.fe:8:16 │ 8 │ return self.abi_encode() - │ ^^^^^^^^^^^^^^^ BuiltinValueMethod(AbiEncode) + │ ^^^^^^^^^^^^^^^ BuiltinValueMethod { method: AbiEncode, typ: Struct(Struct { name: "House", id: StructId(0), field_count: 4 }) } note: ┌─ features/structs.fe:10:5 @@ -300,7 +300,7 @@ note: ┌─ features/structs.fe:27:16 │ 27 │ return self.my_house.to_mem() - │ ^^^^^^^^^^^^^^^^^^^^ BuiltinValueMethod(ToMem) + │ ^^^^^^^^^^^^^^^^^^^^ BuiltinValueMethod { method: ToMem, typ: Struct(Struct { name: "House", id: StructId(0), field_count: 4 }) } note: ┌─ features/structs.fe:29:5 diff --git a/crates/analyzer/tests/snapshots/analysis__tuple_stress.snap b/crates/analyzer/tests/snapshots/analysis__tuple_stress.snap index cce24129f5..bfeb244940 100644 --- a/crates/analyzer/tests/snapshots/analysis__tuple_stress.snap +++ b/crates/analyzer/tests/snapshots/analysis__tuple_stress.snap @@ -1,6 +1,6 @@ --- source: crates/analyzer/tests/analysis.rs -expression: "build_snapshot(&files, module, &db)" +expression: "build_snapshot(&files, module_id, &db)" --- note: @@ -611,7 +611,7 @@ note: ┌─ stress/tuple_stress.fe:34:16 │ 34 │ return self.my_sto_tuple.to_mem() - │ ^^^^^^^^^^^^^^^^^^^^^^^^ BuiltinValueMethod(ToMem) + │ ^^^^^^^^^^^^^^^^^^^^^^^^ BuiltinValueMethod { method: ToMem, typ: Tuple(Tuple { items: [Base(Numeric(U256)), Base(Numeric(I32))] }) } note: ┌─ stress/tuple_stress.fe:36:5 @@ -785,6 +785,6 @@ note: ┌─ stress/tuple_stress.fe:46:16 │ 46 │ return my_tuple.abi_encode() - │ ^^^^^^^^^^^^^^^^^^^ BuiltinValueMethod(AbiEncode) + │ ^^^^^^^^^^^^^^^^^^^ BuiltinValueMethod { method: AbiEncode, typ: Tuple(Tuple { items: [Base(Numeric(U256)), Base(Bool), Base(Address)] }) } diff --git a/crates/analyzer/tests/snapshots/analysis__type_aliases.snap b/crates/analyzer/tests/snapshots/analysis__type_aliases.snap index f2e344ca54..dc9a12fca6 100644 --- a/crates/analyzer/tests/snapshots/analysis__type_aliases.snap +++ b/crates/analyzer/tests/snapshots/analysis__type_aliases.snap @@ -1,6 +1,6 @@ --- source: crates/analyzer/tests/analysis.rs -expression: "build_snapshot(\"features/type_aliases.fe\", &src, module, &db)" +expression: "build_snapshot(&files, module_id, &db)" --- note: @@ -303,6 +303,6 @@ note: ┌─ features/type_aliases.fe:28:16 │ 28 │ return self.posts[id].to_mem() - │ ^^^^^^^^^^^^^^^^^^^^^ BuiltinValueMethod(ToMem) + │ ^^^^^^^^^^^^^^^^^^^^^ BuiltinValueMethod { method: ToMem, typ: String(FeString { max_size: 32 }) } diff --git a/crates/analyzer/tests/snapshots/analysis__uniswap.snap b/crates/analyzer/tests/snapshots/analysis__uniswap.snap index 8f9790c453..e54a08d2b9 100644 --- a/crates/analyzer/tests/snapshots/analysis__uniswap.snap +++ b/crates/analyzer/tests/snapshots/analysis__uniswap.snap @@ -4570,7 +4570,7 @@ note: │ ^^^^^^^ TypeConstructor(Base(Address)) 318 │ 319 │ let salt: u256 = keccak256((token0, token1).abi_encode()) - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ BuiltinValueMethod(AbiEncode) + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ BuiltinValueMethod { method: AbiEncode, typ: Tuple(Tuple { items: [Base(Address), Base(Address)] }) } note: ┌─ demos/uniswap.fe:319:26 diff --git a/crates/driver/src/lib.rs b/crates/driver/src/lib.rs index dae3feb347..6375f66091 100644 --- a/crates/driver/src/lib.rs +++ b/crates/driver/src/lib.rs @@ -54,7 +54,7 @@ pub fn compile_module( let module = Module { name: Path::new(&file.name) - .file_name() + .file_stem() .expect("missing file name") .to_string_lossy() .to_string(), diff --git a/crates/fe/src/main.rs b/crates/fe/src/main.rs index 9002bed945..bc2a84f907 100644 --- a/crates/fe/src/main.rs +++ b/crates/fe/src/main.rs @@ -108,6 +108,8 @@ pub fn main() { }; (content, compiled_module) } else { + dbg!(input_path); + let files = build_ingot_filestore_for_dir(input_path); if !Path::new(input_path).exists() { diff --git a/crates/test-files/fixtures/ingots/basic_ingot/src/bing.fe b/crates/test-files/fixtures/ingots/basic_ingot/src/bing.fe index a6ecad4944..81dda51a14 100644 --- a/crates/test-files/fixtures/ingots/basic_ingot/src/bing.fe +++ b/crates/test-files/fixtures/ingots/basic_ingot/src/bing.fe @@ -4,7 +4,6 @@ struct Bing: fn get_42_backend() -> u256: return 42 -# currently disallowed -#contract BingContract: -# pub fn foo(): -# pass \ No newline at end of file +contract BingContract: + pub fn add(x: u256, y: u256) -> u256: + return x + y diff --git a/crates/test-files/fixtures/ingots/basic_ingot/src/ding/dong.fe b/crates/test-files/fixtures/ingots/basic_ingot/src/ding/dong.fe index e17327dfbf..bb60a2e6ee 100644 --- a/crates/test-files/fixtures/ingots/basic_ingot/src/ding/dong.fe +++ b/crates/test-files/fixtures/ingots/basic_ingot/src/ding/dong.fe @@ -1,4 +1,4 @@ -struct Dong: +struct Dyng: my_address: address my_u256: u256 my_i8: i8 \ No newline at end of file diff --git a/crates/test-files/fixtures/ingots/basic_ingot/src/main.fe b/crates/test-files/fixtures/ingots/basic_ingot/src/main.fe index 9a39cdf0a2..efd014263c 100644 --- a/crates/test-files/fixtures/ingots/basic_ingot/src/main.fe +++ b/crates/test-files/fixtures/ingots/basic_ingot/src/main.fe @@ -3,7 +3,7 @@ use bing::Bing as Bong use bing::get_42_backend use ding::{dang::Dang as Dung, dong} -#use bing::BingContract +use bing::BingContract contract Foo: pub fn get_my_baz() -> Baz: @@ -15,12 +15,13 @@ contract Foo: pub fn get_42() -> u256: return get_42_backend() -# pub fn get_my_dong() -> dong::Dong: -# return dong::Dong( -# my_address=address(26), -# my_u256=42, -# my_i8=-1 -# ) + pub fn get_my_dyng() -> dong::Dyng: + return dong::Dyng( + my_address=address(8), + my_u256=42, + my_i8=-1 + ) -# pub fn create_bing_contract(): -# BingContract.create(0) \ No newline at end of file + pub fn create_bing_contract() -> u256: + let bing: BingContract = BingContract.create(0) + return bing.add(40, 50) \ No newline at end of file diff --git a/crates/test-utils/src/lib.rs b/crates/test-utils/src/lib.rs index da4ae83c99..83ca523a4a 100644 --- a/crates/test-utils/src/lib.rs +++ b/crates/test-utils/src/lib.rs @@ -93,7 +93,12 @@ impl ContractHarness { output: Option<ðabi::Token>, ) { let actual_output = self.call_function(executor, name, input); - assert_eq!(output.map(|token| token.to_owned()), actual_output) + assert_eq!( + output.map(|token| token.to_owned()), + actual_output, + "unexpected output from `fn {}`", + name + ) } pub fn call_function( diff --git a/crates/tests/src/ingots.rs b/crates/tests/src/ingots.rs index 5a5b1bf4d2..ec623b38a8 100644 --- a/crates/tests/src/ingots.rs +++ b/crates/tests/src/ingots.rs @@ -29,5 +29,21 @@ fn test_basic_ingot() { ); harness.test_function(&mut executor, "get_42", &[], Some(&uint_token(42))); + harness.test_function( + &mut executor, + "get_my_dyng", + &[], + Some(&tuple_token(&[ + address_token("8"), + uint_token(42), + int_token(-1), + ])), + ); + harness.test_function( + &mut executor, + "create_bing_contract", + &[], + Some(&uint_token(90)), + ); }) } diff --git a/crates/tests/src/runtime.rs b/crates/tests/src/runtime.rs index 51d9e4a069..e5fb7d3d6d 100644 --- a/crates/tests/src/runtime.rs +++ b/crates/tests/src/runtime.rs @@ -2,9 +2,7 @@ #![cfg(feature = "solc-backend")] -use fe_analyzer::namespace::types::{Base, FixedSize, Integer}; use fe_compiler_test_utils::*; -use fe_yulgen::runtime::functions; use yultsur::*; macro_rules! assert_eq { @@ -295,73 +293,6 @@ fn test_runtime_set_zero() { }) } -#[test] -fn test_runtime_house_struct() { - let house_api = functions::structs::struct_apis( - "House", - &[ - ( - "price".to_string(), - FixedSize::Base(Base::Numeric(Integer::U256)), - ), - ( - "size".to_string(), - FixedSize::Base(Base::Numeric(Integer::U256)), - ), - ( - "rooms".to_string(), - FixedSize::Base(Base::Numeric(Integer::U8)), - ), - ("vacant".to_string(), FixedSize::Base(Base::Bool)), - ], - ); - - with_executor(&|mut executor| { - Runtime::new().with_functions([functions::std(), house_api.clone()].concat()).with_test_statements( - statements! { - (let price := 42) - (let size := 26) - (let rooms := 5) - (let vacant := true) - - (let house := struct_House_new(price, size, rooms, vacant)) - - // For now, all values in a struct occupy a full word. Here we test - // that the word at the given word offset contains the correct 32 - // byte value. - // - // This test confirms that each value is being stored right-aligned - // and in the correct word. - [assert_eq!(price, (mload(house)))] - [assert_eq!(size, (mload((add(house, 32)))))] - [assert_eq!(rooms, (mload((add(house, 64)))))] - [assert_eq!(vacant, (mload((add(house, 96)))))] - - // To retrieve an individual struct value, we need a pointer that - // references the start of the value. - [assert_eq!(price, (mloadn((struct_House_get_price_ptr(house)), 32)))] - [assert_eq!(size, (mloadn((struct_House_get_size_ptr(house)), 32)))] - [assert_eq!(rooms, (mloadn((struct_House_get_rooms_ptr(house)), 1)))] - [assert_eq!(vacant, (mloadn((struct_House_get_vacant_ptr(house)), 1)))] - - - // We test the same thing in storage. - - // Note that the storage pointer for `house` is a multiple of 32. - (let house_storage := 2048) - (bytes_mcopys(house, house_storage, 128)) - - [assert_eq!(price, (bytes_sloadn((struct_House_get_price_ptr(house_storage)), 32)))] - [assert_eq!(size, (bytes_sloadn((struct_House_get_size_ptr(house_storage)), 32)))] - [assert_eq!(rooms, (bytes_sloadn((struct_House_get_rooms_ptr(house_storage)), 1)))] - [assert_eq!(vacant, (bytes_sloadn((struct_House_get_vacant_ptr(house_storage)), 1)))] - }, - ) - .execute(&mut executor) - .expect_success(); - }) -} - #[test] fn checked_exp_signed() { with_executor(&|mut executor| { diff --git a/crates/yulgen/Cargo.toml b/crates/yulgen/Cargo.toml index 2381aa782d..852d3a5920 100644 --- a/crates/yulgen/Cargo.toml +++ b/crates/yulgen/Cargo.toml @@ -19,6 +19,7 @@ salsa = "0.16.1" # This fork contains the shorthand macros and some other necessary updates. yultsur = { git = "https://github.com/g-r-a-n-t/yultsur"} +smol_str = "0.1.21" [dev-dependencies] insta = "1.7.1" diff --git a/crates/yulgen/src/context.rs b/crates/yulgen/src/context.rs index b5c0bd0db3..c54505d033 100644 --- a/crates/yulgen/src/context.rs +++ b/crates/yulgen/src/context.rs @@ -6,16 +6,16 @@ use fe_parser::node::Node; use std::rc::Rc; pub struct FnContext<'a> { - pub db: &'a dyn AnalyzerDb, // XXX - pub ydb: &'a dyn YulgenDb, + pub adb: &'a dyn AnalyzerDb, + pub db: &'a dyn YulgenDb, fn_body: Rc, } impl<'a> FnContext<'a> { pub fn new(db: &'a dyn YulgenDb, fn_body: Rc) -> Self { Self { - db: db.upcast(), - ydb: db, + adb: db.upcast(), + db, fn_body, } } @@ -40,6 +40,6 @@ impl<'a> FnContext<'a> { self.fn_body .emits .get(&emit_stmt.id) - .map(|event| event.typ(self.db)) + .map(|event| event.typ(self.adb)) } } diff --git a/crates/yulgen/src/db.rs b/crates/yulgen/src/db.rs index 3e71ad8958..da52484909 100644 --- a/crates/yulgen/src/db.rs +++ b/crates/yulgen/src/db.rs @@ -4,6 +4,7 @@ use fe_analyzer::AnalyzerDb; use fe_common::Upcast; use fe_lowering::LoweringDb; use indexmap::{IndexMap, IndexSet}; +use smol_str::SmolStr; use std::rc::Rc; use yultsur::yul; @@ -22,21 +23,19 @@ pub trait YulgenDb: // fn contract_constructor(&self, module_id: ModuleId) -> #[salsa::invoke(queries::functions::function_yul_name)] - fn function_yul_name(&self, function: FunctionId) -> Rc; + fn function_yul_name(&self, function: FunctionId) -> SmolStr; + #[salsa::invoke(queries::functions::function_external_call_name)] + fn function_external_call_name(&self, function: FunctionId) -> SmolStr; + #[salsa::invoke(queries::functions::function_external_call_fn)] + fn function_external_call_fn(&self, function: FunctionId) -> Vec; #[salsa::invoke(queries::functions::function_def)] fn function_def(&self, function: FunctionId) -> yul::Statement; #[salsa::invoke(queries::functions::function_sig_abi_types)] fn function_sig_abi_types(&self, function: FunctionId) -> (Rc>, Option); - #[salsa::invoke(queries::functions::created_contracts)] - fn function_created_contracts(&self, function: FunctionId) -> Rc>; - #[salsa::invoke(queries::functions::external_calls)] - fn function_external_calls(&self, function: FunctionId) -> Rc>; #[salsa::invoke(queries::functions::assert_string_types)] fn function_assert_string_types(&self, function: FunctionId) -> Rc>; #[salsa::invoke(queries::functions::revert_types)] fn function_revert_errors(&self, function: FunctionId) -> Rc>; - #[salsa::invoke(queries::functions::string_literals)] - fn function_string_literals(&self, function: FunctionId) -> Rc>; #[salsa::invoke(queries::events::event_idx_abi_types)] fn event_idx_abi_types(&self, event: EventId) -> Rc>; @@ -45,6 +44,18 @@ pub trait YulgenDb: fn struct_abi_type(&self, id: StructId) -> AbiType; #[salsa::invoke(queries::structs::struct_field_abi_types)] fn struct_field_abi_types(&self, id: StructId) -> Rc>; + #[salsa::invoke(queries::structs::struct_qualified_name)] + fn struct_qualified_name(&self, id: StructId) -> SmolStr; + #[salsa::invoke(queries::structs::struct_getter_name)] + fn struct_getter_name(&self, id: StructId, field: SmolStr) -> SmolStr; + #[salsa::invoke(queries::structs::struct_getter_fn)] + fn struct_getter_fn(&self, id: StructId, field: SmolStr) -> yul::Statement; + #[salsa::invoke(queries::structs::struct_init_name)] + fn struct_init_name(&self, id: StructId) -> SmolStr; + #[salsa::invoke(queries::structs::struct_init_fn)] + fn struct_init_fn(&self, id: StructId) -> yul::Statement; + #[salsa::invoke(queries::structs::struct_api_fns)] + fn struct_api_fns(&self, id: StructId) -> Vec; } #[salsa::database( diff --git a/crates/yulgen/src/db/queries/contracts.rs b/crates/yulgen/src/db/queries/contracts.rs index 4be290c6c6..0420a2b062 100644 --- a/crates/yulgen/src/db/queries/contracts.rs +++ b/crates/yulgen/src/db/queries/contracts.rs @@ -1,12 +1,14 @@ use crate::constructor; use crate::db::YulgenDb; use crate::runtime::{self, functions}; -use crate::types::{AbiDecodeLocation, AbiType}; -use fe_analyzer::namespace::items::{ - walk_local_dependencies, ContractId, DepGraph, FunctionId, Item, StructId, TypeDef, -}; +use crate::types::{AbiDecodeLocation, AsAbiType}; +use fe_analyzer::builtins::ValueMethod; +use fe_analyzer::context::CallType; +use fe_analyzer::namespace::items::{walk_local_dependencies, ContractId, DepGraph, Item, TypeDef}; +use fe_analyzer::namespace::types::FixedSize; use fe_common::utils::keccak; use indexmap::IndexSet; +use std::convert::TryInto; use yultsur::*; pub fn contract_object(db: &dyn YulgenDb, contract: ContractId) -> yul::Object { @@ -77,9 +79,6 @@ fn build_dependency_graph( // for that contract, so we need to include encode/decode fns for the pub fns. let compiling_contract_runtime = root.as_contract().is_some(); - let mut external_calls = IndexSet::::new(); - let mut assert_string_types = IndexSet::::new(); - let mut revert_error_types = IndexSet::::new(); let mut string_literals = IndexSet::::new(); let mut created_contracts = IndexSet::::new(); @@ -92,13 +91,41 @@ fn build_dependency_graph( Item::Function(function) => { yulfns.push(db.function_def(function)); - // Collect the extra bits that we'll handle at the end - external_calls.extend(db.function_external_calls(function).iter()); - created_contracts.extend(db.function_created_contracts(function).iter()); - string_literals.extend(db.function_string_literals(function).iter().cloned()); - assert_string_types - .extend(db.function_assert_string_types(function).iter().cloned()); - revert_error_types.extend(db.function_revert_errors(function).iter()); + let body = function.body(adb); + for calltype in body.calls.values() { + match calltype { + CallType::External { function: fun, .. } => { + yulfns.extend(db.function_external_call_fn(*fun)); + } + CallType::BuiltinValueMethod { + method: ValueMethod::AbiEncode, + typ, + } => { + let typ: FixedSize = typ + .clone() + .try_into() + .expect("abi_encode non-fixedsize type"); + yulfns.push(functions::abi::encode(&[typ.as_abi_type(adb)])); + } + CallType::BuiltinAssociatedFunction { contract, .. } => { + created_contracts.insert(*contract); + } + _ => {} + } + } + + for struct_ in db.function_revert_errors(function).iter() { + yulfns.push(functions::abi::encode(&[db.struct_abi_type(*struct_)])); + yulfns.push(functions::revert::revert( + &struct_.name(adb), + &db.struct_abi_type(*struct_), + )); + } + for string_type in db.function_assert_string_types(function).iter() { + yulfns.push(functions::revert::error_revert(&string_type)); + yulfns.push(functions::abi::encode(&[string_type.clone()])); + } + string_literals.extend(body.string_literals.iter().cloned()); if compiling_contract_runtime && matches!(function.parent(adb), Item::Type(TypeDef::Contract(_))) @@ -119,21 +146,9 @@ fn build_dependency_graph( } } Item::Type(TypeDef::Struct(struct_)) => { - let fields = struct_ - .fields(adb) - .iter() - .map(|(name, id)| { - ( - name.to_string(), - id.typ(adb).expect("struct field type error"), - ) - }) - .collect::>(); - // We don't know which struct fields are actually accessed, so we need - // getter/setter functions for all of them. - yulfns.extend(functions::structs::struct_apis(&struct_.name(adb), &fields)); - // XXX old build fn adds struct abi type encoder fn + // accessor functions for all of them. + yulfns.extend(db.struct_api_fns(struct_)); } Item::Event(event) => { yulfns.push(functions::abi::encode(&db.event_idx_abi_types(event))); @@ -142,31 +157,6 @@ fn build_dependency_graph( } }); - for fun in external_calls { - yulfns.push(functions::contracts::call(db, fun)); - let (param_types, ret_type) = db.function_sig_abi_types(fun); - yulfns.push(functions::abi::encode(¶m_types)); - if let Some(ret_type) = ret_type { - yulfns.extend(functions::abi::decode_functions( - &[ret_type], - AbiDecodeLocation::Memory, - )); - } - } - for string_type in assert_string_types { - yulfns.push(functions::revert::error_revert(&string_type)); - yulfns.push(functions::abi::encode(&[string_type])); - } - for struct_ in revert_error_types { - yulfns.push(functions::abi::encode(&[db.struct_abi_type(struct_)])); - yulfns.push(functions::revert::revert( - &struct_.name(adb), - &db.struct_abi_type(struct_), - )); - } - // XXX only include once - yulfns.push(functions::abi::encode(&[AbiType::Uint { size: 32 }])); - yulfns.sort(); yulfns.dedup(); diff --git a/crates/yulgen/src/db/queries/functions.rs b/crates/yulgen/src/db/queries/functions.rs index f11f1df92d..dbc78d3491 100644 --- a/crates/yulgen/src/db/queries/functions.rs +++ b/crates/yulgen/src/db/queries/functions.rs @@ -2,22 +2,21 @@ use crate::context::FnContext; use crate::db::YulgenDb; use crate::mappers::functions::multiple_func_stmt; use crate::names; -use crate::types::{to_abi_types, AbiType, AsAbiType}; -use fe_analyzer::context::CallType; -use fe_analyzer::namespace::items::{ContractId, FunctionId, Item, StructId, TypeDef}; +use crate::operations::abi as abi_operations; +use crate::runtime::functions; +use crate::types::{to_abi_selector_names, to_abi_types, AbiDecodeLocation, AbiType, AsAbiType}; +use fe_abi::utils as abi_utils; +use fe_analyzer::namespace::items::{Class, FunctionId, Item, StructId, TypeDef}; use fe_analyzer::namespace::types::{Struct, Type}; use fe_parser::{ast, node::Node}; use indexmap::IndexSet; -use std::iter::FromIterator; +use smol_str::SmolStr; use std::rc::Rc; use yultsur::*; -pub fn function_yul_name(db: &dyn YulgenDb, function: FunctionId) -> Rc { +pub fn function_yul_name(db: &dyn YulgenDb, function: FunctionId) -> SmolStr { // foo::Bar::new => $$foo$Bar$new - Rc::new(format!( - "$${}", - Item::Function(function).path(db.upcast()).join("$") - )) + format!("$${}", Item::Function(function).path(db.upcast()).join("$")).into() } pub fn function_def(db: &dyn YulgenDb, function: FunctionId) -> yul::Statement { @@ -64,32 +63,6 @@ pub fn function_sig_abi_types( ) } -pub fn created_contracts(db: &dyn YulgenDb, function: FunctionId) -> Rc> { - Rc::new(IndexSet::from_iter( - function - .body(db.upcast()) - .calls - .iter() - .filter_map(|(_, calltype)| match calltype { - CallType::BuiltinAssociatedFunction { contract, .. } => Some(*contract), - _ => None, - }), - )) -} - -pub fn external_calls(db: &dyn YulgenDb, function: FunctionId) -> Rc> { - Rc::new(IndexSet::from_iter( - function - .body(db.upcast()) - .calls - .iter() - .filter_map(|(_, calltype)| match calltype { - CallType::External { function, .. } => Some(*function), - _ => None, - }), - )) -} - pub fn revert_types(db: &dyn YulgenDb, function: FunctionId) -> Rc> { let body = function.body(db.upcast()); @@ -138,8 +111,82 @@ pub fn assert_string_types(db: &dyn YulgenDb, function: FunctionId) -> Rc Rc> { - Rc::new(function.body(db.upcast()).string_literals.clone()) +pub fn function_external_call_name(db: &dyn YulgenDb, function: FunctionId) -> SmolStr { + // foo::Bar::new => $$foo$Bar$new + format!("call_{}", db.function_yul_name(function)).into() +} + +/// Includes required encode/decode functions. +pub fn function_external_call_fn(db: &dyn YulgenDb, function: FunctionId) -> Vec { + let adb = db.upcast(); + if !matches!(function.class(adb), Some(Class::Contract(_))) { + panic!("external call to non-contract fn") + }; + + let function_name = function.name(adb); + // get the name of the call function and its parameters + let call_fn_name = identifier! { (db.function_external_call_name(function)) }; + let (param_types, return_type) = db.function_sig_abi_types(function); + + // create a pair of identifiers and expressions for the parameters + let (param_idents, param_exprs) = names::abi::vals("param", param_types.len()); + // the function selector must be added to the first 4 bytes of the calldata + let selector = { + let selector = + abi_utils::func_selector(&function_name, &to_abi_selector_names(¶m_types)); + literal_expression! { (selector) } + }; + + // the size of the encoded data + let encoding_size = abi_operations::encoding_size(¶m_types, ¶m_exprs); + // the operations used to encode the parameters + let encoding_operation = abi_operations::encode(¶m_types, param_exprs); + + let mut fns = vec![functions::abi::encode(¶m_types)]; + + if let Some(return_type) = return_type { + fns.extend(functions::abi::decode_functions( + &[return_type.clone()], + AbiDecodeLocation::Memory, + )); + let decoding_operation = abi_operations::decode_data( + &[return_type], + expression! { outstart }, + expression! { add(outstart, outsize) }, + AbiDecodeLocation::Memory, + ); + // return data must be captured and decoded + fns.push(function_definition! { + function [call_fn_name](addr, [param_idents...]) -> return_val { + (let instart := alloc_mstoren([selector], 4)) + (let insize := add(4, [encoding_size])) + (pop([encoding_operation])) + (let success := call((gas()), addr, 0, instart, insize, 0, 0)) + (let outsize := returndatasize()) + (let outstart := alloc(outsize)) + (returndatacopy(outstart, 0, outsize)) + (if (iszero(success)) { (revert(outstart, outsize)) }) + (return_val := [decoding_operation]) + } + }) + } else { + // unit type; there is no return data to handle + fns.push(function_definition! { + function [call_fn_name](addr, [param_idents...]) -> return_val { + (let instart := alloc_mstoren([selector], 4)) + (let insize := add(4, [encoding_size])) + (pop([encoding_operation])) + (let success := call((gas()), addr, 0, instart, insize, 0, 0)) + (if (iszero(success)) { + (let outsize := returndatasize()) + (let outstart := alloc(outsize)) + (returndatacopy(outstart, 0, outsize)) + (revert(outstart, outsize)) + }) + } + }) + } + fns } fn for_each_stmt(stmts: &[Node], f: &mut F) diff --git a/crates/yulgen/src/db/queries/structs.rs b/crates/yulgen/src/db/queries/structs.rs index c527c46890..4e9bc90adf 100644 --- a/crates/yulgen/src/db/queries/structs.rs +++ b/crates/yulgen/src/db/queries/structs.rs @@ -1,7 +1,9 @@ use crate::db::YulgenDb; -use crate::types::{AbiType, AsAbiType}; -use fe_analyzer::namespace::items::StructId; +use crate::types::{AbiType, AsAbiType, EvmSized}; +use fe_analyzer::namespace::items::{Item, StructId, TypeDef}; +use smol_str::SmolStr; use std::rc::Rc; +use yultsur::*; pub fn struct_field_abi_types(db: &dyn YulgenDb, struct_: StructId) -> Rc> { let db = db.upcast(); @@ -23,3 +25,112 @@ pub fn struct_abi_type(db: &dyn YulgenDb, struct_: StructId) -> AbiType { let components = db.struct_field_abi_types(struct_).to_vec(); AbiType::Tuple { components } } + +pub fn struct_qualified_name(db: &dyn YulgenDb, struct_: StructId) -> SmolStr { + // foo::Bar => $$foo$Bar + format!( + "$${}", + Item::Type(TypeDef::Struct(struct_)) + .path(db.upcast()) + .join("$") + ) + .into() +} + +pub fn struct_getter_name(db: &dyn YulgenDb, struct_: StructId, field: SmolStr) -> SmolStr { + format!("{}.get_{}_ptr", db.struct_qualified_name(struct_), field).into() +} + +pub fn struct_getter_fn(db: &dyn YulgenDb, struct_: StructId, field: SmolStr) -> yul::Statement { + let fields = struct_.fields(db.upcast()); + + let (index, _, field_id) = fields + .get_full(field.as_str()) + .expect("invalid struct field name"); + + let field_type = field_id.typ(db.upcast()).expect("struct field error"); + + // The value of each field occupies 32 bytes. This includes values with sizes + // less than 32 bytes. So, when we get the pointer to the value of a struct + // field, we must take into consideration the left-padding. The left-padding is + // equal to the difference between the value's size and 32 bytes, so we end up + // adding the word offset and the byte offset. + let field_offset = index * 32 + (32 - field_type.size()); + + let function_name = identifier! { (db.struct_getter_name(struct_, field)) }; + let offset = literal_expression! { (field_offset) }; + function_definition! { + function [function_name](ptr) -> return_val { + (return_val := add(ptr, [offset])) + } + } +} + +pub fn struct_init_name(db: &dyn YulgenDb, struct_: StructId) -> SmolStr { + format!("{}.new", db.struct_qualified_name(struct_)).into() +} + +pub fn struct_init_fn(db: &dyn YulgenDb, struct_: StructId) -> yul::Statement { + let function_name = identifier! { (db.struct_init_name(struct_)) }; + let fields = struct_.fields(db.upcast()); + + if fields.is_empty() { + // We return 0 here because it is safe to assume that we never write to an empty + // struct. If we end up writing to an empty struct that's an actual Fe + // bug. + return function_definition! { + function [function_name]() -> return_val { + (return_val := 0) + } + }; + } + + let params = fields + .keys() + .map(|name| { + identifier! {(name)} + }) + .collect::>(); + + let body = fields + .iter() + .enumerate() + .map(|(index, (name, _))| { + if index == 0 { + let param_identifier_exp = identifier_expression! {(name)}; + statements! { + (return_val := alloc(32)) + (mstore(return_val, [param_identifier_exp])) + } + } else { + let ptr_identifier = format!("{}_ptr", name); + let ptr_identifier = identifier! {(ptr_identifier)}; + let ptr_identifier_exp = identifier_expression! {(ptr_identifier)}; + let param_identifier_exp = identifier_expression! {(name)}; + statements! { + (let [ptr_identifier] := alloc(32)) + (mstore([ptr_identifier_exp], [param_identifier_exp])) + } + } + }) + .flatten() + .collect::>(); + + function_definition! { + function [function_name]([params...]) -> return_val { + [body...] + } + } +} + +pub fn struct_api_fns(db: &dyn YulgenDb, struct_: StructId) -> Vec { + [ + vec![db.struct_init_fn(struct_)], + struct_ + .fields(db.upcast()) + .keys() + .map(|name| db.struct_getter_fn(struct_, name.into())) + .collect(), + ] + .concat() +} diff --git a/crates/yulgen/src/mappers/expressions.rs b/crates/yulgen/src/mappers/expressions.rs index eebef90cca..2e19b9fbb6 100644 --- a/crates/yulgen/src/mappers/expressions.rs +++ b/crates/yulgen/src/mappers/expressions.rs @@ -123,7 +123,7 @@ fn expr_call(context: &mut FnContext, exp: &Node) -> yul::Expression { expression! { balance([yul_args[0].to_owned()]) } } }, - CallType::BuiltinValueMethod(method) => { + CallType::BuiltinValueMethod { method, typ } => { let target = match &func.kind { fe::Expr::Attribute { value, .. } => value, _ => unreachable!(), @@ -134,31 +134,28 @@ fn expr_call(context: &mut FnContext, exp: &Node) -> yul::Expression { // `to_mem` and `clone`. builtins::ValueMethod::ToMem => expr(context, target), builtins::ValueMethod::Clone => expr(context, target), - builtins::ValueMethod::AbiEncode => match context - .expression_attributes(target) - .expect("missing expr attributes") - .typ - .clone() - { + builtins::ValueMethod::AbiEncode => match typ { Type::Struct(struct_) => abi_operations::encode( - &[struct_.as_abi_type(context.db)], + &[struct_.as_abi_type(context.adb)], vec![expr(context, target)], ), _ => panic!("invalid attributes"), }, } } - CallType::TypeConstructor(Type::Struct(val)) => struct_operations::new(&val, yul_args), + CallType::TypeConstructor(Type::Struct(val)) => { + struct_operations::init(context.db, val.id, yul_args) + } CallType::TypeConstructor(Type::Base(Base::Numeric(integer))) => { math_operations::adjust_numeric_size(&integer, yul_args[0].to_owned()) } CallType::TypeConstructor(_) => yul_args[0].to_owned(), CallType::Pure(func) => { - let func_name = identifier! { (context.ydb.function_yul_name(func)) }; + let func_name = identifier! { (context.db.function_yul_name(func)) }; expression! { [func_name]([yul_args...]) } } CallType::BuiltinAssociatedFunction { contract, function } => { - let contract_name = contract.name(context.db); + let contract_name = contract.name(context.adb); match function { ContractTypeMethod::Create2 => contract_operations::create2( &contract_name, @@ -175,7 +172,7 @@ fn expr_call(context: &mut FnContext, exp: &Node) -> yul::Expression { matches!(class, Class::Struct(_)), "call to contract-associated fn should be rejected by analyzer as not-yet-implemented" ); - let func_name = identifier! { (context.ydb.function_yul_name(function)) }; + let func_name = identifier! { (context.db.function_yul_name(function)) }; expression! { [func_name]([yul_args...]) } } CallType::ValueMethod { @@ -194,24 +191,23 @@ fn expr_call(context: &mut FnContext, exp: &Node) -> yul::Expression { is_self, "non-self contract calls should be CallType::External" ); - let fn_name = identifier! { (context.ydb.function_yul_name(method)) }; + let fn_name = identifier! { (context.db.function_yul_name(method)) }; expression! { [fn_name]([yul_args...]) } } Class::Struct(_) => { let target = expr(context, target); - let fn_name = identifier! { (context.ydb.function_yul_name(method)) }; + let fn_name = identifier! { (context.db.function_yul_name(method)) }; expression! { [fn_name]([target], [yul_args...]) } } } } - CallType::External { contract, function } => { + CallType::External { function, .. } => { let target = match &func.kind { fe::Expr::Attribute { value, .. } => value, _ => unreachable!(), }; let address = expr(context, target); - let fn_name = - names::contract_call(&contract.name(context.db), &function.name(context.db)); + let fn_name = identifier! { (context.db.function_external_call_name(function)) }; expression! { [fn_name]([address], [yul_args...]) } } }; @@ -497,7 +493,7 @@ fn expr_attribute(context: &mut FnContext, exp: &Node) -> yul::Express // struct `self` is handled like any other struct value, // and keeps the name `self` in the generated yul. let target = expr(context, target); - struct_operations::get_attribute(struct_, &field.kind, target) + struct_operations::get_attribute(context.db, struct_.id, &field.kind, target) } _ => panic!("invalid type for field access: {:?}", &target_attrs.typ), } diff --git a/crates/yulgen/src/mappers/functions.rs b/crates/yulgen/src/mappers/functions.rs index b94a458a73..f765a05126 100644 --- a/crates/yulgen/src/mappers/functions.rs +++ b/crates/yulgen/src/mappers/functions.rs @@ -115,7 +115,7 @@ fn revert(context: &mut FnContext, stmt: &Node) -> yul::Statement if let Type::Struct(struct_) = &error_attributes.typ { revert_operations::revert( &struct_.name, - &struct_.as_abi_type(context.db), + &struct_.as_abi_type(context.adb), expressions::expr(context, error_expr), ) } else { @@ -147,7 +147,7 @@ fn emit(context: &mut FnContext, stmt: &Node) -> yul::Statement { .typ .clone() .expect("event field type error") - .as_abi_type(context.db), + .as_abi_type(context.adb), field.is_indexed, ) }) @@ -174,7 +174,7 @@ fn assert(context: &mut FnContext, stmt: &Node) -> yul::Statement .clone(); if let Type::String(string) = msg_attributes.typ { - let abi_type = string.as_abi_type(context.db); + let abi_type = string.as_abi_type(context.adb); statement! { if (iszero([test])) { [revert_operations::error_revert(&abi_type, msg)] diff --git a/crates/yulgen/src/names/mod.rs b/crates/yulgen/src/names/mod.rs index a24deabc12..38218df04a 100644 --- a/crates/yulgen/src/names/mod.rs +++ b/crates/yulgen/src/names/mod.rs @@ -60,22 +60,6 @@ pub fn adjust_numeric_size(size: &Integer) -> yul::Identifier { identifier! {(format!("adjust_numeric_{}", size.as_ref().to_lowercase()))} } -// /// Generate a safe function name for a user defined function -// pub fn func_name(name: &str) -> yul::Identifier { -// identifier! { (format!("$${}", name)) } -// } - -// /// Generate a safe function name for a function defined within a struct. -// /// Contract function names currently use [`func_name`] above, but should probably use -// /// this instead to avoid name collisions with functions defined at the module level. -// /// This is for functions that take self, and those that don't. -// /// Note that this assumes that there can't be two "classes" with the same name, which -// /// won't be the case when we support using items from other modules. -// /// Eg. `othermod::Foo::bar()` and `Foo::bar()` yul fn names will collide. -// pub fn associated_function_name(class_name: &str, func_name: &str) -> yul::Identifier { -// identifier! { (format!("$${}${}", class_name, func_name)) } -// } - /// Generate a safe variable name for a user defined function pub fn var_name(name: &str) -> yul::Identifier { identifier! { (format!("${}", name)) } @@ -87,28 +71,3 @@ pub fn revert(name: &str, typ: &AbiType) -> yul::Identifier { identifier! { (name) } } - -// XXX use fully qualified contract name -/// Generates an external call function name for a given type and location. -pub fn contract_call(contract_name: &str, func_name: &str) -> yul::Identifier { - let name = format!("{}_{}", contract_name, func_name); - identifier! { (name) } -} - -/// Generates a function name for creating a certain struct type -pub fn struct_new_call(struct_name: &str) -> yul::Identifier { - struct_function_name(struct_name, "new") -} - -/// Generates a function name for reading a named property of a certain struct -/// type -pub fn struct_getter_call(struct_name: &str, field_name: &str) -> yul::Identifier { - struct_function_name(struct_name, &format!("get_{}_ptr", field_name)) -} - -// XXX use qualified name -/// Generates a function name for to interact with a certain struct type -fn struct_function_name(struct_name: &str, func_name: &str) -> yul::Identifier { - let name = format!("struct_{}_{}", struct_name, func_name); - identifier! { (name) } -} diff --git a/crates/yulgen/src/operations/structs.rs b/crates/yulgen/src/operations/structs.rs index f86d9e6479..5f3982a31f 100644 --- a/crates/yulgen/src/operations/structs.rs +++ b/crates/yulgen/src/operations/structs.rs @@ -1,42 +1,18 @@ -use crate::names; -use fe_analyzer::namespace::types::Struct; +use crate::YulgenDb; +use fe_analyzer::namespace::items::StructId; use yultsur::*; -pub fn new(struct_type: &Struct, params: Vec) -> yul::Expression { - let function_name = names::struct_new_call(&struct_type.name); +pub fn init(db: &dyn YulgenDb, struct_: StructId, params: Vec) -> yul::Expression { + let function_name = identifier! { (db.struct_init_name(struct_)) }; expression! { [function_name]([params...]) } } pub fn get_attribute( - struct_type: &Struct, + db: &dyn YulgenDb, + struct_: StructId, field_name: &str, val: yul::Expression, ) -> yul::Expression { - let function_name = names::struct_getter_call(&struct_type.name, field_name); + let function_name = identifier! { (db.struct_getter_name(struct_, field_name.into())) }; expression! { [function_name]([val]) } } - -#[cfg(test)] -mod tests { - use crate::operations::structs; - use fe_analyzer::namespace::items::StructId; - use fe_analyzer::namespace::types::Struct; - use yultsur::*; - - #[test] - fn test_new() { - let val = Struct { - name: "Foo".to_string(), - id: StructId::default(), - field_count: 2, - }; - let params = vec![ - identifier_expression! { (1) }, - identifier_expression! { (2) }, - ]; - assert_eq!( - structs::new(&val, params).to_string(), - "struct_Foo_new(1, 2)" - ) - } -} diff --git a/crates/yulgen/src/runtime/functions/abi.rs b/crates/yulgen/src/runtime/functions/abi.rs index 925237ba4b..ddd1b956ab 100644 --- a/crates/yulgen/src/runtime/functions/abi.rs +++ b/crates/yulgen/src/runtime/functions/abi.rs @@ -17,65 +17,15 @@ pub fn all() -> Vec { // revert functions. // It will be removed in https://github.com/ethereum/fe/pull/478 along with all other // batches of runtime functions. - - // XXX only include once encode(&[AbiType::Uint { size: 32 }]), ] } -/// Creates a batch of encoding function for the given type arrays. -/// -/// It sorts the functions and removes duplicates. -pub fn batch_encode(batch: Vec>) -> Vec { - let mut yul_functions: Vec<_> = batch.iter().map(|vec| encode(&vec)).collect(); - yul_functions.sort(); - yul_functions.dedup(); - yul_functions -} - -/// Creates a batch of decoding function for the given types and decode -/// locations. -/// -/// It sorts the functions and removes duplicates. -pub fn batch_decode(data_batch: Vec<(Vec, AbiDecodeLocation)>) -> Vec { - let component_batch = data_batch - .iter() - .fold(vec![], |mut accum, (types, location)| { - for typ in types.to_owned() { - accum.push((typ, *location)); - } - accum - }); - - let data_functions: Vec<_> = data_batch - .into_iter() - .map(|(types, location)| decode_data(&types, location)) - .collect(); - let component_functions: Vec<_> = component_batch - .into_iter() - .map(|(typ, location)| { - let top_function = decode_component(&typ, location); - - let inner_functions = match &typ { - AbiType::Tuple { components } => components - .iter() - .map(|typ| decode_component(typ, location)) - .collect(), - AbiType::StaticArray { inner, .. } => vec![decode_component(inner, location)], - _ => vec![], - }; - - [vec![top_function], inner_functions].concat() - }) - .flatten() - .collect(); - - let mut yul_functions: Vec<_> = [data_functions, component_functions].concat(); - yul_functions.sort(); - yul_functions.dedup(); - yul_functions -} - +/// Returns a yul function that decodes a block of abi-encoded data into the +/// specified [`AbiType`] componenets, eg `abi_decode_data_u256_Foo_u8_calldata`. +/// The decoding of each component is handled by a separate function, eg. +/// `abi_decode_component_uint32_mem`; these component decoding functions +/// are also included in the returned `Vec`. pub fn decode_functions(types: &[AbiType], location: AbiDecodeLocation) -> Vec { let mut component_fns: Vec<_> = types.iter().fold(vec![], |mut funcs, typ| { funcs.push(decode_component(typ, location)); diff --git a/crates/yulgen/src/runtime/functions/contracts.rs b/crates/yulgen/src/runtime/functions/contracts.rs index 72147e8014..032fe4a011 100644 --- a/crates/yulgen/src/runtime/functions/contracts.rs +++ b/crates/yulgen/src/runtime/functions/contracts.rs @@ -1,12 +1,5 @@ use crate::constants::{ERROR_FAILED_SEND_VALUE, ERROR_INSUFFICIENT_FUNDS_TO_SEND_VALUE}; -use crate::names; -use crate::names::abi as abi_names; -use crate::operations::abi as abi_operations; use crate::operations::revert as revert_operations; -use crate::types::{to_abi_selector_names, AbiDecodeLocation}; -use crate::YulgenDb; -use fe_abi::utils as abi_utils; -use fe_analyzer::namespace::items::{Class, FunctionId}; use yultsur::*; /// Return all contacts runtime functions @@ -14,72 +7,6 @@ pub fn all() -> Vec { vec![create2(), create(), send_value()] } -pub fn call(db: &dyn YulgenDb, function: FunctionId) -> yul::Statement { - let adb = db.upcast(); - let contract = match function.class(adb) { - Some(Class::Contract(contract)) => contract, - _ => panic!("external call to non-contract fn"), - }; - - let function_name = function.name(adb); - // get the name of the call function and its parameters - let yul_function_name = names::contract_call(&contract.name(adb), &function.name(adb)); - let (param_types, return_type) = db.function_sig_abi_types(function); - - // create a pair of identifiers and expressions for the parameters - let (param_idents, param_exprs) = abi_names::vals("param", param_types.len()); - // the function selector must be added to the first 4 bytes of the calldata - let selector = { - let selector = - abi_utils::func_selector(&function_name, &to_abi_selector_names(¶m_types)); - literal_expression! { (selector) } - }; - - // the size of the encoded data - let encoding_size = abi_operations::encoding_size(¶m_types, ¶m_exprs); - // the operations used to encode the parameters - let encoding_operation = abi_operations::encode(¶m_types, param_exprs); - - if let Some(return_type) = return_type { - let decoding_operation = abi_operations::decode_data( - &[return_type], - expression! { outstart }, - expression! { add(outstart, outsize) }, - AbiDecodeLocation::Memory, - ); - // return data must be captured and decoded - function_definition! { - function [yul_function_name](addr, [param_idents...]) -> return_val { - (let instart := alloc_mstoren([selector], 4)) - (let insize := add(4, [encoding_size])) - (pop([encoding_operation])) - (let success := call((gas()), addr, 0, instart, insize, 0, 0)) - (let outsize := returndatasize()) - (let outstart := alloc(outsize)) - (returndatacopy(outstart, 0, outsize)) - (if (iszero(success)) { (revert(outstart, outsize)) }) - (return_val := [decoding_operation]) - } - } - } else { - // unit type; there is no return data to handle - function_definition! { - function [yul_function_name](addr, [param_idents...]) -> return_val { - (let instart := alloc_mstoren([selector], 4)) - (let insize := add(4, [encoding_size])) - (pop([encoding_operation])) - (let success := call((gas()), addr, 0, instart, insize, 0, 0)) - (if (iszero(success)) { - (let outsize := returndatasize()) - (let outstart := alloc(outsize)) - (returndatacopy(outstart, 0, outsize)) - (revert(outstart, outsize)) - }) - } - } - } -} - /// Function that executes the `create2` operation. pub fn create2() -> yul::Statement { function_definition! { diff --git a/crates/yulgen/src/runtime/functions/mod.rs b/crates/yulgen/src/runtime/functions/mod.rs index 3d27785c34..3e8b800c7b 100644 --- a/crates/yulgen/src/runtime/functions/mod.rs +++ b/crates/yulgen/src/runtime/functions/mod.rs @@ -5,7 +5,6 @@ pub mod contracts; pub mod data; pub mod math; pub mod revert; -pub mod structs; /// Returns all functions that should be available during runtime. pub fn std() -> Vec { diff --git a/crates/yulgen/src/runtime/functions/structs.rs b/crates/yulgen/src/runtime/functions/structs.rs deleted file mode 100644 index 7d9f3fdae4..0000000000 --- a/crates/yulgen/src/runtime/functions/structs.rs +++ /dev/null @@ -1,93 +0,0 @@ -use crate::names; -use crate::types::EvmSized; -use fe_analyzer::namespace::types::FixedSize; -use yultsur::*; - -/// Generate a YUL function that can be used to create an instance of a struct -pub fn generate_new_fn(struct_name: &str, fields: &[(String, FixedSize)]) -> yul::Statement { - let function_name = names::struct_new_call(struct_name); - - if fields.is_empty() { - // We return 0 here because it is safe to assume that we never write to an empty - // struct. If we end up writing to an empty struct that's an actual Fe - // bug. - return function_definition! { - function [function_name]() -> return_val { - (return_val := 0) - } - }; - } - - let params = fields - .iter() - .map(|(name, _)| { - identifier! {(name)} - }) - .collect::>(); - - let body = fields - .iter() - .enumerate() - .map(|(index, (name, _))| { - if index == 0 { - let param_identifier_exp = identifier_expression! {(name)}; - statements! { - (return_val := alloc(32)) - (mstore(return_val, [param_identifier_exp])) - } - } else { - let ptr_identifier = format!("{}_ptr", name); - let ptr_identifier = identifier! {(ptr_identifier)}; - let ptr_identifier_exp = identifier_expression! {(ptr_identifier)}; - let param_identifier_exp = identifier_expression! {(name)}; - statements! { - (let [ptr_identifier] := alloc(32)) - (mstore([ptr_identifier_exp], [param_identifier_exp])) - } - } - }) - .flatten() - .collect::>(); - - function_definition! { - function [function_name]([params...]) -> return_val { - [body...] - } - } -} - -/// Generate a YUL function that can be used to read a property of a struct -pub fn generate_get_fn( - struct_name: &str, - (field_name, field_type): &(String, FixedSize), - field_index: usize, -) -> yul::Statement { - let function_name = names::struct_getter_call(struct_name, field_name); - - // The value of each field occupies 32 bytes. This includes values with sizes - // less than 32 bytes. So, when we get the pointer to the value of a struct - // field, we must take into consideration the left-padding. The left-padding is - // equal to the difference between the value's size and 32 bytes, so we end up - // adding the word offset and the byte offset. - let field_offset = field_index * 32 + (32 - field_type.size()); - - let offset = literal_expression! { (field_offset) }; - function_definition! { - function [function_name](ptr) -> return_val { - (return_val := add(ptr, [offset])) - } - } -} - -/// Builds a set of functions used to interact with structs used in a contract -pub fn struct_apis(name: &str, fields: &[(String, FixedSize)]) -> Vec { - [ - vec![generate_new_fn(name, fields)], - fields - .iter() - .enumerate() - .map(|(index, field)| generate_get_fn(name, field, index)) - .collect(), - ] - .concat() -} diff --git a/crates/yulgen/src/runtime/mod.rs b/crates/yulgen/src/runtime/mod.rs index 94ff723117..9d342fb93d 100644 --- a/crates/yulgen/src/runtime/mod.rs +++ b/crates/yulgen/src/runtime/mod.rs @@ -5,237 +5,6 @@ use crate::YulgenDb; use fe_analyzer::namespace::items::ContractId; use yultsur::*; -// XXX remove -// /// Builds the set of function statements that are needed during runtime. -// pub fn build2( -// db: &dyn AnalyzerDb, -// context: &mut ContractContext, -// contract: ContractId, -// ) -> Vec { -// let module = contract.module(db); -// let module_functions = module -// .items(db) -// .values() -// .filter_map(|item| match item { -// Item::Function(fid) => Some(mappers::functions::func_def( -// db, -// context, -// names::func_name(&fid.name(db)), -// *fid, -// )), -// _ => None, -// }) -// .collect::>(); - -// let struct_apis = module -// .all_structs(db) -// .iter() -// .map(|struct_| { -// let fields = struct_ -// .fields(db) -// .iter() -// .map(|(name, id)| { -// ( -// name.to_string(), -// id.typ(db).expect("struct field type error"), -// ) -// }) -// .collect::>(); - -// let struct_name = struct_.name(db); -// let member_functions = struct_ -// .functions(db) -// .values() -// .map(|fid| { -// mappers::functions::func_def( -// db, -// context, -// names::associated_function_name(&struct_name, &fid.name(db)), -// *fid, -// ) -// }) -// .collect::>(); - -// [ -// functions::structs::struct_apis(&struct_.name(db), &fields), -// member_functions, -// ] -// .concat() -// }) -// .collect::>() -// .concat(); - -// let contract_name = contract.name(db); -// let external_contracts: Vec = module -// .all_contracts(db) -// .iter() -// .filter_map(|id| (id.name(db) != contract_name).then(|| *id)) -// .collect(); -// let external_functions = external_contracts -// .iter() -// .map(|id| { -// id.functions(db) -// .values() -// .map(|func| func.signature(db)) -// .collect::>>() -// }) -// .collect::>() -// .concat(); - -// let public_functions: Vec> = contract -// .public_functions(db) -// .values() -// .map(|func| func.signature(db)) -// .collect(); - -// let encoding = { -// let public_functions_batch = public_functions -// .iter() -// .filter_map(|sig| { -// let typ = sig.return_type.clone().expect("return type error"); -// (!typ.is_unit()).then(|| vec![typ.as_abi_type(db)]) -// }) -// .collect::>(); - -// let events_batch: Vec> = contract -// .events(db) -// .values() -// .map(|event| { -// event -// .typ(db) -// .fields -// .iter() -// .filter_map(|field| { -// (!field.is_indexed).then(|| { -// field -// .typ -// .clone() -// .expect("event field type error") -// .as_abi_type(db) -// }) -// }) -// .collect::>() -// }) -// .collect::>>(); - -// let contracts_batch: Vec> = external_functions -// .iter() -// .map(|fn_sig| to_abi_types(db, &fn_sig.param_types())) -// .collect(); - -// let assert_strings_batch = context -// .assert_strings -// .iter() -// .map(|val| vec![val.as_abi_type(db)]) -// .collect::>(); - -// let revert_errors_batch = context -// .revert_errors -// .iter() -// .map(|struct_| { -// struct_ -// .id -// .fields(db) -// .values() -// .map(|field| { -// field -// .typ(db) -// .expect("struct field type error") -// .as_abi_type(db) -// }) -// .collect::>() -// }) -// .collect::>(); - -// let revert_panic_batch = vec![vec![AbiType::Uint { size: 32 }]]; - -// let structs_batch: Vec> = module -// .all_structs(db) -// .iter() -// .map(|struc| vec![struc.typ(db).as_abi_type(db)]) -// .collect::>>(); - -// let batch = [ -// public_functions_batch, -// events_batch, -// contracts_batch, -// assert_strings_batch, -// revert_errors_batch, -// revert_panic_batch, -// structs_batch, -// ] -// .concat(); -// functions::abi::batch_encode(batch) -// }; -// let decoding = { -// let public_functions_batch = public_functions -// .iter() -// .map(|sig| { -// ( -// to_abi_types(db, &sig.param_types()), -// AbiDecodeLocation::Calldata, -// ) -// }) -// .collect(); - -// let init_params_batch = if let Some(init_fn) = contract.init_function(db) { -// let sig = init_fn.signature(db); -// vec![( -// to_abi_types(db, &sig.param_types()), -// AbiDecodeLocation::Memory, -// )] -// } else { -// vec![] -// }; - -// let contracts_batch = external_functions -// .iter() -// .filter_map(|function| { -// let return_type = function.expect_return_type(); -// (!return_type.is_unit()) -// .then(|| (vec![return_type.as_abi_type(db)], AbiDecodeLocation::Memory)) -// }) -// .collect(); - -// let batch = [public_functions_batch, init_params_batch, contracts_batch].concat(); -// functions::abi::batch_decode(batch) -// }; -// let contract_calls = { -// external_contracts -// .iter() -// .map(|contract| functions::contracts::calls(db, contract.to_owned())) -// .collect::>() -// .concat() -// }; - -// let revert_calls_from_assert = context -// .assert_strings -// .iter() -// .map(|string| functions::revert::error_revert(&string.as_abi_type(db))) -// .collect::>(); - -// let revert_calls = context -// .revert_errors -// .iter() -// .map(|_struct| functions::revert::revert(&_struct.name, &_struct.as_abi_type(db))) -// .collect::>(); - -// let mut funcs = [ -// functions::std(), -// encoding, -// decoding, -// contract_calls, -// revert_calls_from_assert, -// revert_calls, -// struct_apis, -// module_functions, -// ] -// .concat(); -// funcs.sort(); -// funcs.dedup(); -// funcs -// } - pub fn build_abi_dispatcher(db: &dyn YulgenDb, contract: ContractId) -> yul::Statement { let adb = db.upcast(); let public_functions = contract diff --git a/crates/yulgen/tests/snapshots/yulgen__abi_decode_data_address_bool_mem_function.snap b/crates/yulgen/tests/snapshots/yulgen__abi_decode_data_address_bool_mem_function.snap index 853eed4429..59764e726c 100644 --- a/crates/yulgen/tests/snapshots/yulgen__abi_decode_data_address_bool_mem_function.snap +++ b/crates/yulgen/tests/snapshots/yulgen__abi_decode_data_address_bool_mem_function.snap @@ -1,16 +1,28 @@ --- source: crates/yulgen/tests/yulgen.rs -expression: "abi_functions::decode_data(&[AbiType::Bool, AbiType::Address],\n AbiDecodeLocation::Memory)" +expression: "yul::Block{statements:\n abi_functions::decode_functions(&[AbiType::Bool,\n AbiType::Address],\n AbiDecodeLocation::Memory),}" --- -function abi_decode_data_bool_address_mem(head_start, data_end) -> return_val_0, return_val_1 { - let encoding_size := sub(data_end, head_start) - if iszero(eq(encoding_size, 64)) { revert_with_Error_uint256(259) } - let head_offset_0 := 0 - let head_offset_1 := 32 - let decoded_val_0 := abi_decode_component_bool_mem(head_start, head_offset_0) - let decoded_val_1 := abi_decode_component_address_mem(head_start, head_offset_1) - if iszero(eq(encoding_size, 64)) { revert_with_Error_uint256(259) } - return_val_0 := decoded_val_0 - return_val_1 := decoded_val_1 +{ + function abi_decode_component_address_mem(head_start, offset) -> return_val { + let ptr := add(head_start, offset) + return_val := mload(ptr) + if iszero(is_left_padded(96, return_val)) { revert_with_Error_uint256(259) } + } + function abi_decode_component_bool_mem(head_start, offset) -> return_val { + let ptr := add(head_start, offset) + return_val := mload(ptr) + if iszero(is_left_padded(255, return_val)) { revert_with_Error_uint256(259) } + } + function abi_decode_data_bool_address_mem(head_start, data_end) -> return_val_0, return_val_1 { + let encoding_size := sub(data_end, head_start) + if iszero(eq(encoding_size, 64)) { revert_with_Error_uint256(259) } + let head_offset_0 := 0 + let head_offset_1 := 32 + let decoded_val_0 := abi_decode_component_bool_mem(head_start, head_offset_0) + let decoded_val_1 := abi_decode_component_address_mem(head_start, head_offset_1) + if iszero(eq(encoding_size, 64)) { revert_with_Error_uint256(259) } + return_val_0 := decoded_val_0 + return_val_1 := decoded_val_1 + } } diff --git a/crates/yulgen/tests/snapshots/yulgen__abi_decode_data_u256_bytes_string_bool_address_bytes_calldata_function.snap b/crates/yulgen/tests/snapshots/yulgen__abi_decode_data_u256_bytes_string_bool_address_bytes_calldata_function.snap index ab8c35fe34..f6abeecf6c 100644 --- a/crates/yulgen/tests/snapshots/yulgen__abi_decode_data_u256_bytes_string_bool_address_bytes_calldata_function.snap +++ b/crates/yulgen/tests/snapshots/yulgen__abi_decode_data_u256_bytes_string_bool_address_bytes_calldata_function.snap @@ -1,31 +1,76 @@ --- source: crates/yulgen/tests/yulgen.rs -expression: "abi_functions::decode_data(&[AbiType::Uint{size: 32,},\n AbiType::Bytes{size: 100,},\n AbiType::String{max_size: 42,}, AbiType::Bool,\n AbiType::Address, AbiType::Bytes{size: 100,}],\n AbiDecodeLocation::Calldata)" +expression: "yul::Block{statements:\n abi_functions::decode_functions(&[AbiType::Uint{size: 32,},\n AbiType::Bytes{size: 100,},\n AbiType::String{max_size:\n 42,},\n AbiType::Bool,\n AbiType::Address,\n AbiType::Bytes{size: 100,}],\n AbiDecodeLocation::Calldata),}" --- -function abi_decode_data_uint256_bytes_100_string_42_bool_address_bytes_100_calldata(head_start, data_end) -> return_val_0, return_val_1, return_val_2, return_val_3, return_val_4, return_val_5 { - let encoding_size := sub(data_end, head_start) - if or(lt(encoding_size, 544), gt(encoding_size, 608)) { revert_with_Error_uint256(259) } - let head_offset_0 := 0 - let head_offset_1 := 32 - let head_offset_2 := 64 - let head_offset_3 := 96 - let head_offset_4 := 128 - let head_offset_5 := 160 - let decoded_val_0 := abi_decode_component_uint256_calldata(head_start, head_offset_0) - let decoded_val_1, data_start_offset_1, data_end_offset_1 := abi_decode_component_bytes_100_calldata(head_start, head_offset_1) - let decoded_val_2, data_start_offset_2, data_end_offset_2 := abi_decode_component_string_42_calldata(head_start, head_offset_2) - let decoded_val_3 := abi_decode_component_bool_calldata(head_start, head_offset_3) - let decoded_val_4 := abi_decode_component_address_calldata(head_start, head_offset_4) - let decoded_val_5, data_start_offset_5, data_end_offset_5 := abi_decode_component_bytes_100_calldata(head_start, head_offset_5) - if iszero(eq(data_start_offset_1, 192)) { revert_with_Error_uint256(259) } - if iszero(eq(data_start_offset_2, data_end_offset_1)) { revert_with_Error_uint256(259) } - if iszero(eq(data_start_offset_5, data_end_offset_2)) { revert_with_Error_uint256(259) } - if iszero(eq(encoding_size, data_end_offset_5)) { revert_with_Error_uint256(259) } - return_val_0 := decoded_val_0 - return_val_1 := decoded_val_1 - return_val_2 := decoded_val_2 - return_val_3 := decoded_val_3 - return_val_4 := decoded_val_4 - return_val_5 := decoded_val_5 +{ + function abi_decode_component_address_calldata(head_start, offset) -> return_val { + let ptr := add(head_start, offset) + return_val := calldataload(ptr) + if iszero(is_left_padded(96, return_val)) { revert_with_Error_uint256(259) } + } + function abi_decode_component_bool_calldata(head_start, offset) -> return_val { + let ptr := add(head_start, offset) + return_val := calldataload(ptr) + if iszero(is_left_padded(255, return_val)) { revert_with_Error_uint256(259) } + } + function abi_decode_component_bytes_100_calldata(head_start, head_offset) -> return_val, data_start_offset, data_end_offset { + let head_ptr := add(head_start, head_offset) + data_start_offset := calldataload(head_ptr) + let data_start := add(head_start, data_start_offset) + let bytes_size := calldataload(data_start) + if iszero(eq(bytes_size, 100)) { revert_with_Error_uint256(259) } + let data_size := add(bytes_size, 32) + let padded_data_size := ceil32(data_size) + data_end_offset := add(data_start_offset, padded_data_size) + let end_word := calldataload(sub(add(head_start, data_end_offset), 32)) + let padding_size_bits := mul(sub(padded_data_size, data_size), 8) + if iszero(is_right_padded(padding_size_bits, end_word)) { revert_with_Error_uint256(259) } + return_val := ccopym(add(data_start, 32), sub(data_size, 32)) + } + function abi_decode_component_string_42_calldata(head_start, head_offset) -> return_val, data_start_offset, data_end_offset { + let head_ptr := add(head_start, head_offset) + data_start_offset := calldataload(head_ptr) + let data_start := add(head_start, data_start_offset) + let string_size := calldataload(data_start) + if gt(string_size, 42) { revert_with_Error_uint256(259) } + let data_size := add(string_size, 32) + let padded_data_size := ceil32(data_size) + data_end_offset := add(data_start_offset, padded_data_size) + let end_word := calldataload(sub(add(head_start, data_end_offset), 32)) + let padding_size_bits := mul(sub(padded_data_size, data_size), 8) + if iszero(is_right_padded(padding_size_bits, end_word)) { revert_with_Error_uint256(259) } + return_val := ccopym(data_start, data_size) + } + function abi_decode_component_uint256_calldata(head_start, offset) -> return_val { + let ptr := add(head_start, offset) + return_val := calldataload(ptr) + if iszero(is_left_padded(0, return_val)) { revert_with_Error_uint256(259) } + } + function abi_decode_data_uint256_bytes_100_string_42_bool_address_bytes_100_calldata(head_start, data_end) -> return_val_0, return_val_1, return_val_2, return_val_3, return_val_4, return_val_5 { + let encoding_size := sub(data_end, head_start) + if or(lt(encoding_size, 544), gt(encoding_size, 608)) { revert_with_Error_uint256(259) } + let head_offset_0 := 0 + let head_offset_1 := 32 + let head_offset_2 := 64 + let head_offset_3 := 96 + let head_offset_4 := 128 + let head_offset_5 := 160 + let decoded_val_0 := abi_decode_component_uint256_calldata(head_start, head_offset_0) + let decoded_val_1, data_start_offset_1, data_end_offset_1 := abi_decode_component_bytes_100_calldata(head_start, head_offset_1) + let decoded_val_2, data_start_offset_2, data_end_offset_2 := abi_decode_component_string_42_calldata(head_start, head_offset_2) + let decoded_val_3 := abi_decode_component_bool_calldata(head_start, head_offset_3) + let decoded_val_4 := abi_decode_component_address_calldata(head_start, head_offset_4) + let decoded_val_5, data_start_offset_5, data_end_offset_5 := abi_decode_component_bytes_100_calldata(head_start, head_offset_5) + if iszero(eq(data_start_offset_1, 192)) { revert_with_Error_uint256(259) } + if iszero(eq(data_start_offset_2, data_end_offset_1)) { revert_with_Error_uint256(259) } + if iszero(eq(data_start_offset_5, data_end_offset_2)) { revert_with_Error_uint256(259) } + if iszero(eq(encoding_size, data_end_offset_5)) { revert_with_Error_uint256(259) } + return_val_0 := decoded_val_0 + return_val_1 := decoded_val_1 + return_val_2 := decoded_val_2 + return_val_3 := decoded_val_3 + return_val_4 := decoded_val_4 + return_val_5 := decoded_val_5 + } } diff --git a/crates/yulgen/tests/snapshots/yulgen__abi_dispatcher.snap b/crates/yulgen/tests/snapshots/yulgen__abi_dispatcher.snap index d1399b8f91..01efe61a94 100644 --- a/crates/yulgen/tests/snapshots/yulgen__abi_dispatcher.snap +++ b/crates/yulgen/tests/snapshots/yulgen__abi_dispatcher.snap @@ -1,18 +1,18 @@ --- source: crates/yulgen/tests/yulgen.rs -expression: "abi_dispatcher::dispatcher(&function_attributes())" +expression: "abi_dispatcher::dispatcher(functions())" --- switch cloadn(0, 4) case 0x9476f922 { - let return_val := $$hello_world() + let return_val := $$somemod$hello_world() let encoding_start := abi_encode_string_42(return_val) let encoding_size := add(64, ceil32(mload(return_val))) return(encoding_start, encoding_size) } case 0x771602f7 { let call_val_0, call_val_1 := abi_decode_data_uint256_uint256_calldata(4, calldatasize()) - let return_val := $$add(call_val_0, call_val_1) + let return_val := $$somemod$add(call_val_0, call_val_1) let encoding_start := abi_encode_uint256(return_val) let encoding_size := add(32, 0) return(encoding_start, encoding_size) diff --git a/crates/yulgen/tests/yulgen.rs b/crates/yulgen/tests/yulgen.rs index 4a93d564d1..26cb1a32ce 100644 --- a/crates/yulgen/tests/yulgen.rs +++ b/crates/yulgen/tests/yulgen.rs @@ -1,11 +1,8 @@ -use fe_analyzer::namespace::types::{Base, FixedSize}; use fe_yulgen::constructor; use fe_yulgen::names::abi as abi_names; use fe_yulgen::operations::{abi as abi_operations, data as data_operations}; use fe_yulgen::runtime::abi_dispatcher; -use fe_yulgen::runtime::functions::{ - abi as abi_functions, revert as revert_functions, structs as structs_functions, -}; +use fe_yulgen::runtime::functions::{abi as abi_functions, revert as revert_functions}; use fe_yulgen::types::{AbiDecodeLocation, AbiType}; use insta::assert_display_snapshot; use wasm_bindgen_test::wasm_bindgen_test; @@ -109,31 +106,6 @@ test_yulgen! { abi_functions::decode_component_bytes(26, AbiDecodeLocation::Calldata) } -fn struct_bool_bool_fields() -> Vec<(String, FixedSize)> { - vec![ - ("bar".into(), FixedSize::Base(Base::Bool)), - ("bar2".into(), FixedSize::Base(Base::Bool)), - ] -} - -// struct functions -test_yulgen! { - struct_empty_function, - structs_functions::generate_new_fn("Foo", &[]) -} -test_yulgen! { - struct_new_gen_function, - structs_functions::generate_new_fn("Foo", &struct_bool_bool_fields()) -} -test_yulgen! { - struct_getter_gen_bar_function, - structs_functions::generate_get_fn("Foo", &struct_bool_bool_fields()[0], 0) -} -test_yulgen! { - struct_getter_gen_bar2_function, - structs_functions::generate_get_fn("Foo", &struct_bool_bool_fields()[1], 1) -} - // data operations test_yulgen! { emit_event_no_indexed_operation,