def parse_Subscript(self): sub = Expr(self.expr.value, self.context).lll_node if sub.value == "multi": # force literal to memory, e.g. # MY_LIST: constant(decimal[6]) # ... # return MY_LIST[ix] sub = ensure_in_memory(sub, self.context, pos=getpos(self.expr)) if isinstance(sub.typ, MappingType): # TODO sanity check we are in a self.my_map[i] situation index = Expr.parse_value_expr(self.expr.slice.value, self.context) if isinstance(index.typ, ByteArrayLike): # we have to hash the key to get a storage location assert len(index.args) == 1 index = keccak256_helper(self.expr.slice.value, index.args[0], self.context) elif isinstance(sub.typ, ArrayLike): index = Expr.parse_value_expr(self.expr.slice.value, self.context) elif isinstance(sub.typ, TupleType): index = self.expr.slice.value.n # note: this check should also happen in get_element_ptr if not 0 <= index < len(sub.typ.members): return else: return lll_node = get_element_ptr(sub, index, pos=getpos(self.expr)) lll_node.mutable = sub.mutable return lll_node
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
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
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))
def _encode_dyn_array_helper(dst, ir_node, context): # if it's a literal, first serialize to memory as we # don't have a compile-time abi encoder # TODO handle this upstream somewhere if ir_node.value == "multi": buf = context.new_internal_variable(dst.typ) buf = IRnode.from_list(buf, typ=dst.typ, location=MEMORY) _bufsz = dst.typ.abi_type.size_bound() return [ "seq", make_setter(buf, ir_node), [ "set", "dyn_ofst", abi_encode(dst, buf, context, _bufsz, returns_len=True) ], ] subtyp = ir_node.typ.subtype child_abi_t = subtyp.abi_type ret = ["seq"] len_ = get_dyn_array_count(ir_node) with len_.cache_when_complex("len") as (b, len_): # set the length word ret.append(STORE(dst, len_)) # prepare the loop t = BaseType("uint256") i = IRnode.from_list(context.fresh_varname("ix"), typ=t) # offset of the i'th element in ir_node child_location = get_element_ptr(ir_node, i, array_bounds_check=False) # offset of the i'th element in dst dst = add_ofst(dst, 32) # jump past length word static_elem_size = child_abi_t.embedded_static_size() static_ofst = ["mul", i, static_elem_size] loop_body = _encode_child_helper(dst, child_location, static_ofst, "dyn_child_ofst", context) loop = ["repeat", i, 0, len_, ir_node.typ.count, loop_body] x = ["seq", loop, "dyn_child_ofst"] start_dyn_ofst = ["mul", len_, static_elem_size] run_children = ["with", "dyn_child_ofst", start_dyn_ofst, x] new_dyn_ofst = ["add", "dyn_ofst", run_children] # size of dynarray is size of encoded children + size of the length word # TODO optimize by adding 32 to the initial value of dyn_ofst new_dyn_ofst = ["add", 32, new_dyn_ofst] ret.append(["set", "dyn_ofst", new_dyn_ofst]) return b.resolve(ret)
def _deconstruct_complex_type(ir_node): ir_t = ir_node.typ assert isinstance(ir_t, (TupleLike, SArrayType)) if isinstance(ir_t, TupleLike): ks = ir_t.tuple_keys() else: ks = [IRnode.from_list(i, "uint256") for i in range(ir_t.count)] ret = [] for k in ks: ret.append(get_element_ptr(ir_node, k, array_bounds_check=False)) return ret
def _unpack_returndata(buf, contract_sig, skip_contract_check, context, pos): return_t = contract_sig.return_type if return_t is None: return ["pass"], 0, 0 return_t = calculate_type_for_external_return(return_t) # if the abi signature has a different type than # the vyper type, we need to wrap and unwrap the type # so that the ABI decoding works correctly should_unwrap_abi_tuple = return_t != contract_sig.return_type abi_return_t = return_t.abi_type min_return_size = abi_return_t.min_size() max_return_size = abi_return_t.size_bound() assert 0 < min_return_size <= max_return_size ret_ofst = buf ret_len = max_return_size # revert when returndatasize is not in bounds ret = [] # runtime: min_return_size <= returndatasize # TODO move the -1 optimization to LLL optimizer if not skip_contract_check: ret += [["assert", ["gt", "returndatasize", min_return_size - 1]]] # add as the last LLLnode a pointer to the return data structure # the return type has been wrapped by the calling contract; # unwrap it so downstream code isn't confused. # basically this expands to buf+32 if the return type has been wrapped # in a tuple AND its ABI type is dynamic. # in most cases, this simply will evaluate to ret. # in the special case where the return type has been wrapped # in a tuple AND its ABI type is dynamic, it expands to buf+32. buf = LLLnode(buf, typ=return_t, encoding=_returndata_encoding(contract_sig), location="memory") if should_unwrap_abi_tuple: buf = get_element_ptr(buf, 0, pos=None, array_bounds_check=False) ret += [buf] return ret, ret_ofst, ret_len
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
def _register_function_args(context: Context, sig: FunctionSignature) -> List[IRnode]: 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 = IRnode(0, location=DATA, typ=base_args_t, encoding=Encoding.ABI) else: base_args_ofst = IRnode(4, location=CALLDATA, typ=base_args_t, encoding=Encoding.ABI) for i, arg in enumerate(sig.base_args): arg_ir = get_element_ptr(base_args_ofst, i) 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 = IRnode(p, typ=arg.typ, location=MEMORY) copy_arg = make_setter(dst, arg_ir) copy_arg.source_pos = getpos(arg.ast_source) ret.append(copy_arg) else: # leave it in place context.vars[arg.name] = VariableRecord( name=arg.name, pos=arg_ir, typ=arg.typ, mutable=False, location=arg_ir.location, encoding=Encoding.ABI, ) return ret
def build_in_comparator(self): left = Expr(self.expr.left, self.context).lll_node right = Expr(self.expr.right, self.context).lll_node # temporary kludge to block #2637 bug # TODO actually fix the bug if not isinstance(left.typ, BaseType): raise TypeMismatch( "`in` not allowed for arrays of non-base types, tracked in issue #2637", self.expr) if isinstance(self.expr.op, vy_ast.In): found, not_found = 1, 0 elif isinstance(self.expr.op, vy_ast.NotIn): found, not_found = 0, 1 else: return # pragma: notest i = LLLnode.from_list(self.context.fresh_varname("in_ix"), typ="uint256") found_ptr = self.context.new_internal_variable(BaseType("bool")) ret = ["seq"] left = unwrap_location(left) with left.cache_when_complex("needle") as ( b1, left), right.cache_when_complex("haystack") as (b2, right): if right.value == "multi": # Copy literal to memory to be compared. tmp_list = LLLnode.from_list( self.context.new_internal_variable(right.typ), typ=right.typ, location="memory", ) ret.append(make_setter(tmp_list, right, pos=getpos(self.expr))) right = tmp_list # location of i'th item from list pos = getpos(self.expr) ith_element_ptr = get_element_ptr(right, i, array_bounds_check=False, pos=pos) ith_element = unwrap_location(ith_element_ptr) if isinstance(right.typ, SArrayType): len_ = right.typ.count else: len_ = get_dyn_array_count(right) # Condition repeat loop has to break on. # TODO maybe put result on the stack loop_body = [ "if", ["eq", left, ith_element], ["seq", ["mstore", found_ptr, found], "break"], # store true. ] loop = ["repeat", i, 0, len_, right.typ.count, loop_body] ret.append([ "seq", ["mstore", found_ptr, not_found], loop, ["mload", found_ptr], ]) return LLLnode.from_list(b1.resolve(b2.resolve(ret)), typ="bool")
def parse_Attribute(self): # x.balance: balance of address x if self.expr.attr == "balance": addr = Expr.parse_value_expr(self.expr.value, self.context) if is_base_type(addr.typ, "address"): if (isinstance(self.expr.value, vy_ast.Name) and self.expr.value.id == "self" and version_check(begin="istanbul")): seq = ["selfbalance"] else: seq = ["balance", addr] return LLLnode.from_list( seq, typ=BaseType("uint256"), location=None, pos=getpos(self.expr), ) # x.codesize: codesize of address x elif self.expr.attr == "codesize" or self.expr.attr == "is_contract": addr = Expr.parse_value_expr(self.expr.value, self.context) if is_base_type(addr.typ, "address"): if self.expr.attr == "codesize": if self.expr.value.id == "self": eval_code = ["codesize"] else: eval_code = ["extcodesize", addr] output_type = "uint256" else: eval_code = ["gt", ["extcodesize", addr], 0] output_type = "bool" return LLLnode.from_list( eval_code, typ=BaseType(output_type), location=None, pos=getpos(self.expr), ) # x.codehash: keccak of address x elif self.expr.attr == "codehash": addr = Expr.parse_value_expr(self.expr.value, self.context) if not version_check(begin="constantinople"): raise EvmVersionException( "address.codehash is unavailable prior to constantinople ruleset", self.expr) if is_base_type(addr.typ, "address"): return LLLnode.from_list( ["extcodehash", addr], typ=BaseType("bytes32"), location=None, pos=getpos(self.expr), ) # x.code: codecopy/extcodecopy of address x elif self.expr.attr == "code": addr = Expr.parse_value_expr(self.expr.value, self.context) if is_base_type(addr.typ, "address"): # These adhoc nodes will be replaced with a valid node in `Slice.build_LLL` if addr.value == "address": # for `self.code` return LLLnode.from_list(["~selfcode"], typ=ByteArrayType(0)) return LLLnode.from_list(["~extcode", addr], typ=ByteArrayType(0)) # self.x: global attribute elif isinstance(self.expr.value, vy_ast.Name) and self.expr.value.id == "self": type_ = self.expr._metadata["type"] var = self.context.globals[self.expr.attr] return LLLnode.from_list( type_.position.position, typ=var.typ, location="storage", pos=getpos(self.expr), annotation="self." + self.expr.attr, ) # Reserved keywords elif (isinstance(self.expr.value, vy_ast.Name) and self.expr.value.id in ENVIRONMENT_VARIABLES): key = f"{self.expr.value.id}.{self.expr.attr}" if key == "msg.sender": return LLLnode.from_list(["caller"], typ="address", pos=getpos(self.expr)) elif key == "msg.data": # This adhoc node will be replaced with a valid node in `Slice/Len.build_LLL` return LLLnode.from_list(["~calldata"], typ=ByteArrayType(0)) elif key == "msg.value" and self.context.is_payable: return LLLnode.from_list( ["callvalue"], typ=BaseType("uint256"), pos=getpos(self.expr), ) elif key == "msg.gas": return LLLnode.from_list( ["gas"], typ="uint256", pos=getpos(self.expr), ) elif key == "block.difficulty": return LLLnode.from_list( ["difficulty"], typ="uint256", pos=getpos(self.expr), ) elif key == "block.timestamp": return LLLnode.from_list( ["timestamp"], typ=BaseType("uint256"), pos=getpos(self.expr), ) elif key == "block.coinbase": return LLLnode.from_list(["coinbase"], typ="address", pos=getpos(self.expr)) elif key == "block.number": return LLLnode.from_list(["number"], typ="uint256", pos=getpos(self.expr)) elif key == "block.gaslimit": return LLLnode.from_list(["gaslimit"], typ="uint256", pos=getpos(self.expr)) elif key == "block.basefee": return LLLnode.from_list(["basefee"], typ="uint256", pos=getpos(self.expr)) elif key == "block.prevhash": return LLLnode.from_list( ["blockhash", ["sub", "number", 1]], typ="bytes32", pos=getpos(self.expr), ) elif key == "tx.origin": return LLLnode.from_list(["origin"], typ="address", pos=getpos(self.expr)) elif key == "tx.gasprice": return LLLnode.from_list(["gasprice"], typ="uint256", pos=getpos(self.expr)) elif key == "chain.id": if not version_check(begin="istanbul"): raise EvmVersionException( "chain.id is unavailable prior to istanbul ruleset", self.expr) return LLLnode.from_list(["chainid"], typ="uint256", pos=getpos(self.expr)) # Other variables else: sub = Expr(self.expr.value, self.context).lll_node # contract type if isinstance(sub.typ, InterfaceType): return sub if isinstance(sub.typ, StructType) and self.expr.attr in sub.typ.members: return get_element_ptr(sub, self.expr.attr, pos=getpos(self.expr))