Пример #1
0
def add_ofst(loc, ofst):
    ofst = LLLnode.from_list(ofst)
    if isinstance(loc.value, int) and isinstance(ofst.value, int):
        ret = loc.value + ofst.value
    else:
        ret = ["add", loc, ofst]
    return LLLnode.from_list(ret, location=loc.location, encoding=loc.encoding)
Пример #2
0
def pop_dyn_array(darray_node, return_popped_item, pos=None):
    assert isinstance(darray_node.typ, DArrayType)
    ret = ["seq"]
    with darray_node.cache_when_complex("darray") as (b1, darray_node):
        old_len = ["clamp_nonzero", get_dyn_array_count(darray_node)]
        new_len = LLLnode.from_list(["sub", old_len, 1], typ="uint256")

        with new_len.cache_when_complex("new_len") as (b2, new_len):
            ret.append([store_op(darray_node.location), darray_node, new_len])

            # NOTE skip array bounds check bc we already asserted len two lines up
            if return_popped_item:
                popped_item = get_element_ptr(darray_node,
                                              new_len,
                                              array_bounds_check=False,
                                              pos=pos)
                ret.append(popped_item)
                typ = popped_item.typ
                location = popped_item.location
                encoding = popped_item.encoding
            else:
                typ, location, encoding = None, None, None
            return LLLnode.from_list(b1.resolve(b2.resolve(ret)),
                                     typ=typ,
                                     location=location,
                                     encoding=encoding,
                                     pos=pos)
Пример #3
0
def _complex_make_setter(left, right, pos):
    if right.value == "~empty" and left.location == "memory":
        # optimized memzero
        return mzero(left, left.typ.memory_bytes_required)

    ret = ["seq"]

    if isinstance(left.typ, SArrayType):
        n_items = right.typ.count
        keys = [LLLnode.from_list(i, typ="uint256") for i in range(n_items)]

    if isinstance(left.typ, TupleLike):
        keys = left.typ.tuple_keys()

    # if len(keyz) == 0:
    #    return LLLnode.from_list(["pass"])

    # general case
    # TODO use copy_bytes when the generated code is above a certain size
    with left.cache_when_complex("_L") as (
            b1, left), right.cache_when_complex("_R") as (b2, right):

        for k in keys:
            l_i = get_element_ptr(left, k, pos=pos, array_bounds_check=False)
            r_i = get_element_ptr(right, k, pos=pos, array_bounds_check=False)
            ret.append(make_setter(l_i, r_i, pos))

        return b1.resolve(b2.resolve(LLLnode.from_list(ret)))
Пример #4
0
def generate_lll_for_internal_function(code: vy_ast.FunctionDef,
                                       sig: FunctionSignature,
                                       context: Context) -> LLLnode:
    """
    Parse a internal function (FuncDef), and produce full function body.

    :param sig: the FuntionSignature
    :param code: ast of function
    :param context: current calling context
    :return: function body in LLL
    """

    # The calling convention is:
    #   Caller fills in argument buffer
    #   Caller provides return address, return buffer on the stack
    #   Callee runs its code, fills in return buffer provided by caller
    #   Callee jumps back to caller

    # The reason caller fills argument buffer is so there is less
    # complication with passing args on the stack; the caller is better
    # suited to optimize the copy operation. Also it avoids the callee
    # having to handle default args; that is easier left to the caller
    # as well. Meanwhile, the reason the callee fills the return buffer
    # is first, similarly, the callee is more suited to optimize the copy
    # operation. Second, it allows the caller to allocate the return
    # buffer in a way which reduces the number of copies. Third, it
    # reduces the potential for bugs since it forces the caller to have
    # the return data copied into a preallocated location. Otherwise, a
    # situation like the following is easy to bork:
    #   x: T[2] = [self.generate_T(), self.generate_T()]

    func_type = code._metadata["type"]

    # Get nonreentrant lock

    for arg in sig.args:
        # allocate a variable for every arg, setting mutability
        # to False to comply with vyper semantics, function arguments are immutable
        context.new_variable(arg.name, arg.typ, is_mutable=False)

    nonreentrant_pre, nonreentrant_post = get_nonreentrant_lock(func_type)

    function_entry_label = sig.internal_function_label
    cleanup_label = sig.exit_sequence_label

    # jump to the label which was passed in via stack
    stop_func = LLLnode.from_list(["jump", "pass"],
                                  annotation="jump to return address")

    enter = [["label", function_entry_label]] + nonreentrant_pre

    body = [parse_body(c, context) for c in code.body]

    exit = [["label", cleanup_label]] + nonreentrant_post + [stop_func]

    return LLLnode.from_list(
        ["seq"] + enter + body + exit,
        typ=None,
        pos=getpos(code),
    )
