コード例 #1
0
ファイル: function_signature.py プロジェクト: benjyz/vyper
    def calculate_arg_totals(self):
        """ Calculate base arguments, and totals. """

        code = self.func_ast_code
        self.base_args = []
        self.total_default_args = 0

        if hasattr(code.args, "defaults"):
            self.total_default_args = len(code.args.defaults)
            if self.total_default_args > 0:
                # all argument w/o defaults
                self.base_args = self.args[:-self.total_default_args]
            else:
                # No default args, so base_args = args.
                self.base_args = self.args
            # All default argument name/type definitions.
            self.default_args = code.args.args[
                -self.total_default_args:]  # noqa: E203
            # Keep all the value to assign to default parameters.
            self.default_values = dict(
                zip([arg.arg for arg in self.default_args],
                    code.args.defaults))

        # Calculate the total sizes in memory the function arguments will take use.
        # Total memory size of all arguments (base + default together).
        self.max_copy_size = sum([
            32 if isinstance(arg.typ, ByteArrayLike) else
            get_size_of_type(arg.typ) * 32 for arg in self.args
        ])
        # Total memory size of base arguments (arguments exclude default parameters).
        self.base_copy_size = sum([
            32 if isinstance(arg.typ, ByteArrayLike) else
            get_size_of_type(arg.typ) * 32 for arg in self.base_args
        ])
コード例 #2
0
def _call_make_placeholder(stmt_expr, context, sig):
    if sig.output_type is None:
        return 0, 0, 0

    output_placeholder = context.new_internal_variable(typ=sig.output_type)
    output_size = get_size_of_type(sig.output_type) * 32

    if isinstance(sig.output_type, BaseType):
        returner = output_placeholder
    elif isinstance(sig.output_type, ByteArrayLike):
        returner = output_placeholder
    elif isinstance(sig.output_type, TupleLike):
        # incase of struct we need to decode the output and then return it
        returner = ["seq"]
        decoded_placeholder = context.new_internal_variable(
            typ=sig.output_type)
        decoded_node = LLLnode(decoded_placeholder,
                               typ=sig.output_type,
                               location="memory")
        output_node = LLLnode(output_placeholder,
                              typ=sig.output_type,
                              location="memory")
        returner.append(abi_decode(decoded_node, output_node))
        returner.extend([decoded_placeholder])
    elif isinstance(sig.output_type, ListType):
        returner = output_placeholder
    else:
        raise TypeCheckFailure(f"Invalid output type: {sig.output_type}")
    return output_placeholder, returner, output_size
コード例 #3
0
ファイル: function_signature.py プロジェクト: benjyz/vyper
 def size(self):
     if hasattr(self.typ, "size_in_bytes"):
         # temporary requirement to support both new and old type objects
         # we divide by 32 here because the returned value is denominated
         # in "slots" of 32 bytes each
         return math.ceil(self.typ.size_in_bytes / 32)
     return get_size_of_type(self.typ)
コード例 #4
0
    def new_internal_variable(self, typ: NodeType) -> int:
        """
        Allocate memory for an internal variable.

        Arguments
        ---------
        typ : NodeType
            Variable type, used to determine the size of memory allocation

        Returns
        -------
        int
            Memory offset for the variable
        """
        # internal variable names begin with a number sign so there is no chance for collision
        var_id = self._internal_var_iter
        self._internal_var_iter += 1
        name = f"#internal_{var_id}"

        if hasattr(typ, "size_in_bytes"):
            # temporary requirement to support both new and old type objects
            var_size = typ.size_in_bytes  # type: ignore
        else:
            var_size = 32 * get_size_of_type(typ)
        return self._new_variable(name, typ, var_size, True)
