Example #1
0
def call_self_public(stmt_expr, context, sig):
    # self.* style call to a public function.
    method_name, expr_args, sig = call_lookup_specs(stmt_expr, context)
    add_gas = sig.gas  # gas of call
    inargs, inargsize, _ = pack_arguments(sig,
                                          expr_args,
                                          context,
                                          pos=getpos(stmt_expr))
    output_placeholder, returner, output_size = call_make_placeholder(
        stmt_expr, context, sig)
    assert_call = [
        'assert',
        [
            'call', ['gas'], ['address'], 0, inargs, inargsize,
            output_placeholder, output_size
        ]
    ]
    if output_size > 0:
        assert_call = ['seq', assert_call, returner]
    o = LLLnode.from_list(assert_call,
                          typ=sig.output_type,
                          location='memory',
                          pos=getpos(stmt_expr),
                          add_gas_estimate=add_gas,
                          annotation='Internal Call: %s' % method_name)
    o.gas += sig.gas
    return o
Example #2
0
def external_contract_call(node,
                           context,
                           contract_name,
                           contract_address,
                           pos,
                           value=None,
                           gas=None):
    from vyper.parser.parser import parse_expr
    if value is None:
        value = 0
    if gas is None:
        gas = 'gas'
    if contract_name not in context.sigs:
        raise VariableDeclarationException("Contract not declared yet: %s" % contract_name)
    method_name = node.func.attr
    if method_name not in context.sigs[contract_name]:
        raise FunctionDeclarationException(
            (
                "Function not declared yet: %s (reminder: "
                "function must be declared in the correct contract)"
                " The available methods are: %s"
            ) % (method_name, ",".join(context.sigs[contract_name].keys())),
            node.func
        )
    sig = context.sigs[contract_name][method_name]
    inargs, inargsize, _ = pack_arguments(
        sig,
        [parse_expr(arg, context) for arg in node.args],
        context,
        pos=pos,
    )
    output_placeholder, output_size, returner = get_external_contract_call_output(sig, context)
    sub = [
        'seq',
        ['assert', ['extcodesize', contract_address]],
        ['assert', ['ne', 'address', contract_address]],
    ]
    if context.is_constant() or sig.const:
        sub.append([
            'assert',
            [
                'staticcall',
                gas, contract_address, inargs, inargsize, output_placeholder, output_size,
            ]
        ])
    else:
        sub.append([
            'assert',
            [
                'call',
                gas, contract_address, value, inargs, inargsize, output_placeholder, output_size,
            ]
        ])
    sub.extend(returner)
    o = LLLnode.from_list(sub, typ=sig.output_type, location='memory', pos=getpos(node))
    return o
