Exemple #1
0
def convert(expr, context):
    if len(expr.args) != 2:
        raise StructureException(
            "The convert function expects two parameters.", expr)

    arg_ast = expr.args[0]
    arg = Expr(arg_ast, context).ir_node
    out_typ = context.parse_type(expr.args[1])

    if isinstance(arg.typ, BaseType):
        arg = unwrap_location(arg)
    with arg.cache_when_complex("arg") as (b, arg):
        if is_base_type(out_typ, "bool"):
            ret = to_bool(arg_ast, arg, out_typ)
        elif is_base_type(out_typ, "address"):
            ret = to_address(arg_ast, arg, out_typ)
        elif is_integer_type(out_typ):
            ret = to_int(arg_ast, arg, out_typ)
        elif is_bytes_m_type(out_typ):
            ret = to_bytes_m(arg_ast, arg, out_typ)
        elif is_decimal_type(out_typ):
            ret = to_decimal(arg_ast, arg, out_typ)
        elif isinstance(out_typ, ByteArrayType):
            ret = to_bytes(arg_ast, arg, out_typ)
        elif isinstance(out_typ, StringType):
            ret = to_string(arg_ast, arg, out_typ)
        else:
            raise StructureException(f"Conversion to {out_typ} is invalid.",
                                     arg_ast)

        ret = b.resolve(ret)

    return IRnode.from_list(ret)
Exemple #2
0
def lll_for_external_call(stmt_expr, context):
    from vyper.codegen.expr import Expr  # TODO rethink this circular import

    pos = getpos(stmt_expr)

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

    assert isinstance(contract_address.typ, InterfaceType)
    contract_name = contract_address.typ.name
    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
Exemple #3
0
    def parse_Call(self):
        is_self_function = ((isinstance(self.stmt.func, vy_ast.Attribute))
                            and isinstance(self.stmt.func.value, vy_ast.Name)
                            and self.stmt.func.value.id == "self")

        if isinstance(self.stmt.func, vy_ast.Name):
            funcname = self.stmt.func.id
            return STMT_DISPATCH_TABLE[funcname].build_IR(
                self.stmt, self.context)

        elif isinstance(self.stmt.func,
                        vy_ast.Attribute) and self.stmt.func.attr in (
                            "append",
                            "pop",
                        ):
            darray = Expr(self.stmt.func.value, self.context).ir_node
            args = [Expr(x, self.context).ir_node for x in self.stmt.args]
            if self.stmt.func.attr == "append":
                assert len(args) == 1
                arg = args[0]
                assert isinstance(darray.typ, DArrayType)
                assert arg.typ == darray.typ.subtype
                return append_dyn_array(darray, arg)
            else:
                assert len(args) == 0
                return pop_dyn_array(darray, return_popped_item=False)

        elif is_self_function:
            return self_call.ir_for_self_call(self.stmt, self.context)
        else:
            return external_call.ir_for_external_call(self.stmt, self.context)
Exemple #4
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
Exemple #5
0
 def parse_AugAssign(self):
     target = self._get_target(self.stmt.target)
     sub = Expr.parse_value_expr(self.stmt.value, self.context)
     if not isinstance(target.typ, BaseType):
         return
     if target.location == "storage":
         lll_node = Expr.parse_value_expr(
             vy_ast.BinOp(
                 left=LLLnode.from_list(["sload", "_stloc"],
                                        typ=target.typ,
                                        pos=target.pos),
                 right=sub,
                 op=self.stmt.op,
                 lineno=self.stmt.lineno,
                 col_offset=self.stmt.col_offset,
                 end_lineno=self.stmt.end_lineno,
                 end_col_offset=self.stmt.end_col_offset,
                 node_source_code=self.stmt.get("node_source_code"),
             ),
             self.context,
         )
         return LLLnode.from_list(
             [
                 "with", "_stloc", target,
                 ["sstore", "_stloc",
                  unwrap_location(lll_node)]
             ],
             typ=None,
             pos=getpos(self.stmt),
         )
     elif target.location == "memory":
         lll_node = Expr.parse_value_expr(
             vy_ast.BinOp(
                 left=LLLnode.from_list(["mload", "_mloc"],
                                        typ=target.typ,
                                        pos=target.pos),
                 right=sub,
                 op=self.stmt.op,
                 lineno=self.stmt.lineno,
                 col_offset=self.stmt.col_offset,
                 end_lineno=self.stmt.end_lineno,
                 end_col_offset=self.stmt.end_col_offset,
                 node_source_code=self.stmt.get("node_source_code"),
             ),
             self.context,
         )
         return LLLnode.from_list(
             [
                 "with", "_mloc", target,
                 ["mstore", "_mloc",
                  unwrap_location(lll_node)]
             ],
             typ=None,
             pos=getpos(self.stmt),
         )
