Skip to content

Commit

Permalink
Dynamic returns from external calls.
Browse files Browse the repository at this point in the history
  • Loading branch information
g-r-a-n-t authored and cburgdorf committed May 26, 2021
1 parent 3b414ec commit ddd4f0a
Show file tree
Hide file tree
Showing 6 changed files with 153 additions and 66 deletions.
20 changes: 12 additions & 8 deletions analyzer/src/traversal/expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -661,8 +661,18 @@ fn expr_call_value_attribute(
if let fe::Expr::Attribute { value, attr } = &func.kind {
let value_attributes = expr(Rc::clone(&scope), Rc::clone(&context), &value)?;

if let Type::Contract(contract) = value_attributes.typ {
return expr_call_contract_attribute(scope, context, contract, &attr.kind, args);
if let Type::Contract(contract) = &value_attributes.typ {
// We must ensure the expression is loaded onto the stack.
context
.borrow_mut()
.update_expression(value, value_attributes.clone().into_loaded()?);
return expr_call_contract_attribute(
scope,
context,
contract.to_owned(),
&attr.kind,
args,
);
}

// for now all of these function expect 0 arguments
Expand Down Expand Up @@ -791,12 +801,6 @@ fn expr_call_contract_attribute(
{
let return_type = function.return_type.to_owned();

if matches!(return_type, FixedSize::String(_)) {
// we need figure out how to deal with dynamically sized returns
// for now, this only affects strings
todo!("external call string returns")
}

let argument_attributes = args
.kind
.iter()
Expand Down
56 changes: 2 additions & 54 deletions compiler/src/yul/operations/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,41 +51,6 @@ pub fn encode_size<T: AbiEncoding>(types: Vec<T>, vals: Vec<yul::Expression>) ->
return expression! { add([static_size], [data_operations::sum(dyn_size)]) };
}

/// Returns an expression that gives the max size of the encoded values.
///
/// # Panics
/// This will panic if any of the elements of the `types` vector have dynamic
/// size.
pub fn static_encode_size<T: AbiEncoding>(types: Vec<T>) -> yul::Expression {
let mut static_size = 0;

for typ in types {
match typ.abi_type() {
AbiType::Uint { .. } => static_size += 32,
AbiType::Array { inner, size } => {
let inner_size = match *inner {
AbiType::Uint {
size: AbiUintSize { padded_size, .. },
} => padded_size,
AbiType::Array { .. } => todo!(),
AbiType::Tuple { .. } => todo!(),
};
match size {
AbiArraySize::Static { size } => {
static_size += utils::ceil_32(inner_size * size)
}
AbiArraySize::Dynamic => {
panic!("tried to get the static encoding size of dynamically sized value")
}
}
}
AbiType::Tuple { elems } => static_size += elems.len() * 32,
}
}

literal_expression! { (static_size) }
}

/// Returns a list of expressions that can be used to decode given types.
/// `start` is where the encoding starts and `loc` indicates whether the data is
/// in calldata or memory.
Expand All @@ -110,10 +75,8 @@ pub fn decode<T: AbiEncoding>(

#[cfg(test)]
mod tests {
use crate::yul::operations::abi::{decode, encode, encode_size, static_encode_size};
use fe_analyzer::namespace::types::{
AbiDecodeLocation, Array, Base, FeString, FixedSize, U256,
};
use crate::yul::operations::abi::{decode, encode, encode_size};
use fe_analyzer::namespace::types::{AbiDecodeLocation, FeString, U256};
use yultsur::*;

#[test]
Expand Down Expand Up @@ -144,19 +107,4 @@ mod tests {
"abi_decode_string_26_calldata(42, 0)"
)
}

#[test]
fn test_static_encode_size() {
assert_eq!(
static_encode_size(vec![
FixedSize::Array(Array {
inner: Base::Address,
size: 42
}),
FixedSize::bool()
])
.to_string(),
"1376"
)
}
}
7 changes: 3 additions & 4 deletions compiler/src/yul/runtime/functions/contracts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,6 @@ pub fn calls(contract: Contract) -> Vec<yul::Statement> {
}
}
} else {
let decoding_size =
abi_operations::static_encode_size(vec![function.return_type.clone()]);
let decoding_operation = abi_operations::decode(
vec![function.return_type],
identifier_expression! { outstart },
Expand All @@ -73,9 +71,10 @@ pub fn calls(contract: Contract) -> Vec<yul::Statement> {
(let instart := alloc_mstoren([selector], 4))
(let insize := add(4, [encoding_size]))
(pop([encoding_operation]))
(let outsize := [decoding_size])
(pop((call((gas()), addr, 0, instart, insize, 0, 0))))
(let outsize := returndatasize())
(let outstart := alloc(outsize))
(pop((call((gas()), addr, 0, instart, insize, outstart, outsize))))
(returndatacopy(outstart, 0, outsize))
(return_val := [decoding_operation])
}
}
Expand Down
72 changes: 72 additions & 0 deletions compiler/tests/cases/stress.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,3 +256,75 @@ fn tuple_stress() {
);
});
}

