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_LLL( 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).lll_node args = [Expr(x, self.context).lll_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, pos=getpos(self.stmt)) else: assert len(args) == 0 return pop_dyn_array(darray, return_popped_item=False, pos=getpos(self.stmt)) elif is_self_function: return self_call.lll_for_self_call(self.stmt, self.context) else: return external_call.lll_for_external_call(self.stmt, self.context)
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 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, )
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
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 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))
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))
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 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 _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 = LLLnode.from_list(0, typ=iter_typ, pos=getpos(self.stmt)) 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 = LLLnode.from_list(arg0_val, typ=iter_typ, pos=getpos(self.stmt)) rounds = LLLnode.from_list(arg1_val - arg0_val, typ=iter_typ, pos=getpos(self.stmt)) # 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 = LLLnode.from_list(self.context.fresh_varname("range_ix"), typ="uint256") iptr = self.context.new_variable(varname, BaseType(iter_typ), pos=getpos(self.stmt)) 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)) lll_node = LLLnode.from_list( ["repeat", i, start, rounds, rounds, loop_body], pos=getpos(self.stmt), ) del self.context.forvars[varname] return lll_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 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), )
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), )
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), )
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), )
def parse_Raise(self): if self.stmt.exc: return self._assert_reason(None, self.stmt.exc) else: return LLLnode.from_list(["revert", 0, 0], typ=None, pos=getpos(self.stmt))
def to_bytes32(expr, args, kwargs, context): in_arg = args[0] input_type, _len = get_type(in_arg) if input_type == "Bytes": if _len > 32: raise TypeMismatch( f"Unable to convert bytes[{_len}] to bytes32, max length is too " "large.") with in_arg.cache_when_complex("bytes") as (b1, in_arg): op = load_op(in_arg.location) ofst = wordsize(in_arg.location) * DYNAMIC_ARRAY_OVERHEAD bytes_val = [op, ["add", in_arg, ofst]] # zero out any dirty bytes (which can happen in the last # word of a bytearray) len_ = get_bytearray_length(in_arg) num_zero_bits = LLLnode.from_list(["mul", ["sub", 32, len_], 8]) with num_zero_bits.cache_when_complex("bits") as (b2, num_zero_bits): ret = shl(num_zero_bits, shr(num_zero_bits, bytes_val)) ret = b1.resolve(b2.resolve(ret)) else: # literal ret = in_arg return LLLnode.from_list(ret, typ="bytes32", pos=getpos(expr))
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 _run_pass(memory_allocator=None): # Create a local (per function) context. if memory_allocator is None: memory_allocator = MemoryAllocator() nonlocal sig sig = copy.deepcopy(sig) # just in case context = Context( vars_=None, global_ctx=global_ctx, sigs=sigs, memory_allocator=memory_allocator, return_type=sig.return_type, constancy=Constancy.Constant if sig.mutability in ("view", "pure") else Constancy.Mutable, is_payable=sig.mutability == "payable", is_internal=sig.internal, sig=sig, ) if sig.internal: o = generate_ir_for_internal_function(code, sig, context) else: o = generate_ir_for_external_function(code, sig, context, check_nonpayable) o.source_pos = getpos(code) return o, context
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), )
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 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
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: var = self.context.globals[self.expr.id] ofst = self.expr._metadata["type"].position.offset if self.context.sig.is_init_func: mutable = True location = "immutables" else: mutable = False location = "data" return LLLnode.from_list( ofst, typ=var.typ, location=location, pos=getpos(self.expr), annotation=self.expr.id, mutable=mutable, )
def parse_body(code, context): if not isinstance(code, list): return parse_stmt(code, context) lll_node = ["seq"] for stmt in code: lll = parse_stmt(stmt, context) lll_node.append(lll) lll_node.append("pass") # force zerovalent, even last statement return LLLnode.from_list(lll_node, pos=getpos(code[0]) if code else None)
def to_bool(expr, args, kwargs, context): in_arg = args[0] input_type, _ = get_type(in_arg) if input_type == "Bytes": if in_arg.typ.maxlen > 32: raise TypeMismatch( f"Cannot convert bytes array of max length {in_arg.typ.maxlen} to bool", expr, ) else: num = byte_array_to_num(in_arg, "uint256") return LLLnode.from_list(["iszero", ["iszero", num]], typ=BaseType("bool"), pos=getpos(expr)) else: return LLLnode.from_list(["iszero", ["iszero", in_arg]], typ=BaseType("bool"), pos=getpos(expr))
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)
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 parse_Decimal(self): numstring, num, den = get_number_as_fraction(self.expr, self.context) if not (SizeLimits.MIN_INT128 * den <= num <= SizeLimits.MAX_INT128 * den): return if DECIMAL_DIVISOR % den: return return LLLnode.from_list( num * DECIMAL_DIVISOR // den, typ=BaseType("decimal", is_literal=True), pos=getpos(self.expr), )
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, pos=self.stmt) if self.stmt.value is None: return sub = Expr(self.stmt.value, self.context).lll_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 = LLLnode( util.bytes_to_int(self.stmt.value.s), typ=BaseType("bytes32"), pos=getpos(self.stmt), ) variable_loc = LLLnode.from_list( pos, typ=typ, location="memory", pos=getpos(self.stmt), ) lll_node = make_setter(variable_loc, sub, pos=getpos(self.stmt)) return lll_node
def keccak256_helper(expr, lll_arg, context): sub = lll_arg # TODO get rid of useless variable _check_byteslike(sub.typ, expr) # Can hash literals # TODO this is dead code. if isinstance(sub, bytes): return LLLnode.from_list(bytes_to_int(keccak256(sub)), typ=BaseType("bytes32"), pos=getpos(expr)) # Can hash bytes32 objects if is_base_type(sub.typ, "bytes32"): return LLLnode.from_list( [ "seq", ["mstore", MemoryPositions.FREE_VAR_SPACE, sub], ["sha3", MemoryPositions.FREE_VAR_SPACE, 32], ], typ=BaseType("bytes32"), pos=getpos(expr), add_gas_estimate=_gas_bound(1), ) sub = ensure_in_memory(sub, context, pos=getpos(expr)) return LLLnode.from_list( [ "with", "_buf", sub, ["sha3", ["add", "_buf", 32], ["mload", "_buf"]], ], typ=BaseType("bytes32"), pos=getpos(expr), annotation="keccak256", add_gas_estimate=_gas_bound(ceil(sub.typ.maxlen / 32)), )