Exemple #6
0
def ir_for_external_call(call_expr, context):
    from vyper.codegen.expr import Expr  # TODO rethink this circular import

    contract_address = Expr.parse_value_expr(call_expr.func.value, context)
    assert isinstance(contract_address.typ, InterfaceType)
    args_ir = [Expr(x, context).ir_node for x in call_expr.args]
    call_kwargs = _parse_kwargs(call_expr, context)

    with contract_address.cache_when_complex("external_contract") as (
            b1, contract_address):
        return b1.resolve(
            _external_call_helper(contract_address, args_ir, call_kwargs,
                                  call_expr, context))
Exemple #7
0
def process_arg(arg, expected_arg_type, context):
    # If the input value is a typestring, return the equivalent codegen type for IR generation
    if isinstance(expected_arg_type, TypeTypeDefinition):
        return new_type_to_old_type(expected_arg_type.typedef)

    # if it is a word type, return a stack item.
    # TODO: Builtins should not require value expressions
    if isinstance(expected_arg_type, ValueTypeDefinition) and not isinstance(
            expected_arg_type, (StructDefinition, ArrayValueAbstractType)):
        return Expr.parse_value_expr(arg, context)

    if isinstance(expected_arg_type, BaseTypeDefinition):
        return Expr(arg, context).ir_node

    raise CompilerPanic(
        f"Unexpected type: {expected_arg_type}")  # pragma: notest
Exemple #8
0
    def parse_Assign(self):
        # Assignment (e.g. x[4] = y)
        sub = Expr(self.stmt.value, self.context).ir_node
        target = self._get_target(self.stmt.target)

        ir_node = make_setter(target, sub)
        return ir_node
Exemple #9
0
    def parse_AnnAssign(self):
        typ = parse_type(
            self.stmt.annotation,
            sigs=self.context.sigs,
            custom_structs=self.context.structs,
        )
        varname = self.stmt.target.id
        pos = self.context.new_variable(varname, typ)
        if self.stmt.value is None:
            return

        sub = Expr(self.stmt.value, self.context).ir_node

        is_literal_bytes32_assign = (isinstance(sub.typ, ByteArrayType)
                                     and sub.typ.maxlen == 32
                                     and isinstance(typ, BaseType)
                                     and typ.typ == "bytes32"
                                     and sub.typ.is_literal)

        # If bytes[32] to bytes32 assignment rewrite sub as bytes32.
        if is_literal_bytes32_assign:
            sub = IRnode(
                util.bytes_to_int(self.stmt.value.s),
                typ=BaseType("bytes32"),
            )

        variable_loc = IRnode.from_list(pos, typ=typ, location=MEMORY)

        ir_node = make_setter(variable_loc, sub)

        return ir_node
Exemple #10
0
def _parse_kwargs(call_expr, context):
    from vyper.codegen.expr import Expr  # TODO rethink this circular import

    def _bool(x):
        assert x.value in (0, 1), "type checker missed this"
        return bool(x.value)

    # note: codegen for kwarg values in AST order
    call_kwargs = {
        kw.arg: Expr(kw.value, context).ir_node
        for kw in call_expr.keywords
    }

    ret = _CallKwargs(
        value=unwrap_location(call_kwargs.pop("value", IRnode(0))),
        gas=unwrap_location(call_kwargs.pop("gas", IRnode("gas"))),
        skip_contract_check=_bool(
            call_kwargs.pop("skip_contract_check", IRnode(0))),
        default_return_value=call_kwargs.pop("default_return_value", None),
    )

    if len(call_kwargs) != 0:
        raise TypeCheckFailure(f"Unexpected keyword arguments: {call_kwargs}")

    return ret