Example #3
0
def external_call(node,
                  context,
                  interface_name,
                  contract_address,
                  pos,
                  value=None,
                  gas=None):
    from vyper.parser.expr import Expr

    if value is None:
        value = 0
    if gas is None:
        gas = "gas"

    method_name = node.func.attr
    sig = context.sigs[interface_name][method_name]
    inargs, inargsize, _ = pack_arguments(
        sig,
        [Expr(arg, context).lll_node for arg in node.args],
        context,
        node.func,
        is_external_call=True,
    )
    output_placeholder, output_size, returner = get_external_call_output(
        sig, context)
    sub = ["seq"]
    if not output_size:
        # if we do not expect return data, check that a contract exists at the target address
        # we can omit this when we _do_ expect return data because we later check `returndatasize`
        sub.append(["assert", ["extcodesize", contract_address]])
    if context.is_constant() and sig.mutability not in ("view", "pure"):
        # TODO this can probably go
        raise StateAccessViolation(
            f"May not call state modifying function '{method_name}' "
            f"within {context.pp_constancy()}.",
            node,
        )

    if context.is_constant() or sig.mutability in ("view", "pure"):
        sub.append([
            "assert",
            [
                "staticcall",
                gas,
                contract_address,
                inargs,
                inargsize,
                output_placeholder,
                output_size,
            ],
        ])
    else:
        sub.append([
            "assert",
            [
                "call",
                gas,
                contract_address,
                value,
                inargs,
                inargsize,
                output_placeholder,
                output_size,
            ],
        ])
    if output_size:
        # when return data is expected, revert when the length of `returndatasize` is insufficient
        output_type = sig.output_type
        if not has_dynamic_data(output_type):
            static_output_size = get_static_size_of_type(output_type) * 32
            sub.append(
                ["assert", ["gt", "returndatasize", static_output_size - 1]])
        else:
            if isinstance(output_type, ByteArrayLike):
                types_list = (output_type, )
            elif isinstance(output_type, TupleLike):
                types_list = output_type.tuple_members()
            else:
                raise

            dynamic_checks = []
            static_offset = output_placeholder
            static_output_size = 0
            for typ in types_list:
                # ensure length of bytes does not exceed max allowable length for type
                if isinstance(typ, ByteArrayLike):
                    static_output_size += 32
                    # do not perform this check on calls to a JSON interface - we don't know
                    # for certain how long the expected data is
                    if not sig.is_from_json:
                        dynamic_checks.append([
                            "assert",
                            [
                                "lt",
                                [
                                    "mload",
                                    [
                                        "add", ["mload", static_offset],
                                        output_placeholder
                                    ],
                                ],
                                typ.maxlen + 1,
                            ],
                        ])
                static_offset += get_static_size_of_type(typ) * 32
                static_output_size += get_static_size_of_type(typ) * 32

            sub.append(
                ["assert", ["gt", "returndatasize", static_output_size - 1]])
            sub.extend(dynamic_checks)

    sub.extend(returner)

    return LLLnode.from_list(sub,
                             typ=sig.output_type,
                             location="memory",
                             pos=getpos(node))