コード例 #5
0
    def new_variable(self,
                     name: str,
                     typ: NodeType,
                     pos: VyperNode = None) -> int:
        """
        Allocate memory for a user-defined variable.

        Arguments
        ---------
        name : str
            Name of the variable
        typ : NodeType
            Variable type, used to determine the size of memory allocation
        pos : VyperNode
            AST node corresponding to the location where the variable was created,
            used for annotating exceptions

        Returns
        -------
        int
            Memory offset for the variable
        """

        if hasattr(typ, "size_in_bytes"):
            # temporary requirement to support both new and old type objects
            var_size = typ.size_in_bytes  # type: ignore
        else:
            var_size = 32 * get_size_of_type(typ)
        return self._new_variable(name, typ, var_size, False)
コード例 #6
0
def test_get_size_of_type():
    assert get_size_of_type(BaseType("int128")) == 1
    assert get_size_of_type(ByteArrayType(12)) == 3
    assert get_size_of_type(ByteArrayType(33)) == 4
    assert get_size_of_type(ListType(BaseType("int128"), 10)) == 10

    _tuple = TupleType([BaseType("int128"), BaseType("decimal")])
    assert get_size_of_type(_tuple) == 2

    _struct = StructType({
        "a": BaseType("int128"),
        "b": BaseType("decimal")
    }, "Foo")
    assert get_size_of_type(_struct) == 2

    # Don't allow unknown types.
    with raises(Exception):
        get_size_of_type(int)

    # Maps are not supported for function arguments or outputs
    with raises(Exception):
        get_size_of_type(MappingType(BaseType("int128"), BaseType("int128")))
コード例 #7
0
ファイル: parser_utils.py プロジェクト: benjyz/vyper
def _make_array_index_setter(target, target_token, pos, location, offset):
    if location == "memory" and isinstance(target.value, int):
        offset = target.value + 32 * get_size_of_type(
            target.typ.subtype) * offset
        return LLLnode.from_list([offset],
                                 typ=target.typ.subtype,
                                 location=location,
                                 pos=pos)
    else:
        return add_variable_offset(
            target_token,
            LLLnode.from_list(offset, typ="int256"),
            pos=pos,
            array_bounds_check=False,
        )
