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 assign(self): # Assignment (e.g. x[4] = y) if len(self.stmt.targets) != 1: raise StructureException( "Assignment statement must have one target", self.stmt) with self.context.assignment_scope(): 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, ) is_valid_rlp_list_assign = (isinstance( self.stmt.value, ast.Call)) and getattr( self.stmt.value.func, 'id', '') == 'RLPList' # Determine if it's an RLPList assignment. if is_valid_rlp_list_assign: 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)) else: # Error check when assigning to declared variable if isinstance(self.stmt.targets[0], ast.Name): # Do not allow assignment to undefined variables without annotation if self.stmt.targets[0].id not in self.context.vars: raise VariableDeclarationException( "Variable type not defined", self.stmt) # Check against implicit conversion self._check_implicit_conversion(self.stmt.targets[0].id, sub) is_valid_tuple_assign = (isinstance( self.stmt.targets[0], ast.Tuple)) and isinstance( self.stmt.value, 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.targets[0]) if isinstance(target.typ, ContractType) and sub.typ == BaseType('address'): raise TypeMismatchException( 'Contract assignment expects casted address: ' f'{target.typ.unit}(<address_var>)', self.stmt) o = make_setter(target, sub, target.location, pos=getpos(self.stmt)) o.pos = getpos(self.stmt) return o
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) def zero_pad(bytez_placeholder, maxlen): zero_padder = LLLnode.from_list(['pass']) if maxlen > 0: # Iterator used to zero pad memory. zero_pad_i = self.context.new_placeholder(BaseType('uint256')) zero_padder = LLLnode.from_list( [ 'with', '_ceil32_end', ['ceil32', ['mload', bytez_placeholder]], [ 'repeat', zero_pad_i, ['mload', bytez_placeholder], maxlen, [ 'seq', # stay within allocated bounds [ 'if', [ 'gt', ['mload', zero_pad_i], '_ceil32_end' ], 'break' ], [ 'mstore8', [ 'add', ['add', 32, bytez_placeholder], ['mload', zero_pad_i] ], 0 ], ], ], ], annotation="Zero pad") return zero_padder sub = Expr(self.stmt.value, self.context).lll_node self.context.increment_return_counter() # 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( "Return type units mismatch %r %r" % ( sub.typ, self.context.return_type, ), self.stmt.value) elif self.context.return_type != sub.typ and not sub.typ.is_literal: raise TypeMismatchException( "Trying to return base type %r, output expecting %r" % ( sub.typ, 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( "Unsupported type conversion: %r to %r" % (sub.typ, 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( "Trying to return base type %r, output expecting %r" % ( sub.typ, self.context.return_type, ), self.stmt.value, ) if sub.typ.maxlen > self.context.return_type.maxlen: raise TypeMismatchException( "Cannot cast from greater max-length %d to shorter max-length %d" % ( sub.typ.maxlen, 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, sub.typ.maxlen), ['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("Invalid location: %s" % 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( "List return type %r does not match specified return type, expecting %r" % (sub_base_type, 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( "Trying to return %r, output expecting %r" % ( sub.typ, 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( "Trying to return tuple type %r, output expecting %r" % ( sub.typ, 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. {} != {}" .format(type(sub_type), type(ret_x)), self.stmt) return gen_tuple_return(self.stmt, self.context, sub) else: raise TypeMismatchException("Can't return type %r" % sub.typ, self.stmt)
def parse_return(self): from .parser import (make_setter) 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(['return', 0, 0], typ=None, pos=getpos(self.stmt)) if not self.stmt.value: raise TypeMismatchException("Expecting to return a value", self.stmt) sub = Expr(self.stmt.value, self.context).lll_node self.context.increment_return_counter() # Returning a value (most common case) if isinstance(sub.typ, BaseType): if not isinstance(self.context.return_type, BaseType): raise TypeMismatchException( "Trying to return base type %r, output expecting %r" % (sub.typ, self.context.return_type), self.stmt.value) sub = unwrap_location(sub) if not are_units_compatible(sub.typ, self.context.return_type): raise TypeMismatchException( "Return type units mismatch %r %r" % (sub.typ, self.context.return_type), self.stmt.value) elif is_base_type(sub.typ, self.context.return_type.typ) or \ (is_base_type(sub.typ, 'num') and is_base_type(self.context.return_type, 'signed256')): return LLLnode.from_list( ['seq', ['mstore', 0, sub], ['return', 0, 32]], typ=None, pos=getpos(self.stmt)) else: raise TypeMismatchException( "Unsupported type conversion: %r to %r" % (sub.typ, self.context.return_type), self.stmt.value) # Returning a byte array elif isinstance(sub.typ, ByteArrayType): if not isinstance(self.context.return_type, ByteArrayType): raise TypeMismatchException( "Trying to return base type %r, output expecting %r" % (sub.typ, self.context.return_type), self.stmt.value) if sub.typ.maxlen > self.context.return_type.maxlen: raise TypeMismatchException( "Cannot cast from greater max-length %d to shorter max-length %d" % (sub.typ.maxlen, self.context.return_type.maxlen), self.stmt.value) # Returning something already in memory if sub.location == 'memory': return LLLnode.from_list([ 'with', '_loc', sub, [ 'seq', ['mstore', ['sub', '_loc', 32], 32], [ 'return', ['sub', '_loc', 32], ['ceil32', ['add', ['mload', '_loc'], 64]] ] ] ], typ=None, pos=getpos(self.stmt)) # Copying from storage elif sub.location == 'storage': # Instantiate a byte array at some index fake_byte_array = LLLnode(self.context.get_next_mem() + 32, typ=sub.typ, location='memory', pos=getpos(self.stmt)) o = [ 'seq', # Copy the data to this byte array make_byte_array_copier(fake_byte_array, sub), # Store the number 32 before it for ABI formatting purposes ['mstore', self.context.get_next_mem(), 32], # Return it [ 'return', self.context.get_next_mem(), [ 'add', [ 'ceil32', ['mload', self.context.get_next_mem() + 32] ], 64 ] ] ] return LLLnode.from_list(o, typ=None, pos=getpos(self.stmt)) else: raise Exception("Invalid location: %s" % 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] if sub_base_type != ret_base_type and sub.value != 'multi': raise TypeMismatchException( "List return type %r does not match specified return type, expecting %r" % (sub_base_type, ret_base_type), self.stmt) if sub.location == "memory" and sub.value != "multi": return LLLnode.from_list([ 'return', sub, get_size_of_type(self.context.return_type) * 32 ], typ=None, pos=getpos(self.stmt)) 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, [ 'return', new_sub, get_size_of_type(self.context.return_type) * 32 ] ], typ=None, pos=getpos(self.stmt)) # Returning a tuple. elif isinstance(sub.typ, TupleType): if len(self.context.return_type.members) != len(sub.typ.members): raise StructureException("Tuple lengths don't match!") subs = [] dynamic_offset_counter = LLLnode( self.context.get_next_mem(), typ=None, annotation="dynamic_offset_counter" ) # dynamic offset position counter. new_sub = LLLnode.from_list(self.context.get_next_mem() + 32, typ=self.context.return_type, location='memory', annotation='new_sub') keyz = list(range(len(sub.typ.members))) dynamic_offset_start = 32 * len( sub.args) # The static list of args end. left_token = LLLnode.from_list('_loc', typ=new_sub.typ, location="memory") def get_dynamic_offset_value(): # Get value of dynamic offset counter. return ['mload', dynamic_offset_counter] def increment_dynamic_offset(dynamic_spot): # Increment dyanmic offset counter in memory. return [ 'mstore', dynamic_offset_counter, [ 'add', ['add', ['ceil32', ['mload', dynamic_spot]], 32], ['mload', dynamic_offset_counter] ] ] for i, typ in enumerate(keyz): arg = sub.args[i] variable_offset = LLLnode.from_list( ['add', 32 * i, left_token], typ=arg.typ, annotation='variable_offset') if isinstance(arg.typ, ByteArrayType): # Store offset pointer value. subs.append([ 'mstore', variable_offset, get_dynamic_offset_value() ]) # Store dynamic data, from offset pointer onwards. dynamic_spot = LLLnode.from_list( ['add', left_token, get_dynamic_offset_value()], location="memory", typ=arg.typ, annotation='dynamic_spot') subs.append( make_setter(dynamic_spot, arg, location="memory", pos=getpos(self.stmt))) subs.append(increment_dynamic_offset(dynamic_spot)) elif isinstance(arg.typ, BaseType): subs.append( make_setter(variable_offset, arg, "memory", pos=getpos(self.stmt))) else: raise Exception("Can't return type %s as part of tuple", type(arg.typ)) setter = LLLnode.from_list([ 'seq', [ 'mstore', dynamic_offset_counter, dynamic_offset_start ], ['with', '_loc', new_sub, ['seq'] + subs] ], typ=None) return LLLnode.from_list([ 'seq', setter, ['return', new_sub, get_dynamic_offset_value()] ], typ=None, pos=getpos(self.stmt)) else: raise TypeMismatchException("Can only return base type!", self.stmt)
def enforce_units(typ, obj, expected): if not are_units_compatible(typ, expected): raise TypeMismatchException("Invalid units", obj)
def make_byte_array_copier(destination, source, pos=None): if not isinstance(source.typ, (ByteArrayLike, NullType)): btype = 'byte array' if isinstance(destination.typ, ByteArrayType) else 'string' raise TypeMismatchException( f"Can only set a {btype} to another {btype}", pos) if isinstance( source.typ, ByteArrayLike) and source.typ.maxlen > destination.typ.maxlen: raise TypeMismatchException( f"Cannot cast from greater max-length {source.typ.maxlen} to shorter " f"max-length {destination.typ.maxlen}") # Special case: memory to memory if source.location == "memory" and destination.location == "memory": gas_calculation = GAS_IDENTITY + GAS_IDENTITYWORD * ( ceil32(source.typ.maxlen) // 32) o = LLLnode.from_list( [ 'with', '_source', source, [ 'with', '_sz', ['add', 32, ['mload', '_source']], [ 'assert', [ 'call', ['gas'], 4, 0, '_source', '_sz', destination, '_sz' ] ] ] ], # noqa: E501 typ=None, add_gas_estimate=gas_calculation, annotation='Memory copy') return o pos_node = LLLnode.from_list('_pos', typ=source.typ, location=source.location) # Get the length if isinstance(source.typ, NullType): length = 1 elif source.location == "memory": length = ['add', ['mload', '_pos'], 32] elif source.location == "storage": length = ['add', ['sload', '_pos'], 32] pos_node = LLLnode.from_list( ['sha3_32', pos_node], typ=source.typ, location=source.location, ) else: raise Exception("Unsupported location:" + source.location) if destination.location == "storage": destination = LLLnode.from_list( ['sha3_32', destination], typ=destination.typ, location=destination.location, ) # Maximum theoretical length max_length = 32 if isinstance(source.typ, NullType) else source.typ.maxlen + 32 return LLLnode.from_list([ 'with', '_pos', 0 if isinstance(source.typ, NullType) else source, make_byte_slice_copier( destination, pos_node, length, max_length, pos=pos) ], typ=None)
def pack_arguments(signature, args, context, stmt_expr, return_placeholder=True): pos = getpos(stmt_expr) placeholder_typ = ByteArrayType( maxlen=sum([get_size_of_type(arg.typ) for arg in signature.args]) * 32 + 32) placeholder = context.new_placeholder(placeholder_typ) setters = [['mstore', placeholder, signature.method_id]] needpos = False staticarray_offset = 0 expected_arg_count = len(signature.args) actual_arg_count = len(args) if actual_arg_count != expected_arg_count: raise StructureException( f"Wrong number of args for: {signature.name} " f"({actual_arg_count} args given, expected {expected_arg_count}", stmt_expr) for i, (arg, typ) in enumerate(zip(args, [arg.typ for arg in signature.args])): if isinstance(typ, BaseType): setters.append( make_setter(LLLnode.from_list( placeholder + staticarray_offset + 32 + i * 32, typ=typ, ), arg, 'memory', pos=pos, in_function_call=True)) elif isinstance(typ, ByteArrayLike): setters.append([ 'mstore', placeholder + staticarray_offset + 32 + i * 32, '_poz' ]) arg_copy = LLLnode.from_list('_s', typ=arg.typ, location=arg.location) target = LLLnode.from_list( ['add', placeholder + 32, '_poz'], typ=typ, location='memory', ) setters.append([ 'with', '_s', arg, [ 'seq', make_byte_array_copier(target, arg_copy, pos), [ 'set', '_poz', [ 'add', 32, ['ceil32', ['add', '_poz', get_length(arg_copy)]] ] ], ], ]) needpos = True elif isinstance(typ, (StructType, ListType)): if has_dynamic_data(typ): raise TypeMismatchException("Cannot pack bytearray in struct", stmt_expr) target = LLLnode.from_list( [placeholder + 32 + staticarray_offset + i * 32], typ=typ, location='memory', ) setters.append(make_setter(target, arg, 'memory', pos=pos)) if (isinstance(typ, ListType)): count = typ.count else: count = len(typ.tuple_items()) staticarray_offset += 32 * (count - 1) else: raise TypeMismatchException(f"Cannot pack argument of type {typ}", stmt_expr) # For private call usage, doesn't use a returner. returner = [[placeholder + 28]] if return_placeholder else [] if needpos: return (LLLnode.from_list([ 'with', '_poz', len(args) * 32 + staticarray_offset, ['seq'] + setters + returner ], typ=placeholder_typ, location='memory'), placeholder_typ.maxlen - 28, placeholder + 32) else: return (LLLnode.from_list(['seq'] + setters + returner, typ=placeholder_typ, location='memory'), placeholder_typ.maxlen - 28, placeholder + 32)
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 # Node representing the position of the output in memory np = context.new_placeholder(ByteArrayType(maxlen=sub_typ_maxlen + 32)) # 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 process_arg(index, arg, expected_arg_typelist, function_name, context): if isinstance(expected_arg_typelist, Optional): expected_arg_typelist = expected_arg_typelist.typ if not isinstance(expected_arg_typelist, tuple): expected_arg_typelist = (expected_arg_typelist, ) vsub = None for expected_arg in expected_arg_typelist: if expected_arg == 'num_literal': if context.constants.is_constant_of_base_type( arg, ('uint256', 'int128')): return context.constants.get_constant(arg.id, None).value if isinstance(arg, ast.Num) and get_original_if_0_prefixed( arg, context) is None: return arg.n elif expected_arg == 'str_literal': if isinstance(arg, ast.Str) and get_original_if_0_prefixed( arg, context) is None: bytez = b'' for c in arg.s: if ord(c) >= 256: raise InvalidLiteralException( "Cannot insert special character %r into byte array" % c, arg) bytez += bytes([ord(c)]) return bytez elif expected_arg == 'name_literal': if isinstance(arg, ast.Name): return arg.id elif isinstance(arg, ast.Subscript) and arg.value.id == 'bytes': return 'bytes[%s]' % arg.slice.value.n elif expected_arg == '*': return arg elif expected_arg == 'bytes': sub = Expr(arg, context).lll_node if isinstance(sub.typ, ByteArrayType): return sub else: # Does not work for unit-endowed types inside compound types, e.g. timestamp[2] parsed_expected_type = context.parse_type( ast.parse(expected_arg).body[0].value, 'memory') if isinstance(parsed_expected_type, BaseType): vsub = vsub or Expr.parse_value_expr(arg, context) if is_base_type(vsub.typ, expected_arg): return vsub elif expected_arg in ('int128', 'uint256') and \ isinstance(vsub.typ, BaseType) and \ vsub.typ.typ in ('int128', 'uint256') and \ vsub.typ.is_literal and \ SizeLimits.in_bounds(expected_arg, vsub.value): return vsub else: vsub = vsub or Expr(arg, context).lll_node if vsub.typ == parsed_expected_type: return Expr(arg, context).lll_node if len(expected_arg_typelist) == 1: raise TypeMismatchException( "Expecting %s for argument %r of %s" % (expected_arg, index, function_name), arg) else: raise TypeMismatchException( "Expecting one of %r for argument %r of %s" % (expected_arg_typelist, index, function_name), arg) return arg.id
def to_int128(expr, args, kwargs, context): in_arg = args[0] input_type, _ = get_type(in_arg) _unit = in_arg.typ.unit if input_type in ('uint256', 'decimal') else None if input_type == 'num_literal': if isinstance(in_arg, int): if not SizeLimits.in_bounds('int128', in_arg): raise InvalidLiteralException( "Number out of range: {}".format(in_arg)) return LLLnode.from_list(in_arg, typ=BaseType('int128', _unit), pos=getpos(expr)) elif isinstance(in_arg, float): if not SizeLimits.in_bounds('int128', math.trunc(in_arg)): raise InvalidLiteralException("Number out of range: {}".format( math.trunc(in_arg))) return LLLnode.from_list(math.trunc(in_arg), typ=BaseType('int128', _unit), pos=getpos(expr)) else: raise InvalidLiteralException( "Unknown numeric literal type: {}".fornat(in_arg)) elif input_type == 'bytes32': if in_arg.typ.is_literal: if not SizeLimits.in_bounds('int128', in_arg.value): raise InvalidLiteralException( "Number out of range: {}".format(in_arg.value), expr) else: return LLLnode.from_list(in_arg, typ=BaseType('int128', _unit), pos=getpos(expr)) else: return LLLnode.from_list([ 'clamp', ['mload', MemoryPositions.MINNUM], in_arg, ['mload', MemoryPositions.MAXNUM] ], typ=BaseType('int128', _unit), pos=getpos(expr)) elif input_type in ('string', 'bytes'): if in_arg.typ.maxlen > 32: raise TypeMismatchException( "Cannot convert bytes array of max length {} to int128".format( in_arg.value), expr) return byte_array_to_num(in_arg, expr, 'int128') elif input_type == 'uint256': if in_arg.typ.is_literal: if not SizeLimits.in_bounds('int128', in_arg.value): raise InvalidLiteralException( "Number out of range: {}".format(in_arg.value), expr) else: return LLLnode.from_list(in_arg, typ=BaseType('int128', _unit), pos=getpos(expr)) else: return LLLnode.from_list( ['uclample', in_arg, ['mload', MemoryPositions.MAXNUM]], typ=BaseType('int128', _unit), pos=getpos(expr)) elif input_type == 'decimal': return LLLnode.from_list([ 'clamp', ['mload', MemoryPositions.MINNUM], ['sdiv', in_arg, DECIMAL_DIVISOR], ['mload', MemoryPositions.MAXNUM] ], typ=BaseType('int128', _unit), pos=getpos(expr)) elif input_type == 'bool': return LLLnode.from_list(in_arg, typ=BaseType('int128', _unit), pos=getpos(expr)) else: raise InvalidLiteralException("Invalid input for int128: %r" % in_arg, expr)
def raw_call(expr, args, kwargs, context): to, data = args gas, value, outsize, delegate_call = ( kwargs['gas'], kwargs['value'], kwargs['outsize'], kwargs['delegate_call'], ) if delegate_call.typ.is_literal is False: raise TypeMismatchException( 'The delegate_call parameter has to be a static/literal boolean value.' ) if context.is_constant(): raise ConstancyViolationException( "Cannot make calls from %s" % context.pp_constancy(), expr, ) if value != zero_value: enforce_units( value.typ, get_keyword(expr, 'value'), BaseType('uint256', {'wei': 1}), ) placeholder = context.new_placeholder(data.typ) placeholder_node = LLLnode.from_list(placeholder, typ=data.typ, location='memory') copier = make_byte_array_copier(placeholder_node, data, pos=getpos(expr)) output_placeholder = context.new_placeholder(ByteArrayType(outsize)) output_node = LLLnode.from_list( output_placeholder, typ=ByteArrayType(outsize), location='memory', ) if delegate_call.value == 1: z = LLLnode.from_list( [ 'seq', copier, [ 'assert', [ 'delegatecall', gas, to, ['add', placeholder_node, 32], ['mload', placeholder_node], ['add', output_node, 32], outsize, ], ], ['mstore', output_node, outsize], output_node, ], typ=ByteArrayType(outsize), location='memory', pos=getpos(expr), ) else: z = LLLnode.from_list([ 'seq', copier, [ 'assert', [ 'call', gas, to, value, ['add', placeholder_node, 32], ['mload', placeholder_node], ['add', output_node, 32], outsize, ], ], ['mstore', output_node, outsize], output_node, ], typ=ByteArrayType(outsize), location='memory', pos=getpos(expr)) return z
def to_decimal(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 TypeMismatchException( "Cannot convert bytes array of max length {} to decimal". format(in_arg.value), expr) num = byte_array_to_num(in_arg, expr, 'int128') return LLLnode.from_list(['mul', num, DECIMAL_DIVISOR], typ=BaseType('decimal'), pos=getpos(expr)) else: _unit = in_arg.typ.unit _positional = in_arg.typ.positional if input_type == 'uint256': if in_arg.typ.is_literal: if not SizeLimits.in_bounds('int128', (in_arg.value * DECIMAL_DIVISOR)): raise InvalidLiteralException( "Number out of range: {}".format(in_arg.value), expr) else: return LLLnode.from_list(['mul', in_arg, DECIMAL_DIVISOR], typ=BaseType( 'decimal', _unit, _positional), pos=getpos(expr)) else: return LLLnode.from_list([ 'uclample', ['mul', in_arg, DECIMAL_DIVISOR], ['mload', MemoryPositions.MAXDECIMAL] ], typ=BaseType('decimal', _unit, _positional), pos=getpos(expr)) elif input_type == 'bytes32': if in_arg.typ.is_literal: if not SizeLimits.in_bounds('int128', (in_arg.value * DECIMAL_DIVISOR)): raise InvalidLiteralException( "Number out of range: {}".format(in_arg.value), expr) else: return LLLnode.from_list(['mul', in_arg, DECIMAL_DIVISOR], typ=BaseType( 'decimal', _unit, _positional), pos=getpos(expr)) else: return LLLnode.from_list([ 'clamp', ['mload', MemoryPositions.MINDECIMAL], ['mul', in_arg, DECIMAL_DIVISOR], ['mload', MemoryPositions.MAXDECIMAL] ], typ=BaseType('decimal', _unit, _positional), pos=getpos(expr)) elif input_type in ('int128', 'bool'): return LLLnode.from_list(['mul', in_arg, DECIMAL_DIVISOR], typ=BaseType('decimal', _unit, _positional), pos=getpos(expr)) else: raise InvalidLiteralException( "Invalid input for decimal: %r" % in_arg, expr)
def call(self): from .parser import ( external_contract_call, pack_arguments, ) from vyper.functions import ( dispatch_table, ) if isinstance(self.expr.func, ast.Name): function_name = self.expr.func.id if function_name in dispatch_table: return dispatch_table[function_name](self.expr, self.context) else: err_msg = "Not a top-level function: {}".format(function_name) if function_name in self.context.sigs['self']: err_msg += ". Did you mean self.{}?".format(function_name) raise StructureException(err_msg, self.expr) elif isinstance(self.expr.func, ast.Attribute) and isinstance(self.expr.func.value, ast.Name) and self.expr.func.value.id == "self": method_name = self.expr.func.attr if method_name not in self.context.sigs['self']: raise VariableDeclarationException("Function not declared yet (reminder: functions cannot " "call functions later in code than themselves): %s" % self.expr.func.attr) sig = self.context.sigs['self'][method_name] if self.context.is_constant and not sig.const: raise ConstancyViolationException( "May not call non-constant function '%s' within a constant function." % (method_name), getpos(self.expr) ) add_gas = self.context.sigs['self'][method_name].gas # gas of call inargs, inargsize = pack_arguments(sig, [Expr(arg, self.context).lll_node for arg in self.expr.args], self.context, pos=getpos(self.expr)) output_placeholder = self.context.new_placeholder(typ=sig.output_type) multi_arg = [] if isinstance(sig.output_type, BaseType): returner = output_placeholder elif isinstance(sig.output_type, ByteArrayType): returner = output_placeholder + 32 elif self.context.in_assignment and isinstance(sig.output_type, TupleType): returner = output_placeholder else: raise TypeMismatchException("Invalid output type: %r" % sig.output_type, self.expr) o = LLLnode.from_list(multi_arg + ['seq', ['assert', ['call', ['gas'], ['address'], 0, inargs, inargsize, output_placeholder, get_size_of_type(sig.output_type) * 32]], returner], typ=sig.output_type, location='memory', pos=getpos(self.expr), add_gas_estimate=add_gas, annotation='Internal Call: %s' % method_name) o.gas += sig.gas return o elif isinstance(self.expr.func, ast.Attribute) and isinstance(self.expr.func.value, ast.Call): contract_name = self.expr.func.value.func.id contract_address = Expr.parse_value_expr(self.expr.func.value.args[0], self.context) value, gas = self._get_external_contract_keywords() return external_contract_call(self.expr, self.context, contract_name, contract_address, True, pos=getpos(self.expr), value=value, gas=gas) elif isinstance(self.expr.func.value, ast.Attribute) and self.expr.func.value.attr in self.context.sigs: contract_name = self.expr.func.value.attr var = self.context.globals[self.expr.func.value.attr] contract_address = unwrap_location(LLLnode.from_list(var.pos, typ=var.typ, location='storage', pos=getpos(self.expr), annotation='self.' + self.expr.func.value.attr)) value, gas = self._get_external_contract_keywords() return external_contract_call(self.expr, self.context, contract_name, contract_address, True, pos=getpos(self.expr), value=value, gas=gas) elif isinstance(self.expr.func.value, ast.Attribute) and self.expr.func.value.attr in self.context.globals: contract_name = self.context.globals[self.expr.func.value.attr].typ.unit var = self.context.globals[self.expr.func.value.attr] contract_address = unwrap_location(LLLnode.from_list(var.pos, typ=var.typ, location='storage', pos=getpos(self.expr), annotation='self.' + self.expr.func.value.attr)) value, gas = self._get_external_contract_keywords() return external_contract_call(self.expr, self.context, contract_name, contract_address, var.modifiable, pos=getpos(self.expr), value=value, gas=gas) else: raise StructureException("Unsupported operator: %r" % ast.dump(self.expr), self.expr)
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(self.make_return_stmt(0, 0), typ=None, pos=getpos(self.stmt), valency=0) if not self.stmt.value: raise TypeMismatchException("Expecting to return a value", self.stmt) def zero_pad(bytez_placeholder, maxlen): zero_padder = LLLnode.from_list(['pass']) if maxlen > 0: zero_pad_i = self.context.new_placeholder(BaseType('uint256')) # Iterator used to zero pad memory. zero_padder = LLLnode.from_list( ['repeat', zero_pad_i, ['mload', bytez_placeholder], maxlen, ['seq', ['if', ['gt', ['mload', zero_pad_i], maxlen], 'break'], # stay within allocated bounds ['mstore8', ['add', ['add', 32, bytez_placeholder], ['mload', zero_pad_i]], 0]]], annotation="Zero pad" ) return zero_padder sub = Expr(self.stmt.value, self.context).lll_node self.context.increment_return_counter() # Returning a value (most common case) if isinstance(sub.typ, BaseType): if not isinstance(self.context.return_type, BaseType): raise TypeMismatchException("Trying to return base type %r, output expecting %r" % (sub.typ, self.context.return_type), self.stmt.value) sub = unwrap_location(sub) if not are_units_compatible(sub.typ, self.context.return_type): raise TypeMismatchException("Return type units mismatch %r %r" % (sub.typ, 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): 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], self.make_return_stmt(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')): return LLLnode.from_list(['seq', ['mstore', 0, sub], self.make_return_stmt(0, 32)], typ=None, pos=getpos(self.stmt), valency=0) else: raise TypeMismatchException("Unsupported type conversion: %r to %r" % (sub.typ, self.context.return_type), self.stmt.value) # Returning a byte array elif isinstance(sub.typ, ByteArrayType): if not isinstance(self.context.return_type, ByteArrayType): raise TypeMismatchException("Trying to return base type %r, output expecting %r" % (sub.typ, self.context.return_type), self.stmt.value) if sub.typ.maxlen > self.context.return_type.maxlen: raise TypeMismatchException("Cannot cast from greater max-length %d to shorter max-length %d" % (sub.typ.maxlen, self.context.return_type.maxlen), self.stmt.value) loop_memory_position = self.context.new_placeholder(typ=BaseType('uint256')) # loop memory has to be allocated first. len_placeholder = self.context.new_placeholder(typ=BaseType('uint256')) # len & bytez placeholder have to be declared after each other at all times. 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, sub.typ.maxlen), ['mstore', len_placeholder, 32], self.make_return_stmt(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("Invalid location: %s" % 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( "List return type %r does not match specified return type, expecting %r" % ( sub_base_type, ret_base_type ), self.stmt ) elif sub.location == "memory" and sub.value != "multi": return LLLnode.from_list(self.make_return_stmt(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, self.make_return_stmt(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): # TODO: VIP1019 raise TypeMismatchException("Returning structs not allowed yet, see VIP1019", self.stmt) # Returning a tuple. elif isinstance(sub.typ, TupleType): if not isinstance(self.context.return_type, TupleType): raise TypeMismatchException("Trying to return tuple type %r, output expecting %r" % (sub.typ, 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. {} != {}".format( type(sub_type), type(ret_x) ), self.stmt ) # Is from a call expression. if len(sub.args[0].args) > 0 and sub.args[0].args[0].value == 'call': # self-call to public. mem_pos = sub.args[0].args[-1] mem_size = get_size_of_type(sub.typ) * 32 return LLLnode.from_list(['return', mem_pos, mem_size], typ=sub.typ) elif (sub.annotation and 'Internal Call' in sub.annotation): mem_pos = sub.args[-1].value if sub.value == 'seq_unchecked' else sub.args[0].args[-1] mem_size = get_size_of_type(sub.typ) * 32 # Add zero padder if bytes are present in output. zero_padder = ['pass'] byte_arrays = [(i, x) for i, x in enumerate(sub.typ.members) if isinstance(x, ByteArrayType)] if byte_arrays: i, x = byte_arrays[-1] zero_padder = zero_pad(bytez_placeholder=['add', mem_pos, ['mload', mem_pos + i * 32]], maxlen=x.maxlen) return LLLnode.from_list( ['seq'] + [sub] + [zero_padder] + [self.make_return_stmt(mem_pos, mem_size) ], typ=sub.typ, pos=getpos(self.stmt), valency=0) subs = [] # Pre-allocate loop_memory_position if required for private function returning. loop_memory_position = self.context.new_placeholder(typ=BaseType('uint256')) if self.context.is_private else None # Allocate dynamic off set counter, to keep track of the total packed dynamic data size. dynamic_offset_counter_placeholder = self.context.new_placeholder(typ=BaseType('uint256')) dynamic_offset_counter = LLLnode( dynamic_offset_counter_placeholder, typ=None, annotation="dynamic_offset_counter" # dynamic offset position counter. ) new_sub = LLLnode.from_list( self.context.new_placeholder(typ=BaseType('uint256')), typ=self.context.return_type, location='memory', annotation='new_sub' ) keyz = list(range(len(sub.typ.members))) dynamic_offset_start = 32 * len(sub.args) # The static list of args end. left_token = LLLnode.from_list('_loc', typ=new_sub.typ, location="memory") def get_dynamic_offset_value(): # Get value of dynamic offset counter. return ['mload', dynamic_offset_counter] def increment_dynamic_offset(dynamic_spot): # Increment dyanmic offset counter in memory. return [ 'mstore', dynamic_offset_counter, ['add', ['add', ['ceil32', ['mload', dynamic_spot]], 32], ['mload', dynamic_offset_counter]] ] for i, typ in enumerate(keyz): arg = sub.args[i] variable_offset = LLLnode.from_list(['add', 32 * i, left_token], typ=arg.typ, annotation='variable_offset') if isinstance(arg.typ, ByteArrayType): # Store offset pointer value. subs.append(['mstore', variable_offset, get_dynamic_offset_value()]) # Store dynamic data, from offset pointer onwards. dynamic_spot = LLLnode.from_list(['add', left_token, get_dynamic_offset_value()], location="memory", typ=arg.typ, annotation='dynamic_spot') subs.append(make_setter(dynamic_spot, arg, location="memory", pos=getpos(self.stmt))) subs.append(increment_dynamic_offset(dynamic_spot)) elif isinstance(arg.typ, BaseType): subs.append(make_setter(variable_offset, arg, "memory", pos=getpos(self.stmt))) else: raise Exception("Can't return type %s as part of tuple", type(arg.typ)) setter = LLLnode.from_list( ['seq', ['mstore', dynamic_offset_counter, dynamic_offset_start], ['with', '_loc', new_sub, ['seq'] + subs]], typ=None ) return LLLnode.from_list( ['seq', setter, self.make_return_stmt(new_sub, get_dynamic_offset_value(), loop_memory_position)], typ=None, pos=getpos(self.stmt), valency=0 ) else: raise TypeMismatchException("Can only return base type!", self.stmt)
def pack_args_by_32(holder, maxlen, arg, typ, context, placeholder, dynamic_offset_counter=None, datamem_start=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): value = parse_expr(arg, context) value = base_type_conversion(value, value.typ, typ) holder.append( LLLnode.from_list(['mstore', placeholder, value], typ=typ, location='memory')) elif isinstance(typ, ByteArrayType): bytez = b'' source_expr = Expr(arg, context) 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)) for c in arg.s: if ord(c) >= 256: raise InvalidLiteralException( "Cannot insert special character %r into byte array" % c) bytez += bytes([ord(c)]) holder.append(source_expr.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_expr.lll_node) holder.append(copier) # Increment offset counter. increment_counter = LLLnode.from_list([ 'mstore', dynamic_offset_counter, [ 'add', [ 'add', ['mload', dynamic_offset_counter], ['ceil32', ['mload', dest_placeholder]] ], 32 ] ]) 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)) # 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 for offset in range(0, size): arg2 = LLLnode.from_list([ 'sload', ['add', ['sha3_32', Expr(arg, context).lll_node], offset] ], typ=typ) holder, maxlen = pack_args_by_32( holder, maxlen, arg2, typ, context, context.new_placeholder(BaseType(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) for i in range(0, size): offset = 32 * i arg2 = LLLnode.from_list(pos + offset, typ=typ, location='memory') holder, maxlen = pack_args_by_32( holder, maxlen, arg2, typ, context, context.new_placeholder(BaseType(32))) # is list literal. else: holder, maxlen = pack_args_by_32(holder, maxlen, arg.elts[0], typ, context, placeholder) for j, arg2 in enumerate(arg.elts[1:]): holder, maxlen = pack_args_by_32( holder, maxlen, arg2, typ, context, context.new_placeholder(BaseType(32))) return holder, maxlen
def attribute(self): # x.balance: balance of address x if self.expr.attr == 'balance': addr = Expr.parse_value_expr(self.expr.value, self.context) if not is_base_type(addr.typ, 'address'): raise TypeMismatchException( "Type mismatch: balance keyword expects an address as input", self.expr ) return LLLnode.from_list( ['balance', addr], typ=BaseType('uint256', {'wei': 1}), 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 not is_base_type(addr.typ, 'address'): raise TypeMismatchException( "Type mismatch: codesize keyword expects an address as input", self.expr, ) if self.expr.attr == 'codesize': eval_code = ['extcodesize', addr] output_type = 'int128' 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), ) # self.x: global attribute elif isinstance(self.expr.value, ast.Name) and self.expr.value.id == "self": if self.expr.attr not in self.context.globals: raise VariableDeclarationException( "Persistent variable undeclared: " + self.expr.attr, self.expr, ) var = self.context.globals[self.expr.attr] return LLLnode.from_list( var.pos, typ=var.typ, location='storage', pos=getpos(self.expr), annotation='self.' + self.expr.attr, ) # Reserved keywords elif isinstance(self.expr.value, ast.Name) and self.expr.value.id in ("msg", "block", "tx"): key = self.expr.value.id + "." + self.expr.attr if key == "msg.sender": if self.context.is_private: raise ParserException("msg.sender not allowed in private functions.", self.expr) return LLLnode.from_list(['caller'], typ='address', pos=getpos(self.expr)) elif key == "msg.value": if not self.context.is_payable: raise NonPayableViolationException( "Cannot use msg.value in a non-payable function", self.expr, ) return LLLnode.from_list( ['callvalue'], typ=BaseType('uint256', {'wei': 1}), 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', {'sec': 1}, True), 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.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)) else: raise ParserException("Unsupported keyword: " + key, self.expr) # Other variables else: sub = Expr.parse_variable_location(self.expr.value, self.context) # contract type if isinstance(sub.typ, ContractType): return sub if not isinstance(sub.typ, StructType): raise TypeMismatchException( "Type mismatch: member variable access not expected", self.expr.value, ) attrs = list(sub.typ.members.keys()) if self.expr.attr not in attrs: raise TypeMismatchException( "Member %s not found. Only the following available: %s" % ( self.expr.attr, " ".join(attrs) ), self.expr, ) return add_variable_offset(sub, self.expr.attr, pos=getpos(self.expr))
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))
def arithmetic(self): pre_alloc_left, left = self.arithmetic_get_reference(self.expr.left) pre_alloc_right, right = self.arithmetic_get_reference(self.expr.right) if not is_numeric_type(left.typ) or not is_numeric_type(right.typ): raise TypeMismatchException( "Unsupported types for arithmetic op: %r %r" % (left.typ, right.typ), self.expr, ) arithmetic_pair = {left.typ.typ, right.typ.typ} # Special Case: Simplify any literal to literal arithmetic at compile time. if left.typ.is_literal and right.typ.is_literal and \ isinstance(right.value, int) and isinstance(left.value, int): if isinstance(self.expr.op, ast.Add): val = left.value + right.value elif isinstance(self.expr.op, ast.Sub): val = left.value - right.value elif isinstance(self.expr.op, ast.Mult): val = left.value * right.value elif isinstance(self.expr.op, ast.Div): val = left.value // right.value elif isinstance(self.expr.op, ast.Mod): val = left.value % right.value elif isinstance(self.expr.op, ast.Pow): val = left.value ** right.value else: raise ParserException( 'Unsupported literal operator: %s' % str(type(self.expr.op)), self.expr, ) num = ast.Num(val) num.source_code = self.expr.source_code num.lineno = self.expr.lineno num.col_offset = self.expr.col_offset return Expr.parse_value_expr(num, self.context) # Special case with uint256 were int literal may be casted. if arithmetic_pair == {'uint256', 'int128'}: # Check right side literal. if right.typ.is_literal and SizeLimits.in_bounds('uint256', right.value): right = LLLnode.from_list( right.value, typ=BaseType('uint256', None, is_literal=True), pos=getpos(self.expr), ) # Check left side literal. elif left.typ.is_literal and SizeLimits.in_bounds('uint256', left.value): left = LLLnode.from_list( left.value, typ=BaseType('uint256', None, is_literal=True), pos=getpos(self.expr), ) # Only allow explicit conversions to occur. if left.typ.typ != right.typ.typ: raise TypeMismatchException( "Cannot implicitly convert {} to {}.".format(left.typ.typ, right.typ.typ), self.expr, ) ltyp, rtyp = left.typ.typ, right.typ.typ if isinstance(self.expr.op, (ast.Add, ast.Sub)): if left.typ.unit != right.typ.unit and left.typ.unit != {} and right.typ.unit != {}: raise TypeMismatchException( "Unit mismatch: %r %r" % (left.typ.unit, right.typ.unit), self.expr, ) if left.typ.positional and right.typ.positional and isinstance(self.expr.op, ast.Add): raise TypeMismatchException( "Cannot add two positional units!", self.expr, ) new_unit = left.typ.unit or right.typ.unit # xor, as subtracting two positionals gives a delta new_positional = left.typ.positional ^ right.typ.positional op = 'add' if isinstance(self.expr.op, ast.Add) else 'sub' if ltyp == 'uint256' and isinstance(self.expr.op, ast.Add): o = LLLnode.from_list([ 'seq', # Checks that: a + b >= a ['assert', ['ge', ['add', left, right], left]], ['add', left, right], ], typ=BaseType('uint256', new_unit, new_positional), pos=getpos(self.expr)) elif ltyp == 'uint256' and isinstance(self.expr.op, ast.Sub): o = LLLnode.from_list([ 'seq', # Checks that: a >= b ['assert', ['ge', left, right]], ['sub', left, right] ], typ=BaseType('uint256', new_unit, new_positional), pos=getpos(self.expr)) elif ltyp == rtyp: o = LLLnode.from_list( [op, left, right], typ=BaseType(ltyp, new_unit, new_positional), pos=getpos(self.expr), ) else: raise Exception("Unsupported Operation '%r(%r, %r)'" % (op, ltyp, rtyp)) elif isinstance(self.expr.op, ast.Mult): if left.typ.positional or right.typ.positional: raise TypeMismatchException("Cannot multiply positional values!", self.expr) new_unit = combine_units(left.typ.unit, right.typ.unit) if ltyp == rtyp == 'uint256': o = LLLnode.from_list([ 'if', ['eq', left, 0], [0], [ 'seq', ['assert', ['eq', ['div', ['mul', left, right], left], right]], ['mul', left, right] ], ], typ=BaseType('uint256', new_unit), pos=getpos(self.expr)) elif ltyp == rtyp == 'int128': o = LLLnode.from_list( ['mul', left, right], typ=BaseType('int128', new_unit), pos=getpos(self.expr), ) elif ltyp == rtyp == 'decimal': o = LLLnode.from_list([ 'with', 'r', right, [ 'with', 'l', left, [ 'with', 'ans', ['mul', 'l', 'r'], [ 'seq', [ 'assert', ['or', ['eq', ['sdiv', 'ans', 'l'], 'r'], ['iszero', 'l']] ], ['sdiv', 'ans', DECIMAL_DIVISOR], ], ], ], ], typ=BaseType('decimal', new_unit), pos=getpos(self.expr)) else: raise Exception("Unsupported Operation 'mul(%r, %r)'" % (ltyp, rtyp)) elif isinstance(self.expr.op, ast.Div): if left.typ.positional or right.typ.positional: raise TypeMismatchException("Cannot divide positional values!", self.expr) new_unit = combine_units(left.typ.unit, right.typ.unit, div=True) if ltyp == rtyp == 'uint256': o = LLLnode.from_list([ 'seq', # Checks that: b != 0 ['assert', right], ['div', left, right], ], typ=BaseType('uint256', new_unit), pos=getpos(self.expr)) elif ltyp == rtyp == 'int128': o = LLLnode.from_list( ['sdiv', left, ['clamp_nonzero', right]], typ=BaseType('int128', new_unit), pos=getpos(self.expr), ) elif ltyp == rtyp == 'decimal': o = LLLnode.from_list([ 'with', 'l', left, [ 'with', 'r', ['clamp_nonzero', right], [ 'sdiv', ['mul', 'l', DECIMAL_DIVISOR], 'r', ], ] ], typ=BaseType('decimal', new_unit), pos=getpos(self.expr)) else: raise Exception("Unsupported Operation 'div(%r, %r)'" % (ltyp, rtyp)) elif isinstance(self.expr.op, ast.Mod): if left.typ.positional or right.typ.positional: raise TypeMismatchException( "Cannot use positional values as modulus arguments!", self.expr, ) if not are_units_compatible(left.typ, right.typ) and not (left.typ.unit or right.typ.unit): # noqa: E501 raise TypeMismatchException("Modulus arguments must have same unit", self.expr) new_unit = left.typ.unit or right.typ.unit if ltyp == rtyp == 'uint256': o = LLLnode.from_list([ 'seq', ['assert', right], ['mod', left, right] ], typ=BaseType('uint256', new_unit), pos=getpos(self.expr)) elif ltyp == rtyp: o = LLLnode.from_list( ['smod', left, ['clamp_nonzero', right]], typ=BaseType(ltyp, new_unit), pos=getpos(self.expr), ) else: raise Exception("Unsupported Operation 'mod(%r, %r)'" % (ltyp, rtyp)) elif isinstance(self.expr.op, ast.Pow): if left.typ.positional or right.typ.positional: raise TypeMismatchException( "Cannot use positional values as exponential arguments!", self.expr, ) if right.typ.unit: raise TypeMismatchException( "Cannot use unit values as exponents", self.expr, ) if ltyp != 'int128' and ltyp != 'uint256' and isinstance(self.expr.right, ast.Name): raise TypeMismatchException( "Cannot use dynamic values as exponents, for unit base types", self.expr, ) if ltyp == rtyp == 'uint256': o = LLLnode.from_list([ 'seq', [ 'assert', [ 'or', ['or', ['eq', right, 1], ['iszero', right]], ['lt', left, ['exp', left, right]] ], ], ['exp', left, right], ], typ=BaseType('uint256'), pos=getpos(self.expr)) elif ltyp == rtyp == 'int128': new_unit = left.typ.unit if left.typ.unit and not isinstance(self.expr.right, ast.Name): new_unit = {left.typ.unit.copy().popitem()[0]: self.expr.right.n} o = LLLnode.from_list( ['exp', left, right], typ=BaseType('int128', new_unit), pos=getpos(self.expr), ) else: raise TypeMismatchException('Only whole number exponents are supported', self.expr) else: raise ParserException("Unsupported binary operator: %r" % self.expr.op, self.expr) p = ['seq'] if pre_alloc_left: p.append(pre_alloc_left) if pre_alloc_right: p.append(pre_alloc_right) if o.typ.typ == 'int128': p.append([ 'clamp', ['mload', MemoryPositions.MINNUM], o, ['mload', MemoryPositions.MAXNUM], ]) return LLLnode.from_list(p, typ=o.typ, pos=getpos(self.expr)) elif o.typ.typ == 'decimal': p.append([ 'clamp', ['mload', MemoryPositions.MINDECIMAL], o, ['mload', MemoryPositions.MAXDECIMAL], ]) return LLLnode.from_list(p, typ=o.typ, pos=getpos(self.expr)) if o.typ.typ == 'uint256': p.append(o) return LLLnode.from_list(p, typ=o.typ, pos=getpos(self.expr)) else: raise Exception("%r %r" % (o, o.typ))
def add_variable_offset(parent, key, pos, array_bounds_check=True): typ, location = parent.typ, parent.location if isinstance(typ, TupleLike): if isinstance(typ, StructType): if not isinstance(key, str): raise TypeMismatchException( f"Expecting a member variable access; cannot access element {key}", pos) if key not in typ.members: raise TypeMismatchException( f"Object does not have member variable {key}", pos) subtype = typ.members[key] attrs = list(typ.tuple_keys()) if key not in attrs: raise TypeMismatchException( f"Member {key} not found. Only the following available: " + " ".join(attrs), pos) index = attrs.index(key) annotation = key else: if not isinstance(key, int): raise TypeMismatchException( f"Expecting a static index; cannot access element {key}", pos) attrs = list(range(len(typ.members))) index = key annotation = None if location == 'storage': return LLLnode.from_list( [ 'add', ['sha3_32', parent], LLLnode.from_list(index, annotation=annotation) ], typ=subtype, location='storage', ) elif location == 'storage_prehashed': return LLLnode.from_list( [ 'add', parent, LLLnode.from_list(index, annotation=annotation) ], typ=subtype, location='storage', ) elif location in ('calldata', 'memory'): offset = 0 for i in range(index): offset += 32 * get_size_of_type(typ.members[attrs[i]]) return LLLnode.from_list(['add', offset, parent], typ=typ.members[key], location=location, annotation=annotation) else: raise TypeMismatchException( "Not expecting a member variable access", pos) elif isinstance(typ, MappingType): if isinstance(key.typ, ByteArrayLike): if not isinstance(typ.keytype, ByteArrayLike) or ( typ.keytype.maxlen < key.typ.maxlen): raise TypeMismatchException( "Mapping keys of bytes cannot be cast, use exact same bytes type of: " f"{str(typ.keytype)}", pos, ) subtype = typ.valuetype if len(key.args[0].args) >= 3: # handle bytes literal. sub = LLLnode.from_list([ 'seq', key, [ 'sha3', ['add', key.args[0].args[-1], 32], ['mload', key.args[0].args[-1]] ] ]) else: sub = LLLnode.from_list([ 'sha3', ['add', key.args[0].value, 32], ['mload', key.args[0].value] ]) else: subtype = typ.valuetype sub = base_type_conversion(key, key.typ, typ.keytype, pos=pos) if location == 'storage': return LLLnode.from_list(['sha3_64', parent, sub], typ=subtype, location='storage') elif location in ('memory', 'calldata'): raise TypeMismatchException( "Can only have fixed-side arrays in memory, not mappings", pos) elif isinstance(typ, ListType): subtype = typ.subtype k = unwrap_location(key) if not is_base_type(key.typ, ('int128', 'uint256')): raise TypeMismatchException( f'Invalid type for array index: {key.typ}', pos) if not array_bounds_check: sub = k elif key.typ.is_literal: # note: BaseType always has is_literal attr # perform the check at compile time and elide the runtime check. if key.value < 0 or key.value >= typ.count: raise ArrayIndexException( 'Array index determined to be out of bounds. ' f'Index is {key.value} but array size is {typ.count}', pos) sub = k else: # this works, even for int128. for int128, since two's-complement # is used, if the index is negative, (unsigned) LT will interpret # it as a very large number, larger than any practical value for # an array index, and the clamp will throw an error. sub = ['uclamplt', k, typ.count] if location == 'storage': return LLLnode.from_list(['add', ['sha3_32', parent], sub], typ=subtype, location='storage', pos=pos) elif location == 'storage_prehashed': return LLLnode.from_list(['add', parent, sub], typ=subtype, location='storage', pos=pos) elif location in ('calldata', 'memory'): offset = 32 * get_size_of_type(subtype) return LLLnode.from_list(['add', ['mul', offset, sub], parent], typ=subtype, location=location, pos=pos) else: raise TypeMismatchException("Not expecting an array access ", pos) else: raise TypeMismatchException( f"Cannot access the child of a constant variable! {typ}", pos)
def build_in_comparator(self): from vyper.parser.parser import make_setter left = Expr(self.expr.left, self.context).lll_node right = Expr(self.expr.comparators[0], self.context).lll_node if left.typ != right.typ.subtype: raise TypeMismatchException( "%s cannot be in a list of %s" % (left.typ, right.typ.subtype), ) result_placeholder = self.context.new_placeholder(BaseType('bool')) setter = [] # Load nth item from list in memory. if right.value == 'multi': # Copy literal to memory to be compared. tmp_list = LLLnode.from_list( obj=self.context.new_placeholder(ListType(right.typ.subtype, right.typ.count)), typ=ListType(right.typ.subtype, right.typ.count), location='memory' ) setter = make_setter(tmp_list, right, 'memory', pos=getpos(self.expr)) load_i_from_list = [ 'mload', ['add', tmp_list, ['mul', 32, ['mload', MemoryPositions.FREE_LOOP_INDEX]]], ] elif right.location == "storage": load_i_from_list = [ 'sload', ['add', ['sha3_32', right], ['mload', MemoryPositions.FREE_LOOP_INDEX]], ] else: load_i_from_list = [ 'mload', ['add', right, ['mul', 32, ['mload', MemoryPositions.FREE_LOOP_INDEX]]], ] # Condition repeat loop has to break on. break_loop_condition = [ 'if', ['eq', unwrap_location(left), load_i_from_list], ['seq', ['mstore', '_result', 1], # store true. 'break'] ] # Repeat loop to loop-compare each item in the list. for_loop_sequence = [ ['mstore', result_placeholder, 0], ['with', '_result', result_placeholder, [ 'repeat', MemoryPositions.FREE_LOOP_INDEX, 0, right.typ.count, break_loop_condition, ]], ['mload', result_placeholder] ] # Save list to memory, so one can iterate over it, # used when literal was created with tmp_list. if setter: compare_sequence = ['seq', setter] + for_loop_sequence else: compare_sequence = ['seq'] + for_loop_sequence # Compare the result of the repeat loop to 1, to know if a match was found. o = LLLnode.from_list([ 'eq', 1, compare_sequence], typ='bool', annotation="in comporator" ) return o
def make_setter(left, right, location, pos, in_function_call=False): # Basic types if isinstance(left.typ, BaseType): right = base_type_conversion( right, right.typ, left.typ, pos, in_function_call=in_function_call, ) # TODO this overlaps a type check in parser.stmt.Stmt._check_valid_assign # and should be examined during a refactor (@iamdefinitelyahuman) if 'int' in left.typ.typ and isinstance(right.value, int): if not SizeLimits.in_bounds(left.typ.typ, right.value): raise InvalidLiteralException( f"Number out of range for {left.typ}: {right.value}", pos) if location == 'storage': return LLLnode.from_list(['sstore', left, right], typ=None) elif location == 'memory': return LLLnode.from_list(['mstore', left, right], typ=None) # Byte arrays elif isinstance(left.typ, ByteArrayLike): return make_byte_array_copier(left, right, pos) # Can't copy mappings elif isinstance(left.typ, MappingType): raise TypeMismatchException( "Cannot copy mappings; can only copy individual elements", pos) # Arrays elif isinstance(left.typ, ListType): # Cannot do something like [a, b, c] = [1, 2, 3] if left.value == "multi": raise Exception("Target of set statement must be a single item") if not isinstance(right.typ, (ListType, NullType)): raise TypeMismatchException( f"Setter type mismatch: left side is array, right side is {right.typ}", pos) left_token = LLLnode.from_list('_L', typ=left.typ, location=left.location) if left.location == "storage": left = LLLnode.from_list(['sha3_32', left], typ=left.typ, location="storage_prehashed") left_token.location = "storage_prehashed" # Type checks if not isinstance(right.typ, NullType): if not isinstance(right.typ, ListType): raise TypeMismatchException( "Left side is array, right side is not", pos) if left.typ.count != right.typ.count: raise TypeMismatchException("Mismatched number of elements", pos) # If the right side is a literal if right.value == "multi": if len(right.args) != left.typ.count: raise TypeMismatchException("Mismatched number of elements", pos) subs = [] for i in range(left.typ.count): subs.append( make_setter(add_variable_offset( left_token, LLLnode.from_list(i, typ='int128'), pos=pos, array_bounds_check=False, ), right.args[i], location, pos=pos)) return LLLnode.from_list(['with', '_L', left, ['seq'] + subs], typ=None) # If the right side is a null # CC 20190619 probably not needed as of #1106 elif isinstance(right.typ, NullType): subs = [] for i in range(left.typ.count): subs.append( make_setter(add_variable_offset( left_token, LLLnode.from_list(i, typ='int128'), pos=pos, array_bounds_check=False, ), LLLnode.from_list(None, typ=NullType()), location, pos=pos)) return LLLnode.from_list(['with', '_L', left, ['seq'] + subs], typ=None) # If the right side is a variable else: right_token = LLLnode.from_list('_R', typ=right.typ, location=right.location) subs = [] for i in range(left.typ.count): subs.append( make_setter(add_variable_offset( left_token, LLLnode.from_list(i, typ='int128'), pos=pos, array_bounds_check=False, ), add_variable_offset( right_token, LLLnode.from_list(i, typ='int128'), pos=pos, array_bounds_check=False, ), location, pos=pos)) return LLLnode.from_list( ['with', '_L', left, ['with', '_R', right, ['seq'] + subs]], typ=None) # Structs elif isinstance(left.typ, TupleLike): if left.value == "multi" and isinstance(left.typ, StructType): raise Exception("Target of set statement must be a single item") if not isinstance(right.typ, NullType): if not isinstance(right.typ, left.typ.__class__): raise TypeMismatchException( f"Setter type mismatch: left side is {left.typ}, right side is {right.typ}", pos, ) if isinstance(left.typ, StructType): for k in right.args: if k.value is None: raise InvalidLiteralException( 'Setting struct value to None is not allowed, use a default value.', pos, ) for k in left.typ.members: if k not in right.typ.members: raise TypeMismatchException( f"Keys don't match for structs, missing {k}", pos, ) for k in right.typ.members: if k not in left.typ.members: raise TypeMismatchException( f"Keys don't match for structs, extra {k}", pos, ) if left.typ.name != right.typ.name: raise TypeMismatchException( f"Expected {left.typ}, got {right.typ}", pos) else: if len(left.typ.members) != len(right.typ.members): raise TypeMismatchException( "Tuple lengths don't match, " f"{len(left.typ.members)} vs {len(right.typ.members)}", pos, ) left_token = LLLnode.from_list('_L', typ=left.typ, location=left.location) if left.location == "storage": left = LLLnode.from_list(['sha3_32', left], typ=left.typ, location="storage_prehashed") left_token.location = "storage_prehashed" keyz = left.typ.tuple_keys() # If the left side is a literal if left.value == 'multi': locations = [arg.location for arg in left.args] else: locations = [location for _ in keyz] # If the right side is a literal if right.value == "multi": if len(right.args) != len(keyz): raise TypeMismatchException("Mismatched number of elements", pos) # get the RHS arguments into a dict because # they are not guaranteed to be in the same order # the LHS keys. right_args = dict(zip(right.typ.tuple_keys(), right.args)) subs = [] for (key, loc) in zip(keyz, locations): subs.append( make_setter( add_variable_offset(left_token, key, pos=pos), right_args[key], loc, pos=pos, )) return LLLnode.from_list(['with', '_L', left, ['seq'] + subs], typ=None) # If the right side is a null elif isinstance(right.typ, NullType): subs = [] for typ, loc in zip(keyz, locations): subs.append( make_setter( add_variable_offset(left_token, typ, pos=pos), LLLnode.from_list(None, typ=NullType()), loc, pos=pos, )) return LLLnode.from_list(['with', '_L', left, ['seq'] + subs], typ=None) # If tuple assign. elif isinstance(left.typ, TupleType) and isinstance( right.typ, TupleType): subs = [] static_offset_counter = 0 zipped_components = zip(left.args, right.typ.members, locations) for var_arg in left.args: if var_arg.location == 'calldata': raise ConstancyViolationException( f"Cannot modify function argument: {var_arg.annotation}", pos) for left_arg, right_arg, loc in zipped_components: if isinstance(right_arg, ByteArrayLike): RType = ByteArrayType if isinstance( right_arg, ByteArrayType) else StringType offset = LLLnode.from_list([ 'add', '_R', ['mload', ['add', '_R', static_offset_counter]] ], typ=RType(right_arg.maxlen), location='memory', pos=pos) static_offset_counter += 32 else: offset = LLLnode.from_list( ['mload', ['add', '_R', static_offset_counter]], typ=right_arg.typ, pos=pos, ) static_offset_counter += get_size_of_type(right_arg) * 32 subs.append(make_setter(left_arg, offset, loc, pos=pos)) return LLLnode.from_list( ['with', '_R', right, ['seq'] + subs], typ=None, annotation='Tuple assignment', ) # If the right side is a variable else: subs = [] right_token = LLLnode.from_list('_R', typ=right.typ, location=right.location) for typ, loc in zip(keyz, locations): subs.append( make_setter(add_variable_offset(left_token, typ, pos=pos), add_variable_offset(right_token, typ, pos=pos), loc, pos=pos)) return LLLnode.from_list( ['with', '_L', left, ['with', '_R', right, ['seq'] + subs]], typ=None, ) else: raise Exception("Invalid type for setters")
def compare(self): left = Expr.parse_value_expr(self.expr.left, self.context) right = Expr.parse_value_expr(self.expr.comparators[0], self.context) if isinstance(right.typ, NullType): raise InvalidLiteralException( 'Comparison to None is not allowed, compare against a default value.', self.expr, ) if isinstance(left.typ, ByteArrayType) and isinstance(right.typ, ByteArrayType): if left.typ.maxlen != right.typ.maxlen: raise TypeMismatchException('Can only compare bytes of the same length', self.expr) if left.typ.maxlen > 32 or right.typ.maxlen > 32: raise ParserException( 'Can only compare bytes of length shorter than 32 bytes', self.expr, ) elif isinstance(self.expr.ops[0], ast.In) and isinstance(right.typ, ListType): if left.typ != right.typ.subtype: raise TypeMismatchException( "Can't use IN comparison with different types!", self.expr, ) return self.build_in_comparator() else: if not are_units_compatible(left.typ, right.typ) and not are_units_compatible(right.typ, left.typ): # noqa: E501 raise TypeMismatchException("Can't compare values with different units!", self.expr) if len(self.expr.ops) != 1: raise StructureException( "Cannot have a comparison with more than two elements", self.expr, ) if isinstance(self.expr.ops[0], ast.Gt): op = 'sgt' elif isinstance(self.expr.ops[0], ast.GtE): op = 'sge' elif isinstance(self.expr.ops[0], ast.LtE): op = 'sle' elif isinstance(self.expr.ops[0], ast.Lt): op = 'slt' elif isinstance(self.expr.ops[0], ast.Eq): op = 'eq' elif isinstance(self.expr.ops[0], ast.NotEq): op = 'ne' else: raise Exception("Unsupported comparison operator") # Compare (limited to 32) byte arrays. if isinstance(left.typ, ByteArrayType) and isinstance(left.typ, ByteArrayType): left = Expr(self.expr.left, self.context).lll_node right = Expr(self.expr.comparators[0], self.context).lll_node def load_bytearray(side): if side.location == 'memory': return ['mload', ['add', 32, side]] elif side.location == 'storage': return ['sload', ['add', 1, ['sha3_32', side]]] return LLLnode.from_list( [op, load_bytearray(left), load_bytearray(right)], typ='bool', pos=getpos(self.expr), ) # Compare other types. if not is_numeric_type(left.typ) or not is_numeric_type(right.typ): if op not in ('eq', 'ne'): raise TypeMismatchException("Invalid type for comparison op", self.expr) left_type, right_type = left.typ.typ, right.typ.typ # Special Case: comparison of a literal integer. If in valid range allow it to be compared. if {left_type, right_type} == {'int128', 'uint256'} and {left.typ.is_literal, right.typ.is_literal} == {True, False}: # noqa: E501 comparison_allowed = False if left.typ.is_literal and SizeLimits.in_bounds(right_type, left.value): comparison_allowed = True elif right.typ.is_literal and SizeLimits.in_bounds(left_type, right.value): comparison_allowed = True op = self._signed_to_unsigned_comparision_op(op) if comparison_allowed: return LLLnode.from_list([op, left, right], typ='bool', pos=getpos(self.expr)) elif {left_type, right_type} == {'uint256', 'uint256'}: op = self._signed_to_unsigned_comparision_op(op) elif (left_type in ('decimal', 'int128') or right_type in ('decimal', 'int128')) and left_type != right_type: # noqa: E501 raise TypeMismatchException( 'Implicit conversion from {} to {} disallowed, please convert.'.format( left_type, right_type, ), self.expr, ) if left_type == right_type: return LLLnode.from_list([op, left, right], typ='bool', pos=getpos(self.expr)) else: raise TypeMismatchException( "Unsupported types for comparison: %r %r" % (left_type, right_type), self.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('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 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 add_variable_offset(parent, key): typ, location = parent.typ, parent.location if isinstance(typ, (StructType, TupleType)): if isinstance(typ, StructType): if not isinstance(key, str): raise TypeMismatchException( "Expecting a member variable access; cannot access element %r" % key) if key not in typ.members: raise TypeMismatchException( "Object does not have member variable %s" % key) subtype = typ.members[key] attrs = sorted(typ.members.keys()) if key not in attrs: raise TypeMismatchException( "Member %s not found. Only the following available: %s" % (key, " ".join(attrs))) index = attrs.index(key) annotation = key else: if not isinstance(key, int): raise TypeMismatchException( "Expecting a static index; cannot access element %r" % key) attrs = list(range(len(typ.members))) index = key annotation = None if location == 'storage': return LLLnode.from_list([ 'add', ['sha3_32', parent], LLLnode.from_list(index, annotation=annotation) ], typ=subtype, location='storage') elif location == 'storage_prehashed': return LLLnode.from_list([ 'add', parent, LLLnode.from_list(index, annotation=annotation) ], typ=subtype, location='storage') elif location == 'memory': offset = 0 for i in range(index): offset += 32 * get_size_of_type(typ.members[attrs[i]]) return LLLnode.from_list(['add', offset, parent], typ=typ.members[key], location='memory', annotation=annotation) else: raise TypeMismatchException( "Not expecting a member variable access") elif isinstance(typ, (ListType, MappingType)): if isinstance(typ, ListType): subtype = typ.subtype sub = [ 'uclamplt', base_type_conversion(key, key.typ, BaseType('int128')), typ.count ] elif isinstance(typ, MappingType) and isinstance( key.typ, ByteArrayType): if not isinstance(typ.keytype, ByteArrayType) or ( typ.keytype.maxlen < key.typ.maxlen): raise TypeMismatchException( 'Mapping keys of bytes cannot be cast, use exact same bytes type of: %s', str(typ.keytype)) subtype = typ.valuetype if len(key.args[0].args) == 3: # handle bytes literal. sub = LLLnode.from_list([ 'seq', key, [ 'sha3', ['add', key.args[0].args[-1], 32], ceil32(key.typ.maxlen) ] ]) else: sub = LLLnode.from_list([ 'sha3', ['add', key.args[0].value, 32], ceil32(key.typ.maxlen) ]) else: subtype = typ.valuetype sub = base_type_conversion(key, key.typ, typ.keytype) if location == 'storage': return LLLnode.from_list(['add', ['sha3_32', parent], sub], typ=subtype, location='storage') elif location == 'storage_prehashed': return LLLnode.from_list(['add', parent, sub], typ=subtype, location='storage') elif location == 'memory': if isinstance(typ, MappingType): raise TypeMismatchException( "Can only have fixed-side arrays in memory, not mappings") offset = 32 * get_size_of_type(subtype) return LLLnode.from_list(['add', ['mul', offset, sub], parent], typ=subtype, location='memory') else: raise TypeMismatchException("Not expecting an array access ") else: raise TypeMismatchException( "Cannot access the child of a constant variable! %r" % typ)
def _RLPlist(expr, args, kwargs, context): # Second argument must be a list of types if not isinstance(args[1], ast.List): raise TypeMismatchException( "Expecting list of types for second argument", args[1]) if len(args[1].elts) == 0: raise TypeMismatchException("RLP list must have at least one item", expr) if len(args[1].elts) > 32: raise TypeMismatchException("RLP list must have at most 32 items", expr) # Get the output format _format = [] for arg in args[1].elts: if isinstance(arg, ast.Name) and arg.id == "bytes": subtyp = ByteArrayType(args[0].typ.maxlen) else: subtyp = parse_type(arg, 'memory') if not isinstance(subtyp, BaseType): raise TypeMismatchException( "RLP lists only accept BaseTypes and byte arrays", arg) if not is_base_type( subtyp, ('int128', 'uint256', 'bytes32', 'address', 'bool')): raise TypeMismatchException( "Unsupported base type: %s" % subtyp.typ, arg) _format.append(subtyp) output_type = TupleType(_format) output_placeholder_type = ByteArrayType( (2 * len(_format) + 1 + get_size_of_type(output_type)) * 32) output_placeholder = context.new_placeholder(output_placeholder_type) output_node = LLLnode.from_list(output_placeholder, typ=output_placeholder_type, location='memory') # Create a decoder for each element in the tuple decoder = [] for i, typ in enumerate(_format): # Decoder for bytes32 if is_base_type(typ, 'bytes32'): decoder.append( LLLnode.from_list( [ 'seq', [ 'assert', [ 'eq', [ 'mload', [ 'add', output_node, [ 'mload', ['add', output_node, 32 * i] ] ] ], 32 ] ], [ 'mload', [ 'add', 32, [ 'add', output_node, ['mload', ['add', output_node, 32 * i]] ] ] ] ], typ, annotation='getting and checking bytes32 item')) # Decoder for address elif is_base_type(typ, 'address'): decoder.append( LLLnode.from_list( [ 'seq', [ 'assert', [ 'eq', [ 'mload', [ 'add', output_node, [ 'mload', ['add', output_node, 32 * i] ] ] ], 20 ] ], [ 'mod', [ 'mload', [ 'add', 20, [ 'add', output_node, [ 'mload', ['add', output_node, 32 * i] ] ] ] ], ['mload', MemoryPositions.ADDRSIZE] ] ], typ, annotation='getting and checking address item')) # Decoder for bytes elif isinstance(typ, ByteArrayType): decoder.append( LLLnode.from_list([ 'add', output_node, ['mload', ['add', output_node, 32 * i]] ], typ, location='memory', annotation='getting byte array')) # Decoder for num and uint256 elif is_base_type(typ, ('int128', 'uint256')): bytez = LLLnode.from_list( ['add', output_node, ['mload', ['add', output_node, 32 * i]]], typ, location='memory', annotation='getting and checking %s' % typ.typ) decoder.append(byte_array_to_num(bytez, expr, typ.typ)) # Decoder for bools elif is_base_type(typ, ('bool')): # This is basically a really clever way to test for a length-prefixed one or zero. We take the 32 bytes # starting one byte *after* the start of the length declaration; this includes the last 31 bytes of the # length and the first byte of the value. 0 corresponds to length 0, first byte 0, and 257 corresponds # to length 1, first byte \x01 decoder.append( LLLnode.from_list([ 'with', '_ans', [ 'mload', [ 'add', 1, [ 'add', output_node, ['mload', ['add', output_node, 32 * i]] ] ] ], [ 'seq', [ 'assert', ['or', ['eq', '_ans', 0], ['eq', '_ans', 257]] ], ['div', '_ans', 257] ] ], typ, annotation='getting and checking bool')) else: # Should never reach because of top level base level check. raise Exception("Type not yet supported") # pragma: no cover # Copy the input data to memory if args[0].location == "memory": variable_pointer = args[0] elif args[0].location == "storage": placeholder = context.new_placeholder(args[0].typ) placeholder_node = LLLnode.from_list(placeholder, typ=args[0].typ, location='memory') copier = make_byte_array_copier( placeholder_node, LLLnode.from_list('_ptr', typ=args[0].typ, location=args[0].location)) variable_pointer = [ 'with', '_ptr', args[0], ['seq', copier, placeholder_node] ] else: # Should never reach because of top level base level check. raise Exception("Location not yet supported") # pragma: no cover # Decode the input data initial_setter = LLLnode.from_list([ 'seq', [ 'with', '_sub', variable_pointer, [ 'pop', [ 'call', 1500 + 400 * len(_format) + 10 * len(args), LLLnode.from_list(RLP_DECODER_ADDRESS, annotation='RLP decoder'), 0, ['add', '_sub', 32], ['mload', '_sub'], output_node, 64 * len(_format) + 32 + 32 * get_size_of_type(output_type) ] ] ], ['assert', ['eq', ['mload', output_node], 32 * len(_format) + 32]] ], typ=None) # Shove the input data decoder in front of the first variable decoder decoder[0] = LLLnode.from_list(['seq', initial_setter, decoder[0]], typ=decoder[0].typ, location=decoder[0].location) return LLLnode.from_list(["multi"] + decoder, typ=output_type, location='memory', pos=getpos(expr))
def pack_arguments(signature, args, context): placeholder_typ = ByteArrayType( maxlen=sum([get_size_of_type(arg.typ) for arg in signature.args]) * 32 + 32) placeholder = context.new_placeholder(placeholder_typ) setters = [['mstore', placeholder, signature.method_id]] needpos = False staticarray_offset = 0 expected_arg_count = len(signature.args) actual_arg_count = len(args) if actual_arg_count != expected_arg_count: raise StructureException( "Wrong number of args for: %s (%s args, expected %s)" % (signature.name, actual_arg_count, expected_arg_count)) for i, (arg, typ) in enumerate(zip(args, [arg.typ for arg in signature.args])): if isinstance(typ, BaseType): setters.append( make_setter( LLLnode.from_list(placeholder + staticarray_offset + 32 + i * 32, typ=typ), arg, 'memory')) elif isinstance(typ, ByteArrayType): setters.append([ 'mstore', placeholder + staticarray_offset + 32 + i * 32, '_poz' ]) arg_copy = LLLnode.from_list('_s', typ=arg.typ, location=arg.location) target = LLLnode.from_list(['add', placeholder + 32, '_poz'], typ=typ, location='memory') setters.append([ 'with', '_s', arg, [ 'seq', make_byte_array_copier(target, arg_copy), [ 'set', '_poz', ['add', 32, ['add', '_poz', get_length(arg_copy)]] ] ] ]) needpos = True elif isinstance(typ, ListType): target = LLLnode.from_list( [placeholder + 32 + staticarray_offset + i * 32], typ=typ, location='memory') setters.append(make_setter(target, arg, 'memory')) staticarray_offset += 32 * (typ.count - 1) else: raise TypeMismatchException("Cannot pack argument of type %r" % typ) if needpos: return LLLnode.from_list(['with', '_poz', len(args) * 32 + staticarray_offset, ['seq'] + setters + [placeholder + 28]], typ=placeholder_typ, location='memory'), \ placeholder_typ.maxlen - 28 else: return LLLnode.from_list(['seq'] + setters + [placeholder + 28], typ=placeholder_typ, location='memory'), \ placeholder_typ.maxlen - 28
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, ), 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, ), 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 make_setter(left, right, location, pos=None): # Basic types if isinstance(left.typ, BaseType): right = base_type_conversion(right, right.typ, left.typ, pos) if location == 'storage': return LLLnode.from_list(['sstore', left, right], typ=None) elif location == 'memory': return LLLnode.from_list(['mstore', left, right], typ=None) # Byte arrays elif isinstance(left.typ, ByteArrayType): return make_byte_array_copier(left, right) # Can't copy mappings elif isinstance(left.typ, MappingType): raise TypeMismatchException( "Cannot copy mappings; can only copy individual elements", pos) # Arrays elif isinstance(left.typ, ListType): # Cannot do something like [a, b, c] = [1, 2, 3] if left.value == "multi": raise Exception("Target of set statement must be a single item") if not isinstance(right.typ, (ListType, NullType)): raise TypeMismatchException( "Setter type mismatch: left side is array, right side is %r" % right.typ, pos) left_token = LLLnode.from_list('_L', typ=left.typ, location=left.location) if left.location == "storage": left = LLLnode.from_list(['sha3_32', left], typ=left.typ, location="storage_prehashed") left_token.location = "storage_prehashed" # Type checks if not isinstance(right.typ, NullType): if not isinstance(right.typ, ListType): raise TypeMismatchException( "Left side is array, right side is not", pos) if left.typ.count != right.typ.count: raise TypeMismatchException("Mismatched number of elements", pos) # If the right side is a literal if right.value == "multi": if len(right.args) != left.typ.count: raise TypeMismatchException("Mismatched number of elements", pos) subs = [] for i in range(left.typ.count): subs.append( make_setter(add_variable_offset( left_token, LLLnode.from_list(i, typ='num')), right.args[i], location, pos=pos)) return LLLnode.from_list(['with', '_L', left, ['seq'] + subs], typ=None) # If the right side is a null elif isinstance(right.typ, NullType): subs = [] for i in range(left.typ.count): subs.append( make_setter(add_variable_offset( left_token, LLLnode.from_list(i, typ='num')), LLLnode.from_list(None, typ=NullType()), location, pos=pos)) return LLLnode.from_list(['with', '_L', left, ['seq'] + subs], typ=None) # If the right side is a variable else: right_token = LLLnode.from_list('_R', typ=right.typ, location=right.location) subs = [] for i in range(left.typ.count): subs.append( make_setter( add_variable_offset(left_token, LLLnode.from_list(i, typ='num')), add_variable_offset(right_token, LLLnode.from_list(i, typ='num')), location, pos=pos)) return LLLnode.from_list( ['with', '_L', left, ['with', '_R', right, ['seq'] + subs]], typ=None) # Structs elif isinstance(left.typ, (StructType, TupleType)): if left.value == "multi": raise Exception("Target of set statement must be a single item") if not isinstance(right.typ, NullType): if not isinstance(right.typ, left.typ.__class__): raise TypeMismatchException( "Setter type mismatch: left side is %r, right side is %r" % (left.typ, right.typ), pos) if isinstance(left.typ, StructType): for k in left.typ.members: if k not in right.typ.members: raise TypeMismatchException( "Keys don't match for structs, missing %s" % k, pos) for k in right.typ.members: if k not in left.typ.members: raise TypeMismatchException( "Keys don't match for structs, extra %s" % k, pos) else: if len(left.typ.members) != len(right.typ.members): raise TypeMismatchException( "Tuple lengths don't match, %d vs %d" % (len(left.typ.members), len(right.typ.members)), pos) left_token = LLLnode.from_list('_L', typ=left.typ, location=left.location) if left.location == "storage": left = LLLnode.from_list(['sha3_32', left], typ=left.typ, location="storage_prehashed") left_token.location = "storage_prehashed" if isinstance(left.typ, StructType): keyz = sorted(list(left.typ.members.keys())) else: keyz = list(range(len(left.typ.members))) # If the right side is a literal if right.value == "multi": if len(right.args) != len(keyz): raise TypeMismatchException("Mismatched number of elements", pos) subs = [] for i, typ in enumerate(keyz): subs.append( make_setter(add_variable_offset(left_token, typ), right.args[i], location)) return LLLnode.from_list(['with', '_L', left, ['seq'] + subs], typ=None) # If the right side is a null elif isinstance(right.typ, NullType): subs = [] for typ in keyz: subs.append( make_setter(add_variable_offset(left_token, typ), LLLnode.from_list(None, typ=NullType()), location, pos=pos)) return LLLnode.from_list(['with', '_L', left, ['seq'] + subs], typ=None) # If the right side is a variable else: right_token = LLLnode.from_list('_R', typ=right.typ, location=right.location) subs = [] for typ in keyz: subs.append( make_setter(add_variable_offset(left_token, typ), add_variable_offset(right_token, typ), location, pos=pos)) return LLLnode.from_list( ['with', '_L', left, ['with', '_R', right, ['seq'] + subs]], typ=None) else: raise Exception("Invalid type for setters")
def check_list_type_match(provided): # Check list types match. if provided != typ: raise TypeMismatchException( f"Log list type '{provided}' does not match provided, expected '{typ}'" )
def make_setter(left, right, location, pos, in_function_call=False): # Basic types if isinstance(left.typ, BaseType): right = base_type_conversion( right, right.typ, left.typ, pos, in_function_call=in_function_call, ) if location == 'storage': return LLLnode.from_list(['sstore', left, right], typ=None) elif location == 'memory': return LLLnode.from_list(['mstore', left, right], typ=None) # Byte arrays elif isinstance(left.typ, ByteArrayLike): return make_byte_array_copier(left, right, pos) # Can't copy mappings elif isinstance(left.typ, MappingType): raise TypeMismatchException( "Cannot copy mappings; can only copy individual elements", pos) # Arrays elif isinstance(left.typ, ListType): # Cannot do something like [a, b, c] = [1, 2, 3] if left.value == "multi": raise Exception("Target of set statement must be a single item") if not isinstance(right.typ, (ListType, NullType)): raise TypeMismatchException( "Setter type mismatch: left side is array, right side is %r" % right.typ, pos) left_token = LLLnode.from_list('_L', typ=left.typ, location=left.location) if left.location == "storage": left = LLLnode.from_list(['sha3_32', left], typ=left.typ, location="storage_prehashed") left_token.location = "storage_prehashed" # Type checks if not isinstance(right.typ, NullType): if not isinstance(right.typ, ListType): raise TypeMismatchException( "Left side is array, right side is not", pos) if left.typ.count != right.typ.count: raise TypeMismatchException("Mismatched number of elements", pos) # If the right side is a literal if right.value == "multi": if len(right.args) != left.typ.count: raise TypeMismatchException("Mismatched number of elements", pos) subs = [] for i in range(left.typ.count): subs.append( make_setter(add_variable_offset( left_token, LLLnode.from_list(i, typ='int128'), pos=pos, ), right.args[i], location, pos=pos)) return LLLnode.from_list(['with', '_L', left, ['seq'] + subs], typ=None) # If the right side is a null elif isinstance(right.typ, NullType): subs = [] for i in range(left.typ.count): subs.append( make_setter(add_variable_offset( left_token, LLLnode.from_list(i, typ='int128'), pos=pos, ), LLLnode.from_list(None, typ=NullType()), location, pos=pos)) return LLLnode.from_list(['with', '_L', left, ['seq'] + subs], typ=None) # If the right side is a variable else: right_token = LLLnode.from_list('_R', typ=right.typ, location=right.location) subs = [] for i in range(left.typ.count): subs.append( make_setter(add_variable_offset( left_token, LLLnode.from_list(i, typ='int128'), pos=pos, ), add_variable_offset( right_token, LLLnode.from_list(i, typ='int128'), pos=pos, ), location, pos=pos)) return LLLnode.from_list( ['with', '_L', left, ['with', '_R', right, ['seq'] + subs]], typ=None) # Structs elif isinstance(left.typ, (StructType, TupleType)): if left.value == "multi" and isinstance(left.typ, StructType): raise Exception("Target of set statement must be a single item") if not isinstance(right.typ, NullType): if not isinstance(right.typ, left.typ.__class__): raise TypeMismatchException( "Setter type mismatch: left side is %r, right side is %r" % ( left.typ, right.typ, ), pos, ) if isinstance(left.typ, StructType): for k in right.args: if k.value is None: raise InvalidLiteralException( 'Setting struct value to None is not allowed, use a default value.', pos, ) for k in left.typ.members: if k not in right.typ.members: raise TypeMismatchException( "Keys don't match for structs, missing %s" % k, pos, ) for k in right.typ.members: if k not in left.typ.members: raise TypeMismatchException( "Keys don't match for structs, extra %s" % k, pos, ) if left.typ.name != right.typ.name: raise TypeMismatchException( "Expected %r, got %r" % (left.typ, right.typ), pos) else: if len(left.typ.members) != len(right.typ.members): raise TypeMismatchException( "Tuple lengths don't match, %d vs %d" % ( len(left.typ.members), len(right.typ.members), ), pos, ) left_token = LLLnode.from_list('_L', typ=left.typ, location=left.location) if left.location == "storage": left = LLLnode.from_list(['sha3_32', left], typ=left.typ, location="storage_prehashed") left_token.location = "storage_prehashed" if isinstance(left.typ, StructType): keyz = list(left.typ.members.keys()) else: keyz = list(range(len(left.typ.members))) # If the left side is a literal if left.value == 'multi': locations = [arg.location for arg in left.args] else: locations = [location for _ in keyz] # If the right side is a literal if right.value == "multi": if len(right.args) != len(keyz): raise TypeMismatchException("Mismatched number of elements", pos) subs = [] for i, (typ, loc) in enumerate(zip(keyz, locations)): subs.append( make_setter( add_variable_offset(left_token, typ, pos=pos), right.args[i], loc, pos=pos, )) return LLLnode.from_list(['with', '_L', left, ['seq'] + subs], typ=None) # If the right side is a null elif isinstance(right.typ, NullType): subs = [] for typ, loc in zip(keyz, locations): subs.append( make_setter( add_variable_offset(left_token, typ, pos=pos), LLLnode.from_list(None, typ=NullType()), loc, pos=pos, )) return LLLnode.from_list(['with', '_L', left, ['seq'] + subs], typ=None) # If tuple assign. elif isinstance(left.typ, TupleType) and isinstance( right.typ, TupleType): subs = [] static_offset_counter = 0 zipped_components = zip(left.args, right.typ.members, locations) for left_arg, right_arg, loc in zipped_components: if isinstance(right_arg, ByteArrayLike): RType = ByteArrayType if isinstance( right_arg, ByteArrayType) else StringType offset = LLLnode.from_list([ 'add', '_R', ['mload', ['add', '_R', static_offset_counter]] ], typ=RType(right_arg.maxlen), location='memory', pos=pos) static_offset_counter += 32 else: offset = LLLnode.from_list( ['mload', ['add', '_R', static_offset_counter]], typ=right_arg.typ, pos=pos, ) static_offset_counter += get_size_of_type(right_arg) * 32 subs.append(make_setter(left_arg, offset, loc, pos=pos)) return LLLnode.from_list( ['with', '_R', right, ['seq'] + subs], typ=None, annotation='Tuple assignment', ) # If the right side is a variable else: subs = [] right_token = LLLnode.from_list('_R', typ=right.typ, location=right.location) for typ, loc in zip(keyz, locations): subs.append( make_setter(add_variable_offset(left_token, typ, pos=pos), add_variable_offset(right_token, typ, pos=pos), loc, pos=pos)) return LLLnode.from_list( ['with', '_L', left, ['with', '_R', right, ['seq'] + subs]], typ=None, ) else: raise Exception("Invalid type for setters")