Example #4
0
def call_self_private(stmt_expr, context, sig):
    # ** Private Call **
    # Steps:
    # (x) push current local variables
    # (x) push arguments
    # (x) push jumpdest (callback ptr)
    # (x) jump to label
    # (x) pop return values
    # (x) pop local variables

    method_name, expr_args, sig = call_lookup_specs(stmt_expr, context)
    pre_init = []
    pop_local_vars = []
    push_local_vars = []
    pop_return_values = []
    push_args = []

    # Push local variables.
    if context.vars:
        var_slots = [(v.pos, v.size) for name, v in context.vars.items()]
        var_slots.sort(key=lambda x: x[0])
        mem_from, mem_to = var_slots[0][
            0], var_slots[-1][0] + var_slots[-1][1] * 32
        push_local_vars = [['mload', pos]
                           for pos in range(mem_from, mem_to, 32)]
        pop_local_vars = [['mstore', pos, 'pass']
                          for pos in reversed(range(mem_from, mem_to, 32))]

    # Push Arguments
    if expr_args:
        inargs, inargsize, arg_pos = pack_arguments(sig,
                                                    expr_args,
                                                    context,
                                                    return_placeholder=False,
                                                    pos=getpos(stmt_expr))
        push_args += [
            inargs
        ]  # copy arguments first, to not mess up the push/pop sequencing.
        static_arg_count = len(expr_args) * 32
        static_pos = arg_pos + static_arg_count
        total_arg_size = ceil32(inargsize - 4)

        if len(expr_args) * 32 != total_arg_size:  # requires dynamic section.
            ident = 'push_args_%d_%d_%d' % (sig.method_id, stmt_expr.lineno,
                                            stmt_expr.col_offset)
            start_label = ident + '_start'
            end_label = ident + '_end'
            i_placeholder = context.new_placeholder(BaseType('uint256'))
            push_args += [
                ['mstore', i_placeholder, arg_pos + total_arg_size],
                ['label', start_label],
                [
                    'if', ['lt', ['mload', i_placeholder], static_pos],
                    ['goto', end_label]
                ],
                [
                    'if_unchecked',
                    ['ne', ['mload', ['mload', i_placeholder]], 0],
                    ['mload', ['mload', i_placeholder]]
                ],
                [
                    'mstore', i_placeholder,
                    ['sub', ['mload', i_placeholder], 32]
                ],  # decrease i
                ['goto', start_label],
                ['label', end_label]
            ]

        # push static section
        push_args += [['mload', pos]
                      for pos in reversed(range(arg_pos, static_pos, 32))]

    # Jump to function label.
    jump_to_func = [
        ['add', ['pc'], 6],  # set callback pointer.
        ['goto', 'priv_{}'.format(sig.method_id)],
        ['jumpdest'],
    ]

    # Pop return values.
    returner = [0]
    if sig.output_type:
        output_placeholder, returner, output_size = call_make_placeholder(
            stmt_expr, context, sig)
        if output_size > 0:
            dynamic_offsets = []
            if isinstance(sig.output_type, (BaseType, ListType)):
                pop_return_values = [[
                    'mstore', ['add', output_placeholder, pos], 'pass'
                ] for pos in range(0, output_size, 32)]
            elif isinstance(sig.output_type, ByteArrayLike):
                dynamic_offsets = [(0, sig.output_type)]
                pop_return_values = [
                    ['pop', 'pass'],
                ]
            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):
                        pop_return_values.append([
                            'mstore',
                            ['add', output_placeholder, static_offset], 'pass'
                        ])
                        dynamic_offsets.append(([
                            'mload',
                            ['add', output_placeholder, static_offset]
                        ], out_type))
                    else:
                        pop_return_values.append([
                            'mstore',
                            ['add', output_placeholder, static_offset], 'pass'
                        ])
                    static_offset += 32

            # append dynamic unpacker.
            dyn_idx = 0
            for in_memory_offset, out_type in dynamic_offsets:
                ident = "%d_%d_arg_%d" % (stmt_expr.lineno,
                                          stmt_expr.col_offset, dyn_idx)
                dyn_idx += 1
                start_label = 'dyn_unpack_start_' + ident
                end_label = 'dyn_unpack_end_' + ident
                i_placeholder = context.new_placeholder(
                    typ=BaseType('uint256'))
                begin_pos = ['add', output_placeholder, in_memory_offset]
                # loop until length.
                o = LLLnode.from_list(
                    [
                        'seq_unchecked',
                        ['mstore', begin_pos, 'pass'],  # get len
                        ['mstore', i_placeholder, 0],
                        ['label', start_label],
                        [
                            'if',
                            [
                                'ge', ['mload', i_placeholder],
                                ['ceil32', ['mload', begin_pos]]
                            ], ['goto', end_label]
                        ],  # break
                        [
                            'mstore',
                            [
                                'add', ['add', begin_pos, 32],
                                ['mload', i_placeholder]
                            ], 'pass'
                        ],  # pop into correct memory slot.
                        [
                            'mstore', i_placeholder,
                            ['add', 32, ['mload', i_placeholder]]
                        ],  # increment i
                        ['goto', start_label],
                        ['label', end_label]
                    ],
                    typ=None,
                    annotation='dynamic unpacker',
                    pos=getpos(stmt_expr))
                pop_return_values.append(o)

    call_body = (['seq_unchecked'] + pre_init + push_local_vars + push_args +
                 jump_to_func + pop_return_values + pop_local_vars +
                 [returner])
    # If we have no return, we need to pop off
    pop_returner_call_body = ['pop', call_body
                              ] if sig.output_type is None else call_body

    o = LLLnode.from_list(pop_returner_call_body,
                          typ=sig.output_type,
                          location='memory',
                          pos=getpos(stmt_expr),
                          annotation='Internal Call: %s' % method_name,
                          add_gas_estimate=sig.gas)
    o.gas += sig.gas
    return o