コード例 #8
0
ファイル: parser_utils.py プロジェクト: benjyz/vyper
def make_setter(left, right, location, pos, in_function_call=False):
    # Basic types
    if isinstance(left.typ, BaseType):
        right = unwrap_location(right)
        if location == "storage":
            return LLLnode.from_list(["sstore", left, right], typ=None)
        elif location == "memory":
            return LLLnode.from_list(["mstore", left, right], typ=None)
    # Byte arrays
    elif isinstance(left.typ, ByteArrayLike):
        return make_byte_array_copier(left, right, pos)
    # Can't copy mappings
    elif isinstance(left.typ, MappingType):
        raise TypeMismatch(
            "Cannot copy mappings; can only copy individual elements", pos)
    # Arrays
    elif isinstance(left.typ, ListType):
        # Cannot do something like [a, b, c] = [1, 2, 3]
        if left.value == "multi":
            return
        if not isinstance(right.typ, ListType):
            return
        if right.typ.count != left.typ.count:
            return

        left_token = LLLnode.from_list("_L",
                                       typ=left.typ,
                                       location=left.location)
        # If the right side is a literal
        if right.value in ["multi", "seq_unchecked"] and right.typ.is_literal:
            if right.value == "seq_unchecked":
                # when the LLL is `seq_unchecked`, this is a literal where one or
                # more values must be pre-processed to avoid memory corruption
                subs = right.args[:-1]
                right = right.args[-1]
            else:
                subs = []
            for i in range(left.typ.count):
                lhs_setter = _make_array_index_setter(left, left_token, pos,
                                                      location, i)
                subs.append(
                    make_setter(
                        lhs_setter,
                        right.args[i],
                        location,
                        pos=pos,
                    ))
            if left.location == "memory" and isinstance(left.value, int):
                return LLLnode.from_list(["seq"] + subs, typ=None)
            else:
                return LLLnode.from_list(["with", "_L", left, ["seq"] + subs],
                                         typ=None)
        elif right.value is None:
            if right.typ != left.typ:
                return
            if left.location == "memory":
                return mzero(left, 32 * get_size_of_type(left.typ))

            subs = []
            for i in range(left.typ.count):
                subs.append(
                    make_setter(
                        add_variable_offset(
                            left_token,
                            LLLnode.from_list(i, typ="int256"),
                            pos=pos,
                            array_bounds_check=False,
                        ),
                        LLLnode.from_list(None, typ=right.typ.subtype),
                        location,
                        pos=pos,
                    ))
            return LLLnode.from_list(["with", "_L", left, ["seq"] + subs],
                                     typ=None)
        # If the right side is a variable
        else:
            right_token = LLLnode.from_list("_R",
                                            typ=right.typ,
                                            location=right.location)
            subs = []
            for i in range(left.typ.count):
                lhs_setter = _make_array_index_setter(left, left_token, pos,
                                                      left.location, i)
                rhs_setter = _make_array_index_setter(right, right_token, pos,
                                                      right.location, i)
                subs.append(
                    make_setter(
                        lhs_setter,
                        rhs_setter,
                        location,
                        pos=pos,
                    ))
            lll_node = ["seq"] + subs
            if right.location != "memory" or not isinstance(right.value, int):
                lll_node = ["with", "_R", right, lll_node]
            if left.location != "memory" or not isinstance(left.value, int):
                lll_node = ["with", "_L", left, lll_node]
            return LLLnode.from_list(lll_node, typ=None)
    # Structs
    elif isinstance(left.typ, TupleLike):
        if left.value == "multi" and isinstance(left.typ, StructType):
            return
        if right.value is not None:
            if not isinstance(right.typ, left.typ.__class__):
                return
            if isinstance(left.typ, StructType):
                for k in left.typ.members:
                    if k not in right.typ.members:
                        return
                for k in right.typ.members:
                    if k not in left.typ.members:
                        return
                if left.typ.name != right.typ.name:
                    return
            else:
                if len(left.typ.members) != len(right.typ.members):
                    return

        left_token = LLLnode.from_list("_L",
                                       typ=left.typ,
                                       location=left.location)
        keyz = left.typ.tuple_keys()

        # If the left side is a literal
        if left.value == "multi":
            locations = [arg.location for arg in left.args]
        else:
            locations = [location for _ in keyz]

        # If the right side is a literal
        if right.value == "multi":
            if len(right.args) != len(keyz):
                return
            # get the RHS arguments into a dict because
            # they are not guaranteed to be in the same order
            # the LHS keys.
            right_args = dict(zip(right.typ.tuple_keys(), right.args))
            subs = []
            for (key, loc) in zip(keyz, locations):
                subs.append(
                    make_setter(
                        add_variable_offset(left_token, key, pos=pos),
                        right_args[key],
                        loc,
                        pos=pos,
                    ))
            return LLLnode.from_list(["with", "_L", left, ["seq"] + subs],
                                     typ=None)
        # If the right side is a null
        elif right.value is None:
            if left.typ != right.typ:
                return

            if left.location == "memory":
                return mzero(left, 32 * get_size_of_type(left.typ))

            subs = []
            for key, loc in zip(keyz, locations):
                subs.append(
                    make_setter(
                        add_variable_offset(left_token, key, pos=pos),
                        LLLnode.from_list(None, typ=right.typ.members[key]),
                        loc,
                        pos=pos,
                    ))
            return LLLnode.from_list(["with", "_L", left, ["seq"] + subs],
                                     typ=None)
        # If tuple assign.
        elif isinstance(left.typ, TupleType) and isinstance(
                right.typ, TupleType):
            subs = []
            for var_arg in left.args:
                if var_arg.location == "calldata":
                    return

            right_token = LLLnode.from_list("_R",
                                            typ=right.typ,
                                            location=right.location)
            for left_arg, key, loc in zip(left.args, keyz, locations):
                subs.append(
                    make_setter(left_arg,
                                add_variable_offset(right_token, key, pos=pos),
                                loc,
                                pos=pos))

            return LLLnode.from_list(
                ["with", "_R", right, ["seq"] + subs],
                typ=None,
                annotation="Tuple assignment",
            )
        # If the left side is a variable i.e struct type
        else:
            subs = []
            right_token = LLLnode.from_list("_R",
                                            typ=right.typ,
                                            location=right.location)
            for typ, loc in zip(keyz, locations):
                subs.append(
                    make_setter(
                        add_variable_offset(left_token, typ, pos=pos),
                        add_variable_offset(right_token, typ, pos=pos),
                        loc,
                        pos=pos,
                    ))
            return LLLnode.from_list(
                ["with", "_L", left, ["with", "_R", right, ["seq"] + subs]],
                typ=None,
            )