Пример #5
0
def _register_function_args(context: Context, sig: FunctionSignature) -> List[LLLnode]:
    pos = None

    ret = []

    # the type of the calldata
    base_args_t = TupleType([arg.typ for arg in sig.base_args])

    # tuple with the abi_encoded args
    if sig.is_init_func:
        base_args_ofst = LLLnode(0, location="data", typ=base_args_t, encoding=Encoding.ABI)
    else:
        base_args_ofst = LLLnode(4, location="calldata", typ=base_args_t, encoding=Encoding.ABI)

    for i, arg in enumerate(sig.base_args):

        arg_lll = get_element_ptr(base_args_ofst, i, pos=pos)

        if _should_decode(arg.typ):
            # allocate a memory slot for it and copy
            p = context.new_variable(arg.name, arg.typ, is_mutable=False)
            dst = LLLnode(p, typ=arg.typ, location="memory")
            ret.append(make_setter(dst, arg_lll, pos=pos))
        else:
            # leave it in place
            context.vars[arg.name] = VariableRecord(
                name=arg.name,
                pos=arg_lll,
                typ=arg.typ,
                mutable=False,
                location=arg_lll.location,
                encoding=Encoding.ABI,
            )

    return ret
Пример #6
0
    def parse_Name(self):

        if self.expr.id == "self":
            return LLLnode.from_list(["address"],
                                     typ="address",
                                     pos=getpos(self.expr))
        elif self.expr.id in self.context.vars:
            var = self.context.vars[self.expr.id]
            return LLLnode.from_list(
                var.pos,
                typ=var.typ,
                location=var.
                location,  # either 'memory' or 'calldata' storage is handled above.
                encoding=var.encoding,
                pos=getpos(self.expr),
                annotation=self.expr.id,
                mutable=var.mutable,
            )

        elif self.expr.id in BUILTIN_CONSTANTS:
            obj, typ = BUILTIN_CONSTANTS[self.expr.id]
            return LLLnode.from_list([obj],
                                     typ=BaseType(typ, is_literal=True),
                                     pos=getpos(self.expr))
        elif self.expr._metadata["type"].is_immutable:
            # immutable variable
            # need to handle constructor and outside constructor
            var = self.context.globals[self.expr.id]
            is_constructor = self.expr.get_ancestor(
                vy_ast.FunctionDef).get("name") == "__init__"
            if is_constructor:
                # store memory position for later access in module.py in the variable record
                memory_loc = self.context.new_variable(self.expr.id, var.typ)
                self.context.global_ctx._globals[self.expr.id].pos = memory_loc
                # store the data offset in the variable record as well for accessing
                data_offset = self.expr._metadata["type"].position.offset
                self.context.global_ctx._globals[
                    self.expr.id].data_offset = data_offset

                return LLLnode.from_list(
                    memory_loc,
                    typ=var.typ,
                    location="memory",
                    pos=getpos(self.expr),
                    annotation=self.expr.id,
                    mutable=True,
                )
            else:
                immutable_section_size = self.context.global_ctx.immutable_section_size
                offset = self.expr._metadata["type"].position.offset
                # TODO: resolve code offsets for immutables at compile time
                return LLLnode.from_list(
                    ["sub", "codesize", immutable_section_size - offset],
                    typ=var.typ,
                    location="code",
                    pos=getpos(self.expr),
                    annotation=self.expr.id,
                    mutable=False,
                )
Пример #7
0
def _mul(x, y):
    x = LLLnode.from_list(x)
    y = LLLnode.from_list(y)
    if isinstance(x.value, int) and isinstance(y.value, int):
        ret = x.value * y.value
    else:
        ret = ["mul", x, y]
    return LLLnode.from_list(ret)
Пример #8
0
def unwrap_location(orig):
    if orig.location in ("memory", "storage", "calldata", "code"):
        return LLLnode.from_list([load_op(orig.location), orig], typ=orig.typ)
    else:
        # CMC 20210909 TODO double check if this branch can be removed
        if orig.value == "~empty":
            return LLLnode.from_list(0, typ=orig.typ)
        return orig