Example #5
0
def call_self_private(stmt_expr, context, sig):
    # ** Private Call **
    # Steps:
    # (x) push current local variables
    # (x) push arguments
    # (x) push jumpdest (callback ptr)
    # (x) jump to label
    # (x) pop return values
    # (x) pop local variables

    method_name, expr_args, sig = call_lookup_specs(stmt_expr, context)
    pre_init = []
    pop_local_vars = []
    push_local_vars = []
    pop_return_values = []
    push_args = []

    # Push local variables.
    var_slots = [(v.pos, v.size) for name, v in context.vars.items()
                 if v.location == 'memory']
    if var_slots:
        var_slots.sort(key=lambda x: x[0])
        mem_from, mem_to = var_slots[0][
            0], var_slots[-1][0] + var_slots[-1][1] * 32

        i_placeholder = context.new_placeholder(BaseType('uint256'))
        local_save_ident = "_%d_%d" % (stmt_expr.lineno, stmt_expr.col_offset)
        push_loop_label = 'save_locals_start' + local_save_ident
        pop_loop_label = 'restore_locals_start' + local_save_ident

        if mem_to - mem_from > 320:
            push_local_vars = [['mstore', i_placeholder, mem_from],
                               ['label', push_loop_label],
                               ['mload', ['mload', i_placeholder]],
                               [
                                   'mstore', i_placeholder,
                                   ['add', ['mload', i_placeholder], 32]
                               ],
                               [
                                   'if',
                                   ['lt', ['mload', i_placeholder], mem_to],
                                   ['goto', push_loop_label]
                               ]]
            pop_local_vars = [['mstore', i_placeholder, mem_to - 32],
                              ['label', pop_loop_label],
                              ['mstore', ['mload', i_placeholder], 'pass'],
                              [
                                  'mstore', i_placeholder,
                                  ['sub', ['mload', i_placeholder], 32]
                              ],
                              [
                                  'if',
                                  ['ge', ['mload', i_placeholder], mem_from],
                                  ['goto', pop_loop_label]
                              ]]
        else:
            push_local_vars = [['mload', pos]
                               for pos in range(mem_from, mem_to, 32)]
            pop_local_vars = [['mstore', pos, 'pass']
                              for pos in range(mem_to - 32, mem_from - 32, -32)
                              ]

    # Push Arguments
    if expr_args:
        inargs, inargsize, arg_pos = pack_arguments(
            sig,
            expr_args,
            context,
            return_placeholder=False,
            pos=getpos(stmt_expr),
        )
        push_args += [
            inargs
        ]  # copy arguments first, to not mess up the push/pop sequencing.

        static_arg_size = 32 * sum(
            [get_static_size_of_type(arg.typ) for arg in expr_args])
        static_pos = arg_pos + static_arg_size
        needs_dyn_section = any(
            [has_dynamic_data(arg.typ) for arg in expr_args])

        if needs_dyn_section:
            ident = 'push_args_%d_%d_%d' % (sig.method_id, stmt_expr.lineno,
                                            stmt_expr.col_offset)
            start_label = ident + '_start'
            end_label = ident + '_end'
            i_placeholder = context.new_placeholder(BaseType('uint256'))

            # Calculate copy start position.
            # Given | static | dynamic | section in memory,
            # copy backwards so the values are in order on the stack.
            # We calculate i, the end of the whole encoded part
            # (i.e. the starting index for copy)
            # by taking ceil32(len<arg>) + offset<arg> + arg_pos
            # for the last dynamic argument and arg_pos is the start
            # the whole argument section.
            for idx, arg in enumerate(expr_args):
                if isinstance(arg.typ, ByteArrayLike):
                    last_idx = idx
            push_args += [[
                'with', 'offset', ['mload', arg_pos + last_idx * 32],
                [
                    'with', 'len_pos', ['add', arg_pos, 'offset'],
                    [
                        'with', 'len_value', ['mload', 'len_pos'],
                        [
                            'mstore', i_placeholder,
                            ['add', 'len_pos', ['ceil32', 'len_value']]
                        ]
                    ]
                ]
            ]]
            # loop from end of dynamic section to start of dynamic section,
            # pushing each element onto the stack.
            push_args += [
                ['label', start_label],
                [
                    'if', ['lt', ['mload', i_placeholder], static_pos],
                    ['goto', end_label]
                ],
                ['mload', ['mload', i_placeholder]],
                [
                    'mstore', i_placeholder,
                    ['sub', ['mload', i_placeholder], 32]
                ],  # decrease i
                ['goto', start_label],
                ['label', end_label]
            ]

        # push static section
        push_args += [['mload', pos]
                      for pos in reversed(range(arg_pos, static_pos, 32))]

    # Jump to function label.
    jump_to_func = [
        ['add', ['pc'], 6],  # set callback pointer.
        ['goto', 'priv_{}'.format(sig.method_id)],
        ['jumpdest'],
    ]

    # Pop return values.
    returner = [0]
    if sig.output_type:
        output_placeholder, returner, output_size = call_make_placeholder(
            stmt_expr, context, sig)
        if output_size > 0:
            dynamic_offsets = []
            if isinstance(sig.output_type, (BaseType, ListType)):
                pop_return_values = [[
                    'mstore', ['add', output_placeholder, pos], 'pass'
                ] for pos in range(0, output_size, 32)]
            elif isinstance(sig.output_type, ByteArrayLike):
                dynamic_offsets = [(0, sig.output_type)]
                pop_return_values = [
                    ['pop', 'pass'],
                ]
            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):
                        pop_return_values.append([
                            'mstore',
                            ['add', output_placeholder, static_offset], 'pass'
                        ])
                        dynamic_offsets.append(([
                            'mload',
                            ['add', output_placeholder, static_offset]
                        ], out_type))
                    else:
                        pop_return_values.append([
                            'mstore',
                            ['add', output_placeholder, static_offset], 'pass'
                        ])
                    static_offset += 32

            # append dynamic unpacker.
            dyn_idx = 0
            for in_memory_offset, _out_type in dynamic_offsets:
                ident = "%d_%d_arg_%d" % (stmt_expr.lineno,
                                          stmt_expr.col_offset, dyn_idx)
                dyn_idx += 1
                start_label = 'dyn_unpack_start_' + ident
                end_label = 'dyn_unpack_end_' + ident
                i_placeholder = context.new_placeholder(
                    typ=BaseType('uint256'))
                begin_pos = ['add', output_placeholder, in_memory_offset]
                # loop until length.
                o = LLLnode.from_list(
                    [
                        'seq_unchecked',
                        ['mstore', begin_pos, 'pass'],  # get len
                        ['mstore', i_placeholder, 0],
                        ['label', start_label],
                        [  # break
                            'if',
                            [
                                'ge', ['mload', i_placeholder],
                                ['ceil32', ['mload', begin_pos]]
                            ], ['goto', end_label]
                        ],
                        [  # pop into correct memory slot.
                            'mstore',
                            [
                                'add', ['add', begin_pos, 32],
                                ['mload', i_placeholder]
                            ],
                            'pass',
                        ],
                        # increment i
                        [
                            'mstore', i_placeholder,
                            ['add', 32, ['mload', i_placeholder]]
                        ],
                        ['goto', start_label],
                        ['label', end_label]
                    ],
                    typ=None,
                    annotation='dynamic unpacker',
                    pos=getpos(stmt_expr))
                pop_return_values.append(o)

    call_body = list(
        itertools.chain(
            ['seq_unchecked'],
            pre_init,
            push_local_vars,
            push_args,
            jump_to_func,
            pop_return_values,
            pop_local_vars,
            [returner],
        ))
    # If we have no return, we need to pop off
    pop_returner_call_body = ['pop', call_body
                              ] if sig.output_type is None else call_body

    o = LLLnode.from_list(pop_returner_call_body,
                          typ=sig.output_type,
                          location='memory',
                          pos=getpos(stmt_expr),
                          annotation='Internal Call: %s' % method_name,
                          add_gas_estimate=sig.gas)
    o.gas += sig.gas
    return o