Exemple #11
0
    def parse_Assert(self):
        test_expr = Expr.parse_value_expr(self.stmt.test, self.context)

        if self.stmt.msg:
            return self._assert_reason(test_expr, self.stmt.msg)
        else:
            return IRnode.from_list(["assert", test_expr])
Exemple #12
0
    def _get_target(self, target):
        _dbg_expr = target

        if isinstance(target, vy_ast.Name) and target.id in self.context.forvars:
            raise TypeCheckFailure(f"Failed constancy check\n{_dbg_expr}")

        if isinstance(target, vy_ast.Tuple):
            target = Expr(target, self.context).lll_node
            for node in target.args:
                if (node.location == "storage" and self.context.is_constant()) or not node.mutable:
                    raise TypeCheckFailure(f"Failed constancy check\n{_dbg_expr}")
            return target

        target = Expr.parse_pointer_expr(target, self.context)
        if (target.location == "storage" and self.context.is_constant()) or not target.mutable:
            raise TypeCheckFailure(f"Failed constancy check\n{_dbg_expr}")
        return target
Exemple #13
0
    def parse_Assign(self):
        # Assignment (e.g. x[4] = y)
        sub = Expr(self.stmt.value, self.context).lll_node
        target = self._get_target(self.stmt.target)

        lll_node = make_setter(target, sub, pos=getpos(self.stmt))
        lll_node.pos = getpos(self.stmt)
        return lll_node
Exemple #14
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 = IRnode(
            4, location=CALLDATA, typ=calldata_args_t, encoding=Encoding.ABI
        )

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

        # ensure calldata is at least of minimum length
        args_abi_t = calldata_args_t.abi_type
        calldata_min_size = args_abi_t.min_size() + 4
        if args_abi_t.is_dynamic():
            ret.append(["assert", ["ge", "calldatasize", calldata_min_size]])
        else:
            # stricter for static data
            ret.append(["assert", ["eq", "calldatasize", calldata_min_size]])

        # 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 = IRnode(dst, location=MEMORY, typ=arg_meta.typ)

            rhs = get_element_ptr(calldata_kwargs_ofst, k, array_bounds_check=False)

            copy_arg = make_setter(lhs, rhs)
            copy_arg.source_pos = getpos(arg_meta.ast_source)
            ret.append(copy_arg)

        for x in default_kwargs:
            dst = context.lookup_var(x.name).pos
            lhs = IRnode(dst, location=MEMORY, typ=x.typ)
            lhs.source_pos = getpos(x.ast_source)
            kw_ast_val = sig.default_values[x.name]  # e.g. `3` in x: int = 3
            rhs = Expr(kw_ast_val, context).ir_node

            copy_arg = make_setter(lhs, rhs)
            copy_arg.source_pos = getpos(x.ast_source)
            ret.append(copy_arg)

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

        ret = ["if", ["eq", "_calldata_method_id", method_id], ret]
        return ret
Exemple #15
0
def _get_special_kwargs(stmt_expr, context):
    from vyper.codegen.expr import Expr  # TODO rethink this circular import

    value, gas, skip_contract_check = None, None, None
    for kw in stmt_expr.keywords:
        if kw.arg == "gas":
            gas = Expr.parse_value_expr(kw.value, context)
        elif kw.arg == "value":
            value = Expr.parse_value_expr(kw.value, context)
        elif kw.arg == "skip_contract_check":
            skip_contract_check = kw.value.value
            assert isinstance(skip_contract_check,
                              bool), "type checker missed this"
        else:
            raise TypeCheckFailure("Unexpected keyword argument")

    # TODO maybe return a small dataclass to reduce verbosity
    return value, gas, skip_contract_check