Пример #9
0
def _get_element_ptr_tuplelike(parent, key, pos):
    typ = parent.typ
    assert isinstance(typ, TupleLike)

    if isinstance(typ, StructType):
        assert isinstance(key, str)
        subtype = typ.members[key]
        attrs = list(typ.tuple_keys())
        index = attrs.index(key)
        annotation = key
    else:
        assert isinstance(key, int)
        subtype = typ.members[key]
        attrs = list(range(len(typ.members)))
        index = key
        annotation = None

    # generated by empty() + make_setter
    if parent.value == "~empty":
        return LLLnode.from_list("~empty", typ=subtype)

    if parent.value == "multi":
        assert parent.encoding != Encoding.ABI, "no abi-encoded literals"
        return parent.args[index]

    ofst = 0  # offset from parent start

    if parent.encoding in (Encoding.ABI, Encoding.JSON_ABI):
        if parent.location == "storage":
            raise CompilerPanic("storage variables should not be abi encoded"
                                )  # pragma: notest

        member_t = typ.members[attrs[index]]

        for i in range(index):
            member_abi_t = typ.members[attrs[i]].abi_type
            ofst += member_abi_t.embedded_static_size()

        return _getelemptr_abi_helper(parent, member_t, ofst, pos)

    if parent.location == "storage":
        for i in range(index):
            ofst += typ.members[attrs[i]].storage_size_in_words
    elif parent.location in ("calldata", "memory", "code"):
        for i in range(index):
            ofst += typ.members[attrs[i]].memory_bytes_required
    else:
        raise CompilerPanic(
            f"bad location {parent.location}")  # pragma: notest

    return LLLnode.from_list(
        add_ofst(parent, ofst),
        typ=subtype,
        location=parent.location,
        encoding=parent.encoding,
        annotation=annotation,
        pos=pos,
    )
Пример #10
0
def parse_tree_to_lll(global_ctx: GlobalContext) -> Tuple[LLLnode, LLLnode, FunctionSignatures]:
    _names_def = [_def.name for _def in global_ctx._defs]
    # Checks for duplicate function names
    if len(set(_names_def)) < len(_names_def):
        raise FunctionDeclarationException(
            "Duplicate function name: "
            f"{[name for name in _names_def if _names_def.count(name) > 1][0]}"
        )
    _names_events = [_event.name for _event in global_ctx._events]
    # Checks for duplicate event names
    if len(set(_names_events)) < len(_names_events):
        raise EventDeclarationException(
            f"""Duplicate event name:
            {[name for name in _names_events if _names_events.count(name) > 1][0]}"""
        )
    # Initialization function
    init_function = next((_def for _def in global_ctx._defs if is_initializer(_def)), None)
    # Default function
    default_function = next((i for i in global_ctx._defs if is_default_func(i)), None)

    regular_functions = [
        _def for _def in global_ctx._defs if not is_initializer(_def) and not is_default_func(_def)
    ]

    sigs: dict = {}
    external_interfaces: dict = {}
    # Create the main statement
    o: List[Union[str, LLLnode]] = ["seq"]
    if global_ctx._contracts or global_ctx._interfaces:
        external_interfaces = parse_external_interfaces(external_interfaces, global_ctx)

    # TODO: fix for #2251 is to move this after parse_regular_functions
    if init_function:
        o.append(init_func_init_lll())
        init_func_lll, _frame_start, _frame_size = generate_lll_for_function(
            init_function,
            {**{"self": sigs}, **external_interfaces},
            global_ctx,
            False,
        )
        o.append(init_func_lll)

    if regular_functions or default_function:
        o, runtime = parse_regular_functions(
            o,
            regular_functions,
            sigs,
            external_interfaces,
            global_ctx,
            default_function,
        )
    else:
        runtime = o.copy()

    return LLLnode.from_list(o), LLLnode.from_list(runtime), sigs
Пример #11
0
    def parse_List(self):
        pos = getpos(self.expr)
        typ = new_type_to_old_type(self.expr._metadata["type"])
        if len(self.expr.elements) == 0:
            return LLLnode.from_list("~empty", typ=typ, pos=pos)

        multi_lll = [
            Expr(x, self.context).lll_node for x in self.expr.elements
        ]

        return LLLnode.from_list(["multi"] + multi_lll, typ=typ, pos=pos)