Example #6
0
def external_contract_call(node,
                           context,
                           contract_name,
                           contract_address,
                           pos,
                           value=None,
                           gas=None):
    from vyper.parser.expr import (
        Expr, )
    if value is None:
        value = 0
    if gas is None:
        gas = 'gas'
    if not contract_name:
        raise StructureException(
            f'Invalid external contract call "{node.func.attr}".', node)
    if contract_name not in context.sigs:
        raise VariableDeclarationException(
            f'Contract "{contract_name}" not declared yet', node)
    if contract_address.value == "address":
        raise StructureException(f"External calls to self are not permitted.",
                                 node)
    method_name = node.func.attr
    if method_name not in context.sigs[contract_name]:
        raise FunctionDeclarationException((
            f"Function not declared yet: {method_name} (reminder: "
            "function must be declared in the correct contract)"
            f"The available methods are: {','.join(context.sigs[contract_name].keys())}"
        ), node.func)
    sig = context.sigs[contract_name][method_name]
    inargs, inargsize, _ = pack_arguments(
        sig,
        [Expr(arg, context).lll_node for arg in node.args],
        context,
        node.func,
    )
    output_placeholder, output_size, returner = get_external_contract_call_output(
        sig, context)
    sub = [
        'seq',
        ['assert', ['extcodesize', contract_address]],
        ['assert', ['ne', 'address', contract_address]],
    ]
    if context.is_constant() and not sig.const:
        raise ConstancyViolationException(
            f"May not call non-constant function '{method_name}' within {context.pp_constancy()}."
            " For asserting the result of modifiable contract calls, try assert_modifiable.",
            node)

    if context.is_constant() or sig.const:
        sub.append([
            'assert',
            [
                'staticcall',
                gas,
                contract_address,
                inargs,
                inargsize,
                output_placeholder,
                output_size,
            ]
        ])
    else:
        sub.append([
            'assert',
            [
                'call',
                gas,
                contract_address,
                value,
                inargs,
                inargsize,
                output_placeholder,
                output_size,
            ]
        ])
    sub.extend(returner)
    o = LLLnode.from_list(sub,
                          typ=sig.output_type,
                          location='memory',
                          pos=getpos(node))
    return o
