We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Malformed data when passing a tuple or struct that contains an array
### Version Information

* vyper Version (output of `vyper --version`): `0.1.0-beta16` (current master)
* OS: linux
* Python Version: 3.7

### What's your issue about?

As reported by @michwill on Gitter, passing a struct between functions, where the struct contains an array, results in malformed data. WIth the following contract:

```python
struct A:
    many: uint256[4]
    one: uint256

@private
@constant
def _foo(_many: uint256[4], _one: uint256) -> A:
    return A({many: _many, one: _one})

@public
@constant
def foo() -> A:
    return self._foo([1, 2, 3, 4], 5)

@public
@constant
def bar() -> (uint256[4], uint256):
    out: A = self._foo([1, 2, 3, 4], 5)
    return out.many, out.one
```

Here is the output of each public function:

```python
>>> contract.foo()
((1, 2, 5, 0), 0)
>>> contract.bar()
((0, 0, 5, 4), 3)
```

Observations:
1. Generating the struct within the public function and returning it, or a component of it, works as expected.
2. Passing the struct between public functions of two different contracts works as expected.
3. Structs that do not contain arrays work as expected.
4. Removing the `@constant` modifier has no effect.

### How can it be fixed?
It looks like an off-by-one (or 2?) error to me, but I'm unsure how to fix it.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `vyper/parser/self_call.py`
Content:
```
1 import itertools
2 
3 from vyper.exceptions import (
4     ConstancyViolation,
5     StructureException,
6     TypeMismatch,
7 )
8 from vyper.parser.lll_node import LLLnode
9 from vyper.parser.parser_utils import getpos, pack_arguments
10 from vyper.signatures.function_signature import FunctionSignature
11 from vyper.types import (
12     BaseType,
13     ByteArrayLike,
14     ListType,
15     TupleLike,
16     get_size_of_type,
17     get_static_size_of_type,
18     has_dynamic_data,
19 )
20 
21 
22 def call_lookup_specs(stmt_expr, context):
23     from vyper.parser.expr import Expr
24 
25     method_name = stmt_expr.func.attr
26 
27     if len(stmt_expr.keywords):
28         raise TypeMismatch(
29             "Cannot use keyword arguments in calls to functions via 'self'",
30             stmt_expr,
31         )
32     expr_args = [
33         Expr(arg, context).lll_node
34         for arg in stmt_expr.args
35     ]
36 
37     sig = FunctionSignature.lookup_sig(
38         context.sigs,
39         method_name,
40         expr_args,
41         stmt_expr,
42         context,
43     )
44 
45     return method_name, expr_args, sig
46 
47 
48 def make_call(stmt_expr, context):
49     method_name, _, sig = call_lookup_specs(stmt_expr, context)
50 
51     if context.is_constant() and not sig.const:
52         raise ConstancyViolation(
53             f"May not call non-constant function '{method_name}' within {context.pp_constancy()}.",
54             getpos(stmt_expr)
55         )
56 
57     if not sig.private:
58         raise StructureException("Cannot call public functions via 'self'", stmt_expr)
59 
60     return call_self_private(stmt_expr, context, sig)
61 
62 
63 def call_make_placeholder(stmt_expr, context, sig):
64     if sig.output_type is None:
65         return 0, 0, 0
66 
67     output_placeholder = context.new_placeholder(typ=sig.output_type)
68     out_size = get_size_of_type(sig.output_type) * 32
69     returner = output_placeholder
70 
71     if not sig.private and isinstance(sig.output_type, ByteArrayLike):
72         returner = output_placeholder + 32
73 
74     return output_placeholder, returner, out_size
75 
76 
77 def call_self_private(stmt_expr, context, sig):
78     # ** Private Call **
79     # Steps:
80     # (x) push current local variables
81     # (x) push arguments
82     # (x) push jumpdest (callback ptr)
83     # (x) jump to label
84     # (x) pop return values
85     # (x) pop local variables
86 
87     method_name, expr_args, sig = call_lookup_specs(stmt_expr, context)
88     pre_init = []
89     pop_local_vars = []
90     push_local_vars = []
91     pop_return_values = []
92     push_args = []
93 
94     # Push local variables.
95     var_slots = [
96         (v.pos, v.size) for name, v in context.vars.items()
97         if v.location == 'memory'
98     ]
99     if var_slots:
100         var_slots.sort(key=lambda x: x[0])
101         mem_from, mem_to = var_slots[0][0], var_slots[-1][0] + var_slots[-1][1] * 32
102 
103         i_placeholder = context.new_placeholder(BaseType('uint256'))
104         local_save_ident = f"_{stmt_expr.lineno}_{stmt_expr.col_offset}"
105         push_loop_label = 'save_locals_start' + local_save_ident
106         pop_loop_label = 'restore_locals_start' + local_save_ident
107 
108         if mem_to - mem_from > 320:
109             push_local_vars = [
110                     ['mstore', i_placeholder, mem_from],
111                     ['label', push_loop_label],
112                     ['mload', ['mload', i_placeholder]],
113                     ['mstore', i_placeholder, ['add', ['mload', i_placeholder], 32]],
114                     ['if', ['lt', ['mload', i_placeholder], mem_to],
115                         ['goto', push_loop_label]]
116             ]
117             pop_local_vars = [
118                 ['mstore', i_placeholder, mem_to - 32],
119                 ['label', pop_loop_label],
120                 ['mstore', ['mload', i_placeholder], 'pass'],
121                 ['mstore', i_placeholder, ['sub', ['mload', i_placeholder], 32]],
122                 ['if', ['ge', ['mload', i_placeholder], mem_from],
123                        ['goto', pop_loop_label]]
124             ]
125         else:
126             push_local_vars = [['mload', pos] for pos in range(mem_from, mem_to, 32)]
127             pop_local_vars = [['mstore', pos, 'pass'] for pos in range(mem_to-32, mem_from-32, -32)]
128 
129     # Push Arguments
130     if expr_args:
131         inargs, inargsize, arg_pos = pack_arguments(
132             sig,
133             expr_args,
134             context,
135             stmt_expr,
136             return_placeholder=False,
137         )
138         push_args += [inargs]  # copy arguments first, to not mess up the push/pop sequencing.
139 
140         static_arg_size = 32 * sum(
141                 [get_static_size_of_type(arg.typ)
142                     for arg in expr_args])
143         static_pos = int(arg_pos + static_arg_size)
144         needs_dyn_section = any(
145                 [has_dynamic_data(arg.typ)
146                     for arg in expr_args])
147 
148         if needs_dyn_section:
149             ident = f'push_args_{sig.method_id}_{stmt_expr.lineno}_{stmt_expr.col_offset}'
150             start_label = ident + '_start'
151             end_label = ident + '_end'
152             i_placeholder = context.new_placeholder(BaseType('uint256'))
153 
154             # Calculate copy start position.
155             # Given | static | dynamic | section in memory,
156             # copy backwards so the values are in order on the stack.
157             # We calculate i, the end of the whole encoded part
158             # (i.e. the starting index for copy)
159             # by taking ceil32(len<arg>) + offset<arg> + arg_pos
160             # for the last dynamic argument and arg_pos is the start
161             # the whole argument section.
162             idx = 0
163             for arg in expr_args:
164                 if isinstance(arg.typ, ByteArrayLike):
165                     last_idx = idx
166                 idx += get_static_size_of_type(arg.typ)
167             push_args += [
168                 ['with', 'offset', ['mload', arg_pos + last_idx * 32],
169                     ['with', 'len_pos', ['add', arg_pos, 'offset'],
170                         ['with', 'len_value', ['mload', 'len_pos'],
171                             ['mstore', i_placeholder,
172                                 ['add', 'len_pos', ['ceil32', 'len_value']]]]]]
173             ]
174             # loop from end of dynamic section to start of dynamic section,
175             # pushing each element onto the stack.
176             push_args += [
177 
178                 ['label', start_label],
179                 ['if', ['lt', ['mload', i_placeholder], static_pos],
180                     ['goto', end_label]],
181                 ['mload', ['mload', i_placeholder]],
182                 ['mstore', i_placeholder, ['sub', ['mload', i_placeholder], 32]],  # decrease i
183                 ['goto', start_label],
184                 ['label', end_label]
185             ]
186 
187         # push static section
188         push_args += [
189             ['mload', pos] for pos in reversed(range(arg_pos, static_pos, 32))
190         ]
191     elif sig.args:
192         raise StructureException(
193             f"Wrong number of args for: {sig.name} (0 args given, expected {len(sig.args)})",
194             stmt_expr
195         )
196 
197     # Jump to function label.
198     jump_to_func = [
199         ['add', ['pc'], 6],  # set callback pointer.
200         ['goto', f'priv_{sig.method_id}'],
201         ['jumpdest'],
202     ]
203 
204     # Pop return values.
205     returner = [0]
206     if sig.output_type:
207         output_placeholder, returner, output_size = call_make_placeholder(stmt_expr, context, sig)
208         if output_size > 0:
209             dynamic_offsets = []
210             if isinstance(sig.output_type, (BaseType, ListType)):
211                 pop_return_values = [
212                     ['mstore', ['add', output_placeholder, pos], 'pass']
213                     for pos in range(0, output_size, 32)
214                 ]
215             elif isinstance(sig.output_type, ByteArrayLike):
216                 dynamic_offsets = [(0, sig.output_type)]
217                 pop_return_values = [
218                     ['pop', 'pass'],
219                 ]
220             elif isinstance(sig.output_type, TupleLike):
221                 static_offset = 0
222                 pop_return_values = []
223                 for out_type in sig.output_type.members:
224                     if isinstance(out_type, ByteArrayLike):
225                         pop_return_values.append(
226                             ['mstore', ['add', output_placeholder, static_offset], 'pass']
227                         )
228                         dynamic_offsets.append(
229                             (['mload', ['add', output_placeholder, static_offset]], out_type)
230                         )
231                     else:
232                         pop_return_values.append(
233                             ['mstore', ['add', output_placeholder, static_offset], 'pass']
234                         )
235                     static_offset += 32
236 
237             # append dynamic unpacker.
238             dyn_idx = 0
239             for in_memory_offset, _out_type in dynamic_offsets:
240                 ident = f"{stmt_expr.lineno}_{stmt_expr.col_offset}_arg_{dyn_idx}"
241                 dyn_idx += 1
242                 start_label = 'dyn_unpack_start_' + ident
243                 end_label = 'dyn_unpack_end_' + ident
244                 i_placeholder = context.new_placeholder(typ=BaseType('uint256'))
245                 begin_pos = ['add', output_placeholder, in_memory_offset]
246                 # loop until length.
247                 o = LLLnode.from_list(
248                     ['seq_unchecked',
249                         ['mstore', begin_pos, 'pass'],  # get len
250                         ['mstore', i_placeholder, 0],
251                         ['label', start_label],
252                         [  # break
253                             'if',
254                             ['ge', ['mload', i_placeholder], ['ceil32', ['mload', begin_pos]]],
255                             ['goto', end_label]
256                         ],
257                         [  # pop into correct memory slot.
258                             'mstore',
259                             ['add', ['add', begin_pos, 32], ['mload', i_placeholder]],
260                             'pass',
261                         ],
262                         # increment i
263                         ['mstore', i_placeholder, ['add', 32, ['mload', i_placeholder]]],
264                         ['goto', start_label],
265                         ['label', end_label]],
266                     typ=None, annotation='dynamic unpacker', pos=getpos(stmt_expr))
267                 pop_return_values.append(o)
268 
269     call_body = list(itertools.chain(
270         ['seq_unchecked'],
271         pre_init,
272         push_local_vars,
273         push_args,
274         jump_to_func,
275         pop_return_values,
276         pop_local_vars,
277         [returner],
278     ))
279     # If we have no return, we need to pop off
280     pop_returner_call_body = ['pop', call_body] if sig.output_type is None else call_body
281 
282     o = LLLnode.from_list(
283         pop_returner_call_body,
284         typ=sig.output_type,
285         location='memory',
286         pos=getpos(stmt_expr),
287         annotation=f'Internal Call: {method_name}',
288         add_gas_estimate=sig.gas
289     )
290     o.gas += sig.gas
291     return o
292
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/vyper/parser/self_call.py b/vyper/parser/self_call.py
--- a/vyper/parser/self_call.py
+++ b/vyper/parser/self_call.py
@@ -220,19 +220,22 @@
             elif isinstance(sig.output_type, TupleLike):
                 static_offset = 0
                 pop_return_values = []
-                for out_type in sig.output_type.members:
-                    if isinstance(out_type, ByteArrayLike):
+                for name, typ in sig.output_type.tuple_items():
+                    if isinstance(typ, ByteArrayLike):
                         pop_return_values.append(
                             ['mstore', ['add', output_placeholder, static_offset], 'pass']
                         )
                         dynamic_offsets.append(
-                            (['mload', ['add', output_placeholder, static_offset]], out_type)
+                            (['mload', ['add', output_placeholder, static_offset]], name)
                         )
+                        static_offset += 32
                     else:
-                        pop_return_values.append(
-                            ['mstore', ['add', output_placeholder, static_offset], 'pass']
-                        )
-                    static_offset += 32
+                        member_output_size = get_size_of_type(typ) * 32
+                        pop_return_values.extend([
+                            ['mstore', ['add', output_placeholder, pos], 'pass']
+                            for pos in range(static_offset, static_offset + member_output_size, 32)
+                        ])
+                        static_offset += member_output_size
 
             # append dynamic unpacker.
             dyn_idx = 0

{"golden_diff": "diff --git a/vyper/parser/self_call.py b/vyper/parser/self_call.py\n--- a/vyper/parser/self_call.py\n+++ b/vyper/parser/self_call.py\n@@ -220,19 +220,22 @@\n             elif isinstance(sig.output_type, TupleLike):\n                 static_offset = 0\n                 pop_return_values = []\n-                for out_type in sig.output_type.members:\n-                    if isinstance(out_type, ByteArrayLike):\n+                for name, typ in sig.output_type.tuple_items():\n+                    if isinstance(typ, ByteArrayLike):\n                         pop_return_values.append(\n                             ['mstore', ['add', output_placeholder, static_offset], 'pass']\n                         )\n                         dynamic_offsets.append(\n-                            (['mload', ['add', output_placeholder, static_offset]], out_type)\n+                            (['mload', ['add', output_placeholder, static_offset]], name)\n                         )\n+                        static_offset += 32\n                     else:\n-                        pop_return_values.append(\n-                            ['mstore', ['add', output_placeholder, static_offset], 'pass']\n-                        )\n-                    static_offset += 32\n+                        member_output_size = get_size_of_type(typ) * 32\n+                        pop_return_values.extend([\n+                            ['mstore', ['add', output_placeholder, pos], 'pass']\n+                            for pos in range(static_offset, static_offset + member_output_size, 32)\n+                        ])\n+                        static_offset += member_output_size\n \n             # append dynamic unpacker.\n             dyn_idx = 0\n", "issue": "Malformed data when passing a tuple or struct that contains an array\n### Version Information\r\n\r\n* vyper Version (output of `vyper --version`): `0.1.0-beta16` (current master)\r\n* OS: linux\r\n* Python Version: 3.7\r\n\r\n### What's your issue about?\r\n\r\nAs reported by @michwill on Gitter, passing a struct between functions, where the struct contains an array, results in malformed data. WIth the following contract:\r\n\r\n```python\r\nstruct A:\r\n    many: uint256[4]\r\n    one: uint256\r\n\r\n@private\r\n@constant\r\ndef _foo(_many: uint256[4], _one: uint256) -> A:\r\n    return A({many: _many, one: _one})\r\n\r\n@public\r\n@constant\r\ndef foo() -> A:\r\n    return self._foo([1, 2, 3, 4], 5)\r\n\r\n@public\r\n@constant\r\ndef bar() -> (uint256[4], uint256):\r\n    out: A = self._foo([1, 2, 3, 4], 5)\r\n    return out.many, out.one\r\n```\r\n\r\nHere is the output of each public function:\r\n\r\n```python\r\n>>> contract.foo()\r\n((1, 2, 5, 0), 0)\r\n>>> contract.bar()\r\n((0, 0, 5, 4), 3)\r\n```\r\n\r\nObservations:\r\n1. Generating the struct within the public function and returning it, or a component of it, works as expected.\r\n2. Passing the struct between public functions of two different contracts works as expected.\r\n3. Structs that do not contain arrays work as expected.\r\n4. Removing the `@constant` modifier has no effect.\r\n\r\n### How can it be fixed?\r\nIt looks like an off-by-one (or 2?) error to me, but I'm unsure how to fix it.\n", "before_files": [{"content": "import itertools\n\nfrom vyper.exceptions import (\n    ConstancyViolation,\n    StructureException,\n    TypeMismatch,\n)\nfrom vyper.parser.lll_node import LLLnode\nfrom vyper.parser.parser_utils import getpos, pack_arguments\nfrom vyper.signatures.function_signature import FunctionSignature\nfrom vyper.types import (\n    BaseType,\n    ByteArrayLike,\n    ListType,\n    TupleLike,\n    get_size_of_type,\n    get_static_size_of_type,\n    has_dynamic_data,\n)\n\n\ndef call_lookup_specs(stmt_expr, context):\n    from vyper.parser.expr import Expr\n\n    method_name = stmt_expr.func.attr\n\n    if len(stmt_expr.keywords):\n        raise TypeMismatch(\n            \"Cannot use keyword arguments in calls to functions via 'self'\",\n            stmt_expr,\n        )\n    expr_args = [\n        Expr(arg, context).lll_node\n        for arg in stmt_expr.args\n    ]\n\n    sig = FunctionSignature.lookup_sig(\n        context.sigs,\n        method_name,\n        expr_args,\n        stmt_expr,\n        context,\n    )\n\n    return method_name, expr_args, sig\n\n\ndef make_call(stmt_expr, context):\n    method_name, _, sig = call_lookup_specs(stmt_expr, context)\n\n    if context.is_constant() and not sig.const:\n        raise ConstancyViolation(\n            f\"May not call non-constant function '{method_name}' within {context.pp_constancy()}.\",\n            getpos(stmt_expr)\n        )\n\n    if not sig.private:\n        raise StructureException(\"Cannot call public functions via 'self'\", stmt_expr)\n\n    return call_self_private(stmt_expr, context, sig)\n\n\ndef call_make_placeholder(stmt_expr, context, sig):\n    if sig.output_type is None:\n        return 0, 0, 0\n\n    output_placeholder = context.new_placeholder(typ=sig.output_type)\n    out_size = get_size_of_type(sig.output_type) * 32\n    returner = output_placeholder\n\n    if not sig.private and isinstance(sig.output_type, ByteArrayLike):\n        returner = output_placeholder + 32\n\n    return output_placeholder, returner, out_size\n\n\ndef call_self_private(stmt_expr, context, sig):\n    # ** Private Call **\n    # Steps:\n    # (x) push current local variables\n    # (x) push arguments\n    # (x) push jumpdest (callback ptr)\n    # (x) jump to label\n    # (x) pop return values\n    # (x) pop local variables\n\n    method_name, expr_args, sig = call_lookup_specs(stmt_expr, context)\n    pre_init = []\n    pop_local_vars = []\n    push_local_vars = []\n    pop_return_values = []\n    push_args = []\n\n    # Push local variables.\n    var_slots = [\n        (v.pos, v.size) for name, v in context.vars.items()\n        if v.location == 'memory'\n    ]\n    if var_slots:\n        var_slots.sort(key=lambda x: x[0])\n        mem_from, mem_to = var_slots[0][0], var_slots[-1][0] + var_slots[-1][1] * 32\n\n        i_placeholder = context.new_placeholder(BaseType('uint256'))\n        local_save_ident = f\"_{stmt_expr.lineno}_{stmt_expr.col_offset}\"\n        push_loop_label = 'save_locals_start' + local_save_ident\n        pop_loop_label = 'restore_locals_start' + local_save_ident\n\n        if mem_to - mem_from > 320:\n            push_local_vars = [\n                    ['mstore', i_placeholder, mem_from],\n                    ['label', push_loop_label],\n                    ['mload', ['mload', i_placeholder]],\n                    ['mstore', i_placeholder, ['add', ['mload', i_placeholder], 32]],\n                    ['if', ['lt', ['mload', i_placeholder], mem_to],\n                        ['goto', push_loop_label]]\n            ]\n            pop_local_vars = [\n                ['mstore', i_placeholder, mem_to - 32],\n                ['label', pop_loop_label],\n                ['mstore', ['mload', i_placeholder], 'pass'],\n                ['mstore', i_placeholder, ['sub', ['mload', i_placeholder], 32]],\n                ['if', ['ge', ['mload', i_placeholder], mem_from],\n                       ['goto', pop_loop_label]]\n            ]\n        else:\n            push_local_vars = [['mload', pos] for pos in range(mem_from, mem_to, 32)]\n            pop_local_vars = [['mstore', pos, 'pass'] for pos in range(mem_to-32, mem_from-32, -32)]\n\n    # Push Arguments\n    if expr_args:\n        inargs, inargsize, arg_pos = pack_arguments(\n            sig,\n            expr_args,\n            context,\n            stmt_expr,\n            return_placeholder=False,\n        )\n        push_args += [inargs]  # copy arguments first, to not mess up the push/pop sequencing.\n\n        static_arg_size = 32 * sum(\n                [get_static_size_of_type(arg.typ)\n                    for arg in expr_args])\n        static_pos = int(arg_pos + static_arg_size)\n        needs_dyn_section = any(\n                [has_dynamic_data(arg.typ)\n                    for arg in expr_args])\n\n        if needs_dyn_section:\n            ident = f'push_args_{sig.method_id}_{stmt_expr.lineno}_{stmt_expr.col_offset}'\n            start_label = ident + '_start'\n            end_label = ident + '_end'\n            i_placeholder = context.new_placeholder(BaseType('uint256'))\n\n            # Calculate copy start position.\n            # Given | static | dynamic | section in memory,\n            # copy backwards so the values are in order on the stack.\n            # We calculate i, the end of the whole encoded part\n            # (i.e. the starting index for copy)\n            # by taking ceil32(len<arg>) + offset<arg> + arg_pos\n            # for the last dynamic argument and arg_pos is the start\n            # the whole argument section.\n            idx = 0\n            for arg in expr_args:\n                if isinstance(arg.typ, ByteArrayLike):\n                    last_idx = idx\n                idx += get_static_size_of_type(arg.typ)\n            push_args += [\n                ['with', 'offset', ['mload', arg_pos + last_idx * 32],\n                    ['with', 'len_pos', ['add', arg_pos, 'offset'],\n                        ['with', 'len_value', ['mload', 'len_pos'],\n                            ['mstore', i_placeholder,\n                                ['add', 'len_pos', ['ceil32', 'len_value']]]]]]\n            ]\n            # loop from end of dynamic section to start of dynamic section,\n            # pushing each element onto the stack.\n            push_args += [\n\n                ['label', start_label],\n                ['if', ['lt', ['mload', i_placeholder], static_pos],\n                    ['goto', end_label]],\n                ['mload', ['mload', i_placeholder]],\n                ['mstore', i_placeholder, ['sub', ['mload', i_placeholder], 32]],  # decrease i\n                ['goto', start_label],\n                ['label', end_label]\n            ]\n\n        # push static section\n        push_args += [\n            ['mload', pos] for pos in reversed(range(arg_pos, static_pos, 32))\n        ]\n    elif sig.args:\n        raise StructureException(\n            f\"Wrong number of args for: {sig.name} (0 args given, expected {len(sig.args)})\",\n            stmt_expr\n        )\n\n    # Jump to function label.\n    jump_to_func = [\n        ['add', ['pc'], 6],  # set callback pointer.\n        ['goto', f'priv_{sig.method_id}'],\n        ['jumpdest'],\n    ]\n\n    # Pop return values.\n    returner = [0]\n    if sig.output_type:\n        output_placeholder, returner, output_size = call_make_placeholder(stmt_expr, context, sig)\n        if output_size > 0:\n            dynamic_offsets = []\n            if isinstance(sig.output_type, (BaseType, ListType)):\n                pop_return_values = [\n                    ['mstore', ['add', output_placeholder, pos], 'pass']\n                    for pos in range(0, output_size, 32)\n                ]\n            elif isinstance(sig.output_type, ByteArrayLike):\n                dynamic_offsets = [(0, sig.output_type)]\n                pop_return_values = [\n                    ['pop', 'pass'],\n                ]\n            elif isinstance(sig.output_type, TupleLike):\n                static_offset = 0\n                pop_return_values = []\n                for out_type in sig.output_type.members:\n                    if isinstance(out_type, ByteArrayLike):\n                        pop_return_values.append(\n                            ['mstore', ['add', output_placeholder, static_offset], 'pass']\n                        )\n                        dynamic_offsets.append(\n                            (['mload', ['add', output_placeholder, static_offset]], out_type)\n                        )\n                    else:\n                        pop_return_values.append(\n                            ['mstore', ['add', output_placeholder, static_offset], 'pass']\n                        )\n                    static_offset += 32\n\n            # append dynamic unpacker.\n            dyn_idx = 0\n            for in_memory_offset, _out_type in dynamic_offsets:\n                ident = f\"{stmt_expr.lineno}_{stmt_expr.col_offset}_arg_{dyn_idx}\"\n                dyn_idx += 1\n                start_label = 'dyn_unpack_start_' + ident\n                end_label = 'dyn_unpack_end_' + ident\n                i_placeholder = context.new_placeholder(typ=BaseType('uint256'))\n                begin_pos = ['add', output_placeholder, in_memory_offset]\n                # loop until length.\n                o = LLLnode.from_list(\n                    ['seq_unchecked',\n                        ['mstore', begin_pos, 'pass'],  # get len\n                        ['mstore', i_placeholder, 0],\n                        ['label', start_label],\n                        [  # break\n                            'if',\n                            ['ge', ['mload', i_placeholder], ['ceil32', ['mload', begin_pos]]],\n                            ['goto', end_label]\n                        ],\n                        [  # pop into correct memory slot.\n                            'mstore',\n                            ['add', ['add', begin_pos, 32], ['mload', i_placeholder]],\n                            'pass',\n                        ],\n                        # increment i\n                        ['mstore', i_placeholder, ['add', 32, ['mload', i_placeholder]]],\n                        ['goto', start_label],\n                        ['label', end_label]],\n                    typ=None, annotation='dynamic unpacker', pos=getpos(stmt_expr))\n                pop_return_values.append(o)\n\n    call_body = list(itertools.chain(\n        ['seq_unchecked'],\n        pre_init,\n        push_local_vars,\n        push_args,\n        jump_to_func,\n        pop_return_values,\n        pop_local_vars,\n        [returner],\n    ))\n    # If we have no return, we need to pop off\n    pop_returner_call_body = ['pop', call_body] if sig.output_type is None else call_body\n\n    o = LLLnode.from_list(\n        pop_returner_call_body,\n        typ=sig.output_type,\n        location='memory',\n        pos=getpos(stmt_expr),\n        annotation=f'Internal Call: {method_name}',\n        add_gas_estimate=sig.gas\n    )\n    o.gas += sig.gas\n    return o\n", "path": "vyper/parser/self_call.py"}], "after_files": [{"content": "import itertools\n\nfrom vyper.exceptions import (\n    ConstancyViolation,\n    StructureException,\n    TypeMismatch,\n)\nfrom vyper.parser.lll_node import LLLnode\nfrom vyper.parser.parser_utils import getpos, pack_arguments\nfrom vyper.signatures.function_signature import FunctionSignature\nfrom vyper.types import (\n    BaseType,\n    ByteArrayLike,\n    ListType,\n    TupleLike,\n    get_size_of_type,\n    get_static_size_of_type,\n    has_dynamic_data,\n)\n\n\ndef call_lookup_specs(stmt_expr, context):\n    from vyper.parser.expr import Expr\n\n    method_name = stmt_expr.func.attr\n\n    if len(stmt_expr.keywords):\n        raise TypeMismatch(\n            \"Cannot use keyword arguments in calls to functions via 'self'\",\n            stmt_expr,\n        )\n    expr_args = [\n        Expr(arg, context).lll_node\n        for arg in stmt_expr.args\n    ]\n\n    sig = FunctionSignature.lookup_sig(\n        context.sigs,\n        method_name,\n        expr_args,\n        stmt_expr,\n        context,\n    )\n\n    return method_name, expr_args, sig\n\n\ndef make_call(stmt_expr, context):\n    method_name, _, sig = call_lookup_specs(stmt_expr, context)\n\n    if context.is_constant() and not sig.const:\n        raise ConstancyViolation(\n            f\"May not call non-constant function '{method_name}' within {context.pp_constancy()}.\",\n            getpos(stmt_expr)\n        )\n\n    if not sig.private:\n        raise StructureException(\"Cannot call public functions via 'self'\", stmt_expr)\n\n    return call_self_private(stmt_expr, context, sig)\n\n\ndef call_make_placeholder(stmt_expr, context, sig):\n    if sig.output_type is None:\n        return 0, 0, 0\n\n    output_placeholder = context.new_placeholder(typ=sig.output_type)\n    out_size = get_size_of_type(sig.output_type) * 32\n    returner = output_placeholder\n\n    if not sig.private and isinstance(sig.output_type, ByteArrayLike):\n        returner = output_placeholder + 32\n\n    return output_placeholder, returner, out_size\n\n\ndef call_self_private(stmt_expr, context, sig):\n    # ** Private Call **\n    # Steps:\n    # (x) push current local variables\n    # (x) push arguments\n    # (x) push jumpdest (callback ptr)\n    # (x) jump to label\n    # (x) pop return values\n    # (x) pop local variables\n\n    method_name, expr_args, sig = call_lookup_specs(stmt_expr, context)\n    pre_init = []\n    pop_local_vars = []\n    push_local_vars = []\n    pop_return_values = []\n    push_args = []\n\n    # Push local variables.\n    var_slots = [\n        (v.pos, v.size) for name, v in context.vars.items()\n        if v.location == 'memory'\n    ]\n    if var_slots:\n        var_slots.sort(key=lambda x: x[0])\n        mem_from, mem_to = var_slots[0][0], var_slots[-1][0] + var_slots[-1][1] * 32\n\n        i_placeholder = context.new_placeholder(BaseType('uint256'))\n        local_save_ident = f\"_{stmt_expr.lineno}_{stmt_expr.col_offset}\"\n        push_loop_label = 'save_locals_start' + local_save_ident\n        pop_loop_label = 'restore_locals_start' + local_save_ident\n\n        if mem_to - mem_from > 320:\n            push_local_vars = [\n                    ['mstore', i_placeholder, mem_from],\n                    ['label', push_loop_label],\n                    ['mload', ['mload', i_placeholder]],\n                    ['mstore', i_placeholder, ['add', ['mload', i_placeholder], 32]],\n                    ['if', ['lt', ['mload', i_placeholder], mem_to],\n                        ['goto', push_loop_label]]\n            ]\n            pop_local_vars = [\n                ['mstore', i_placeholder, mem_to - 32],\n                ['label', pop_loop_label],\n                ['mstore', ['mload', i_placeholder], 'pass'],\n                ['mstore', i_placeholder, ['sub', ['mload', i_placeholder], 32]],\n                ['if', ['ge', ['mload', i_placeholder], mem_from],\n                       ['goto', pop_loop_label]]\n            ]\n        else:\n            push_local_vars = [['mload', pos] for pos in range(mem_from, mem_to, 32)]\n            pop_local_vars = [['mstore', pos, 'pass'] for pos in range(mem_to-32, mem_from-32, -32)]\n\n    # Push Arguments\n    if expr_args:\n        inargs, inargsize, arg_pos = pack_arguments(\n            sig,\n            expr_args,\n            context,\n            stmt_expr,\n            return_placeholder=False,\n        )\n        push_args += [inargs]  # copy arguments first, to not mess up the push/pop sequencing.\n\n        static_arg_size = 32 * sum(\n                [get_static_size_of_type(arg.typ)\n                    for arg in expr_args])\n        static_pos = int(arg_pos + static_arg_size)\n        needs_dyn_section = any(\n                [has_dynamic_data(arg.typ)\n                    for arg in expr_args])\n\n        if needs_dyn_section:\n            ident = f'push_args_{sig.method_id}_{stmt_expr.lineno}_{stmt_expr.col_offset}'\n            start_label = ident + '_start'\n            end_label = ident + '_end'\n            i_placeholder = context.new_placeholder(BaseType('uint256'))\n\n            # Calculate copy start position.\n            # Given | static | dynamic | section in memory,\n            # copy backwards so the values are in order on the stack.\n            # We calculate i, the end of the whole encoded part\n            # (i.e. the starting index for copy)\n            # by taking ceil32(len<arg>) + offset<arg> + arg_pos\n            # for the last dynamic argument and arg_pos is the start\n            # the whole argument section.\n            idx = 0\n            for arg in expr_args:\n                if isinstance(arg.typ, ByteArrayLike):\n                    last_idx = idx\n                idx += get_static_size_of_type(arg.typ)\n            push_args += [\n                ['with', 'offset', ['mload', arg_pos + last_idx * 32],\n                    ['with', 'len_pos', ['add', arg_pos, 'offset'],\n                        ['with', 'len_value', ['mload', 'len_pos'],\n                            ['mstore', i_placeholder,\n                                ['add', 'len_pos', ['ceil32', 'len_value']]]]]]\n            ]\n            # loop from end of dynamic section to start of dynamic section,\n            # pushing each element onto the stack.\n            push_args += [\n\n                ['label', start_label],\n                ['if', ['lt', ['mload', i_placeholder], static_pos],\n                    ['goto', end_label]],\n                ['mload', ['mload', i_placeholder]],\n                ['mstore', i_placeholder, ['sub', ['mload', i_placeholder], 32]],  # decrease i\n                ['goto', start_label],\n                ['label', end_label]\n            ]\n\n        # push static section\n        push_args += [\n            ['mload', pos] for pos in reversed(range(arg_pos, static_pos, 32))\n        ]\n    elif sig.args:\n        raise StructureException(\n            f\"Wrong number of args for: {sig.name} (0 args given, expected {len(sig.args)})\",\n            stmt_expr\n        )\n\n    # Jump to function label.\n    jump_to_func = [\n        ['add', ['pc'], 6],  # set callback pointer.\n        ['goto', f'priv_{sig.method_id}'],\n        ['jumpdest'],\n    ]\n\n    # Pop return values.\n    returner = [0]\n    if sig.output_type:\n        output_placeholder, returner, output_size = call_make_placeholder(stmt_expr, context, sig)\n        if output_size > 0:\n            dynamic_offsets = []\n            if isinstance(sig.output_type, (BaseType, ListType)):\n                pop_return_values = [\n                    ['mstore', ['add', output_placeholder, pos], 'pass']\n                    for pos in range(0, output_size, 32)\n                ]\n            elif isinstance(sig.output_type, ByteArrayLike):\n                dynamic_offsets = [(0, sig.output_type)]\n                pop_return_values = [\n                    ['pop', 'pass'],\n                ]\n            elif isinstance(sig.output_type, TupleLike):\n                static_offset = 0\n                pop_return_values = []\n                for name, typ in sig.output_type.tuple_items():\n                    if isinstance(typ, ByteArrayLike):\n                        pop_return_values.append(\n                            ['mstore', ['add', output_placeholder, static_offset], 'pass']\n                        )\n                        dynamic_offsets.append(\n                            (['mload', ['add', output_placeholder, static_offset]], name)\n                        )\n                        static_offset += 32\n                    else:\n                        member_output_size = get_size_of_type(typ) * 32\n                        pop_return_values.extend([\n                            ['mstore', ['add', output_placeholder, pos], 'pass']\n                            for pos in range(static_offset, static_offset + member_output_size, 32)\n                        ])\n                        static_offset += member_output_size\n\n            # append dynamic unpacker.\n            dyn_idx = 0\n            for in_memory_offset, _out_type in dynamic_offsets:\n                ident = f\"{stmt_expr.lineno}_{stmt_expr.col_offset}_arg_{dyn_idx}\"\n                dyn_idx += 1\n                start_label = 'dyn_unpack_start_' + ident\n                end_label = 'dyn_unpack_end_' + ident\n                i_placeholder = context.new_placeholder(typ=BaseType('uint256'))\n                begin_pos = ['add', output_placeholder, in_memory_offset]\n                # loop until length.\n                o = LLLnode.from_list(\n                    ['seq_unchecked',\n                        ['mstore', begin_pos, 'pass'],  # get len\n                        ['mstore', i_placeholder, 0],\n                        ['label', start_label],\n                        [  # break\n                            'if',\n                            ['ge', ['mload', i_placeholder], ['ceil32', ['mload', begin_pos]]],\n                            ['goto', end_label]\n                        ],\n                        [  # pop into correct memory slot.\n                            'mstore',\n                            ['add', ['add', begin_pos, 32], ['mload', i_placeholder]],\n                            'pass',\n                        ],\n                        # increment i\n                        ['mstore', i_placeholder, ['add', 32, ['mload', i_placeholder]]],\n                        ['goto', start_label],\n                        ['label', end_label]],\n                    typ=None, annotation='dynamic unpacker', pos=getpos(stmt_expr))\n                pop_return_values.append(o)\n\n    call_body = list(itertools.chain(\n        ['seq_unchecked'],\n        pre_init,\n        push_local_vars,\n        push_args,\n        jump_to_func,\n        pop_return_values,\n        pop_local_vars,\n        [returner],\n    ))\n    # If we have no return, we need to pop off\n    pop_returner_call_body = ['pop', call_body] if sig.output_type is None else call_body\n\n    o = LLLnode.from_list(\n        pop_returner_call_body,\n        typ=sig.output_type,\n        location='memory',\n        pos=getpos(stmt_expr),\n        annotation=f'Internal Call: {method_name}',\n        add_gas_estimate=sig.gas\n    )\n    o.gas += sig.gas\n    return o\n", "path": "vyper/parser/self_call.py"}]}