Пример #12
0
 def finalize(fill_return_buffer):
     # do NOT bypass this. jump_to_exit may do important function cleanup.
     fill_return_buffer = LLLnode.from_list(
         fill_return_buffer,
         annotation=f"fill return buffer {sig._lll_identifier}")
     cleanup_loops = "cleanup_repeat" if context.forvars else "pass"
     return LLLnode.from_list(
         ["seq", cleanup_loops, fill_return_buffer, jump_to_exit],
         typ=None,
         pos=_pos,
     )
Пример #13
0
 def finalize(fill_return_buffer):
     # do NOT bypass this. jump_to_exit may do important function cleanup.
     fill_return_buffer = LLLnode.from_list(
         fill_return_buffer,
         annotation=f"fill return buffer {sig._lll_identifier}")
     cleanup_loops = "cleanup_repeat" if context.forvars else "pass"
     # NOTE: because stack analysis is incomplete, cleanup_repeat must
     # come after fill_return_buffer otherwise the stack will break
     return LLLnode.from_list(
         ["seq", fill_return_buffer, cleanup_loops, jump_to_exit],
         pos=_pos,
     )
Пример #14
0
def get_dyn_array_count(arg):
    assert isinstance(arg.typ, DArrayType)

    typ = BaseType("uint256")

    if arg.value == "multi":
        return LLLnode.from_list(len(arg.args), typ=typ)

    if arg.value == "~empty":
        # empty(DynArray[])
        return LLLnode.from_list(0, typ=typ)

    return LLLnode.from_list([load_op(arg.location), arg], typ=typ)
Пример #15
0
 def parse_NameConstant(self):
     if self.expr.value is True:
         return LLLnode.from_list(
             1,
             typ=BaseType("bool", is_literal=True),
             pos=getpos(self.expr),
         )
     elif self.expr.value is False:
         return LLLnode.from_list(
             0,
             typ=BaseType("bool", is_literal=True),
             pos=getpos(self.expr),
         )
Пример #16
0
 def parse_Hex(self):
     orignum = self.expr.value
     if len(orignum) == 42 and checksum_encode(orignum) == orignum:
         return LLLnode.from_list(
             int(self.expr.value, 16),
             typ=BaseType("address", is_literal=True),
             pos=getpos(self.expr),
         )
     elif len(orignum) == 66:
         return LLLnode.from_list(
             int(self.expr.value, 16),
             typ=BaseType("bytes32", is_literal=True),
             pos=getpos(self.expr),
         )
Пример #17
0
def ensure_in_memory(lll_var, context, pos=None):
    """Ensure a variable is in memory. This is useful for functions
    which expect to operate on memory variables.
    """
    if lll_var.location == "memory":
        return lll_var

    typ = lll_var.typ
    buf = LLLnode.from_list(context.new_internal_variable(typ),
                            typ=typ,
                            location="memory")
    do_copy = make_setter(buf, lll_var, pos=pos)

    return LLLnode.from_list(["seq", do_copy, buf], typ=typ, location="memory")
Пример #18
0
 def parse_Int(self):
     # Literal (mostly likely) becomes int256
     if self.expr.n < 0:
         return LLLnode.from_list(
             self.expr.n,
             typ=BaseType("int256", is_literal=True),
             pos=getpos(self.expr),
         )
     # Literal is large enough (mostly likely) becomes uint256.
     else:
         return LLLnode.from_list(
             self.expr.n,
             typ=BaseType("uint256", is_literal=True),
             pos=getpos(self.expr),
         )
Пример #19
0
def append_dyn_array(darray_node, elem_node, pos=None):
    assert isinstance(darray_node.typ, DArrayType)

    assert darray_node.typ.count > 0, "jerk boy u r out"

    ret = ["seq"]
    with darray_node.cache_when_complex("darray") as (b1, darray_node):
        len_ = get_dyn_array_count(darray_node)
        with len_.cache_when_complex("old_darray_len") as (b2, len_):
            ret.append(["assert", ["le", len_, darray_node.typ.count - 1]])
            ret.append([
                store_op(darray_node.location), darray_node, ["add", len_, 1]
            ])
            # NOTE: typechecks elem_node
            # NOTE skip array bounds check bc we already asserted len two lines up
            ret.append(
                make_setter(
                    get_element_ptr(darray_node,
                                    len_,
                                    array_bounds_check=False,
                                    pos=pos),
                    elem_node,
                    pos=pos,
                ))
            return LLLnode.from_list(b1.resolve(b2.resolve(ret)), pos=pos)