コード例 #9
0
ファイル: parser_utils.py プロジェクト: benjyz/vyper
def pack_arguments(signature, args, context, stmt_expr, is_external_call):
    pos = getpos(stmt_expr)
    setters = []
    staticarray_offset = 0

    maxlen = sum([get_size_of_type(arg.typ) for arg in signature.args]) * 32
    if is_external_call:
        maxlen += 32

    placeholder_typ = ByteArrayType(maxlen=maxlen)
    placeholder = context.new_internal_variable(placeholder_typ)
    if is_external_call:
        setters.append(["mstore", placeholder, signature.method_id])
        placeholder += 32

    if len(signature.args) != len(args):
        return

    # check for dynamic-length types
    dynamic_remaining = len(
        [i for i in signature.args if isinstance(i.typ, ByteArrayLike)])
    needpos = bool(dynamic_remaining)

    for i, (arg,
            typ) in enumerate(zip(args, [arg.typ for arg in signature.args])):
        if isinstance(typ, BaseType):
            setters.append(
                make_setter(
                    LLLnode.from_list(
                        placeholder + staticarray_offset + i * 32,
                        typ=typ,
                    ),
                    arg,
                    "memory",
                    pos=pos,
                    in_function_call=True,
                ))

        elif isinstance(typ, ByteArrayLike):
            dynamic_remaining -= 1
            setters.append(
                ["mstore", placeholder + staticarray_offset + i * 32, "_poz"])
            arg_copy = LLLnode.from_list("_s",
                                         typ=arg.typ,
                                         location=arg.location)
            target = LLLnode.from_list(
                ["add", placeholder, "_poz"],
                typ=typ,
                location="memory",
            )
            pos_setter = "pass"

            # if `arg.value` is None, this is a call to `empty()`
            # if `arg.typ.maxlen` is 0, this is a literal "" or b""
            if arg.value is None or arg.typ.maxlen == 0:
                if dynamic_remaining:
                    # only adjust the dynamic pointer if this is not the last dynamic type
                    pos_setter = ["set", "_poz", ["add", "_poz", 64]]
                setters.append(["seq", mzero(target, 64), pos_setter])
            else:
                if dynamic_remaining:
                    pos_setter = [
                        "set",
                        "_poz",
                        [
                            "add", 32,
                            ["ceil32", ["add", "_poz",
                                        get_length(arg_copy)]]
                        ],
                    ]
                setters.append([
                    "with",
                    "_s",
                    arg,
                    [
                        "seq",
                        make_byte_array_copier(target, arg_copy, pos),
                        pos_setter
                    ],
                ])

        elif isinstance(typ, (StructType, ListType)):
            if has_dynamic_data(typ):
                return
            target = LLLnode.from_list(
                [placeholder + staticarray_offset + i * 32],
                typ=typ,
                location="memory",
            )
            setters.append(make_setter(target, arg, "memory", pos=pos))
            staticarray_offset += 32 * (get_size_of_type(typ) - 1)

        else:
            return

    if is_external_call:
        returner = [[placeholder - 4]]
        inargsize = placeholder_typ.maxlen - 28
    else:
        # internal call does not use a returner or adjust max length for signature
        returner = []
        inargsize = placeholder_typ.maxlen

    if needpos:
        return (
            LLLnode.from_list(
                [
                    "with", "_poz",
                    len(args) * 32 + staticarray_offset,
                    ["seq"] + setters + returner
                ],
                typ=placeholder_typ,
                location="memory",
            ),
            inargsize,
            placeholder,
        )
    else:
        return (
            LLLnode.from_list(["seq"] + setters + returner,
                              typ=placeholder_typ,
                              location="memory"),
            inargsize,
            placeholder,
        )