Exemple #16
0
    def _assert_reason(self, test_expr, msg):
        if isinstance(msg, vy_ast.Name) and msg.id == "UNREACHABLE":
            return LLLnode.from_list(["assert_unreachable", test_expr],
                                     typ=None,
                                     pos=getpos(msg))

        # set constant so that revert reason str is well behaved
        try:
            tmp = self.context.constancy
            self.context.constancy = Constancy.Constant
            msg_lll = Expr(msg, self.context).lll_node
        finally:
            self.context.constancy = tmp

        # TODO this is probably useful in codegen.core
        # compare with eval_seq.
        def _get_last(lll):
            if len(lll.args) == 0:
                return lll.value
            return _get_last(lll.args[-1])

        # TODO maybe use ensure_in_memory
        if msg_lll.location != "memory":
            buf = self.context.new_internal_variable(msg_lll.typ)
            instantiate_msg = make_byte_array_copier(buf, msg_lll)
        else:
            buf = _get_last(msg_lll)
            if not isinstance(buf, int):
                raise CompilerPanic(f"invalid bytestring {buf}\n{self}")
            instantiate_msg = msg_lll

        # offset of bytes in (bytes,)
        method_id = util.abi_method_id("Error(string)")

        # abi encode method_id + bytestring
        assert buf >= 36, "invalid buffer"
        # we don't mind overwriting other memory because we are
        # getting out of here anyway.
        _runtime_length = ["mload", buf]
        revert_seq = [
            "seq",
            instantiate_msg,
            zero_pad(buf),
            ["mstore", buf - 64, method_id],
            ["mstore", buf - 32, 0x20],
            [
                "revert", buf - 36,
                ["add", 4 + 32 + 32, ["ceil32", _runtime_length]]
            ],
        ]

        if test_expr is not None:
            lll_node = ["if", ["iszero", test_expr], revert_seq]
        else:
            lll_node = revert_seq

        return LLLnode.from_list(lll_node, typ=None, pos=getpos(self.stmt))
Exemple #17
0
    def _parse_For_list(self):
        with self.context.range_scope():
            iter_list = Expr(self.stmt.iter, self.context).lll_node

        # override with type inferred at typechecking time
        # TODO investigate why stmt.target.type != stmt.iter.type.subtype
        target_type = new_type_to_old_type(self.stmt.target._metadata["type"])
        iter_list.typ.subtype = target_type

        # user-supplied name for loop variable
        varname = self.stmt.target.id
        loop_var = LLLnode.from_list(
            self.context.new_variable(varname, target_type),
            typ=target_type,
            location="memory",
        )

        i = LLLnode.from_list(self.context.fresh_varname("for_list_ix"),
                              typ="uint256")

        self.context.forvars[varname] = True

        ret = ["seq"]

        # list literal, force it to memory first
        if isinstance(self.stmt.iter, vy_ast.List):
            tmp_list = LLLnode.from_list(
                self.context.new_internal_variable(iter_list.typ),
                typ=iter_list.typ,
                location="memory",
            )
            ret.append(make_setter(tmp_list, iter_list, pos=getpos(self.stmt)))
            iter_list = tmp_list

        # set up the loop variable
        loop_var_ast = getpos(self.stmt.target)
        e = get_element_ptr(iter_list,
                            i,
                            array_bounds_check=False,
                            pos=loop_var_ast)
        body = [
            "seq",
            make_setter(loop_var, e, pos=loop_var_ast),
            parse_body(self.stmt.body, self.context),
        ]

        repeat_bound = iter_list.typ.count
        if isinstance(iter_list.typ, DArrayType):
            array_len = get_dyn_array_count(iter_list)
        else:
            array_len = repeat_bound

        ret.append(["repeat", i, 0, array_len, repeat_bound, body])

        del self.context.forvars[varname]
        return LLLnode.from_list(ret, pos=getpos(self.stmt))