Пример #20
0
def _rewrite_return_sequences(lll_node, label_params=None):
    args = lll_node.args

    if lll_node.value == "return":
        if args[0].value == "ret_ofst" and args[1].value == "ret_len":
            lll_node.args[0].value = "pass"
            lll_node.args[1].value = "pass"
    if lll_node.value == "exit_to":
        # handle exit from private function
        if args[0].value == "return_pc":
            lll_node.value = "jump"
            args[0].value = "pass"
        else:
            # handle jump to cleanup
            assert is_symbol(args[0].value)
            lll_node.value = "seq"

            _t = ["seq"]
            if "return_buffer" in label_params:
                _t.append(["pop", "pass"])

            dest = args[0].value[5:]  # `_sym_foo` -> `foo`
            more_args = ["pass" if t.value == "return_pc" else t for t in args[1:]]
            _t.append(["goto", dest] + more_args)
            lll_node.args = LLLnode.from_list(_t, pos=lll_node.pos).args

    if lll_node.value == "label":
        label_params = set(t.value for t in lll_node.args[1].args)

    for t in args:
        _rewrite_return_sequences(t, label_params)
Пример #21
0
def _getelemptr_abi_helper(parent, member_t, ofst, pos=None, clamp=True):
    member_abi_t = member_t.abi_type

    # ABI encoding has length word and then pretends length is not there
    # e.g. [[1,2]] is encoded as 0x01 <len> 0x20 <inner array ofst> <encode(inner array)>
    # note that inner array ofst is 0x20, not 0x40.
    if has_length_word(parent.typ):
        parent = add_ofst(parent,
                          wordsize(parent.location) * DYNAMIC_ARRAY_OVERHEAD)

    ofst_lll = add_ofst(parent, ofst)

    if member_abi_t.is_dynamic():
        # double dereference, according to ABI spec
        # TODO optimize special case: first dynamic item
        # offset is statically known.
        ofst_lll = add_ofst(parent, unwrap_location(ofst_lll))

    return LLLnode.from_list(
        ofst_lll,
        typ=member_t,
        location=parent.location,
        encoding=parent.encoding,
        pos=pos,
        annotation=f"{parent}{ofst}",
    )
Пример #22
0
def test_lll_from_s_expression(get_contract_from_lll):
    code = """
(seq
  (deploy
    0
    (seq ; just return 32 byte of calldata back
      (calldatacopy 0 4 32)
      (return 0 32)
      stop
     )
    0))
    """
    abi = [{
        "name": "test",
        "outputs": [{
            "type": "int128",
            "name": "out"
        }],
        "inputs": [{
            "type": "int128",
            "name": "a"
        }],
        "stateMutability": "nonpayable",
        "type": "function",
        "gas": 394,
    }]

    s_expressions = parse_s_exp(code)
    lll = LLLnode.from_list(s_expressions[0])
    c = get_contract_from_lll(lll, abi=abi)
    assert c.test(-123456) == -123456
Пример #23
0
def mzero(dst, nbytes):
    # calldatacopy from past-the-end gives zero bytes.
    # cf. YP H.2 (ops section) with CALLDATACOPY spec.
    return LLLnode.from_list(
        # calldatacopy mempos calldatapos len
        ["calldatacopy", dst, "calldatasize", nbytes],
        annotation="mzero",
    )
Пример #24
0
 def parse_UnaryOp(self):
     operand = Expr.parse_value_expr(self.expr.operand, self.context)
     if isinstance(self.expr.op, vy_ast.Not):
         if isinstance(operand.typ, BaseType) and operand.typ.typ == "bool":
             return LLLnode.from_list(["iszero", operand],
                                      typ="bool",
                                      pos=getpos(self.expr))
     elif isinstance(self.expr.op, vy_ast.USub) and is_numeric_type(
             operand.typ):
         # Clamp on minimum integer value as we cannot negate that value
         # (all other integer values are fine)
         min_int_val = get_min_val_for_type(operand.typ.typ)
         return LLLnode.from_list(
             ["sub", 0, ["clampgt", operand, min_int_val]],
             typ=operand.typ,
             pos=getpos(self.expr),
         )
