def _sha3(expr, args, kwargs, context): raise StructureException( "sha3 function has been deprecated in favor of keccak256")
def make_external_call(stmt_expr, context): # circular import! from vyper.old_codegen.expr import Expr value, gas = get_external_interface_keywords(stmt_expr, context) if isinstance(stmt_expr.func, vy_ast.Attribute) and isinstance( stmt_expr.func.value, vy_ast.Call): contract_name = stmt_expr.func.value.func.id contract_address = Expr.parse_value_expr(stmt_expr.func.value.args[0], context) return external_call( stmt_expr, context, contract_name, contract_address, pos=getpos(stmt_expr), value=value, gas=gas, ) elif (isinstance(stmt_expr.func.value, vy_ast.Attribute) and stmt_expr.func.value.attr in context.sigs): # noqa: E501 contract_name = stmt_expr.func.value.attr type_ = stmt_expr.func.value._metadata["type"] var = context.globals[stmt_expr.func.value.attr] contract_address = unwrap_location( LLLnode.from_list( type_.position.position, typ=var.typ, location="storage", pos=getpos(stmt_expr), annotation="self." + stmt_expr.func.value.attr, )) return external_call( stmt_expr, context, contract_name, contract_address, pos=getpos(stmt_expr), value=value, gas=gas, ) elif (isinstance(stmt_expr.func.value, vy_ast.Attribute) and stmt_expr.func.value.attr in context.globals and hasattr(context.globals[stmt_expr.func.value.attr].typ, "name")): contract_name = context.globals[stmt_expr.func.value.attr].typ.name type_ = stmt_expr.func.value._metadata["type"] var = context.globals[stmt_expr.func.value.attr] contract_address = unwrap_location( LLLnode.from_list( type_.position.position, typ=var.typ, location="storage", pos=getpos(stmt_expr), annotation="self." + stmt_expr.func.value.attr, )) return external_call( stmt_expr, context, contract_name, contract_address, pos=getpos(stmt_expr), value=value, gas=gas, ) else: raise StructureException("Unsupported operator.", stmt_expr)
def parse_raise(self): if self.stmt.exc is None: raise StructureException('Raise must have a reason', self.stmt) return self._assert_reason(0, self.stmt.exc)
def from_definition(cls, code, sigs=None, custom_units=None, custom_structs=None, contract_def=False, constants=None, constant=False): if not custom_structs: custom_structs = {} name = code.name mem_pos = 0 valid_name, msg = is_varname_valid(name, custom_units, custom_structs, constants) if not valid_name and (not name.lower() in function_whitelist): raise FunctionDeclarationException("Function name invalid. " + msg, code) # Determine the arguments, expects something of the form def foo(arg1: # int128, arg2: int128 ... args = [] for arg in code.args.args: # Each arg needs a type specified. typ = arg.annotation if not typ: raise InvalidTypeException("Argument must have type", arg) # Validate arg name. check_valid_varname( arg.arg, custom_units, custom_structs, constants, arg, "Argument name invalid or reserved. ", FunctionDeclarationException, ) # Check for duplicate arg name. if arg.arg in (x.name for x in args): raise FunctionDeclarationException( "Duplicate function argument name: " + arg.arg, arg, ) parsed_type = parse_type( typ, None, sigs, custom_units=custom_units, custom_structs=custom_structs, constants=constants, ) args.append( VariableRecord( arg.arg, mem_pos, parsed_type, False, defined_at=getpos(arg), )) if isinstance(parsed_type, ByteArrayLike): mem_pos += 32 else: mem_pos += get_size_of_type(parsed_type) * 32 # Apply decorators const, payable, private, public, nonreentrant_key = False, False, False, False, '' for dec in code.decorator_list: if isinstance(dec, ast.Name) and dec.id == "constant": const = True elif isinstance(dec, ast.Name) and dec.id == "payable": payable = True elif isinstance(dec, ast.Name) and dec.id == "private": private = True elif isinstance(dec, ast.Name) and dec.id == "public": public = True elif isinstance(dec, ast.Call) and dec.func.id == "nonreentrant": if dec.args and len(dec.args) == 1 and isinstance( dec.args[0], ast.Str) and dec.args[0].s: # noqa: E501 nonreentrant_key = dec.args[0].s else: raise StructureException( "@nonreentrant decorator requires a non-empty string to use as a key.", dec) else: raise StructureException("Bad decorator", dec) if public and private: raise StructureException( "Cannot use public and private decorators on the same function: {}" .format(name)) if payable and const: raise StructureException( "Function {} cannot be both constant and payable.".format( name)) if payable and private: raise StructureException( "Function {} cannot be both private and payable.".format(name)) if (not public and not private) and not contract_def: raise StructureException( "Function visibility must be declared (@public or @private)", code, ) if constant and nonreentrant_key: raise StructureException( "@nonreentrant makes no sense on a @constant function.", code) if constant: const = True # Determine the return type and whether or not it's constant. Expects something # of the form: # def foo(): ... # def foo() -> int128: ... # If there is no return type, ie. it's of the form def foo(): ... # and NOT def foo() -> type: ..., then it's null if not code.returns: output_type = None elif isinstance( code.returns, (ast.Name, ast.Compare, ast.Subscript, ast.Call, ast.Tuple)): output_type = parse_type( code.returns, None, sigs, custom_units=custom_units, custom_structs=custom_structs, constants=constants, ) else: raise InvalidTypeException( "Output type invalid or unsupported: %r" % parse_type(code.returns, None), code.returns, ) # Output type must be canonicalizable if output_type is not None: assert isinstance(output_type, TupleType) or canonicalize_type(output_type) # Get the canonical function signature sig = cls.get_full_sig(name, code.args.args, sigs, custom_units, custom_structs, constants) # Take the first 4 bytes of the hash of the sig to get the method ID method_id = fourbytes_to_int(sha3(bytes(sig, 'utf-8'))[:4]) return cls(name, args, output_type, const, payable, private, nonreentrant_key, sig, method_id, custom_units, code)
def call(self): from .parser import ( external_contract_call_expr, 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) ) 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) output_placeholder = self.context.new_placeholder(typ=sig.output_type) if isinstance(sig.output_type, BaseType): returner = output_placeholder elif isinstance(sig.output_type, ByteArrayType): returner = output_placeholder + 32 else: raise TypeMismatchException("Invalid output type: %r" % sig.output_type, self.expr) o = LLLnode.from_list(['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) return external_contract_call_expr(self.expr, self.context, contract_name, contract_address) 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)) return external_contract_call_expr(self.expr, self.context, contract_name, contract_address) 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)) return external_contract_call_expr(self.expr, self.context, contract_name, contract_address) else: raise StructureException("Unsupported operator: %r" % ast.dump(self.expr), self.expr)
def parse_for(self): from .parser import ( parse_body, ) # Type 0 for, e.g. for i in list(): ... if self._is_list_iter(): return self.parse_for_list() is_invalid_for_statement = any(( not isinstance(self.stmt.iter, ast.Call), not isinstance(self.stmt.iter.func, ast.Name), not isinstance(self.stmt.target, ast.Name), self.stmt.iter.func.id != "range", len(self.stmt.iter.args) not in {1, 2}, )) if is_invalid_for_statement: raise StructureException( ("For statements must be of the form `for i in range(rounds): " "..` or `for i in range(start, start + rounds): ..`"), self.stmt.iter) block_scope_id = id(self.stmt.orelse) with self.context.make_blockscope(block_scope_id): # Get arg0 arg0 = self.stmt.iter.args[0] num_of_args = len(self.stmt.iter.args) # Type 1 for, e.g. for i in range(10): ... if num_of_args == 1: arg0_val = self._get_range_const_value(arg0) start = LLLnode.from_list(0, typ='int128', pos=getpos(self.stmt)) rounds = arg0_val # Type 2 for, e.g. for i in range(100, 110): ... elif self._check_valid_range_constant(self.stmt.iter.args[1], raise_exception=False)[0]: arg0_val = self._get_range_const_value(arg0) arg1_val = self._get_range_const_value(self.stmt.iter.args[1]) start = LLLnode.from_list(arg0_val, typ='int128', pos=getpos(self.stmt)) rounds = LLLnode.from_list(arg1_val - arg0_val, typ='int128', pos=getpos(self.stmt)) # Type 3 for, e.g. for i in range(x, x + 10): ... else: arg1 = self.stmt.iter.args[1] if not isinstance(arg1, ast.BinOp) or not isinstance( arg1.op, ast.Add): raise StructureException( ("Two-arg for statements must be of the form `for i " "in range(start, start + rounds): ...`"), arg1, ) if ast.dump(arg0) != ast.dump(arg1.left): raise StructureException( ("Two-arg for statements of the form `for i in " "range(x, x + y): ...` must have x identical in both " "places: %r %r") % (ast.dump(arg0), ast.dump(arg1.left)), self.stmt.iter, ) rounds = self._get_range_const_value(arg1.right) start = Expr.parse_value_expr(arg0, self.context) varname = self.stmt.target.id pos = self.context.new_variable(varname, BaseType('int128'), pos=getpos(self.stmt)) self.context.forvars[varname] = True o = LLLnode.from_list( [ 'repeat', pos, start, rounds, parse_body(self.stmt.body, self.context) ], typ=None, pos=getpos(self.stmt), ) del self.context.vars[varname] del self.context.forvars[varname] return o
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 add_globals_and_events(self, item): item_attributes = {"public": False} if len(self._globals) > NONRENTRANT_STORAGE_OFFSET: raise ParserException( "Too many globals defined, only {} globals are allowed".format( NONRENTRANT_STORAGE_OFFSET), item, ) # Make sure we have a valid variable name. if not isinstance(item.target, ast.Name): raise StructureException('Invalid global variable name', item.target) # Handle constants. if self.get_call_func_name(item) == "constant": self._constants.add_constant(item, global_ctx=self) return # Handle events. if not (self.get_call_func_name(item) == "event"): item_name, item_attributes = self.get_item_name_and_attributes( item, item_attributes) if not all([ attr in valid_global_keywords for attr in item_attributes.keys() ]): raise StructureException( 'Invalid global keyword used: %s' % item_attributes, item) if item.value is not None: raise StructureException( 'May not assign value whilst defining type', item) elif self.get_call_func_name(item) == "event": if self._globals or len(self._defs): raise EventDeclarationException( "Events must all come before global declarations and function definitions", item) self._events.append(item) elif not isinstance(item.target, ast.Name): raise StructureException( "Can only assign type to variable in top-level statement", item) # Is this a custom unit definition. elif item.target.id == 'units': if not self._custom_units: if not isinstance(item.annotation, ast.Dict): raise VariableDeclarationException( "Define custom units using units: { }.", item.target) for key, value in zip(item.annotation.keys, item.annotation.values): if not isinstance(value, ast.Str): raise VariableDeclarationException( "Custom unit description must be a valid string", value) if not isinstance(key, ast.Name): raise VariableDeclarationException( "Custom unit name must be a valid string", key) check_valid_varname(key.id, self._custom_units, self._structs, self._constants, key, "Custom unit invalid.") self._custom_units.add(key.id) self._custom_units_descriptions[key.id] = value.s else: raise VariableDeclarationException( "Custom units can only be defined once", item.target) # Check if variable name is valid. # Don't move this check higher, as unit parsing has to happen first. elif not self.is_valid_varname(item.target.id, item): pass elif len(self._defs): raise StructureException( "Global variables must all come before function definitions", item, ) elif item_name in self._contracts or item_name in self._interfaces: if self.get_call_func_name(item) == "address": raise StructureException( f"Persistent address({item_name}) style contract declarations " "are not support anymore." f" Use {item.target.id}: {item_name} instead") self._globals[item.target.id] = ContractRecord( item.target.id, len(self._globals), ContractType(item_name), True, ) if item_attributes["public"]: typ = ContractType(item_name) for getter in self.mk_getter(item.target.id, typ): self._getters.append( self.parse_line('\n' * (item.lineno - 1) + getter)) self._getters[-1].pos = getpos(item) elif self.get_call_func_name(item) == "public": if isinstance(item.annotation.args[0], ast.Name) and item_name in self._contracts: typ = ContractType(item_name) else: typ = parse_type( item.annotation.args[0], 'storage', custom_units=self._custom_units, custom_structs=self._structs, constants=self._constants, ) self._globals[item.target.id] = VariableRecord( item.target.id, len(self._globals), typ, True, ) # Adding getters here for getter in self.mk_getter(item.target.id, typ): self._getters.append( self.parse_line('\n' * (item.lineno - 1) + getter)) self._getters[-1].pos = getpos(item) elif isinstance(item.annotation, (ast.Name, ast.Call, ast.Subscript)): self._globals[item.target.id] = VariableRecord( item.target.id, len(self._globals), parse_type(item.annotation, 'storage', custom_units=self._custom_units, custom_structs=self._structs, constants=self._constants), True) else: raise InvalidTypeException('Invalid global type specified', item)
def parse_Name(self): if self.stmt.id == "vdb": return IRnode("debugger") else: raise StructureException( f"Unsupported statement type: {type(self.stmt)}", 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 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 parse_for(self): from .parser import ( parse_body, ) # Type 0 for, eg. for i in list(): ... if self._is_list_iter(): return self.parse_for_list() if not isinstance(self.stmt.iter, ast.Call) or \ not isinstance(self.stmt.iter.func, ast.Name) or \ not isinstance(self.stmt.target, ast.Name) or \ self.stmt.iter.func.id != "range" or \ len(self.stmt.iter.args) not in (1, 2): raise StructureException( "For statements must be of the form `for i in range(rounds): ..` or `for i in range(start, start + rounds): ..`", self.stmt.iter) # noqa block_scope_id = id(self.stmt.orelse) self.context.start_blockscope(block_scope_id) # Type 1 for, eg. for i in range(10): ... if len(self.stmt.iter.args) == 1: if not isinstance(self.stmt.iter.args[0], ast.Num): raise StructureException("Range only accepts literal values", self.stmt.iter) start = LLLnode.from_list(0, typ='num', pos=getpos(self.stmt)) rounds = self.stmt.iter.args[0].n elif isinstance(self.stmt.iter.args[0], ast.Num) and isinstance( self.stmt.iter.args[1], ast.Num): # Type 2 for, eg. for i in range(100, 110): ... start = LLLnode.from_list(self.stmt.iter.args[0].n, typ='num', pos=getpos(self.stmt)) rounds = LLLnode.from_list(self.stmt.iter.args[1].n - self.stmt.iter.args[0].n, typ='num', pos=getpos(self.stmt)) else: # Type 3 for, eg. for i in range(x, x + 10): ... if not isinstance(self.stmt.iter.args[1], ast.BinOp) or not isinstance( self.stmt.iter.args[1].op, ast.Add): raise StructureException( "Two-arg for statements must be of the form `for i in range(start, start + rounds): ...`", self.stmt.iter.args[1]) if ast.dump(self.stmt.iter.args[0]) != ast.dump( self.stmt.iter.args[1].left): raise StructureException( "Two-arg for statements of the form `for i in range(x, x + y): ...` must have x identical in both places: %r %r" % (ast.dump(self.stmt.iter.args[0]), ast.dump(self.stmt.iter.args[1].left)), self.stmt.iter) if not isinstance(self.stmt.iter.args[1].right, ast.Num): raise StructureException("Range only accepts literal values", self.stmt.iter.args[1]) start = Expr.parse_value_expr(self.stmt.iter.args[0], self.context) rounds = self.stmt.iter.args[1].right.n varname = self.stmt.target.id pos = self.context.new_variable(varname, BaseType('num')) self.context.forvars[varname] = True o = LLLnode.from_list([ 'repeat', pos, start, rounds, parse_body(self.stmt.body, self.context) ], typ=None, pos=getpos(self.stmt)) del self.context.vars[varname] del self.context.forvars[varname] self.context.end_blockscope(block_scope_id) return o
def call(self): from .parser import ( pack_arguments, pack_logging_data, pack_logging_topics, external_contract_call_stmt, ) if isinstance(self.stmt.func, ast.Name) and self.stmt.func.id in stmt_dispatch_table: return stmt_dispatch_table[self.stmt.func.id](self.stmt, self.context) elif isinstance(self.stmt.func, ast.Attribute) and isinstance( self.stmt.func.value, ast.Name) and self.stmt.func.value.id == "self": method_name = self.stmt.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.stmt.func.attr) add_gas = self.context.sigs['self'][method_name].gas inargs, inargsize = pack_arguments( self.context.sigs['self'][self.stmt.func.attr], [Expr(arg, self.context).lll_node for arg in self.stmt.args], self.context) return LLLnode.from_list([ 'assert', ['call', ['gas'], ['address'], 0, inargs, inargsize, 0, 0] ], typ=None, pos=getpos(self.stmt), add_gas_estimate=add_gas, annotation='Internal Call: %s' % method_name) elif isinstance(self.stmt.func, ast.Attribute) and isinstance( self.stmt.func.value, ast.Call): contract_name = self.stmt.func.value.func.id contract_address = Expr.parse_value_expr( self.stmt.func.value.args[0], self.context) return external_contract_call_stmt(self.stmt, self.context, contract_name, contract_address) elif isinstance(self.stmt.func.value, ast.Attribute ) and self.stmt.func.value.attr in self.context.sigs: contract_name = self.stmt.func.value.attr var = self.context.globals[self.stmt.func.value.attr] contract_address = unwrap_location( LLLnode.from_list(var.pos, typ=var.typ, location='storage', pos=getpos(self.stmt), annotation='self.' + self.stmt.func.value.attr)) return external_contract_call_stmt(self.stmt, self.context, contract_name, contract_address) elif isinstance( self.stmt.func.value, ast.Attribute ) and self.stmt.func.value.attr in self.context.globals: contract_name = self.context.globals[ self.stmt.func.value.attr].typ.unit var = self.context.globals[self.stmt.func.value.attr] contract_address = unwrap_location( LLLnode.from_list(var.pos, typ=var.typ, location='storage', pos=getpos(self.stmt), annotation='self.' + self.stmt.func.value.attr)) return external_contract_call_stmt(self.stmt, self.context, contract_name, contract_address) elif isinstance(self.stmt.func, ast.Attribute) and self.stmt.func.value.id == 'log': if self.stmt.func.attr not in self.context.sigs['self']: raise VariableDeclarationException( "Event not declared yet: %s" % self.stmt.func.attr) event = self.context.sigs['self'][self.stmt.func.attr] if len(event.indexed_list) != len(self.stmt.args): raise VariableDeclarationException( "%s received %s arguments but expected %s" % (event.name, len(self.stmt.args), len(event.indexed_list))) expected_topics, topics = [], [] expected_data, data = [], [] for pos, is_indexed in enumerate(event.indexed_list): if is_indexed: expected_topics.append(event.args[pos]) topics.append(self.stmt.args[pos]) else: expected_data.append(event.args[pos]) data.append(self.stmt.args[pos]) topics = pack_logging_topics(event.event_id, topics, expected_topics, self.context) inargs, inargsize, inargsize_node, inarg_start = pack_logging_data( expected_data, data, self.context) if inargsize_node is None: sz = inargsize else: sz = ['mload', inargsize_node] return LLLnode.from_list([ 'seq', inargs, LLLnode.from_list( ["log" + str(len(topics)), inarg_start, sz] + topics, add_gas_estimate=inargsize * 10) ], typ=None, pos=getpos(self.stmt)) else: raise StructureException( "Unsupported operator: %r" % ast.dump(self.stmt), self.stmt)
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 parse_variable_location(cls, expr, context): o = cls(expr, context).lll_node if not o.location: raise StructureException( "Looking for a variable location, instead got a value", expr) return o
def visit_For(self, node): if isinstance(node.iter, vy_ast.Subscript): raise StructureException("Cannot iterate over a nested list", node.iter) if isinstance(node.iter, vy_ast.Call): # iteration via range() if node.iter.get("func.id") != "range": raise IteratorException( "Cannot iterate over the result of a function call", node.iter) validate_call_args(node.iter, (1, 2)) args = node.iter.args if len(args) == 1: # range(CONSTANT) if not isinstance(args[0], vy_ast.Num): raise StateAccessViolation("Value must be a literal", node) if args[0].value <= 0: raise StructureException( "For loop must have at least 1 iteration", args[0]) validate_expected_type(args[0], IntegerAbstractType()) type_list = get_possible_types_from_node(args[0]) else: validate_expected_type(args[0], IntegerAbstractType()) type_list = get_common_types(*args) if not isinstance(args[0], vy_ast.Constant): # range(x, x + CONSTANT) if not isinstance(args[1], vy_ast.BinOp) or not isinstance( args[1].op, vy_ast.Add): raise StructureException( "Second element must be the first element plus a literal value", args[0], ) if not vy_ast.compare_nodes(args[0], args[1].left): raise StructureException( "First and second variable must be the same", args[1].left) if not isinstance(args[1].right, vy_ast.Int): raise InvalidLiteral("Literal must be an integer", args[1].right) if args[1].right.value < 1: raise StructureException( f"For loop has invalid number of iterations ({args[1].right.value})," " the value must be greater than zero", args[1].right, ) else: # range(CONSTANT, CONSTANT) if not isinstance(args[1], vy_ast.Int): raise InvalidType("Value must be a literal integer", args[1]) validate_expected_type(args[1], IntegerAbstractType()) if args[0].value >= args[1].value: raise StructureException( "Second value must be > first value", args[1]) else: # iteration over a variable or literal list type_list = [ i.value_type for i in get_possible_types_from_node(node.iter) if isinstance(i, (DynamicArrayDefinition, ArrayDefinition)) ] if not type_list: raise InvalidType("Not an iterable type", node.iter) if isinstance(node.iter, (vy_ast.Name, vy_ast.Attribute)): # check for references to the iterated value within the body of the loop assign = _check_iterator_assign(node.iter, node) if assign: raise ImmutableViolation( "Cannot modify array during iteration", assign) if node.iter.get("value.id") == "self": # check if iterated value may be modified by function calls inside the loop iter_name = node.iter.attr for call_node in node.get_descendants(vy_ast.Call, {"func.value.id": "self"}): fn_name = call_node.func.attr fn_node = self.vyper_module.get_children( vy_ast.FunctionDef, {"name": fn_name})[0] if _check_iterator_assign(node.iter, fn_node): # check for direct modification raise ImmutableViolation( f"Cannot call '{fn_name}' inside for loop, it potentially " f"modifies iterated storage variable '{iter_name}'", call_node, ) for name in self.namespace["self"].members[ fn_name].recursive_calls: # check for indirect modification fn_node = self.vyper_module.get_children( vy_ast.FunctionDef, {"name": name})[0] if _check_iterator_assign(node.iter, fn_node): raise ImmutableViolation( f"Cannot call '{fn_name}' inside for loop, it may call to '{name}' " f"which potentially modifies iterated storage variable '{iter_name}'", call_node, ) self.expr_visitor.visit(node.iter) for_loop_exceptions = [] iter_name = node.target.id for type_ in type_list: # type check the for loop body using each possible type for iterator value type_ = copy.deepcopy(type_) type_.is_constant = True with self.namespace.enter_scope(): try: self.namespace[iter_name] = type_ except VyperException as exc: raise exc.with_annotation(node) from None try: for n in node.body: self.visit(n) # type information is applied directly because the scope is # closed prior to the call to `StatementAnnotationVisitor` node.target._metadata["type"] = type_ return except (TypeMismatch, InvalidOperation) as exc: for_loop_exceptions.append(exc) if len(set(str(i) for i in for_loop_exceptions)) == 1: # if every attempt at type checking raised the same exception raise for_loop_exceptions[0] # return an aggregate TypeMismatch that shows all possible exceptions # depending on which type is used types_str = [str(i) for i in type_list] given_str = f"{', '.join(types_str[:1])} or {types_str[-1]}" raise TypeMismatch( f"Iterator value '{iter_name}' may be cast as {given_str}, " "but type checking fails with all possible types:", node, *((f"Casting '{iter_name}' as {type_}: {exc.message}", exc.annotations[0]) for type_, exc in zip(type_list, for_loop_exceptions)), )
def call(self): from .parser import ( pack_logging_data, pack_logging_topics, ) is_self_function = (isinstance( self.stmt.func, ast.Attribute)) and isinstance( self.stmt.func.value, ast.Name) and self.stmt.func.value.id == "self" is_log_call = (isinstance( self.stmt.func, ast.Attribute)) and isinstance( self.stmt.func.value, ast.Name) and self.stmt.func.value.id == 'log' if isinstance(self.stmt.func, ast.Name): if self.stmt.func.id in stmt_dispatch_table: if self.stmt.func.id == 'clear': return self._clear() else: return stmt_dispatch_table[self.stmt.func.id](self.stmt, self.context) elif self.stmt.func.id in dispatch_table: raise StructureException( "Function {} can not be called without being used.".format( self.stmt.func.id), self.stmt, ) else: raise StructureException( "Unknown function: '{}'.".format(self.stmt.func.id), self.stmt, ) elif is_self_function: return self_call.make_call(self.stmt, self.context) elif is_log_call: if self.stmt.func.attr not in self.context.sigs['self']: raise EventDeclarationException("Event not declared yet: %s" % self.stmt.func.attr) event = self.context.sigs['self'][self.stmt.func.attr] if len(event.indexed_list) != len(self.stmt.args): raise EventDeclarationException( "%s received %s arguments but expected %s" % (event.name, len(self.stmt.args), len(event.indexed_list))) expected_topics, topics = [], [] expected_data, data = [], [] for pos, is_indexed in enumerate(event.indexed_list): if is_indexed: expected_topics.append(event.args[pos]) topics.append(self.stmt.args[pos]) else: expected_data.append(event.args[pos]) data.append(self.stmt.args[pos]) topics = pack_logging_topics( event.event_id, topics, expected_topics, self.context, pos=getpos(self.stmt), ) inargs, inargsize, inargsize_node, inarg_start = pack_logging_data( expected_data, data, self.context, pos=getpos(self.stmt), ) if inargsize_node is None: sz = inargsize else: sz = ['mload', inargsize_node] return LLLnode.from_list([ 'seq', inargs, LLLnode.from_list( ["log" + str(len(topics)), inarg_start, sz] + topics, add_gas_estimate=inargsize * 10, ) ], typ=None, pos=getpos(self.stmt)) else: return external_call.make_external_call(self.stmt, self.context)
def lll_for_self_call(stmt_expr, context): from vyper.codegen.expr import Expr # TODO rethink this circular import pos = getpos(stmt_expr) # ** Internal Call ** # Steps: # - copy arguments into the soon-to-be callee # - allocate return buffer # - push jumpdest (callback ptr) and return buffer location # - jump to label # - (private function will fill return buffer and jump back) method_name = stmt_expr.func.attr pos_args_lll = [Expr(x, context).lll_node for x in stmt_expr.args] sig, kw_vals = context.lookup_internal_function(method_name, pos_args_lll) kw_args_lll = [Expr(x, context).lll_node for x in kw_vals] args_lll = pos_args_lll + kw_args_lll args_tuple_t = TupleType([x.typ for x in args_lll]) args_as_tuple = LLLnode.from_list(["multi"] + [x for x in args_lll], typ=args_tuple_t) # register callee to help calculate our starting frame offset context.register_callee(sig.frame_size) if context.is_constant() and sig.mutability not in ("view", "pure"): raise StateAccessViolation( f"May not call state modifying function " f"'{method_name}' within {context.pp_constancy()}.", getpos(stmt_expr), ) # TODO move me to type checker phase if not sig.internal: raise StructureException("Cannot call external functions via 'self'", stmt_expr) return_label = _generate_label(f"{sig.internal_function_label}_call") # allocate space for the return buffer # TODO allocate in stmt and/or expr.py return_buffer = (context.new_internal_variable(sig.return_type) if sig.return_type is not None else "pass") return_buffer = LLLnode.from_list([return_buffer], annotation=f"{return_label}_return_buf") # note: dst_tuple_t != args_tuple_t dst_tuple_t = TupleType([arg.typ for arg in sig.args]) args_dst = LLLnode(sig.frame_start, typ=dst_tuple_t, location="memory") # if one of the arguments is a self call, the argument # buffer could get borked. to prevent against that, # write args to a temporary buffer until all the arguments # are fully evaluated. if args_as_tuple.contains_self_call: copy_args = ["seq"] # TODO deallocate me tmp_args_buf = LLLnode( context.new_internal_variable(dst_tuple_t), typ=dst_tuple_t, location="memory", ) copy_args.append( # --> args evaluate here <-- make_setter(tmp_args_buf, args_as_tuple, pos)) copy_args.append(make_setter(args_dst, tmp_args_buf, pos)) else: copy_args = make_setter(args_dst, args_as_tuple, pos) call_sequence = [ "seq", copy_args, [ "goto", sig.internal_function_label, return_buffer, # pass return buffer to subroutine push_label_to_stack( return_label), # pass return label to subroutine ], ["label", return_label], return_buffer, # push return buffer location to stack ] o = LLLnode.from_list( call_sequence, typ=sig.return_type, location="memory", pos=pos, annotation=stmt_expr.get("node_source_code"), add_gas_estimate=sig.gas, ) o.is_self_call = True return o
def parse_for_list(self): from .parser import (parse_body, make_setter) iter_list_node = Expr(self.stmt.iter, self.context).lll_node if not isinstance(iter_list_node.typ.subtype, BaseType): # Sanity check on list subtype. raise StructureException( 'For loops allowed only on basetype lists.', self.stmt.iter) iter_var_type = (self.context.vars.get(self.stmt.iter.id).typ if isinstance(self.stmt.iter, ast.Name) else None) subtype = iter_list_node.typ.subtype.typ varname = self.stmt.target.id value_pos = self.context.new_variable( varname, BaseType(subtype, unit=iter_list_node.typ.subtype.unit), ) i_pos = self.context.new_variable('_index_for_' + varname, BaseType(subtype)) self.context.forvars[varname] = True # Is a list that is already allocated to memory. if iter_var_type: list_name = self.stmt.iter.id # make sure list cannot be altered whilst iterating. with self.context.in_for_loop_scope(list_name): iter_var = self.context.vars.get(self.stmt.iter.id) body = [ 'seq', [ 'mstore', value_pos, [ 'mload', [ 'add', iter_var.pos, ['mul', ['mload', i_pos], 32] ] ], ], parse_body(self.stmt.body, self.context) ] o = LLLnode.from_list( ['repeat', i_pos, 0, iter_var.size, body], typ=None, pos=getpos(self.stmt)) # List gets defined in the for statement. elif isinstance(self.stmt.iter, ast.List): # Allocate list to memory. count = iter_list_node.typ.count tmp_list = LLLnode.from_list(obj=self.context.new_placeholder( ListType(iter_list_node.typ.subtype, count)), typ=ListType( iter_list_node.typ.subtype, count), location='memory') setter = make_setter(tmp_list, iter_list_node, 'memory', pos=getpos(self.stmt)) body = [ 'seq', [ 'mstore', value_pos, [ 'mload', ['add', tmp_list, ['mul', ['mload', i_pos], 32]] ] ], parse_body(self.stmt.body, self.context) ] o = LLLnode.from_list( ['seq', setter, ['repeat', i_pos, 0, count, body]], typ=None, pos=getpos(self.stmt)) # List contained in storage. elif isinstance(self.stmt.iter, ast.Attribute): count = iter_list_node.typ.count list_name = iter_list_node.annotation # make sure list cannot be altered whilst iterating. with self.context.in_for_loop_scope(list_name): body = [ 'seq', [ 'mstore', value_pos, [ 'sload', [ 'add', ['sha3_32', iter_list_node], ['mload', i_pos] ] ] ], parse_body(self.stmt.body, self.context), ] o = LLLnode.from_list( ['seq', ['repeat', i_pos, 0, count, body]], typ=None, pos=getpos(self.stmt)) del self.context.vars[varname] del self.context.vars['_index_for_' + varname] del self.context.forvars[varname] return o
def add_globals_and_events(self, item): item_attributes = {"public": False} if len(self._globals) > NONRENTRANT_STORAGE_OFFSET: raise StructureException( f"Too many globals defined, only {NONRENTRANT_STORAGE_OFFSET} globals are allowed", item, ) # Make sure we have a valid variable name. if not isinstance(item.target, vy_ast.Name): raise StructureException('Invalid global variable name', item.target) # Handle constants. if self.get_call_func_name(item) == "constant": self._constants.add_constant(item, global_ctx=self) return # Handle events. if not (self.get_call_func_name(item) == "event"): item_name, item_attributes = self.get_item_name_and_attributes(item, item_attributes) if not all([attr in VALID_GLOBAL_KEYWORDS for attr in item_attributes.keys()]): raise StructureException(f'Invalid global keyword used: {item_attributes}', item) if item.value is not None: raise StructureException('May not assign value whilst defining type', item) elif self.get_call_func_name(item) == "event": if self._globals or len(self._defs): raise EventDeclarationException( "Events must all come before global declarations and function definitions", item ) self._events.append(item) elif not isinstance(item.target, vy_ast.Name): raise StructureException( "Can only assign type to variable in top-level statement", item ) # Check if variable name is valid. # Don't move this check higher, as unit parsing has to happen first. elif not self.is_valid_varname(item.target.id, item): pass elif len(self._defs): raise StructureException( "Global variables must all come before function definitions", item, ) elif item_name in self._contracts or item_name in self._interfaces: if self.get_call_func_name(item) == "address": raise StructureException( f"Persistent address({item_name}) style contract declarations " "are not support anymore." f" Use {item.target.id}: {item_name} instead" ) self._globals[item.target.id] = ContractRecord( item.target.id, len(self._globals), ContractType(item_name), True, ) if item_attributes["public"]: typ = ContractType(item_name) for getter in self.mk_getter(item.target.id, typ): self._getters.append(self.parse_line('\n' * (item.lineno - 1) + getter)) self._getters[-1].pos = getpos(item) set_offsets(self._getters[-1], self._getters[-1].pos) elif self.get_call_func_name(item) == "public": if isinstance(item.annotation.args[0], vy_ast.Name) and item_name in self._contracts: typ = ContractType(item_name) else: typ = parse_type( item.annotation.args[0], 'storage', custom_structs=self._structs, constants=self._constants, ) self._globals[item.target.id] = VariableRecord( item.target.id, len(self._globals), typ, True, ) # Adding getters here for getter in self.mk_getter(item.target.id, typ): self._getters.append(self.parse_line('\n' * (item.lineno - 1) + getter)) self._getters[-1].pos = getpos(item) set_offsets(self._getters[-1], self._getters[-1].pos) elif isinstance(item.annotation, (vy_ast.Name, vy_ast.Call, vy_ast.Subscript)): self._globals[item.target.id] = VariableRecord( item.target.id, len(self._globals), parse_type( item.annotation, 'storage', custom_structs=self._structs, constants=self._constants ), True ) else: raise InvalidType('Invalid global type specified', item)
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 get_global_context( cls, vyper_module: "vy_ast.Module", interface_codes: Optional[InterfaceImports] = None ) -> "GlobalContext": from vyper.signatures.interface import ( extract_sigs, get_builtin_interfaces, ) interface_codes = {} if interface_codes is None else interface_codes global_ctx = cls() for item in vyper_module: # Contract references if isinstance(item, vy_ast.ClassDef): if global_ctx._events or global_ctx._globals or global_ctx._defs: raise StructureException(( "External contract and struct declarations must come " "before event declarations, global declarations, and " "function definitions" ), item) if item.class_type == 'struct': if global_ctx._contracts: raise StructureException( "Structs must come before external contract definitions", item ) global_ctx._structs[item.name] = global_ctx.make_struct(item) elif item.class_type == 'contract': if item.name in global_ctx._contracts or item.name in global_ctx._interfaces: raise StructureException( f"Contract '{item.name}' is already defined", item, ) global_ctx._contracts[item.name] = GlobalContext.make_contract(item) else: raise StructureException( "Unknown class_type. This is likely a compiler bug, please report", item ) # Statements of the form: # variable_name: type elif isinstance(item, vy_ast.AnnAssign): is_implements_statement = ( isinstance(item.target, vy_ast.Name) and item.target.id == 'implements' ) and item.annotation # implements statement. if is_implements_statement: interface_name = item.annotation.id # type: ignore if interface_name not in global_ctx._interfaces: raise StructureException( f'Unknown interface specified: {interface_name}', item ) global_ctx._implemented_interfaces.add(interface_name) else: global_ctx.add_globals_and_events(item) # Function definitions elif isinstance(item, vy_ast.FunctionDef): if item.name in global_ctx._globals: raise FunctionDeclarationException( f"Function name shadowing a variable name: {item.name}" ) global_ctx._defs.append(item) elif isinstance(item, vy_ast.ImportFrom): if not item.level and item.module == 'vyper.interfaces': built_in_interfaces = get_builtin_interfaces() for item_alias in item.names: interface_name = item_alias.name if interface_name in global_ctx._interfaces: raise StructureException( f'Duplicate import of {interface_name}', item ) if interface_name not in built_in_interfaces: raise StructureException( f'Built-In interface {interface_name} does not exist.', item ) global_ctx._interfaces[interface_name] = built_in_interfaces[interface_name].copy() # noqa: E501 else: for item_alias in item.names: interface_name = item_alias.name if interface_name in global_ctx._interfaces: raise StructureException( f'Duplicate import of {interface_name}', item ) if interface_name not in interface_codes: raise StructureException( f'Unknown interface {interface_name}', item ) global_ctx._interfaces[interface_name] = extract_sigs(interface_codes[interface_name]) # noqa: E501 elif isinstance(item, vy_ast.Import): for item_alias in item.names: if not item_alias.asname: raise StructureException( 'External interface import expects an alias using `as` statement', item ) interface_name = item_alias.asname if interface_name in global_ctx._interfaces: raise StructureException( f'Duplicate import of {interface_name}', item ) if interface_name not in interface_codes: raise StructureException( f'Unknown interface {interface_name}', item ) global_ctx._interfaces[interface_name] = extract_sigs(interface_codes[interface_name]) # noqa: E501 else: raise StructureException("Invalid top-level statement", item) # Merge intefaces. if global_ctx._interfaces: for interface_name, sigs in global_ctx._interfaces.items(): if interface_name in global_ctx._implemented_interfaces: for func_sig in sigs: func_sig.defined_in_interface = interface_name global_ctx._interface[func_sig.sig] = func_sig # Add getters to _defs global_ctx._defs += global_ctx._getters return global_ctx
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, ByteArrayLike) and isinstance(right.typ, ByteArrayLike): # TODO: Can this if branch be removed ^ pass 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, ByteArrayLike) and isinstance(right.typ, ByteArrayLike): left = Expr(self.expr.left, self.context).lll_node right = Expr(self.expr.comparators[0], self.context).lll_node length_mismatch = (left.typ.maxlen != right.typ.maxlen) left_over_32 = left.typ.maxlen > 32 right_over_32 = right.typ.maxlen > 32 if length_mismatch or left_over_32 or right_over_32: left_keccak = keccak256_helper(self.expr, [left], None, self.context) right_keccak = keccak256_helper(self.expr, [right], None, self.context) if op == 'eq' or op == 'ne': return LLLnode.from_list( [op, left_keccak, right_keccak], typ='bool', pos=getpos(self.expr), ) else: raise ParserException( "Can only compare strings/bytes of length shorter", " than 32 bytes other than equality comparisons", self.expr, ) else: 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 add_globals_and_events(self, item): item_attributes = {"public": False} if len(self._globals) > NONRENTRANT_STORAGE_OFFSET: raise StructureException( f"Too many globals defined, only {NONRENTRANT_STORAGE_OFFSET} globals are allowed", item, ) # Make sure we have a valid variable name. if not isinstance(item.target, vy_ast.Name): raise StructureException("Invalid global variable name", item.target) # Handle constants. if self.get_call_func_name(item) == "constant": self.is_valid_varname(item.target.id, item) return item_name, item_attributes = self.get_item_name_and_attributes( item, item_attributes) if not all( [attr in VALID_GLOBAL_KEYWORDS for attr in item_attributes.keys()]): raise StructureException( f"Invalid global keyword used: {item_attributes}", item) self.is_valid_varname(item.target.id, item) if item_name in self._contracts or item_name in self._interfaces: if self.get_call_func_name(item) == "address": raise StructureException( f"Persistent address({item_name}) style contract declarations " "are not support anymore." f" Use {item.target.id}: {item_name} instead") self._globals[item.target.id] = ContractRecord( item.target.id, len(self._globals), InterfaceType(item_name), True, ) if item_attributes["public"]: typ = InterfaceType(item_name) for getter in self.mk_getter(item.target.id, typ): self._getters.append( self.parse_line("\n" * (item.lineno - 1) + getter)) self._getters[-1].pos = getpos(item) set_offsets(self._getters[-1], self._getters[-1].pos) elif self.get_call_func_name(item) == "public": if isinstance(item.annotation.args[0], vy_ast.Name) and item_name in self._contracts: typ = InterfaceType(item_name) else: typ = parse_type( item.annotation.args[0], "storage", custom_structs=self._structs, ) self._globals[item.target.id] = VariableRecord( item.target.id, len(self._globals), typ, True, ) # Adding getters here for getter in self.mk_getter(item.target.id, typ): self._getters.append( self.parse_line("\n" * (item.lineno - 1) + getter)) self._getters[-1].pos = getpos(item) set_offsets(self._getters[-1], self._getters[-1].pos) elif isinstance(item.annotation, (vy_ast.Name, vy_ast.Call, vy_ast.Subscript)): self._globals[item.target.id] = VariableRecord( item.target.id, len(self._globals), parse_type( item.annotation, "storage", custom_structs=self._structs, ), True, ) else: raise InvalidType("Invalid global type specified", item)
def parse_tree_to_lll(code, origcode, runtime_only=False, interface_codes=None): global_ctx = GlobalContext.get_global_context( code, interface_codes=interface_codes) _names_def = [_def.name for _def in global_ctx._defs] # Checks for duplicate function names if len(set(_names_def)) < len(_names_def): raise FunctionDeclarationException( "Duplicate function name: %s" % [name for name in _names_def if _names_def.count(name) > 1][0]) _names_events = [_event.target.id for _event in global_ctx._events] # Checks for duplicate event names if len(set(_names_events)) < len(_names_events): raise EventDeclarationException( "Duplicate event name: %s" % [name for name in _names_events if _names_events.count(name) > 1][0]) # Initialization function initfunc = [_def for _def in global_ctx._defs if is_initializer(_def)] # Default function defaultfunc = [_def for _def in global_ctx._defs if is_default_func(_def)] # Regular functions otherfuncs = [ _def for _def in global_ctx._defs if not is_initializer(_def) and not is_default_func(_def) ] sigs = {} external_contracts = {} # Create the main statement o = ['seq'] if global_ctx._events: sigs = parse_events(sigs, global_ctx) if global_ctx._contracts: external_contracts = parse_external_contracts(external_contracts, global_ctx._contracts, global_ctx._structs, global_ctx._constants) # If there is an init func... if initfunc: o.append(['seq', initializer_lll]) o.append( parse_func(initfunc[0], { **{ 'self': sigs }, **external_contracts }, origcode, global_ctx)) # If there are regular functions... if otherfuncs or defaultfunc: o = parse_other_functions(o, otherfuncs, sigs, external_contracts, origcode, global_ctx, defaultfunc, runtime_only) # Check interface. if global_ctx._interface: funcs_left = global_ctx._interface.copy() for sig, func_sig in sigs.items(): if isinstance(func_sig, FunctionSignature): if sig in funcs_left and not func_sig.private: del funcs_left[sig] if isinstance(func_sig, EventSignature) and func_sig.sig in funcs_left: del funcs_left[func_sig.sig] if funcs_left: error_message = 'Contract does not comply to supplied Interface(s).\n' missing_functions = [ sig_name for sig_name, func_sig in funcs_left.items() if isinstance(func_sig, FunctionSignature) ] missing_events = [ sig_name for sig_name, func_sig in funcs_left.items() if isinstance(func_sig, EventSignature) ] if missing_functions: error_message += 'Missing interface functions:\n\t{}'.format( '\n\t'.join(missing_functions)) if missing_events: error_message += 'Missing interface events:\n\t{}'.format( '\n\t'.join(missing_events)) raise StructureException(error_message) return LLLnode.from_list(o, typ=None)
def get_global_context( cls, vyper_module: "vy_ast.Module", interface_codes: Optional[InterfaceImports] = None ) -> "GlobalContext": from vyper.signatures.interface import ( extract_sigs, get_builtin_interfaces, ) interface_codes = {} if interface_codes is None else interface_codes global_ctx = cls() for item in vyper_module: if isinstance(item, vy_ast.StructDef): global_ctx._structs[item.name] = global_ctx.make_struct(item) elif isinstance(item, vy_ast.InterfaceDef): global_ctx._contracts[item.name] = GlobalContext.make_contract( item) elif isinstance(item, vy_ast.EventDef): global_ctx._events.append(item) # Statements of the form: # variable_name: type elif isinstance(item, vy_ast.AnnAssign): is_implements_statement = (isinstance(item.target, vy_ast.Name) and item.target.id == "implements") and item.annotation # implements statement. if is_implements_statement: interface_name = item.annotation.id # type: ignore if interface_name not in global_ctx._interfaces: raise StructureException( f"Unknown interface specified: {interface_name}", item) global_ctx._implemented_interfaces.add(interface_name) else: global_ctx.add_globals_and_events(item) # Function definitions elif isinstance(item, vy_ast.FunctionDef): global_ctx._defs.append(item) elif isinstance(item, vy_ast.ImportFrom): interface_name = item.name assigned_name = item.alias or item.name if assigned_name in global_ctx._interfaces: raise StructureException( f"Duplicate import of {interface_name}", item) if not item.level and item.module == "vyper.interfaces": built_in_interfaces = get_builtin_interfaces() if interface_name not in built_in_interfaces: raise StructureException( f"Built-In interface {interface_name} does not exist.", item) global_ctx._interfaces[ assigned_name] = built_in_interfaces[ interface_name].copy() else: if interface_name not in interface_codes: raise StructureException( f"Unknown interface {interface_name}", item) global_ctx._interfaces[assigned_name] = extract_sigs( interface_codes[interface_name], interface_name) elif isinstance(item, vy_ast.Import): interface_name = item.alias if interface_name in global_ctx._interfaces: raise StructureException( f"Duplicate import of {interface_name}", item) if interface_name not in interface_codes: raise StructureException( f"Unknown interface {interface_name}", item) global_ctx._interfaces[interface_name] = extract_sigs( interface_codes[interface_name], interface_name) else: raise StructureException("Invalid top-level statement", item) # Merge intefaces. if global_ctx._interfaces: for interface_name, sigs in global_ctx._interfaces.items(): if interface_name in global_ctx._implemented_interfaces: for func_sig in sigs: func_sig.defined_in_interface = interface_name global_ctx._interface[func_sig.sig] = func_sig # Add getters to _defs global_ctx._defs += global_ctx._getters return global_ctx
def parse_delete(self): raise StructureException( "Deleting is not supported, use built-in `clear()` function.", self.stmt)
def ir_for_self_call(stmt_expr, context): from vyper.codegen.expr import Expr # TODO rethink this circular import # ** Internal Call ** # Steps: # - copy arguments into the soon-to-be callee # - allocate return buffer # - push jumpdest (callback ptr) and return buffer location # - jump to label # - (private function will fill return buffer and jump back) method_name = stmt_expr.func.attr pos_args_ir = [Expr(x, context).ir_node for x in stmt_expr.args] sig, kw_vals = context.lookup_internal_function(method_name, pos_args_ir, stmt_expr) kw_args_ir = [Expr(x, context).ir_node for x in kw_vals] args_ir = pos_args_ir + kw_args_ir args_tuple_t = TupleType([x.typ for x in args_ir]) args_as_tuple = IRnode.from_list(["multi"] + [x for x in args_ir], typ=args_tuple_t) if context.is_constant() and sig.mutability not in ("view", "pure"): raise StateAccessViolation( f"May not call state modifying function " f"'{method_name}' within {context.pp_constancy()}.", stmt_expr, ) # TODO move me to type checker phase if not sig.internal: raise StructureException("Cannot call external functions via 'self'", stmt_expr) return_label = _generate_label(f"{sig.internal_function_label}_call") # allocate space for the return buffer # TODO allocate in stmt and/or expr.py if sig.return_type is not None: return_buffer = IRnode.from_list( context.new_internal_variable(sig.return_type), annotation=f"{return_label}_return_buf") else: return_buffer = None # note: dst_tuple_t != args_tuple_t dst_tuple_t = TupleType([arg.typ for arg in sig.args]) args_dst = IRnode(sig.frame_info.frame_start, typ=dst_tuple_t, location=MEMORY) # if one of the arguments is a self call, the argument # buffer could get borked. to prevent against that, # write args to a temporary buffer until all the arguments # are fully evaluated. if args_as_tuple.contains_self_call: copy_args = ["seq"] # TODO deallocate me tmp_args_buf = IRnode(context.new_internal_variable(dst_tuple_t), typ=dst_tuple_t, location=MEMORY) copy_args.append( # --> args evaluate here <-- make_setter(tmp_args_buf, args_as_tuple)) copy_args.append(make_setter(args_dst, tmp_args_buf)) else: copy_args = make_setter(args_dst, args_as_tuple) goto_op = ["goto", sig.internal_function_label] # pass return buffer to subroutine if return_buffer is not None: goto_op += [return_buffer] # pass return label to subroutine goto_op += [push_label_to_stack(return_label)] call_sequence = ["seq"] call_sequence.append( eval_once_check(_freshname(stmt_expr.node_source_code))) call_sequence.extend( [copy_args, goto_op, ["label", return_label, ["var_list"], "pass"]]) if return_buffer is not None: # push return buffer location to stack call_sequence += [return_buffer] o = IRnode.from_list( call_sequence, typ=sig.return_type, location=MEMORY, annotation=stmt_expr.get("node_source_code"), add_gas_estimate=sig.gas_estimate, ) o.is_self_call = True return o
def assign(self): # Assignment (e.g. x[4] = y) if len(self.stmt.targets) != 1: raise StructureException( "Assignment statement must have one target", self.stmt) 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 not isinstance( sub.typ, ContractType): 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_func(code, _globals, sigs, origcode, _vars=None): if _vars is None: _vars = {} sig = FunctionSignature.from_definition(code, sigs) # Check for duplicate variables with globals for arg in sig.args: if arg.name in _globals: raise VariableDeclarationException( "Variable name duplicated between function arguments and globals: " + arg.name) # Create a context context = Context(vars=_vars, globals=_globals, sigs=sigs, return_type=sig.output_type, is_constant=sig.const, is_payable=sig.payable, origcode=origcode) # Copy calldata to memory for fixed-size arguments copy_size = sum([ 32 if isinstance(arg.typ, ByteArrayType) else get_size_of_type(arg.typ) * 32 for arg in sig.args ]) context.next_mem += copy_size if not len(sig.args): copier = 'pass' elif sig.name == '__init__': copier = [ 'codecopy', MemoryPositions.RESERVED_MEMORY, '~codelen', copy_size ] else: copier = [ 'calldatacopy', MemoryPositions.RESERVED_MEMORY, 4, copy_size ] clampers = [copier] # Add asserts for payable and internal if not sig.payable: clampers.append(['assert', ['iszero', 'callvalue']]) if sig.private: clampers.append(['assert', ['eq', 'caller', 'address']]) # Fill in variable positions for arg in sig.args: clampers.append( make_clamper(arg.pos, context.next_mem, arg.typ, sig.name == '__init__')) if isinstance(arg.typ, ByteArrayType): context.vars[arg.name] = VariableRecord(arg.name, context.next_mem, arg.typ, False) context.next_mem += 32 * get_size_of_type(arg.typ) else: context.vars[arg.name] = VariableRecord( arg.name, MemoryPositions.RESERVED_MEMORY + arg.pos, arg.typ, False) # Create "clampers" (input well-formedness checkers) # Return function body if sig.name == '__init__': o = LLLnode.from_list(['seq'] + clampers + [parse_body(code.body, context)], pos=getpos(code)) else: method_id_node = LLLnode.from_list(sig.method_id, pos=getpos(code), annotation='%s' % sig.name) o = LLLnode.from_list([ 'if', ['eq', ['mload', 0], method_id_node], ['seq'] + clampers + [parse_body(c, context) for c in code.body] + ['stop'] ], typ=None, pos=getpos(code)) # Check for at leasts one return statement if necessary. if context.return_type and context.function_return_count == 0: raise StructureException( "Missing return statement in function '%s' " % sig.name, code) o.context = context o.total_gas = o.gas + calc_mem_gas(o.context.next_mem) o.func_name = sig.name return o