#[test]
fn external_calls_stress() {
with_executor(&|mut executor| {
let foo_harness = deploy_contract(&mut executor, "external_calls.fe", "Foo", &[]);
let foo_address = ethabi::Token::Address(foo_harness.address);
let proxy_harness = deploy_contract(
&mut executor,
"external_calls.fe",
"FooProxy",
&[foo_address],
);

let my_tuple = tuple_token(&[uint_token(42), address_token("26")]);
let my_string = string_token("hello world");
let my_other_tuple = tuple_token(&[uint_token(99), address_token("ab")]);
let my_other_string = string_token("foo");

proxy_harness.test_function(
&mut executor,
"call_set_my_string",
&[my_string.clone()],
None,
);
proxy_harness.test_function(&mut executor, "call_get_my_string", &[], Some(&my_string));

proxy_harness.test_function(
&mut executor,
"call_set_my_tuple",
&[my_tuple.clone()],
None,
);
proxy_harness.test_function(&mut executor, "call_get_my_tuple", &[], Some(&my_tuple));

proxy_harness.test_function(
&mut executor,
"call_set_my_string_and_tuple",
&[my_other_string.clone(), my_other_tuple.clone()],
None,
);
proxy_harness.test_function(
&mut executor,
"call_get_my_tuple",
&[],
Some(&my_other_tuple),
);
proxy_harness.test_function(
&mut executor,
"call_get_my_string",
&[],
Some(&my_other_string),
);

proxy_harness.test_function(
&mut executor,
"call_get_tuple",
&[],
Some(&tuple_token(&[
uint_token(42),
uint_token(26),
bool_token(false),
])),
);
proxy_harness.test_function(
&mut executor,
"call_get_string",
&[],
Some(&string_token("hi")),
);
proxy_harness.test_function(&mut executor, "call_get_array", &[], None);
})
}
63 changes: 63 additions & 0 deletions compiler/tests/fixtures/stress/external_calls.fe
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
contract Foo:
my_tuple: (u256, address)
my_string: string100

pub def get_my_string() -> string100:
return self.my_string.to_mem()

pub def set_my_string(some_string: string100):
self.my_string = some_string

pub def get_my_tuple() -> (u256, address):
return self.my_tuple.to_mem()

pub def set_my_tuple(some_tuple: (u256, address)):
self.my_tuple = some_tuple

pub def set_my_string_and_tuple(some_string: string100, some_tuple: (u256, address)):
self.my_string = some_string
self.my_tuple = some_tuple

pub def get_string() -> string10:
return string10("hi")

pub def get_array() -> u16[4]:
return [u16(1), u16(2), u16(3), u16(257)]

pub def get_tuple() -> (u256, u256, bool):
return (42, 26, false)


contract FooProxy:
foo: Foo

pub def __init__(foo_addr: address):
self.foo = Foo(foo_addr)

pub def call_set_my_string(some_string: string100):
self.foo.set_my_string(some_string)

pub def call_get_my_string() -> string100:
return self.foo.get_my_string()

pub def call_set_my_tuple(some_tuple: (u256, address)):
self.foo.set_my_tuple(some_tuple)

pub def call_get_my_tuple() -> (u256, address):
return self.foo.get_my_tuple()

pub def call_set_my_string_and_tuple(some_string: string100, some_tuple: (u256, address)):
self.foo.set_my_string_and_tuple(some_string, some_tuple)

pub def call_get_string() -> string10:
return self.foo.get_string()

pub def call_get_tuple() -> (u256, u256, bool):
return self.foo.get_tuple()

pub def call_get_array():
my_array: u16[4] = self.foo.get_array()
assert my_array[0] == u16(1)
assert my_array[1] == u16(2)
assert my_array[2] == u16(3)
assert my_array[3] == u16(257)
1 change: 1 addition & 0 deletions newsfragments/415.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
External calls can now handle dynamically-sized return types.

0 comments on commit ddd4f0a

Please sign in to comment.