Пример #25
0
def generate_lll_for_external_function(code, sig, context, check_nonpayable):
    # TODO type hints:
    # def generate_lll_for_external_function(
    #    code: vy_ast.FunctionDef, sig: FunctionSignature, context: Context, check_nonpayable: bool,
    # ) -> LLLnode:
    """Return the LLL for an external function. Includes code to inspect the method_id,
    enter the function (nonpayable and reentrancy checks), handle kwargs and exit
    the function (clean up reentrancy storage variables)
    """
    func_type = code._metadata["type"]
    pos = getpos(code)

    nonreentrant_pre, nonreentrant_post = get_nonreentrant_lock(func_type)

    # generate handlers for base args and register the variable records
    handle_base_args = _register_function_args(context, sig)

    # generate handlers for kwargs and register the variable records
    kwarg_handlers = _generate_kwarg_handlers(context, sig, pos)

    # once optional args have been handled,
    # generate the main body of the function
    entrance = [["label", sig.external_function_base_entry_label]]

    entrance += handle_base_args

    if check_nonpayable and sig.mutability != "payable":
        # if the contract contains payable functions, but this is not one of them
        # add an assertion that the value of the call is zero
        entrance += [["assert", ["iszero", "callvalue"]]]

    entrance += nonreentrant_pre

    body = [parse_body(c, context) for c in code.body]

    exit = [["label", sig.exit_sequence_label]] + nonreentrant_post
    if sig.is_init_func:
        pass  # init func has special exit sequence generated by module.py
    elif context.return_type is None:
        exit += [["stop"]]
    else:
        # ret_ofst and ret_len stack items passed by function body; consume using 'pass'
        exit += [["return", "pass", "pass"]]

    # the lll which comprises the main body of the function,
    # besides any kwarg handling
    func_common_lll = ["seq"] + entrance + body + exit

    if sig.is_default_func or sig.is_init_func:
        # default and init funcs have special entries generated by module.py
        ret = func_common_lll
    else:
        ret = kwarg_handlers
        # sneak the base code into the kwarg handler
        # TODO rethink this / make it clearer
        ret[-1][-1].append(func_common_lll)

    return LLLnode.from_list(ret, pos=getpos(code))
Пример #26
0
def make_setter(left, right, pos):
    check_assign(left, right)

    # Basic types
    if isinstance(left.typ, BaseType):
        enc = right.encoding  # unwrap_location butchers encoding
        right = unwrap_location(right)
        # TODO rethink/streamline the clamp_basetype logic
        if _needs_clamp(right.typ, enc):
            right = clamp_basetype(right)

        op = store_op(left.location)
        return LLLnode.from_list([op, left, right], pos=pos)

    # Byte arrays
    elif isinstance(left.typ, ByteArrayLike):
        # TODO rethink/streamline the clamp_basetype logic
        if _needs_clamp(right.typ, right.encoding):
            with right.cache_when_complex("bs_ptr") as (b, right):
                copier = make_byte_array_copier(left, right, pos)
                ret = b.resolve(["seq", clamp_bytestring(right), copier])
        else:
            ret = make_byte_array_copier(left, right, pos)

        return LLLnode.from_list(ret)

    elif isinstance(left.typ, DArrayType):
        # TODO should we enable this?
        # implicit conversion from sarray to darray
        # if isinstance(right.typ, SArrayType):
        #    return _complex_make_setter(left, right, pos)

        # TODO rethink/streamline the clamp_basetype logic
        if _needs_clamp(right.typ, right.encoding):
            with right.cache_when_complex("arr_ptr") as (b, right):
                copier = _dynarray_make_setter(left, right, pos)
                ret = b.resolve(["seq", clamp_dyn_array(right), copier])
        else:
            ret = _dynarray_make_setter(left, right, pos)

        return LLLnode.from_list(ret)

    # Arrays
    elif isinstance(left.typ, (SArrayType, TupleLike)):
        return _complex_make_setter(left, right, pos)