Exemple #18
0
def convert(expr, context):
    if len(expr.args) != 2:
        raise StructureException(
            "The convert function expects two parameters.", expr)

    arg_ast = expr.args[0]
    arg = Expr(arg_ast, context).ir_node
    original_arg = arg
    out_typ = context.parse_type(expr.args[1])

    if isinstance(arg.typ, BaseType):
        arg = unwrap_location(arg)
    with arg.cache_when_complex("arg") as (b, arg):
        if is_base_type(out_typ, "bool"):
            ret = to_bool(arg_ast, arg, out_typ)
        elif is_base_type(out_typ, "address"):
            ret = to_address(arg_ast, arg, out_typ)
        elif isinstance(out_typ, EnumType):
            ret = to_enum(arg_ast, arg, out_typ)
        elif is_integer_type(out_typ):
            ret = to_int(arg_ast, arg, out_typ)
        elif is_bytes_m_type(out_typ):
            ret = to_bytes_m(arg_ast, arg, out_typ)
        elif is_decimal_type(out_typ):
            ret = to_decimal(arg_ast, arg, out_typ)
        elif isinstance(out_typ, ByteArrayType):
            ret = to_bytes(arg_ast, arg, out_typ)
        elif isinstance(out_typ, StringType):
            ret = to_string(arg_ast, arg, out_typ)
        else:
            raise StructureException(f"Conversion to {out_typ} is invalid.",
                                     arg_ast)

        # test if arg actually changed. if not, we do not need to use
        # unwrap_location (this can reduce memory traffic for downstream
        # operations which are in-place, like the returndata routine)
        test_arg = IRnode.from_list(arg, typ=out_typ)
        if test_arg == ret:
            original_arg.typ = out_typ
            return original_arg

        return IRnode.from_list(b.resolve(ret))
Exemple #19
0
    def parse_If(self):
        if self.stmt.orelse:
            with self.context.block_scope():
                add_on = [parse_body(self.stmt.orelse, self.context)]
        else:
            add_on = []

        with self.context.block_scope():
            test_expr = Expr.parse_value_expr(self.stmt.test, self.context)
            body = ["if", test_expr, parse_body(self.stmt.body, self.context)] + add_on
            lll_node = LLLnode.from_list(body, typ=None, pos=getpos(self.stmt))
        return lll_node
Exemple #20
0
    def parse_AugAssign(self):
        target = self._get_target(self.stmt.target)
        sub = Expr.parse_value_expr(self.stmt.value, self.context)
        if not isinstance(target.typ, BaseType):
            return

        with target.cache_when_complex("_loc") as (b, target):
            rhs = Expr.parse_value_expr(
                vy_ast.BinOp(
                    left=IRnode.from_list(LOAD(target), typ=target.typ),
                    right=sub,
                    op=self.stmt.op,
                    lineno=self.stmt.lineno,
                    col_offset=self.stmt.col_offset,
                    end_lineno=self.stmt.end_lineno,
                    end_col_offset=self.stmt.end_col_offset,
                    node_source_code=self.stmt.get("node_source_code"),
                ),
                self.context,
            )
            return b.resolve(STORE(target, rhs))
Exemple #21
0
    def _parse_For_range(self):
        # attempt to use the type specified by type checking, fall back to `int256`
        # this is a stopgap solution to allow uint256 - it will be properly solved
        # once we refactor type system
        iter_typ = "int256"
        if "type" in self.stmt.target._metadata:
            iter_typ = self.stmt.target._metadata["type"]._id

        # Get arg0
        arg0 = self.stmt.iter.args[0]
        num_of_args = len(self.stmt.iter.args)

        # Type 1 for, e.g. for i in range(10): ...
        if num_of_args == 1:
            arg0_val = self._get_range_const_value(arg0)
            start = IRnode.from_list(0, typ=iter_typ)
            rounds = arg0_val

        # Type 2 for, e.g. for i in range(100, 110): ...
        elif self._check_valid_range_constant(self.stmt.iter.args[1],
                                              raise_exception=False)[0]:
            arg0_val = self._get_range_const_value(arg0)
            arg1_val = self._get_range_const_value(self.stmt.iter.args[1])
            start = IRnode.from_list(arg0_val, typ=iter_typ)
            rounds = IRnode.from_list(arg1_val - arg0_val, typ=iter_typ)

        # Type 3 for, e.g. for i in range(x, x + 10): ...
        else:
            arg1 = self.stmt.iter.args[1]
            rounds = self._get_range_const_value(arg1.right)
            start = Expr.parse_value_expr(arg0, self.context)

        r = rounds if isinstance(rounds, int) else rounds.value
        if r < 1:
            return

        varname = self.stmt.target.id
        i = IRnode.from_list(self.context.fresh_varname("range_ix"),
                             typ="uint256")
        iptr = self.context.new_variable(varname, BaseType(iter_typ))

        self.context.forvars[varname] = True

        loop_body = ["seq"]
        # store the current value of i so it is accessible to userland
        loop_body.append(["mstore", iptr, i])
        loop_body.append(parse_body(self.stmt.body, self.context))

        ir_node = IRnode.from_list(
            ["repeat", i, start, rounds, rounds, loop_body])
        del self.context.forvars[varname]

        return ir_node