Example #7
0
def external_call(node,
                  context,
                  interface_name,
                  contract_address,
                  pos,
                  value=None,
                  gas=None):
    from vyper.parser.expr import Expr

    if value is None:
        value = 0
    if gas is None:
        gas = "gas"

    method_name = node.func.attr
    sig = context.sigs[interface_name][method_name]
    inargs, inargsize, _ = pack_arguments(
        sig,
        [Expr(arg, context).lll_node for arg in node.args],
        context,
        node.func,
        is_external_call=True,
    )
    output_placeholder, output_size, returner = get_external_call_output(
        sig, context)
    sub = [
        "seq",
        ["assert", ["extcodesize", contract_address]],
    ]
    if context.is_constant() and sig.mutability not in ("view", "pure"):
        # TODO this can probably go
        raise StateAccessViolation(
            f"May not call state modifying function '{method_name}' "
            f"within {context.pp_constancy()}.",
            node,
        )

    if context.is_constant() or sig.mutability in ("view", "pure"):
        sub.append([
            "assert",
            [
                "staticcall",
                gas,
                contract_address,
                inargs,
                inargsize,
                output_placeholder,
                output_size,
            ],
        ])
    else:
        sub.append([
            "assert",
            [
                "call",
                gas,
                contract_address,
                value,
                inargs,
                inargsize,
                output_placeholder,
                output_size,
            ],
        ])
    sub.extend(returner)
    o = LLLnode.from_list(sub,
                          typ=sig.output_type,
                          location="memory",
                          pos=getpos(node))
    return o