Пример #27
0
    def handler_for(calldata_kwargs, default_kwargs):
        calldata_args = sig.base_args + calldata_kwargs
        # create a fake type so that get_element_ptr works
        calldata_args_t = TupleType(list(arg.typ for arg in calldata_args))

        abi_sig = sig.abi_signature_for_kwargs(calldata_kwargs)
        method_id = _annotated_method_id(abi_sig)

        calldata_kwargs_ofst = LLLnode(4,
                                       location="calldata",
                                       typ=calldata_args_t,
                                       encoding=Encoding.ABI)

        # a sequence of statements to strictify kwargs into memory
        ret = ["seq"]

        # TODO optimize make_setter by using
        # TupleType(list(arg.typ for arg in calldata_kwargs + default_kwargs))
        # (must ensure memory area is contiguous)

        n_base_args = len(sig.base_args)

        for i, arg_meta in enumerate(calldata_kwargs):
            k = n_base_args + i

            dst = context.lookup_var(arg_meta.name).pos

            lhs = LLLnode(dst, location="memory", typ=arg_meta.typ)
            rhs = get_element_ptr(calldata_kwargs_ofst,
                                  k,
                                  pos=None,
                                  array_bounds_check=False)
            ret.append(make_setter(lhs, rhs, pos))

        for x in default_kwargs:
            dst = context.lookup_var(x.name).pos
            lhs = LLLnode(dst, location="memory", typ=x.typ)
            kw_ast_val = sig.default_values[x.name]  # e.g. `3` in x: int = 3
            rhs = Expr(kw_ast_val, context).lll_node
            ret.append(make_setter(lhs, rhs, pos))

        ret.append(["goto", sig.external_function_base_entry_label])

        ret = ["if", ["eq", "_calldata_method_id", method_id], ret]
        return ret
Пример #28
0
def eval_seq(lll_node):
    """Tries to find the "return" value of a `seq` statement, in order so
    that the value can be known without possibly evaluating side effects
    """
    if lll_node.value in ("seq", "with") and len(lll_node.args) > 0:
        return eval_seq(lll_node.args[-1])
    if isinstance(lll_node.value, int):
        return LLLnode.from_list(lll_node)
    return None
Пример #29
0
 def parse_Tuple(self):
     tuple_elements = [
         Expr(x, self.context).lll_node for x in self.expr.elements
     ]
     typ = TupleType([x.typ for x in tuple_elements], is_literal=True)
     multi_lll = LLLnode.from_list(["multi"] + tuple_elements,
                                   typ=typ,
                                   pos=getpos(self.expr))
     return multi_lll
Пример #30
0
def lll_for_external_call(stmt_expr, context):
    from vyper.codegen.expr import Expr  # TODO rethink this circular import

    pos = getpos(stmt_expr)
    value, gas, skip_contract_check = _get_special_kwargs(stmt_expr, context)
    args_lll = [Expr(x, context).lll_node for x in stmt_expr.args]

    if isinstance(stmt_expr.func, vy_ast.Attribute) and isinstance(
            stmt_expr.func.value, vy_ast.Call):
        # e.g. `Foo(address).bar()`

        # sanity check
        assert len(stmt_expr.func.value.args) == 1
        contract_name = stmt_expr.func.value.func.id
        contract_address = Expr.parse_value_expr(stmt_expr.func.value.args[0],
                                                 context)

    elif (isinstance(stmt_expr.func.value, vy_ast.Attribute)
          and stmt_expr.func.value.attr in context.globals
          # TODO check for self?
          and hasattr(context.globals[stmt_expr.func.value.attr].typ, "name")):
        # e.g. `self.foo.bar()`

        # sanity check
        assert stmt_expr.func.value.value.id == "self", stmt_expr

        contract_name = context.globals[stmt_expr.func.value.attr].typ.name
        type_ = stmt_expr.func.value._metadata["type"]
        var = context.globals[stmt_expr.func.value.attr]
        contract_address = unwrap_location(
            LLLnode.from_list(
                type_.position.position,
                typ=var.typ,
                location="storage",
                pos=pos,
                annotation="self." + stmt_expr.func.value.attr,
            ))
    else:
        # TODO catch this during type checking
        raise StructureException("Unsupported operator.", stmt_expr)

    method_name = stmt_expr.func.attr
    contract_sig = context.sigs[contract_name][method_name]

    ret = _external_call_helper(
        contract_address,
        contract_sig,
        args_lll,
        context,
        pos,
        value=value,
        gas=gas,
        skip_contract_check=skip_contract_check,
    )
    ret.annotation = stmt_expr.get("node_source_code")

    return ret