Exemple #22
0
    def parse_If(self):
        if self.stmt.orelse:
            with self.context.block_scope():
                add_on = [parse_body(self.stmt.orelse, self.context)]
        else:
            add_on = []

        with self.context.block_scope():
            test_expr = Expr.parse_value_expr(self.stmt.test, self.context)
            body = ["if", test_expr,
                    parse_body(self.stmt.body, self.context)] + add_on
            ir_node = IRnode.from_list(body)
        return ir_node
Exemple #23
0
    def parse_Assert(self):
        test_expr = Expr.parse_value_expr(self.stmt.test, self.context)
        if test_expr.typ.is_literal:
            if test_expr.value == 1:
                # skip literal assertions that always pass
                return LLLnode.from_list(["pass"], typ=None, pos=getpos(self.stmt))
            else:
                test_expr = test_expr.value

        if self.stmt.msg:
            return self._assert_reason(test_expr, self.stmt.msg)
        else:
            return LLLnode.from_list(["assert", test_expr], typ=None, pos=getpos(self.stmt))
Exemple #24
0
    def parse_Log(self):
        event = self.stmt._metadata["type"]

        args = [Expr(arg, self.context).lll_node for arg in self.stmt.value.args]

        topic_lll = []
        data_lll = []
        for arg, is_indexed in zip(args, event.indexed):
            if is_indexed:
                topic_lll.append(arg)
            else:
                data_lll.append(arg)

        return events.lll_node_for_log(self.stmt, event, topic_lll, data_lll, self.context)
Exemple #25
0
    def _check_valid_range_constant(self, arg_ast_node, raise_exception=True):
        with self.context.range_scope():
            # TODO should catch if raise_exception == False?
            arg_expr = Expr.parse_value_expr(arg_ast_node, self.context)

        is_integer_literal = (isinstance(arg_expr.typ, BaseType)
                              and arg_expr.typ.is_literal
                              and arg_expr.typ.typ in {"uint256", "int256"})
        if not is_integer_literal and raise_exception:
            raise StructureException(
                "Range only accepts literal (constant) values of type uint256 or int256",
                arg_ast_node,
            )
        return is_integer_literal, arg_expr
Exemple #26
0
    def parse_Assert(self):
        test_expr = Expr.parse_value_expr(self.stmt.test, self.context)
        if test_expr.typ.is_literal:
            if test_expr.value == 1:
                # skip literal assertions that always pass
                # TODO move this to optimizer
                return IRnode.from_list(["pass"])
            else:
                test_expr = test_expr.value

        if self.stmt.msg:
            return self._assert_reason(test_expr, self.stmt.msg)
        else:
            return IRnode.from_list(["assert", test_expr])
Exemple #27
0
    def parse_Call(self):
        # TODO use expr.func.type.is_internal once type annotations
        # are consistently available.
        is_self_function = ((isinstance(self.stmt.func, vy_ast.Attribute))
                            and isinstance(self.stmt.func.value, vy_ast.Name)
                            and self.stmt.func.value.id == "self")

        if isinstance(self.stmt.func, vy_ast.Name):
            funcname = self.stmt.func.id
            return STMT_DISPATCH_TABLE[funcname].build_IR(
                self.stmt, self.context)

        elif isinstance(self.stmt.func,
                        vy_ast.Attribute) and self.stmt.func.attr in (
                            "append",
                            "pop",
                        ):
            # TODO: consider moving this to builtins
            darray = Expr(self.stmt.func.value, self.context).ir_node
            args = [Expr(x, self.context).ir_node for x in self.stmt.args]
            if self.stmt.func.attr == "append":
                # sanity checks
                assert len(args) == 1
                arg = args[0]
                assert isinstance(darray.typ, DArrayType)
                check_assign(dummy_node_for_type(darray.typ.subtype),
                             dummy_node_for_type(arg.typ))

                return append_dyn_array(darray, arg)
            else:
                assert len(args) == 0
                return pop_dyn_array(darray, return_popped_item=False)

        elif is_self_function:
            return self_call.ir_for_self_call(self.stmt, self.context)
        else:
            return external_call.ir_for_external_call(self.stmt, self.context)
