From 0ed8cfc7bbeaa5794b478558f26d2e80a3927cf9 Mon Sep 17 00:00:00 2001 From: Christoph Burgdorf Date: Mon, 15 Feb 2021 17:27:24 +0100 Subject: [PATCH] Allow compilation of contracts that have no public functions Fixes #219 --- compiler/src/yul/runtime/abi_dispatcher.rs | 10 +- compiler/tests/evm_contracts.rs | 7 +- compiler/tests/fixtures/empty.fe | 2 + newsfragments/219.bugfix.md | 8 + output/Empty/Empty_ir.yul | 168 +++++++++++++++++++++ 5 files changed, 191 insertions(+), 4 deletions(-) create mode 100644 compiler/tests/fixtures/empty.fe create mode 100644 newsfragments/219.bugfix.md create mode 100644 output/Empty/Empty_ir.yul diff --git a/compiler/src/yul/runtime/abi_dispatcher.rs b/compiler/src/yul/runtime/abi_dispatcher.rs index 4690dc593c..5be0f5e36c 100644 --- a/compiler/src/yul/runtime/abi_dispatcher.rs +++ b/compiler/src/yul/runtime/abi_dispatcher.rs @@ -16,9 +16,13 @@ pub fn dispatcher(attributes: Vec) -> yul::Statement { .map(|arm| dispatch_arm(arm.to_owned())) .collect::>(); - switch! { - switch (cloadn(0, 4)) - [arms...] + if arms.is_empty() { + return statement! { pop(0) }; + } else { + switch! { + switch (cloadn(0, 4)) + [arms...] + } } } diff --git a/compiler/tests/evm_contracts.rs b/compiler/tests/evm_contracts.rs index fd5b66b0ac..97a494988d 100644 --- a/compiler/tests/evm_contracts.rs +++ b/compiler/tests/evm_contracts.rs @@ -1054,7 +1054,12 @@ fn create_contract() { }) } -#[rstest(fixture_file, contract_name, case("ownable.fe", "Ownable"))] +#[rstest( + fixture_file, + contract_name, + case("ownable.fe", "Ownable"), + case("empty.fe", "Empty") +)] fn can_deploy_fixture(fixture_file: &str, contract_name: &str) { with_executor(&|mut executor| { deploy_contract(&mut executor, fixture_file, contract_name, &[]); diff --git a/compiler/tests/fixtures/empty.fe b/compiler/tests/fixtures/empty.fe new file mode 100644 index 0000000000..2dd735eac7 --- /dev/null +++ b/compiler/tests/fixtures/empty.fe @@ -0,0 +1,2 @@ +contract Empty: + lonely: u256 \ No newline at end of file diff --git a/newsfragments/219.bugfix.md b/newsfragments/219.bugfix.md new file mode 100644 index 0000000000..6728952d10 --- /dev/null +++ b/newsfragments/219.bugfix.md @@ -0,0 +1,8 @@ +Fix bug where compilation of contracts without public functions would result in illegal YUL. + +E.g without this change, the following doesn't compile to proper YUL + +``` +contract Empty: + lonely: u256 +``` \ No newline at end of file diff --git a/output/Empty/Empty_ir.yul b/output/Empty/Empty_ir.yul new file mode 100644 index 0000000000..9365c3f819 --- /dev/null +++ b/output/Empty/Empty_ir.yul @@ -0,0 +1,168 @@ +object \"Empty\" { + code { + let size := datasize(\"runtime\") datacopy(0, dataoffset(\"runtime\"), size) return(0, size) + } + object \"runtime\" { + code { + function avail() -> ptr { + ptr := mload(0x00) if eq(ptr, 0x00) { + ptr := 0x20 + } + + } + function alloc(size) -> ptr { + ptr := mload(0x00) if eq(ptr, 0x00) { + ptr := 0x20 + } + mstore(0x00, add(ptr, size)) + } + function alloc_mstoren(val, size) -> ptr { + ptr := alloc(size) mstoren(ptr, val, size) + } + function free(ptr) { + mstore(0x00, ptr) + } + function ccopym(cptr, size) -> mptr { + mptr := alloc(size) calldatacopy(mptr, cptr, size) + } + function load_data_string(code_ptr, size) -> mptr { + mptr := alloc(32) mstore(mptr, size) let content_ptr := alloc(size) datacopy(content_ptr, code_ptr, size) + } + function mcopys(mptr, sptr, size) { + let offset := 0 for { + + } + lt(add(offset, 32), size) { + + } + { + let _mptr := add(mptr, offset) let _sptr := add(sptr, offset) sstore(_sptr, mload(_mptr)) offset := add(offset, 32) + } + let rem := sub(size, offset) if gt(rem, 0) { + let _mptr := add(mptr, offset) let _sptr := add(sptr, offset) sstoren(_sptr, mloadn(_mptr, rem), rem) + } + + } + function scopym(sptr, size) -> mptr { + mptr := alloc(size) let offset := 0 for { + + } + lt(add(offset, 32), size) { + + } + { + let _mptr := add(mptr, offset) let _sptr := add(sptr, offset) mstore(_mptr, sload(_sptr)) offset := add(offset, 32) + } + let rem := sub(size, offset) if gt(rem, 0) { + let _mptr := add(mptr, offset) let _sptr := add(sptr, offset) mstoren(_mptr, sloadn(_sptr, rem), rem) + } + + } + function mcopym(ptr1, size) -> ptr2 { + ptr2 := alloc(size) let offset := 0 for { + + } + lt(add(offset, 32), size) { + + } + { + let _ptr1 := add(ptr1, offset) let _ptr2 := add(ptr2, offset) mstore(_ptr2, mload(_ptr1)) offset := add(offset, 32) + } + let rem := sub(size, offset) if gt(rem, 0) { + let _ptr1 := add(ptr1, offset) let _ptr2 := add(ptr2, offset) mstoren(_ptr2, mloadn(_ptr1, rem), rem) + } + + } + function scopys(ptr1, ptr2, size) { + let offset := 0 for { + + } + lt(add(offset, 32), size) { + + } + { + let _ptr1 := add(ptr1, offset) let _ptr2 := add(ptr2, offset) sstore(_ptr2, sload(_ptr1)) offset := add(offset, 32) + } + let rem := sub(size, offset) if gt(rem, 0) { + let _ptr1 := add(ptr1, offset) let _ptr2 := add(ptr2, offset) sstoren(_ptr2, sloadn(_ptr1, rem), rem) + } + + } + function mloadn(ptr, size) -> val { + val := shr(sub(256, mul(8, size)), mload(ptr)) + } + function sloadn(ptr, size) -> val { + val := shr(sub(256, mul(8, size)), sload(ptr)) + } + function cloadn(ptr, size) -> val { + val := shr(sub(256, mul(8, size)), calldataload(ptr)) + } + function mstoren(ptr, val, size) { + let size_bits := mul(8, size) let left := shl(sub(256, size_bits), val) let right := shr(size_bits, mload(add(ptr, size))) mstore(ptr, or(left, right)) + } + function sstoren(ptr, val, size) { + let size_bits := mul(8, size) let left := shl(sub(256, size_bits), val) let right := shr(size_bits, sload(add(ptr, size))) sstore(ptr, or(left, right)) + } + function dualkeccak256(a, b) -> return_val { + let ptr := avail() mstore(ptr, a) mstore(add(ptr, 32), b) return_val := keccak256(ptr, 64) + } + function ceil32(n) -> return_val { + return_val := mul(div(add(n, 31), 32), 32) + } + function ternary(test, if_expr, else_expr) -> result { + switch test case 1 { + result := if_expr + } + case 0 { + result := else_expr + } + + } + function abi_unpack(mptr, array_size, inner_data_size) { + for { + let i := 0 + } + lt(i, array_size) { + i := add(i, 1) + } + { + let val_ptr := add(mptr, mul(i, inner_data_size)) let val := mloadn(val_ptr, inner_data_size) pop(alloc_mstoren(val, 32)) + } + + } + function abi_pack_calldata(mptr, array_size, inner_data_size) -> packed_ptr { + packed_ptr := avail() for { + let i := 0 + } + lt(i, array_size) { + i := add(i, 1) + } + { + let val_ptr := add(mptr, mul(i, 32)) let val := calldataload(val_ptr) pop(alloc_mstoren(val, inner_data_size)) + } + + } + function abi_pack_mem(mptr, array_size, inner_data_size) -> packed_ptr { + packed_ptr := avail() for { + let i := 0 + } + lt(i, array_size) { + i := add(i, 1) + } + { + let val_ptr := add(mptr, mul(i, 32)) let val := mload(val_ptr) pop(alloc_mstoren(val, inner_data_size)) + } + + } + function contract_create2(data_ptr, data_size, value, salt) -> return_address { + let mptr := alloc(data_size) datacopy(mptr, data_ptr, data_size) return_address := create2(value, mptr, data_size, salt) + } + function contract_create(data_ptr, data_size, value) -> return_address { + let mptr := alloc(data_size) datacopy(mptr, data_ptr, data_size) return_address := create(value, mptr, data_size) + } + pop(0) return(0, 0) + } + + } + +}