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), )
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
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])
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
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
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))
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
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
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))
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
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))
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
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])
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
def _check_valid_range_constant(self, arg_ast_node): with self.context.range_scope(): arg_expr = Expr.parse_value_expr(arg_ast_node, self.context) return arg_expr
def process_arg(index, arg, expected_arg_typelist, function_name, context): # temporary hack to support abstract types if hasattr(expected_arg_typelist, "_id_list"): expected_arg_typelist = expected_arg_typelist._id_list if isinstance(expected_arg_typelist, Optional): expected_arg_typelist = expected_arg_typelist.typ if not isinstance(expected_arg_typelist, tuple): expected_arg_typelist = (expected_arg_typelist, ) vsub = None for expected_arg in expected_arg_typelist: # temporary hack, once we refactor this package none of this will exist if hasattr(expected_arg, "_id"): expected_arg = expected_arg._id if expected_arg == "num_literal": if isinstance(arg, (vy_ast.Int, vy_ast.Decimal)): return arg.n elif expected_arg == "str_literal": if isinstance(arg, vy_ast.Str): bytez = b"" for c in arg.s: if ord(c) >= 256: raise InvalidLiteral( f"Cannot insert special character {c} into byte array", arg, ) bytez += bytes([ord(c)]) return bytez elif expected_arg == "bytes_literal": if isinstance(arg, vy_ast.Bytes): return arg.s elif expected_arg == "name_literal": if isinstance(arg, vy_ast.Name): return arg.id elif isinstance(arg, vy_ast.Subscript) and arg.value.id == "Bytes": return f"Bytes[{arg.slice.value.n}]" elif expected_arg == "*": return arg elif expected_arg == "Bytes": sub = Expr(arg, context).ir_node if isinstance(sub.typ, ByteArrayType): return sub elif expected_arg == "String": sub = Expr(arg, context).ir_node if isinstance(sub.typ, StringType): return sub else: parsed_expected_type = context.parse_type( vy_ast.parse_to_ast(expected_arg)[0].value) if isinstance(parsed_expected_type, BaseType): vsub = vsub or Expr.parse_value_expr(arg, context) is_valid_integer = ( (expected_arg in INTEGER_TYPES and isinstance(vsub.typ, BaseType)) and (vsub.typ.typ in INTEGER_TYPES and vsub.typ.is_literal) and (SizeLimits.in_bounds(expected_arg, vsub.value))) if is_base_type(vsub.typ, expected_arg): return vsub elif is_valid_integer: return vsub else: vsub = vsub or Expr(arg, context).ir_node if vsub.typ == parsed_expected_type: return Expr(arg, context).ir_node if len(expected_arg_typelist) == 1: raise TypeMismatch( f"Expecting {expected_arg} for argument {index} of {function_name}", arg) else: raise TypeMismatch( f"Expecting one of {expected_arg_typelist} for argument {index} of {function_name}", arg)