Exemple #28
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
Exemple #29
0
def lll_for_self_call(stmt_expr, context):
    from vyper.codegen.expr import Expr  # TODO rethink this circular import

    pos = getpos(stmt_expr)

    # ** Internal Call **
    # Steps:
    # - copy arguments into the soon-to-be callee
    # - allocate return buffer
    # - push jumpdest (callback ptr) and return buffer location
    # - jump to label
    # - (private function will fill return buffer and jump back)

    method_name = stmt_expr.func.attr

    pos_args_lll = [Expr(x, context).lll_node for x in stmt_expr.args]

    sig, kw_vals = context.lookup_internal_function(method_name, pos_args_lll)

    kw_args_lll = [Expr(x, context).lll_node for x in kw_vals]

    args_lll = pos_args_lll + kw_args_lll

    args_tuple_t = TupleType([x.typ for x in args_lll])
    args_as_tuple = LLLnode.from_list(["multi"] + [x for x in args_lll], typ=args_tuple_t)

    # register callee to help calculate our starting frame offset
    context.register_callee(sig.frame_size)

    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),
        )

    # TODO move me to type checker phase
    if not sig.internal:
        raise StructureException("Cannot call external functions via 'self'", stmt_expr)

    return_label = _generate_label(f"{sig.internal_function_label}_call")

    # allocate space for the return buffer
    # TODO allocate in stmt and/or expr.py
    if sig.return_type is not None:
        return_buffer = LLLnode.from_list(
            context.new_internal_variable(sig.return_type), annotation=f"{return_label}_return_buf"
        )
    else:
        return_buffer = None

    # note: dst_tuple_t != args_tuple_t
    dst_tuple_t = TupleType([arg.typ for arg in sig.args])
    args_dst = LLLnode(sig.frame_start, typ=dst_tuple_t, location="memory")

    # if one of the arguments is a self call, the argument
    # buffer could get borked. to prevent against that,
    # write args to a temporary buffer until all the arguments
    # are fully evaluated.
    if args_as_tuple.contains_self_call:
        copy_args = ["seq"]
        # TODO deallocate me
        tmp_args_buf = LLLnode(
            context.new_internal_variable(dst_tuple_t),
            typ=dst_tuple_t,
            location="memory",
        )
        copy_args.append(
            # --> args evaluate here <--
            make_setter(tmp_args_buf, args_as_tuple, pos)
        )

        copy_args.append(make_setter(args_dst, tmp_args_buf, pos))

    else:
        copy_args = make_setter(args_dst, args_as_tuple, pos)

    goto_op = ["goto", sig.internal_function_label]
    # pass return buffer to subroutine
    if return_buffer is not None:
        goto_op += [return_buffer]
    # pass return label to subroutine
    goto_op += [push_label_to_stack(return_label)]

    call_sequence = [
        "seq",
        copy_args,
        goto_op,
        ["label", return_label, ["var_list"], "pass"],
    ]
    if return_buffer is not None:
        # push return buffer location to stack
        call_sequence += [return_buffer]

    o = LLLnode.from_list(
        call_sequence,
        typ=sig.return_type,
        location="memory",
        pos=pos,
        annotation=stmt_expr.get("node_source_code"),
        add_gas_estimate=sig.gas,
    )
    o.is_self_call = True
    return o
Exemple #30
0
 def parse_Return(self):
     ir_val = None
     if self.stmt.value is not None:
         ir_val = Expr(self.stmt.value, self.context).ir_node
     return make_return_stmt(ir_val, self.stmt, self.context)