コード例 #10
0
ファイル: parser_utils.py プロジェクト: benjyz/vyper
def add_variable_offset(parent, key, pos, array_bounds_check=True):
    typ, location = parent.typ, parent.location
    if isinstance(typ, TupleLike):
        if isinstance(typ, StructType):
            subtype = typ.members[key]
            attrs = list(typ.tuple_keys())
            index = attrs.index(key)
            annotation = key
        else:
            attrs = list(range(len(typ.members)))
            index = key
            annotation = None

        if location == "storage":
            # for arrays and structs, calculate the storage slot by adding an offset
            # of [index value being accessed] * [size of each item within the sequence]
            offset = 0
            for i in range(index):
                offset += get_size_of_type(typ.members[attrs[i]])
            return LLLnode.from_list(
                ["add", parent, offset],
                typ=subtype,
                location="storage",
            )
        elif location == "storage_prehashed":
            return LLLnode.from_list(
                [
                    "add", parent,
                    LLLnode.from_list(index, annotation=annotation)
                ],
                typ=subtype,
                location="storage",
            )
        elif location in ("calldata", "memory"):
            offset = 0
            for i in range(index):
                offset += 32 * get_size_of_type(typ.members[attrs[i]])
            return LLLnode.from_list(
                ["add", offset, parent],
                typ=typ.members[key],
                location=location,
                annotation=annotation,
            )

    elif isinstance(typ, MappingType):

        sub = None
        if isinstance(key.typ, ByteArrayLike):
            if isinstance(
                    typ.keytype,
                    ByteArrayLike) and (typ.keytype.maxlen >= key.typ.maxlen):

                subtype = typ.valuetype
                if len(key.args[0].args) >= 3:  # handle bytes literal.
                    sub = LLLnode.from_list([
                        "seq",
                        key,
                        [
                            "sha3",
                            ["add", key.args[0].args[-1], 32],
                            ["mload", key.args[0].args[-1]],
                        ],
                    ])
                else:
                    value = key.args[0].value
                    if value == "add":
                        # special case, key is a bytes array within a tuple/struct
                        value = key.args[0]
                    sub = LLLnode.from_list(["sha3", ["add", value, 32], key])
        else:
            subtype = typ.valuetype
            sub = unwrap_location(key)

        if sub is not None and location == "storage":
            return LLLnode.from_list(["sha3_64", parent, sub],
                                     typ=subtype,
                                     location="storage")

    elif isinstance(typ, ListType) and is_base_type(
            key.typ, ("int128", "int256", "uint256")):

        subtype = typ.subtype
        k = unwrap_location(key)
        if not array_bounds_check:
            sub = k
        elif key.typ.is_literal:  # note: BaseType always has is_literal attr
            # perform the check at compile time and elide the runtime check.
            if key.value < 0 or key.value >= typ.count:
                return
            sub = k
        else:
            # this works, even for int128. for int128, since two's-complement
            # is used, if the index is negative, (unsigned) LT will interpret
            # it as a very large number, larger than any practical value for
            # an array index, and the clamp will throw an error.
            sub = ["uclamplt", k, typ.count]

        if location == "storage":
            # storage slot determined as [initial storage slot] + [index] * [size of base type]
            offset = get_size_of_type(subtype)
            return LLLnode.from_list(["add", parent, ["mul", sub, offset]],
                                     typ=subtype,
                                     location="storage",
                                     pos=pos)
        elif location == "storage_prehashed":
            return LLLnode.from_list(["add", parent, sub],
                                     typ=subtype,
                                     location="storage",
                                     pos=pos)
        elif location in ("calldata", "memory"):
            offset = 32 * get_size_of_type(subtype)
            return LLLnode.from_list(["add", ["mul", offset, sub], parent],
                                     typ=subtype,
                                     location=location,
                                     pos=pos)
