Skip to content

Commit

Permalink
Allow compilation of contracts that have no public functions
Browse files Browse the repository at this point in the history
  • Loading branch information
cburgdorf committed Feb 15, 2021
1 parent fee4658 commit 0ed8cfc
Show file tree
Hide file tree
Showing 5 changed files with 191 additions and 4 deletions.
10 changes: 7 additions & 3 deletions compiler/src/yul/runtime/abi_dispatcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,13 @@ pub fn dispatcher(attributes: Vec<FunctionAttributes>) -> yul::Statement {
.map(|arm| dispatch_arm(arm.to_owned()))
.collect::<Vec<_>>();

switch! {
switch (cloadn(0, 4))
[arms...]
if arms.is_empty() {
return statement! { pop(0) };
} else {
switch! {
switch (cloadn(0, 4))
[arms...]
}
}
}

Expand Down
7 changes: 6 additions & 1 deletion compiler/tests/evm_contracts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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, &[]);
Expand Down
2 changes: 2 additions & 0 deletions compiler/tests/fixtures/empty.fe
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
contract Empty:
lonely: u256
8 changes: 8 additions & 0 deletions newsfragments/219.bugfix.md
Original file line number Diff line number Diff line change
@@ -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
```
168 changes: 168 additions & 0 deletions output/Empty/Empty_ir.yul
Original file line number Diff line number Diff line change
@@ -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)
}

}

}

0 comments on commit 0ed8cfc

Please sign in to comment.