Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Streamlined ABI encoder logic for returning tuple types #2302

Merged
merged 2 commits into from
Feb 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions tests/parser/syntax/test_return_tuple.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,3 +140,57 @@ def foo(a: address) -> (uint256, uint256, uint256, uint256, uint256):
c2 = get_contract(code2)

assert c2.foo(c.address) == [1, 2, 3, 4, 5]


def test_single_type_tuple_int(get_contract):
code = """
@view
@external
def foo() -> (uint256[3], uint256, uint256[2][2]):
return [1,2,3], 4, [[5,6], [7,8]]

@view
@external
def foo2(a: int128, b: int128) -> (int128[5], int128, int128[2]):
return [1,2,3,a,5], b, [7,8]
"""

c = get_contract(code)

assert c.foo() == [[1, 2, 3], 4, [[5, 6], [7, 8]]]
assert c.foo2(4, 6) == [[1, 2, 3, 4, 5], 6, [7, 8]]


def test_single_type_tuple_address(get_contract):
code = """
@view
@external
def foo() -> (address, address[2]):
return (
self,
[0xF5D4020dCA6a62bB1efFcC9212AAF3c9819E30D7, 0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF]
)
"""

c = get_contract(code)

assert c.foo() == [
c.address,
[
"0xF5D4020dCA6a62bB1efFcC9212AAF3c9819E30D7",
"0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF",
],
]


def test_single_type_tuple_bytes(get_contract):
code = """
@view
@external
def foo() -> (Bytes[5], Bytes[5]):
return b"hello", b"there"
"""

c = get_contract(code)

assert c.foo() == [b"hello", b"there"]
37 changes: 33 additions & 4 deletions vyper/codegen/return_.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from vyper import ast as vy_ast
from vyper.parser.lll_node import LLLnode
from vyper.parser.parser_utils import getpos
from vyper.types import BaseType
from vyper.parser.parser_utils import getpos, make_setter
from vyper.types import BaseType, ListType, TupleType, get_size_of_type
from vyper.types.check import check_assign
from vyper.utils import MemoryPositions

Expand Down Expand Up @@ -88,9 +88,38 @@ def gen_tuple_return(stmt, context, sub):

check_assign(return_buffer, sub, pos=getpos(stmt))

# in case of multi we can't create a variable to store location of the return expression
# as multi can have data from multiple location like store, calldata etc
if sub.value == "multi":

if isinstance(context.return_type, TupleType) and not abi_typ.dynamic_size_bound():
# for tuples where every value is of the same type and a fixed length,
# we can simplify the encoding by treating it as though it were an array
base_types = set()
for typ in context.return_type.members:
while isinstance(typ, ListType):
typ = typ.subtype
base_types.add(typ.typ)

if len(base_types) == 1:
new_sub = LLLnode.from_list(
context.new_internal_variable(context.return_type),
typ=context.return_type,
location="memory",
)
setter = make_setter(new_sub, sub, "memory", pos=getpos(stmt))
return LLLnode.from_list(
[
"seq",
setter,
make_return_stmt(
stmt, context, new_sub, get_size_of_type(context.return_type) * 32,
),
],
typ=None,
pos=getpos(stmt),
)

# in case of multi we can't create a variable to store location of the return expression
# as multi can have data from multiple location like store, calldata etc
encode_out = abi_encode(return_buffer, sub, pos=getpos(stmt), returns=True)
load_return_len = ["mload", MemoryPositions.FREE_VAR_SPACE]
os = [
Expand Down