Example #8
0
def _call_self_private(stmt_expr, context, sig):
    # ** Private Call **
    # Steps:
    # (x) push current local variables
    # (x) push arguments
    # (x) push jumpdest (callback ptr)
    # (x) jump to label
    # (x) pop return values
    # (x) pop local variables

    method_name, expr_args, sig = _call_lookup_specs(stmt_expr, context)
    pre_init = []
    pop_local_vars = []
    push_local_vars = []
    pop_return_values = []
    push_args = []

    # Push local variables.
    var_slots = [(v.pos, v.size) for name, v in context.vars.items()
                 if v.location == "memory"]
    if var_slots:
        var_slots.sort(key=lambda x: x[0])
        mem_from, mem_to = var_slots[0][
            0], var_slots[-1][0] + var_slots[-1][1] * 32

        i_placeholder = context.new_placeholder(BaseType("uint256"))
        local_save_ident = f"_{stmt_expr.lineno}_{stmt_expr.col_offset}"
        push_loop_label = "save_locals_start" + local_save_ident
        pop_loop_label = "restore_locals_start" + local_save_ident

        if mem_to - mem_from > 320:
            push_local_vars = [
                ["mstore", i_placeholder, mem_from],
                ["label", push_loop_label],
                ["mload", ["mload", i_placeholder]],
                [
                    "mstore", i_placeholder,
                    ["add", ["mload", i_placeholder], 32]
                ],
                [
                    "if", ["lt", ["mload", i_placeholder], mem_to],
                    ["goto", push_loop_label]
                ],
            ]
            pop_local_vars = [
                ["mstore", i_placeholder, mem_to - 32],
                ["label", pop_loop_label],
                ["mstore", ["mload", i_placeholder], "pass"],
                [
                    "mstore", i_placeholder,
                    ["sub", ["mload", i_placeholder], 32]
                ],
                [
                    "if", ["ge", ["mload", i_placeholder], mem_from],
                    ["goto", pop_loop_label]
                ],
            ]
        else:
            push_local_vars = [["mload", pos]
                               for pos in range(mem_from, mem_to, 32)]
            pop_local_vars = [["mstore", pos, "pass"]
                              for pos in range(mem_to - 32, mem_from - 32, -32)
                              ]

    # Push Arguments
    if expr_args:
        inargs, inargsize, arg_pos = pack_arguments(sig,
                                                    expr_args,
                                                    context,
                                                    stmt_expr,
                                                    is_external_call=False)
        push_args += [
            inargs
        ]  # copy arguments first, to not mess up the push/pop sequencing.

        static_arg_size = 32 * sum(
            [get_static_size_of_type(arg.typ) for arg in expr_args])
        static_pos = int(arg_pos + static_arg_size)
        needs_dyn_section = any(
            [has_dynamic_data(arg.typ) for arg in expr_args])

        if needs_dyn_section:
            ident = f"push_args_{sig.method_id}_{stmt_expr.lineno}_{stmt_expr.col_offset}"
            start_label = ident + "_start"
            end_label = ident + "_end"
            i_placeholder = context.new_placeholder(BaseType("uint256"))

            # Calculate copy start position.
            # Given | static | dynamic | section in memory,
            # copy backwards so the values are in order on the stack.
            # We calculate i, the end of the whole encoded part
            # (i.e. the starting index for copy)
            # by taking ceil32(len<arg>) + offset<arg> + arg_pos
            # for the last dynamic argument and arg_pos is the start
            # the whole argument section.
            idx = 0
            for arg in expr_args:
                if isinstance(arg.typ, ByteArrayLike):
                    last_idx = idx
                idx += get_static_size_of_type(arg.typ)
            push_args += [[
                "with",
                "offset",
                ["mload", arg_pos + last_idx * 32],
                [
                    "with",
                    "len_pos",
                    ["add", arg_pos, "offset"],
                    [
                        "with",
                        "len_value",
                        ["mload", "len_pos"],
                        [
                            "mstore", i_placeholder,
                            ["add", "len_pos", ["ceil32", "len_value"]]
                        ],
                    ],
                ],
            ]]
            # loop from end of dynamic section to start of dynamic section,
            # pushing each element onto the stack.
            push_args += [
                ["label", start_label],
                [
                    "if", ["lt", ["mload", i_placeholder], static_pos],
                    ["goto", end_label]
                ],
                ["mload", ["mload", i_placeholder]],
                [
                    "mstore", i_placeholder,
                    ["sub", ["mload", i_placeholder], 32]
                ],  # decrease i
                ["goto", start_label],
                ["label", end_label],
            ]

        # push static section
        push_args += [["mload", pos]
                      for pos in reversed(range(arg_pos, static_pos, 32))]
    elif sig.args:
        raise StructureException(
            f"Wrong number of args for: {sig.name} (0 args given, expected {len(sig.args)})",
            stmt_expr,
        )

    # Jump to function label.
    jump_to_func = [
        ["add", ["pc"], 6],  # set callback pointer.
        ["goto", f"priv_{sig.method_id}"],
        ["jumpdest"],
    ]

    # Pop return values.
    returner = [0]
    if sig.output_type:
        output_placeholder, returner, output_size = _call_make_placeholder(
            stmt_expr, context, sig)
        if output_size > 0:
            dynamic_offsets = []
            if isinstance(sig.output_type, (BaseType, ListType)):
                pop_return_values = [[
                    "mstore", ["add", output_placeholder, pos], "pass"
                ] for pos in range(0, output_size, 32)]
            elif isinstance(sig.output_type, ByteArrayLike):
                dynamic_offsets = [(0, sig.output_type)]
                pop_return_values = [
                    ["pop", "pass"],
                ]
            elif isinstance(sig.output_type, TupleLike):
                static_offset = 0
                pop_return_values = []
                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]
                        ], name))
                        static_offset += 32
                    else:
                        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
            for in_memory_offset, _out_type in dynamic_offsets:
                ident = f"{stmt_expr.lineno}_{stmt_expr.col_offset}_arg_{dyn_idx}"
                dyn_idx += 1
                start_label = "dyn_unpack_start_" + ident
                end_label = "dyn_unpack_end_" + ident
                i_placeholder = context.new_placeholder(
                    typ=BaseType("uint256"))
                begin_pos = ["add", output_placeholder, in_memory_offset]
                # loop until length.
                o = LLLnode.from_list(
                    [
                        "seq_unchecked",
                        ["mstore", begin_pos, "pass"],  # get len
                        ["mstore", i_placeholder, 0],
                        ["label", start_label],
                        [  # break
                            "if",
                            [
                                "ge", ["mload", i_placeholder],
                                ["ceil32", ["mload", begin_pos]]
                            ],
                            ["goto", end_label],
                        ],
                        [  # pop into correct memory slot.
                            "mstore",
                            [
                                "add", ["add", begin_pos, 32],
                                ["mload", i_placeholder]
                            ],
                            "pass",
                        ],
                        # increment i
                        [
                            "mstore", i_placeholder,
                            ["add", 32, ["mload", i_placeholder]]
                        ],
                        ["goto", start_label],
                        ["label", end_label],
                    ],
                    typ=None,
                    annotation="dynamic unpacker",
                    pos=getpos(stmt_expr),
                )
                pop_return_values.append(o)

    call_body = list(
        itertools.chain(
            ["seq_unchecked"],
            pre_init,
            push_local_vars,
            push_args,
            jump_to_func,
            pop_return_values,
            pop_local_vars,
            [returner],
        ))
    # If we have no return, we need to pop off
    pop_returner_call_body = ["pop", call_body
                              ] if sig.output_type is None else call_body

    o = LLLnode.from_list(
        pop_returner_call_body,
        typ=sig.output_type,
        location="memory",
        pos=getpos(stmt_expr),
        annotation=f"Internal Call: {method_name}",
        add_gas_estimate=sig.gas,
    )
    o.gas += sig.gas
    return o