コード例 #11
0
def make_call(stmt_expr, context):
    # ** Internal 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

    pop_local_vars = []
    push_local_vars = []
    pop_return_values = []
    push_args = []
    method_name = stmt_expr.func.attr

    # TODO check this out
    from vyper.old_codegen.expr import parse_sequence

    pre_init, expr_args = parse_sequence(stmt_expr, stmt_expr.args, context)
    sig = FunctionSignature.lookup_sig(
        context.sigs,
        method_name,
        expr_args,
        stmt_expr,
        context,
    )

    if context.is_constant() and sig.mutability not in ("view", "pure"):
        raise StateAccessViolation(
            f"May not call state modifying function "
            f"'{method_name}' within {context.pp_constancy()}.",
            getpos(stmt_expr),
        )

    if not sig.internal:
        raise StructureException("Cannot call external functions via 'self'",
                                 stmt_expr)

    # 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])

        if len(var_slots) > 10:
            # if memory is large enough, push and pop it via iteration
            mem_from, mem_to = var_slots[0][
                0], var_slots[-1][0] + var_slots[-1][1] * 32
            i_placeholder = context.new_internal_variable(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
            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:
            # for smaller memory, hardcode the mload/mstore locations
            push_mem_slots = []
            for pos, size in var_slots:
                push_mem_slots.extend([pos + i * 32 for i in range(size)])

            push_local_vars = [["mload", pos] for pos in push_mem_slots]
            pop_local_vars = [["mstore", pos, "pass"]
                              for pos in push_mem_slots[::-1]]

    # 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_internal_variable(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_internal_variable(
                    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
コード例 #12
0
def gen_tuple_return(stmt, context, sub):
    abi_typ = abi_type_of(context.return_type)
    # according to the ABI, return types are ALWAYS tuples even if
    # only one element is being returned.
    # https://solidity.readthedocs.io/en/latest/abi-spec.html#function-selector-and-argument-encoding
    # "and the return values v_1, ..., v_k of f are encoded as
    #
    #    enc((v_1, ..., v_k))
    #    i.e. the values are combined into a tuple and encoded.
    # "
    # therefore, wrap it in a tuple if it's not already a tuple.
    # (big difference between returning `(bytes,)` and `bytes`.
    abi_typ = ensure_tuple(abi_typ)
    abi_bytes_needed = abi_typ.static_size() + abi_typ.dynamic_size_bound()
    dst = context.memory_allocator.expand_memory(abi_bytes_needed)
    return_buffer = LLLnode(dst,
                            location="memory",
                            annotation="return_buffer",
                            typ=context.return_type)

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

    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 using make_setter, since
            # our memory encoding happens to be identical to the ABI
            # encoding.
            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 = [
            "seq",
            ["mstore", MemoryPositions.FREE_VAR_SPACE, encode_out],
            make_return_stmt(stmt, context, return_buffer, load_return_len),
        ]
        return LLLnode.from_list(os, typ=None, pos=getpos(stmt), valency=0)

    # for tuple return types where a function is called inside the tuple, we
    # process the calls prior to encoding the return data
    if sub.value == "seq_unchecked" and sub.args[-1].value == "multi":
        encode_out = abi_encode(return_buffer,
                                sub.args[-1],
                                pos=getpos(stmt),
                                returns=True)
        load_return_len = ["mload", MemoryPositions.FREE_VAR_SPACE]
        os = (["seq"] + sub.args[:-1] + [
            ["mstore", MemoryPositions.FREE_VAR_SPACE, encode_out],
            make_return_stmt(stmt, context, return_buffer, load_return_len),
        ])
        return LLLnode.from_list(os, typ=None, pos=getpos(stmt), valency=0)

    # for all othe cases we are creating a stack variable named sub_loc to store the  location
    # of the return expression. This is done so that the return expression does not get evaluated
    # abi-encode uses a function named o_list which evaluate the expression multiple times
    sub_loc = LLLnode("sub_loc", typ=sub.typ, location=sub.location)
    encode_out = abi_encode(return_buffer,
                            sub_loc,
                            pos=getpos(stmt),
                            returns=True)
    load_return_len = ["mload", MemoryPositions.FREE_VAR_SPACE]
    os = [
        "with",
        "sub_loc",
        sub,
        [
            "seq",
            ["mstore", MemoryPositions.FREE_VAR_SPACE, encode_out],
            make_return_stmt(stmt, context, return_buffer, load_return_len),
        ],
    ]
    return LLLnode.from_list(os, typ=None, pos=getpos(stmt), valency=0)
コード例 #13
0
ファイル: function_signature.py プロジェクト: benjyz/vyper
    def from_definition(
        cls,
        code,
        sigs=None,
        custom_structs=None,
        interface_def=False,
        constant_override=False,
        is_from_json=False,
    ):
        if not custom_structs:
            custom_structs = {}

        name = code.name
        mem_pos = 0

        # Determine the arguments, expects something of the form def foo(arg1:
        # int128, arg2: int128 ...
        args = []
        for arg in code.args.args:
            # Each arg needs a type specified.
            typ = arg.annotation
            parsed_type = parse_type(
                typ,
                None,
                sigs,
                custom_structs=custom_structs,
            )
            args.append(
                VariableRecord(
                    arg.arg,
                    mem_pos,
                    parsed_type,
                    False,
                    defined_at=getpos(arg),
                ))

            if isinstance(parsed_type, ByteArrayLike):
                mem_pos += 32
            else:
                mem_pos += get_size_of_type(parsed_type) * 32

        mutability = "nonpayable"  # Assume nonpayable by default
        nonreentrant_key = ""
        is_internal = False

        # Update function properties from decorators
        # NOTE: Can't import enums here because of circular import
        for dec in code.decorator_list:
            if isinstance(dec, vy_ast.Name) and dec.id in ("payable", "view",
                                                           "pure"):
                mutability = dec.id
            elif isinstance(dec, vy_ast.Name) and dec.id == "internal":
                is_internal = True
            elif isinstance(dec, vy_ast.Name) and dec.id == "external":
                is_internal = False
            elif isinstance(dec,
                            vy_ast.Call) and dec.func.id == "nonreentrant":
                nonreentrant_key = dec.args[0].s

        if constant_override:
            # In case this override is abused, match previous behavior
            if mutability == "payable":
                raise StructureException(
                    f"Function {name} cannot be both constant and payable.")
            mutability = "view"

        # Determine the return type and whether or not it's constant. Expects something
        # of the form:
        # def foo(): ...
        # def foo() -> int128: ...
        # If there is no return type, ie. it's of the form def foo(): ...
        # and NOT def foo() -> type: ..., then it's null
        output_type = None
        if code.returns:
            output_type = parse_type(
                code.returns,
                None,
                sigs,
                custom_structs=custom_structs,
            )
            # Output type must be canonicalizable
            assert isinstance(output_type,
                              TupleType) or canonicalize_type(output_type)
        # Get the canonical function signature
        sig = cls.get_full_sig(name, code.args.args, sigs, custom_structs)

        # Take the first 4 bytes of the hash of the sig to get the method ID
        method_id = fourbytes_to_int(keccak256(bytes(sig, "utf-8"))[:4])
        return cls(
            name,
            args,
            output_type,
            mutability,
            is_internal,
            nonreentrant_key,
            sig,
            method_id,
            code,
            is_from_json,
        )