def parse_delete(self): from .parser import ( make_setter, ) if len(self.stmt.targets) != 1: raise StructureException("Can delete one variable at a time", self.stmt) target = self.stmt.targets[0] target_lll = Expr(self.stmt.targets[0], self.context).lll_node if isinstance(target, ast.Subscript): if target_lll.location == "storage": return make_setter(target_lll, LLLnode.from_list(None, typ=NullType()), "storage", pos=getpos(self.stmt)) raise StructureException("Deleting type not supported.", self.stmt)
def get_constant(self, const_name, context): """ Return unrolled const """ # check if value is compatible with const = self._constants[const_name] if isinstance(const, ast.AnnAssign): # Handle ByteArrays. if context: expr = Expr(const.value, context).lll_node return expr else: raise VariableDeclarationException( "ByteArray: Can not be used outside of a function context: %s" % const_name) # Other types are already unwrapped, no need return self._constants[const_name]
def _clear(self): # Create zero node none = ast.NameConstant(value=None) none.lineno = self.stmt.lineno none.col_offset = self.stmt.col_offset none.end_lineno = self.stmt.end_lineno none.end_col_offset = self.stmt.end_col_offset zero = Expr(none, self.context).lll_node # Get target variable target = self.get_target(self.stmt.args[0]) # Generate LLL node to set to zero o = make_setter(target, zero, target.location, pos=getpos(self.stmt)) o.pos = getpos(self.stmt) return o
def ann_assign(self): with self.context.assignment_scope(): typ = parse_type( self.stmt.annotation, location='memory', custom_structs=self.context.structs, constants=self.context.constants, ) if isinstance(self.stmt.target, vy_ast.Attribute): raise TypeMismatch( f'May not set type for field {self.stmt.target.attr}', self.stmt, ) varname = self.stmt.target.id pos = self.context.new_variable(varname, typ) if self.stmt.value is None: raise StructureException( 'New variables must be initialized explicitly', self.stmt) 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( bytes_to_int(self.stmt.value.s), typ=BaseType('bytes32'), pos=getpos(self.stmt), ) self._check_valid_assign(sub) self._check_same_variable_assign(sub) variable_loc = LLLnode.from_list( pos, typ=typ, location='memory', pos=getpos(self.stmt), ) o = make_setter(variable_loc, sub, 'memory', pos=getpos(self.stmt)) return o
def parse_for(self): from .parser import ( parse_body, ) # Type 0 for, e.g. for i in list(): ... if self._is_list_iter(): return self.parse_for_list() if not isinstance(self.stmt.iter, ast.Call) or \ not isinstance(self.stmt.iter.func, ast.Name) or \ not isinstance(self.stmt.target, ast.Name) or \ self.stmt.iter.func.id != "range" or \ len(self.stmt.iter.args) not in (1, 2): raise StructureException("For statements must be of the form `for i in range(rounds): ..` or `for i in range(start, start + rounds): ..`", self.stmt.iter) # noqa block_scope_id = id(self.stmt.orelse) self.context.start_blockscope(block_scope_id) # Type 1 for, e.g. for i in range(10): ... if len(self.stmt.iter.args) == 1: if not isinstance(self.stmt.iter.args[0], ast.Num): raise StructureException("Range only accepts literal values", self.stmt.iter) start = LLLnode.from_list(0, typ='int128', pos=getpos(self.stmt)) rounds = self.stmt.iter.args[0].n elif isinstance(self.stmt.iter.args[0], ast.Num) and isinstance(self.stmt.iter.args[1], ast.Num): # Type 2 for, e.g. for i in range(100, 110): ... start = LLLnode.from_list(self.stmt.iter.args[0].n, typ='int128', pos=getpos(self.stmt)) rounds = LLLnode.from_list(self.stmt.iter.args[1].n - self.stmt.iter.args[0].n, typ='int128', pos=getpos(self.stmt)) else: # Type 3 for, e.g. for i in range(x, x + 10): ... if not isinstance(self.stmt.iter.args[1], ast.BinOp) or not isinstance(self.stmt.iter.args[1].op, ast.Add): raise StructureException("Two-arg for statements must be of the form `for i in range(start, start + rounds): ...`", self.stmt.iter.args[1]) if ast.dump(self.stmt.iter.args[0]) != ast.dump(self.stmt.iter.args[1].left): raise StructureException("Two-arg for statements of the form `for i in range(x, x + y): ...` must have x identical in both places: %r %r" % (ast.dump(self.stmt.iter.args[0]), ast.dump(self.stmt.iter.args[1].left)), self.stmt.iter) if not isinstance(self.stmt.iter.args[1].right, ast.Num): raise StructureException("Range only accepts literal values", self.stmt.iter.args[1]) start = Expr.parse_value_expr(self.stmt.iter.args[0], self.context) rounds = self.stmt.iter.args[1].right.n varname = self.stmt.target.id pos = self.context.new_variable(varname, BaseType('int128')) self.context.forvars[varname] = True o = LLLnode.from_list(['repeat', pos, start, rounds, parse_body(self.stmt.body, self.context)], typ=None, pos=getpos(self.stmt)) del self.context.vars[varname] del self.context.forvars[varname] self.context.end_blockscope(block_scope_id) return o
def _parse_For_range(self): # attempt to use the type specified by type checking, fall back to `int128` # this is a stopgap solution to allow uint256 - it will be properly solved # once we refactor `vyper.parser` iter_typ = "int128" 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 pos = self.context.new_variable(varname, BaseType(iter_typ), pos=getpos(self.stmt)) self.context.forvars[varname] = True lll_node = LLLnode.from_list( ["repeat", pos, start, rounds, parse_body(self.stmt.body, self.context)], typ=None, pos=getpos(self.stmt), ) del self.context.forvars[varname] return lll_node
def ann_assign(self): with self.context.assignment_scope(): typ = parse_type(self.stmt.annotation, location='memory', custom_units=self.context.custom_units, custom_structs=self.context.structs, constants=self.context.constants) if isinstance(self.stmt.target, ast.Attribute): raise TypeMismatchException( 'May not set type for field %r' % self.stmt.target.attr, self.stmt) varname = self.stmt.target.id pos = self.context.new_variable(varname, typ) o = LLLnode.from_list('pass', typ=None, pos=pos) if self.stmt.value is not None: sub = Expr(self.stmt.value, self.context).lll_node # Disallow assignment to None if isinstance(sub.typ, NullType): raise InvalidLiteralException( 'Assignment to None is not allowed, use a default value or built-in `clear()`.', self.stmt) # If bytes[32] to bytes32 assignment rewrite sub as bytes32. if isinstance( sub.typ, ByteArrayType) and sub.typ.maxlen == 32 and isinstance( typ, BaseType) and typ.typ == 'bytes32': sub = LLLnode(bytes_to_int(self.stmt.value.s), typ=BaseType('bytes32'), pos=getpos(self.stmt)) self._check_valid_assign(sub) self._check_same_variable_assign(sub) variable_loc = LLLnode.from_list(pos, typ=typ, location='memory', pos=getpos(self.stmt)) o = make_setter(variable_loc, sub, 'memory', pos=getpos(self.stmt)) # o.pos = getpos(self.stmt) # TODO: Should this be here like in assign()? return o
def ann_assign(self): from .parser import ( make_setter, ) self.context.set_in_assignment(True) typ = parse_type(self.stmt.annotation, location='memory', custom_units=self.context.custom_units) if isinstance(self.stmt.target, ast.Attribute) and self.stmt.target.value.id == 'self': raise TypeMismatchException('May not redefine storage variables.', self.stmt) varname = self.stmt.target.id pos = self.context.new_variable(varname, typ) o = LLLnode.from_list('pass', typ=None, pos=pos) if self.stmt.value is not None: sub = Expr(self.stmt.value, self.context).lll_node self._check_valid_assign(sub) variable_loc = LLLnode.from_list(pos, typ=typ, location='memory', pos=getpos(self.stmt)) o = make_setter(variable_loc, sub, 'memory', pos=getpos(self.stmt)) self.context.set_in_assignment(False) return o
def parse_if(self): if self.stmt.orelse: block_scope_id = id(self.stmt.orelse) with self.context.make_blockscope(block_scope_id): add_on = [parse_body(self.stmt.orelse, self.context)] else: add_on = [] block_scope_id = id(self.stmt) with self.context.make_blockscope(block_scope_id): test_expr = Expr.parse_value_expr(self.stmt.test, self.context) if not self.is_bool_expr(test_expr): raise TypeMismatchException('Only boolean expressions allowed', self.stmt.test) body = ['if', test_expr, parse_body(self.stmt.body, self.context)] + add_on o = LLLnode.from_list(body, typ=None, pos=getpos(self.stmt)) return o
def pack_logging_topics(event_id, args, expected_topics, context, pos): topics = [event_id] code_pos = pos for pos, expected_topic in enumerate(expected_topics): expected_type = expected_topic.typ arg = args[pos] value = Expr(arg, context).lll_node arg_type = value.typ if isinstance(arg_type, ByteArrayLike) and isinstance( expected_type, ByteArrayLike): if arg_type.maxlen > expected_type.maxlen: raise TypeMismatchException( "Topic input bytes are too big: %r %r" % (arg_type, expected_type), code_pos) if isinstance(arg, ast.Str): bytez, bytez_length = string_to_bytes(arg.s) if len(bytez) > 32: raise InvalidLiteralException( "Can only log a maximum of 32 bytes at a time.", code_pos) topics.append( bytes_to_int(bytez + b'\x00' * (32 - bytez_length))) else: if value.location == "memory": size = ['mload', value] elif value.location == "storage": size = ['sload', ['sha3_32', value]] topics.append(byte_array_to_num(value, arg, 'uint256', size)) else: if arg_type != expected_type: raise TypeMismatchException( f"Invalid type for logging topic, got {arg_type} expected {expected_type}", value.pos) value = unwrap_location(value) value = base_type_conversion(value, arg_type, expected_type, pos=code_pos) topics.append(value) return topics
def assign(self): # Assignment (e.g. x[4] = y) with self.context.assignment_scope(): sub = Expr(self.stmt.value, self.context).lll_node # Error check when assigning to declared variable if isinstance(self.stmt.target, vy_ast.Name): # Do not allow assignment to undefined variables without annotation if self.stmt.target.id not in self.context.vars: raise VariableDeclarationException( "Variable type not defined", self.stmt) # Check against implicit conversion self._check_implicit_conversion(self.stmt.target.id, sub) is_valid_tuple_assign = (isinstance( self.stmt.target, vy_ast.Tuple)) and isinstance( self.stmt.value, vy_ast.Tuple) # Do no allow tuple-to-tuple assignment if is_valid_tuple_assign: raise VariableDeclarationException( "Tuple to tuple assignment not supported", self.stmt, ) # Checks to see if assignment is valid target = self.get_target(self.stmt.target) if isinstance(target.typ, ContractType) and not isinstance( sub.typ, ContractType): raise TypeMismatch( 'Contract assignment expects casted address: ' f'{target.typ}(<address_var>)', self.stmt) o = make_setter(target, sub, target.location, pos=getpos(self.stmt)) o.pos = getpos(self.stmt) return o
def assign(self): # Assignment (e.g. x[4] = y) if len(self.stmt.targets) != 1: raise StructureException( "Assignment statement must have one target", self.stmt) self.context.set_in_assignment(True) sub = Expr(self.stmt.value, self.context).lll_node # Determine if it's an RLPList assignment. if isinstance(self.stmt.value, ast.Call) and getattr( self.stmt.value.func, 'id', '') is 'RLPList': pos = self.context.new_variable(self.stmt.targets[0].id, sub.typ) variable_loc = LLLnode.from_list( pos, typ=sub.typ, location='memory', pos=getpos(self.stmt), annotation=self.stmt.targets[0].id) o = make_setter(variable_loc, sub, 'memory', pos=getpos(self.stmt)) # All other assignments are forbidden. elif isinstance( self.stmt.targets[0], ast.Name) and self.stmt.targets[0].id not in self.context.vars: raise VariableDeclarationException("Variable type not defined", self.stmt) elif isinstance(self.stmt.targets[0], ast.Tuple) and isinstance( self.stmt.value, ast.Tuple): raise VariableDeclarationException( "Tuple to tuple assignment not supported", self.stmt) else: # Checks to see if assignment is valid target = self.get_target(self.stmt.targets[0]) o = make_setter(target, sub, target.location, pos=getpos(self.stmt)) o.pos = getpos(self.stmt) self.context.set_in_assignment(False) return o
def parse_if(self): from .parser import ( parse_body, ) if self.stmt.orelse: block_scope_id = id(self.stmt.orelse) self.context.start_blockscope(block_scope_id) add_on = [parse_body(self.stmt.orelse, self.context)] self.context.end_blockscope(block_scope_id) else: add_on = [] block_scope_id = id(self.stmt) self.context.start_blockscope(block_scope_id) o = LLLnode.from_list( ['if', Expr.parse_value_expr(self.stmt.test, self.context), parse_body(self.stmt.body, self.context)] + add_on, typ=None, pos=getpos(self.stmt) ) self.context.end_blockscope(block_scope_id) return o
def ann_assign(self): self.context.set_in_assignment(True) typ = parse_type(self.stmt.annotation, location='memory', custom_units=self.context.custom_units) if isinstance(self.stmt.target, ast.Attribute) and self.stmt.target.value.id == 'self': raise TypeMismatchException('May not redefine storage variables.', self.stmt) varname = self.stmt.target.id pos = self.context.new_variable(varname, typ) o = LLLnode.from_list('pass', typ=None, pos=pos) if self.stmt.value is not None: sub = Expr(self.stmt.value, self.context).lll_node # If bytes[32] to bytes32 assignment rewrite sub as bytes32. if isinstance(sub.typ, ByteArrayType) and sub.typ.maxlen == 32 and isinstance(typ, BaseType) and typ.typ == 'bytes32': bytez, bytez_length = string_to_bytes(self.stmt.value.s) sub = LLLnode(bytes_to_int(bytez), typ=BaseType('bytes32'), pos=getpos(self.stmt)) self._check_valid_assign(sub) self._check_same_variable_assign(sub) variable_loc = LLLnode.from_list(pos, typ=typ, location='memory', pos=getpos(self.stmt)) o = make_setter(variable_loc, sub, 'memory', pos=getpos(self.stmt)) self.context.set_in_assignment(False) return o
def raw_log(expr, args, kwargs, context): if not isinstance(args[0], ast.List) or len(args[0].elts) > 4: raise StructureException( "Expecting a list of 0-4 topics as first argument", args[0]) topics = [] for elt in args[0].elts: arg = Expr.parse_value_expr(elt, context) if not is_base_type(arg.typ, 'bytes32'): raise TypeMismatchException( "Expecting a bytes32 argument as topic", elt) topics.append(arg) if args[1].location == "memory": return LLLnode.from_list([ "with", "_arr", args[1], ["log" + str(len(topics)), ["add", "_arr", 32], ["mload", "_arr"]] + topics ], typ=None, pos=getpos(expr)) placeholder = context.new_placeholder(args[1].typ) placeholder_node = LLLnode.from_list(placeholder, typ=args[1].typ, location='memory') copier = make_byte_array_copier(placeholder_node, LLLnode.from_list( '_sub', typ=args[1].typ, location=args[1].location), pos=getpos(expr)) return LLLnode.from_list([ "with", "_sub", args[1], [ "seq", copier, [ "log" + str(len(topics)), ["add", placeholder_node, 32], ["mload", placeholder_node] ] + topics ] ], typ=None, pos=getpos(expr))
def parse_AnnAssign(self): with self.context.assignment_scope(): typ = parse_type( self.stmt.annotation, location="memory", custom_structs=self.context.structs, constants=self.context.constants, ) 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( 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, "memory", pos=getpos(self.stmt)) return lll_node
def _call_lookup_specs(stmt_expr, context): from vyper.parser.expr import Expr method_name = stmt_expr.func.attr if len(stmt_expr.keywords): raise TypeMismatch( "Cannot use keyword arguments in calls to functions via 'self'", stmt_expr, ) expr_args = [Expr(arg, context).lll_node for arg in stmt_expr.args] sig = FunctionSignature.lookup_sig( context.sigs, method_name, expr_args, stmt_expr, context, ) return method_name, expr_args, sig
def unroll_constant(self, const, global_ctx): # const = self._constants[self.expr.id] ann_expr = None expr = Expr.parse_value_expr( const.value, Context(vars=None, global_ctx=global_ctx, origcode=const.source_code)) annotation_type = global_ctx.parse_type(const.annotation.args[0], None) fail = False if is_instances([expr.typ, annotation_type], ByteArrayType): if expr.typ.maxlen < annotation_type.maxlen: return const fail = True elif expr.typ != annotation_type: fail = True # special case for literals, which can be uint256 types as well. if is_instances([expr.typ, annotation_type], BaseType) and \ [annotation_type.typ, expr.typ.typ] == ['uint256', 'int128'] and \ SizeLimits.in_bounds('uint256', expr.value): fail = False elif is_instances([expr.typ, annotation_type], BaseType) and \ [annotation_type.typ, expr.typ.typ] == ['int128', 'int128'] and \ SizeLimits.in_bounds('int128', expr.value): fail = False if fail: raise TypeMismatchException( 'Invalid value for constant type, expected %r' % annotation_type, const.value) ann_expr = copy.deepcopy(expr) ann_expr.typ = annotation_type ann_expr.typ.is_literal = expr.typ.is_literal # Annotation type doesn't have literal set. return ann_expr
def unroll_constant(self, const): # const = self.context.constants[self.expr.id] expr = Expr.parse_value_expr( const.value, Context(vars=None, global_ctx=self, origcode=const.source_code)) annotation_type = parse_type(const.annotation.args[0], None, custom_units=self._custom_units, custom_structs=self._structs) fail = False if self.is_instances([expr.typ, annotation_type], ByteArrayType): if expr.typ.maxlen < annotation_type.maxlen: return const fail = True elif expr.typ != annotation_type: fail = True # special case for literals, which can be uint256 types as well. if self.is_instances([expr.typ, annotation_type], BaseType) and \ [annotation_type.typ, expr.typ.typ] == ['uint256', 'int128'] and \ SizeLimits.in_bounds('uint256', expr.value): fail = False elif self.is_instances([expr.typ, annotation_type], BaseType) and \ [annotation_type.typ, expr.typ.typ] == ['int128', 'int128'] and \ SizeLimits.in_bounds('int128', expr.value): fail = False if fail: raise TypeMismatchException( 'Invalid value for constant type, expected %r' % annotation_type, const.value) else: expr.typ = annotation_type return expr
def parse_if(self): from .parser import ( parse_body, ) if self.stmt.orelse: block_scope_id = id(self.stmt.orelse) self.context.start_blockscope(block_scope_id) add_on = [parse_body(self.stmt.orelse, self.context)] self.context.end_blockscope(block_scope_id) else: add_on = [] block_scope_id = id(self.stmt) self.context.start_blockscope(block_scope_id) test_expr = Expr.parse_value_expr(self.stmt.test, self.context) if not self.is_bool_expr(test_expr): raise TypeMismatchException('Only boolean expressions allowed', self.stmt.test) o = LLLnode.from_list( ['if', test_expr, parse_body(self.stmt.body, self.context)] + add_on, typ=None, pos=getpos(self.stmt) ) self.context.end_blockscope(block_scope_id) return o
def pack_args_by_32(holder, maxlen, arg, typ, context, placeholder, dynamic_offset_counter=None, datamem_start=None, zero_pad_i=None, pos=None): """ Copy necessary variables to pre-allocated memory section. :param holder: Complete holder for all args :param maxlen: Total length in bytes of the full arg section (static + dynamic). :param arg: Current arg to pack :param context: Context of arg :param placeholder: Static placeholder for static argument part. :param dynamic_offset_counter: position counter stored in static args. :param dynamic_placeholder: pointer to current position in memory to write dynamic values to. :param datamem_start: position where the whole datemem section starts. """ if isinstance(typ, BaseType): if isinstance(arg, LLLnode): value = unwrap_location(arg) else: value = Expr(arg, context).lll_node value = base_type_conversion(value, value.typ, typ, pos) holder.append( LLLnode.from_list(['mstore', placeholder, value], typ=typ, location='memory')) elif isinstance(typ, ByteArrayLike): if isinstance(arg, LLLnode): # Is prealloacted variable. source_lll = arg else: source_lll = Expr(arg, context).lll_node # Set static offset, in arg slot. holder.append( LLLnode.from_list( ['mstore', placeholder, ['mload', dynamic_offset_counter]])) # Get the biginning to write the ByteArray to. dest_placeholder = LLLnode.from_list( ['add', datamem_start, ['mload', dynamic_offset_counter]], typ=typ, location='memory', annotation="pack_args_by_32:dest_placeholder") copier = make_byte_array_copier(dest_placeholder, source_lll, pos=pos) holder.append(copier) # Add zero padding. holder.append(zero_pad(dest_placeholder, maxlen, zero_pad_i=zero_pad_i)) # Increment offset counter. increment_counter = LLLnode.from_list( [ 'mstore', dynamic_offset_counter, [ 'add', [ 'add', ['mload', dynamic_offset_counter], ['ceil32', ['mload', dest_placeholder]] ], 32, ], ], annotation='Increment dynamic offset counter') holder.append(increment_counter) elif isinstance(typ, ListType): maxlen += (typ.count - 1) * 32 typ = typ.subtype def check_list_type_match(provided): # Check list types match. if provided != typ: raise TypeMismatchException( "Log list type '%s' does not match provided, expected '%s'" % (provided, typ)) # NOTE: Below code could be refactored into iterators/getter functions for each type of # repetitive loop. But seeing how each one is a unique for loop, and in which way # the sub value makes the difference in each type of list clearer. # List from storage if isinstance(arg, ast.Attribute) and arg.value.id == 'self': stor_list = context.globals[arg.attr] check_list_type_match(stor_list.typ.subtype) size = stor_list.typ.count mem_offset = 0 for i in range(0, size): storage_offset = i arg2 = LLLnode.from_list( [ 'sload', [ 'add', ['sha3_32', Expr(arg, context).lll_node], storage_offset ] ], typ=typ, ) holder, maxlen = pack_args_by_32( holder, maxlen, arg2, typ, context, placeholder + mem_offset, pos=pos, ) mem_offset += get_size_of_type(typ) * 32 # List from variable. elif isinstance(arg, ast.Name): size = context.vars[arg.id].size pos = context.vars[arg.id].pos check_list_type_match(context.vars[arg.id].typ.subtype) mem_offset = 0 for _ in range(0, size): arg2 = LLLnode.from_list( pos + mem_offset, typ=typ, location=context.vars[arg.id].location) holder, maxlen = pack_args_by_32( holder, maxlen, arg2, typ, context, placeholder + mem_offset, pos=pos, ) mem_offset += get_size_of_type(typ) * 32 # List from list literal. else: mem_offset = 0 for arg2 in arg.elts: holder, maxlen = pack_args_by_32( holder, maxlen, arg2, typ, context, placeholder + mem_offset, pos=pos, ) mem_offset += get_size_of_type(typ) * 32 return holder, maxlen
def pack_logging_data(expected_data, args, context, pos): # Checks to see if there's any data if not args: return ['seq'], 0, None, 0 holder = ['seq'] maxlen = len(args) * 32 # total size of all packed args (upper limit) # Unroll any function calls, to temp variables. prealloacted = {} for idx, (arg, _expected_arg) in enumerate(zip(args, expected_data)): if isinstance(arg, (ast.Str, ast.Call)): expr = Expr(arg, context) source_lll = expr.lll_node typ = source_lll.typ if isinstance(arg, ast.Str): if len(arg.s) > typ.maxlen: raise TypeMismatchException( "Data input bytes are to big: %r %r" % (len(arg.s), typ), pos) tmp_variable = context.new_variable( '_log_pack_var_%i_%i' % (arg.lineno, arg.col_offset), source_lll.typ, ) tmp_variable_node = LLLnode.from_list( tmp_variable, typ=source_lll.typ, pos=getpos(arg), location="memory", annotation='log_prealloacted %r' % source_lll.typ, ) # Store len. # holder.append(['mstore', len_placeholder, ['mload', unwrap_location(source_lll)]]) # Copy bytes. holder.append( make_setter(tmp_variable_node, source_lll, pos=getpos(arg), location='memory')) prealloacted[idx] = tmp_variable_node requires_dynamic_offset = any( [isinstance(data.typ, ByteArrayLike) for data in expected_data]) if requires_dynamic_offset: # Iterator used to zero pad memory. zero_pad_i = context.new_placeholder(BaseType('uint256')) dynamic_offset_counter = context.new_placeholder(BaseType(32)) dynamic_placeholder = context.new_placeholder(BaseType(32)) else: dynamic_offset_counter = None zero_pad_i = None # Create placeholder for static args. Note: order of new_*() is important. placeholder_map = {} for i, (_arg, data) in enumerate(zip(args, expected_data)): typ = data.typ if not isinstance(typ, ByteArrayLike): placeholder = context.new_placeholder(typ) else: placeholder = context.new_placeholder(BaseType(32)) placeholder_map[i] = placeholder # Populate static placeholders. for i, (arg, data) in enumerate(zip(args, expected_data)): typ = data.typ placeholder = placeholder_map[i] if not isinstance(typ, ByteArrayLike): holder, maxlen = pack_args_by_32( holder, maxlen, prealloacted.get(i, arg), typ, context, placeholder, zero_pad_i=zero_pad_i, pos=pos, ) # Dynamic position starts right after the static args. if requires_dynamic_offset: holder.append( LLLnode.from_list(['mstore', dynamic_offset_counter, maxlen])) # Calculate maximum dynamic offset placeholders, used for gas estimation. for _arg, data in zip(args, expected_data): typ = data.typ if isinstance(typ, ByteArrayLike): maxlen += 32 + ceil32(typ.maxlen) if requires_dynamic_offset: datamem_start = dynamic_placeholder + 32 else: datamem_start = placeholder_map[0] # Copy necessary data into allocated dynamic section. for i, (arg, data) in enumerate(zip(args, expected_data)): typ = data.typ if isinstance(typ, ByteArrayLike): pack_args_by_32(holder=holder, maxlen=maxlen, arg=prealloacted.get(i, arg), typ=typ, context=context, placeholder=placeholder_map[i], datamem_start=datamem_start, dynamic_offset_counter=dynamic_offset_counter, zero_pad_i=zero_pad_i, pos=pos) return holder, maxlen, dynamic_offset_counter, datamem_start
def concat(expr, context): args = [Expr(arg, context).lll_node for arg in expr.args] if len(args) < 2: raise StructureException("Concat expects at least two arguments", expr) prev_type = '' for _, (expr_arg, arg) in enumerate(zip(expr.args, args)): if not isinstance(arg.typ, ByteArrayLike) and not is_base_type(arg.typ, 'bytes32'): raise TypeMismatchException("Concat expects string, bytes or bytes32 objects", expr_arg) current_type = ( 'bytes' if isinstance(arg.typ, ByteArrayType) or is_base_type(arg.typ, 'bytes32') else 'string' ) if prev_type and current_type != prev_type: raise TypeMismatchException( ( "Concat expects consistant use of string or byte types, " "user either bytes or string." ), expr_arg, ) prev_type = current_type if current_type == 'string': ReturnType = StringType else: ReturnType = ByteArrayType # Maximum length of the output total_maxlen = sum([ arg.typ.maxlen if isinstance(arg.typ, ByteArrayType) else 32 for arg in args ]) # Node representing the position of the output in memory placeholder = context.new_placeholder(ReturnType(total_maxlen)) # Object representing the output seq = [] # For each argument we are concatenating... for arg in args: # Start pasting into a position the starts at zero, and keeps # incrementing as we concatenate arguments placeholder_node = LLLnode.from_list( ['add', placeholder, '_poz'], typ=ReturnType(total_maxlen), location='memory', ) placeholder_node_plus_32 = LLLnode.from_list( ['add', ['add', placeholder, '_poz'], 32], typ=ReturnType(total_maxlen), location='memory', ) if isinstance(arg.typ, ReturnType): # Ignore empty strings if arg.typ.maxlen == 0: continue # Get the length of the current argument if arg.location == "memory": length = LLLnode.from_list(['mload', '_arg'], typ=BaseType('int128')) argstart = LLLnode.from_list( ['add', '_arg', 32], typ=arg.typ, location=arg.location, ) elif arg.location == "storage": length = LLLnode.from_list(['sload', ['sha3_32', '_arg']], typ=BaseType('int128')) argstart = LLLnode.from_list( ['add', ['sha3_32', '_arg'], 1], typ=arg.typ, location=arg.location, ) # Make a copier to copy over data from that argument seq.append([ 'with', '_arg', arg, [ 'seq', make_byte_slice_copier( placeholder_node_plus_32, argstart, length, arg.typ.maxlen, pos=getpos(expr), ), # Change the position to start at the correct # place to paste the next value ['set', '_poz', ['add', '_poz', length]], ], ]) else: seq.append([ 'seq', ['mstore', ['add', placeholder_node, 32], unwrap_location(arg)], ['set', '_poz', ['add', '_poz', 32]], ]) # The position, after all arguments are processing, equals the total # length. Paste this in to make the output a proper bytearray seq.append(['mstore', placeholder, '_poz']) # Memory location of the output seq.append(placeholder) return LLLnode.from_list( ['with', '_poz', 0, ['seq'] + seq], typ=ReturnType(total_maxlen), location='memory', pos=getpos(expr), annotation='concat', )
def _slice(expr, args, kwargs, context): sub, start, length = args[0], kwargs['start'], kwargs['len'] if not are_units_compatible(start.typ, BaseType('int128')): raise TypeMismatchException("Type for slice start index must be a unitless number", expr) # Expression representing the length of the slice if not are_units_compatible(length.typ, BaseType('int128')): raise TypeMismatchException("Type for slice length must be a unitless number", expr) if is_base_type(sub.typ, 'bytes32'): if (start.typ.is_literal and length.typ.is_literal) and \ not (0 <= start.value + length.value <= 32): raise InvalidLiteralException( 'Invalid start / length values needs to be between 0 and 32.', expr, ) sub_typ_maxlen = 32 else: sub_typ_maxlen = sub.typ.maxlen # Get returntype string or bytes if isinstance(args[0].typ, ByteArrayType) or is_base_type(sub.typ, 'bytes32'): ReturnType = ByteArrayType else: ReturnType = StringType # Node representing the position of the output in memory np = context.new_placeholder(ReturnType(maxlen=sub_typ_maxlen + 32)) placeholder_node = LLLnode.from_list(np, typ=sub.typ, location='memory') placeholder_plus_32_node = LLLnode.from_list(np + 32, typ=sub.typ, location='memory') # Copies over bytearray data if sub.location == 'storage': adj_sub = LLLnode.from_list( ['add', ['sha3_32', sub], ['add', ['div', '_start', 32], 1]], typ=sub.typ, location=sub.location, ) else: adj_sub = LLLnode.from_list( ['add', sub, ['add', ['sub', '_start', ['mod', '_start', 32]], 32]], typ=sub.typ, location=sub.location, ) if is_base_type(sub.typ, 'bytes32'): adj_sub = LLLnode.from_list( sub.args[0], typ=sub.typ, location="memory" ) copier = make_byte_slice_copier( placeholder_plus_32_node, adj_sub, ['add', '_length', 32], sub_typ_maxlen, pos=getpos(expr), ) # New maximum length in the type of the result newmaxlen = length.value if not len(length.args) else sub_typ_maxlen if is_base_type(sub.typ, 'bytes32'): maxlen = 32 else: maxlen = ['mload', Expr(sub, context=context).lll_node] # Retrieve length of the bytes. out = [ 'with', '_start', start, [ 'with', '_length', length, [ 'with', '_opos', ['add', placeholder_node, ['mod', '_start', 32]], [ 'seq', ['assert', ['le', ['add', '_start', '_length'], maxlen]], copier, ['mstore', '_opos', '_length'], '_opos' ], ], ], ] return LLLnode.from_list(out, typ=ReturnType(newmaxlen), location='memory', pos=getpos(expr))
def external_call(node, context, interface_name, contract_address, pos, value=None, gas=None): from vyper.parser.expr import Expr if value is None: value = 0 if gas is None: gas = "gas" method_name = node.func.attr sig = context.sigs[interface_name][method_name] inargs, inargsize, _ = pack_arguments( sig, [Expr(arg, context).lll_node for arg in node.args], context, node.func, is_external_call=True, ) output_placeholder, output_size, returner = get_external_call_output( sig, context) sub = ["seq"] if not output_size: # if we do not expect return data, check that a contract exists at the target address # we can omit this when we _do_ expect return data because we later check `returndatasize` sub.append(["assert", ["extcodesize", contract_address]]) if context.is_constant() and sig.mutability not in ("view", "pure"): # TODO this can probably go raise StateAccessViolation( f"May not call state modifying function '{method_name}' " f"within {context.pp_constancy()}.", node, ) if context.is_constant() or sig.mutability in ("view", "pure"): sub.append([ "assert", [ "staticcall", gas, contract_address, inargs, inargsize, output_placeholder, output_size, ], ]) else: sub.append([ "assert", [ "call", gas, contract_address, value, inargs, inargsize, output_placeholder, output_size, ], ]) if output_size: # when return data is expected, revert when the length of `returndatasize` is insufficient output_type = sig.output_type if not has_dynamic_data(output_type): static_output_size = get_static_size_of_type(output_type) * 32 sub.append( ["assert", ["gt", "returndatasize", static_output_size - 1]]) else: if isinstance(output_type, ByteArrayLike): types_list = (output_type, ) elif isinstance(output_type, TupleLike): types_list = output_type.tuple_members() else: raise dynamic_checks = [] static_offset = output_placeholder static_output_size = 0 for typ in types_list: # ensure length of bytes does not exceed max allowable length for type if isinstance(typ, ByteArrayLike): static_output_size += 32 # do not perform this check on calls to a JSON interface - we don't know # for certain how long the expected data is if not sig.is_from_json: dynamic_checks.append([ "assert", [ "lt", [ "mload", [ "add", ["mload", static_offset], output_placeholder ], ], typ.maxlen + 1, ], ]) static_offset += get_static_size_of_type(typ) * 32 static_output_size += get_static_size_of_type(typ) * 32 sub.append( ["assert", ["gt", "returndatasize", static_output_size - 1]]) sub.extend(dynamic_checks) sub.extend(returner) return LLLnode.from_list(sub, typ=sig.output_type, location="memory", pos=getpos(node))
def make_external_call(stmt_expr, context): from vyper.parser.expr import Expr value, gas = get_external_interface_keywords(stmt_expr, context) if isinstance(stmt_expr.func, vy_ast.Attribute) and isinstance( stmt_expr.func.value, vy_ast.Call): contract_name = stmt_expr.func.value.func.id contract_address = Expr.parse_value_expr(stmt_expr.func.value.args[0], context) return external_call( stmt_expr, context, contract_name, contract_address, pos=getpos(stmt_expr), value=value, gas=gas, ) elif (isinstance(stmt_expr.func.value, vy_ast.Attribute) and stmt_expr.func.value.attr in context.sigs): # noqa: E501 contract_name = stmt_expr.func.value.attr var = context.globals[stmt_expr.func.value.attr] contract_address = unwrap_location( LLLnode.from_list( var.pos, typ=var.typ, location="storage", pos=getpos(stmt_expr), annotation="self." + stmt_expr.func.value.attr, )) return external_call( stmt_expr, context, contract_name, contract_address, pos=getpos(stmt_expr), value=value, gas=gas, ) elif (isinstance(stmt_expr.func.value, vy_ast.Attribute) and stmt_expr.func.value.attr in context.globals and hasattr(context.globals[stmt_expr.func.value.attr].typ, "name")): contract_name = context.globals[stmt_expr.func.value.attr].typ.name var = context.globals[stmt_expr.func.value.attr] contract_address = unwrap_location( LLLnode.from_list( var.pos, typ=var.typ, location="storage", pos=getpos(stmt_expr), annotation="self." + stmt_expr.func.value.attr, )) return external_call( stmt_expr, context, contract_name, contract_address, pos=getpos(stmt_expr), value=value, gas=gas, ) else: raise StructureException("Unsupported operator.", stmt_expr)
def concat(expr, context): args = [Expr(arg, context).lll_node for arg in expr.args] if len(args) < 2: raise StructureException("Concat expects at least two arguments", expr) for expr_arg, arg in zip(expr.args, args): if not isinstance(arg.typ, ByteArrayType) and not is_base_type( arg.typ, 'bytes32') and not is_base_type(arg.typ, 'method_id'): raise TypeMismatchException( "Concat expects byte arrays or bytes32 objects", expr_arg) # Maximum length of the output total_maxlen = sum([ arg.typ.maxlen if isinstance(arg.typ, ByteArrayType) else 32 for arg in args ]) # Node representing the position of the output in memory placeholder = context.new_placeholder(ByteArrayType(total_maxlen)) # Object representing the output seq = [] # For each argument we are concatenating... for arg in args: # Start pasting into a position the starts at zero, and keeps # incrementing as we concatenate arguments placeholder_node = LLLnode.from_list(['add', placeholder, '_poz'], typ=ByteArrayType(total_maxlen), location='memory') placeholder_node_plus_32 = LLLnode.from_list( ['add', ['add', placeholder, '_poz'], 32], typ=ByteArrayType(total_maxlen), location='memory') if isinstance(arg.typ, ByteArrayType): # Ignore empty strings if arg.typ.maxlen == 0: continue # Get the length of the current argument if arg.location == "memory": length = LLLnode.from_list(['mload', '_arg'], typ=BaseType('num')) argstart = LLLnode.from_list(['add', '_arg', 32], typ=arg.typ, location=arg.location) elif arg.location == "storage": length = LLLnode.from_list(['sload', ['sha3_32', '_arg']], typ=BaseType('num')) argstart = LLLnode.from_list(['add', ['sha3_32', '_arg'], 1], typ=arg.typ, location=arg.location) # Make a copier to copy over data from that argyument seq.append([ 'with', '_arg', arg, [ 'seq', make_byte_slice_copier(placeholder_node_plus_32, argstart, length, arg.typ.maxlen), # Change the position to start at the correct # place to paste the next value ['set', '_poz', ['add', '_poz', length]] ] ]) elif isinstance(arg.typ, BaseType) and arg.typ.typ == "method_id": seq.append([ 'seq', ['mstore', ['add', placeholder_node, 32], arg.value * 2**224], ['set', '_poz', ['add', '_poz', 4]] ]) else: seq.append([ 'seq', [ 'mstore', ['add', placeholder_node, 32], unwrap_location(arg) ], ['set', '_poz', ['add', '_poz', 32]] ]) # The position, after all arguments are processing, equals the total # length. Paste this in to make the output a proper bytearray seq.append(['mstore', placeholder, '_poz']) # Memory location of the output seq.append(placeholder) return LLLnode.from_list(['with', '_poz', 0, ['seq'] + seq], typ=ByteArrayType(total_maxlen), location='memory', pos=getpos(expr), annotation='concat')
def parse_return(self): if self.context.return_type is None: if self.stmt.value: raise TypeMismatchException("Not expecting to return a value", self.stmt) return LLLnode.from_list( make_return_stmt(self.stmt, self.context, 0, 0), typ=None, pos=getpos(self.stmt), valency=0, ) if not self.stmt.value: raise TypeMismatchException("Expecting to return a value", self.stmt) sub = Expr(self.stmt.value, self.context).lll_node # Returning a value (most common case) if isinstance(sub.typ, BaseType): sub = unwrap_location(sub) if not isinstance(self.context.return_type, BaseType): raise TypeMismatchException( f"Return type units mismatch {sub.typ} {self.context.return_type}", self.stmt.value) elif self.context.return_type != sub.typ and not sub.typ.is_literal: raise TypeMismatchException( f"Trying to return base type {sub.typ}, output expecting " f"{self.context.return_type}", self.stmt.value, ) elif sub.typ.is_literal and ( self.context.return_type.typ == sub.typ or 'int' in self.context.return_type.typ and 'int' in sub.typ.typ): # noqa: E501 if not SizeLimits.in_bounds(self.context.return_type.typ, sub.value): raise InvalidLiteralException( "Number out of range: " + str(sub.value), self.stmt) else: return LLLnode.from_list( [ 'seq', ['mstore', 0, sub], make_return_stmt(self.stmt, self.context, 0, 32) ], typ=None, pos=getpos(self.stmt), valency=0, ) elif is_base_type(sub.typ, self.context.return_type.typ) or ( is_base_type(sub.typ, 'int128') and is_base_type( self.context.return_type, 'int256')): # noqa: E501 return LLLnode.from_list( [ 'seq', ['mstore', 0, sub], make_return_stmt(self.stmt, self.context, 0, 32) ], typ=None, pos=getpos(self.stmt), valency=0, ) else: raise TypeMismatchException( f"Unsupported type conversion: {sub.typ} to {self.context.return_type}", self.stmt.value, ) # Returning a byte array elif isinstance(sub.typ, ByteArrayLike): if not sub.typ.eq_base(self.context.return_type): raise TypeMismatchException( f"Trying to return base type {sub.typ}, output expecting " f"{self.context.return_type}", self.stmt.value, ) if sub.typ.maxlen > self.context.return_type.maxlen: raise TypeMismatchException( f"Cannot cast from greater max-length {sub.typ.maxlen} to shorter " f"max-length {self.context.return_type.maxlen}", self.stmt.value, ) # loop memory has to be allocated first. loop_memory_position = self.context.new_placeholder( typ=BaseType('uint256')) # len & bytez placeholder have to be declared after each other at all times. len_placeholder = self.context.new_placeholder( typ=BaseType('uint256')) bytez_placeholder = self.context.new_placeholder(typ=sub.typ) if sub.location in ('storage', 'memory'): return LLLnode.from_list([ 'seq', make_byte_array_copier(LLLnode( bytez_placeholder, location='memory', typ=sub.typ), sub, pos=getpos(self.stmt)), zero_pad(bytez_placeholder), ['mstore', len_placeholder, 32], make_return_stmt( self.stmt, self.context, len_placeholder, ['ceil32', ['add', ['mload', bytez_placeholder], 64]], loop_memory_position=loop_memory_position, ) ], typ=None, pos=getpos(self.stmt), valency=0) else: raise Exception(f"Invalid location: {sub.location}") elif isinstance(sub.typ, ListType): sub_base_type = re.split(r'\(|\[', str(sub.typ.subtype))[0] ret_base_type = re.split(r'\(|\[', str(self.context.return_type.subtype))[0] loop_memory_position = self.context.new_placeholder( typ=BaseType('uint256')) if sub_base_type != ret_base_type: raise TypeMismatchException( f"List return type {sub_base_type} does not match specified " f"return type, expecting {ret_base_type}", self.stmt) elif sub.location == "memory" and sub.value != "multi": return LLLnode.from_list( make_return_stmt( self.stmt, self.context, sub, get_size_of_type(self.context.return_type) * 32, loop_memory_position=loop_memory_position, ), typ=None, pos=getpos(self.stmt), valency=0, ) else: new_sub = LLLnode.from_list( self.context.new_placeholder(self.context.return_type), typ=self.context.return_type, location='memory', ) setter = make_setter(new_sub, sub, 'memory', pos=getpos(self.stmt)) return LLLnode.from_list([ 'seq', setter, make_return_stmt( self.stmt, self.context, new_sub, get_size_of_type(self.context.return_type) * 32, loop_memory_position=loop_memory_position, ) ], typ=None, pos=getpos(self.stmt)) # Returning a struct elif isinstance(sub.typ, StructType): retty = self.context.return_type if not isinstance(retty, StructType) or retty.name != sub.typ.name: raise TypeMismatchException( f"Trying to return {sub.typ}, output expecting {self.context.return_type}", self.stmt.value, ) return gen_tuple_return(self.stmt, self.context, sub) # Returning a tuple. elif isinstance(sub.typ, TupleType): if not isinstance(self.context.return_type, TupleType): raise TypeMismatchException( f"Trying to return tuple type {sub.typ}, output expecting " f"{self.context.return_type}", self.stmt.value, ) if len(self.context.return_type.members) != len(sub.typ.members): raise StructureException("Tuple lengths don't match!", self.stmt) # check return type matches, sub type. for i, ret_x in enumerate(self.context.return_type.members): s_member = sub.typ.members[i] sub_type = s_member if isinstance(s_member, NodeType) else s_member.typ if type(sub_type) is not type(ret_x): raise StructureException( "Tuple return type does not match annotated return. " f"{type(sub_type)} != {type(ret_x)}", self.stmt) return gen_tuple_return(self.stmt, self.context, sub) else: raise TypeMismatchException(f"Can't return type {sub.typ}", self.stmt)
def aug_assign(self): target = self.get_target(self.stmt.target) sub = Expr.parse_value_expr(self.stmt.value, self.context) if not isinstance(self.stmt.op, (ast.Add, ast.Sub, ast.Mult, ast.Div, ast.Mod)): raise StructureException("Unsupported operator for augassign", self.stmt) if not isinstance(target.typ, BaseType): raise TypeMismatchException( "Can only use aug-assign operators with simple types!", self.stmt.target) if target.location == 'storage': o = Expr.parse_value_expr( 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, ), self.context, ) return LLLnode.from_list([ 'with', '_stloc', target, [ 'sstore', '_stloc', base_type_conversion( o, o.typ, target.typ, pos=getpos(self.stmt)), ], ], typ=None, pos=getpos(self.stmt)) elif target.location == 'memory': o = Expr.parse_value_expr( 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, ), self.context, ) return LLLnode.from_list([ 'with', '_mloc', target, [ 'mstore', '_mloc', base_type_conversion( o, o.typ, target.typ, pos=getpos(self.stmt)), ], ], typ=None, pos=getpos(self.stmt))
def parse_for_list(self): with self.context.range_scope(): iter_list_node = Expr(self.stmt.iter, self.context).lll_node if not isinstance(iter_list_node.typ.subtype, BaseType): # Sanity check on list subtype. raise StructureException( 'For loops allowed only on basetype lists.', self.stmt.iter) iter_var_type = (self.context.vars.get(self.stmt.iter.id).typ if isinstance(self.stmt.iter, ast.Name) else None) subtype = iter_list_node.typ.subtype.typ varname = self.stmt.target.id value_pos = self.context.new_variable( varname, BaseType(subtype, unit=iter_list_node.typ.subtype.unit), ) i_pos_raw_name = '_index_for_' + varname i_pos = self.context.new_internal_variable( i_pos_raw_name, BaseType(subtype), ) self.context.forvars[varname] = True # Is a list that is already allocated to memory. if iter_var_type: list_name = self.stmt.iter.id # make sure list cannot be altered whilst iterating. with self.context.in_for_loop_scope(list_name): iter_var = self.context.vars.get(self.stmt.iter.id) if iter_var.location == 'calldata': fetcher = 'calldataload' elif iter_var.location == 'memory': fetcher = 'mload' else: raise CompilerPanic( 'List iteration only supported on in-memory types', self.expr) body = [ 'seq', [ 'mstore', value_pos, [ fetcher, [ 'add', iter_var.pos, ['mul', ['mload', i_pos], 32] ] ], ], parse_body(self.stmt.body, self.context) ] o = LLLnode.from_list( ['repeat', i_pos, 0, iter_var.size, body], typ=None, pos=getpos(self.stmt)) # List gets defined in the for statement. elif isinstance(self.stmt.iter, ast.List): # Allocate list to memory. count = iter_list_node.typ.count tmp_list = LLLnode.from_list(obj=self.context.new_placeholder( ListType(iter_list_node.typ.subtype, count)), typ=ListType( iter_list_node.typ.subtype, count), location='memory') setter = make_setter(tmp_list, iter_list_node, 'memory', pos=getpos(self.stmt)) body = [ 'seq', [ 'mstore', value_pos, [ 'mload', ['add', tmp_list, ['mul', ['mload', i_pos], 32]] ] ], parse_body(self.stmt.body, self.context) ] o = LLLnode.from_list( ['seq', setter, ['repeat', i_pos, 0, count, body]], typ=None, pos=getpos(self.stmt)) # List contained in storage. elif isinstance(self.stmt.iter, ast.Attribute): count = iter_list_node.typ.count list_name = iter_list_node.annotation # make sure list cannot be altered whilst iterating. with self.context.in_for_loop_scope(list_name): body = [ 'seq', [ 'mstore', value_pos, [ 'sload', [ 'add', ['sha3_32', iter_list_node], ['mload', i_pos] ] ] ], parse_body(self.stmt.body, self.context), ] o = LLLnode.from_list( ['seq', ['repeat', i_pos, 0, count, body]], typ=None, pos=getpos(self.stmt)) del self.context.vars[varname] # this kind of open access to the vars dict should be disallowed. # we should use member functions to provide an API for these kinds # of operations. del self.context.vars[self.context._mangle(i_pos_raw_name)] del self.context.forvars[varname] return o