def struct_literals(expr, name, context): o = {} members = {} for key, value in zip(expr.keys, expr.values): if not isinstance(key, ast.Name): raise TypeMismatchException( "Invalid member variable for struct: %r" % vars(key).get('id', key), key, ) check_valid_varname( key.id, context.custom_units, context.structs, context.constants, "Invalid member variable for struct", ) if key.id in o: raise TypeMismatchException( "Member variable duplicated: " + key.id, key) o[key.id] = Expr(value, context).lll_node members[key.id] = o[key.id].typ return LLLnode.from_list( ["multi"] + [o[key] for key in (list(o.keys()))], typ=StructType(members, name, is_literal=True), pos=getpos(expr), )
def make_struct_type(name, location, members, custom_units, custom_structs, constants): o = OrderedDict() for key, value in members: if not isinstance(key, ast.Name): raise InvalidTypeException( "Invalid member variable for struct %r, expected a name." % key.id, key, ) check_valid_varname( key.id, custom_units, custom_structs, constants, "Invalid member variable for struct", ) o[key.id] = parse_type( value, location, custom_units=custom_units, custom_structs=custom_structs, constants=constants, ) return StructType(o, name)
def struct_literals(expr, name, context): member_subs = {} member_typs = {} for key, value in zip(expr.keys, expr.values): if not isinstance(key, vy_ast.Name): raise TypeMismatch( f"Invalid member variable for struct: {getattr(key, 'id', '')}", key, ) check_valid_varname( key.id, context.structs, context.constants, "Invalid member variable for struct", ) if key.id in member_subs: raise TypeMismatch("Member variable duplicated: " + key.id, key) sub = Expr(value, context).lll_node member_subs[key.id] = sub member_typs[key.id] = sub.typ return LLLnode.from_list( ["multi"] + [member_subs[key] for key in member_subs.keys()], typ=StructType(member_typs, name, is_literal=True), pos=getpos(expr), )
def make_struct(self, name, body): members = [] for item in body: if isinstance(item, ast.AnnAssign): member_name = item.target member_type = item.annotation # Check well-formedness of member names if not isinstance(member_name, ast.Name): raise InvalidTypeException( "Invalid member name for struct %r, needs to be a valid name. " % name, item) check_valid_varname(member_name.id, self._custom_units, self._structs, self._constants, item, "Invalid member name for struct. ") # Check well-formedness of member types # Note this kicks out mutually recursive structs, # raising an exception instead of stackoverflow. # A struct must be defined before it is referenced. # This feels like a semantic step and maybe should be pushed # to a later compilation stage. parse_type(member_type, 'storage', custom_units=self._custom_units, custom_structs=self._structs, constants=self._constants) members.append((member_name, member_type)) else: raise StructureException("Structs can only contain variables", item) return members
def is_valid_varname(self, name, pos): # Global context check first. if self.global_ctx.is_valid_varname(name, pos): check_valid_varname( name, custom_structs=self.structs, pos=pos, ) return True
def is_valid_varname(self, name, item): """ Valid variable name, checked against global context. """ check_valid_varname(name, self._structs, self._constants, item) if name in self._globals: raise VariableDeclarationException( f'Invalid name "{name}", previously defined as global.', item) return True
def is_valid_varname(self, name, pos): # Global context check first. if self.global_ctx.is_valid_varname(name, pos): check_valid_varname(name, custom_units=self.custom_units, custom_structs=self.structs, constants=self.constants, pos=pos) # Local context duplicate context check. if any((name in self.vars, name in self.globals, name in self.constants)): raise VariableDeclarationException("Duplicate variable name: %s" % name, name) return True
def is_valid_varname(self, name, pos): # Global context check first. if self.global_ctx.is_valid_varname(name, pos): check_valid_varname( name, custom_structs=self.structs, constants=self.constants, pos=pos, ) # Local context duplicate context check. if any((name in self.vars, name in self.globals, name in self.constants)): raise TypeCheckFailure(f"Duplicate variable name: {name}") return True
def from_declaration(cls, class_node, global_ctx): name = class_node.name pos = 0 check_valid_varname( name, global_ctx._structs, global_ctx._constants, pos=class_node, error_prefix="Event name invalid. ", exc=EventDeclarationException, ) args = [] indexed_list = [] if len(class_node.body) != 1 or not isinstance(class_node.body[0], vy_ast.Pass): for node in class_node.body: arg_item = node.target arg = node.target.id typ = node.annotation if isinstance(typ, vy_ast.Call) and typ.get("func.id") == "indexed": indexed_list.append(True) typ = typ.args[0] else: indexed_list.append(False) check_valid_varname( arg, global_ctx._structs, global_ctx._constants, pos=arg_item, error_prefix="Event argument name invalid or reserved.", ) if arg in (x.name for x in args): raise TypeCheckFailure( f"Duplicate function argument name: {arg}") # Can struct be logged? parsed_type = global_ctx.parse_type(typ, None) args.append(VariableRecord(arg, pos, parsed_type, False)) if isinstance(parsed_type, ByteArrayType): pos += ceil32(typ.slice.value.n) else: pos += get_size_of_type(parsed_type) * 32 sig = (name + "(" + ",".join([ canonicalize_type(arg.typ, indexed_list[pos]) for pos, arg in enumerate(args) ]) + ")") # noqa F812 event_id = bytes_to_int(keccak256(bytes(sig, "utf-8"))) return cls(name, args, indexed_list, event_id, sig)
def struct_literals(expr, name, context): member_subs = {} member_typs = {} for key, value in zip(expr.keys, expr.values): if not isinstance(key, vy_ast.Name): return check_valid_varname( key.id, context.structs, "Invalid member variable for struct", ) if key.id in member_subs: return sub = Expr(value, context).lll_node member_subs[key.id] = sub member_typs[key.id] = sub.typ return LLLnode.from_list( ["multi"] + [member_subs[key] for key in member_subs.keys()], typ=StructType(member_typs, name, is_literal=True), pos=getpos(expr), )
def new_variable(self, name: str, typ: NodeType, pos: VyperNode = None) -> int: """ Allocate memory for a user-defined variable. Arguments --------- name : str Name of the variable typ : NodeType Variable type, used to determine the size of memory allocation pos : VyperNode AST node corresponding to the location where the variable was created, used for annotating exceptions Returns ------- int Memory offset for the variable """ self.global_ctx.is_valid_varname(name, pos) check_valid_varname(name, custom_structs=self.structs, pos=pos) var_size = 32 * get_size_of_type(typ) return self._new_variable(name, typ, var_size, False)
def add_globals_and_events(self, item): item_attributes = {"public": False} # 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) # If the type declaration is of the form public(<type here>), then proceed with # the underlying type but also add getters elif self.get_call_func_name(item) == "address": if item.annotation.args[0].id not in premade_contracts: raise VariableDeclarationException( "Unsupported premade contract declaration", item.annotation.args[0]) premade_contract = premade_contracts[item.annotation.args[0].id] self._contracts[item.target.id] = self.make_contract( premade_contract.body) self._globals[item.target.id] = VariableRecord( item.target.id, len(self._globals), BaseType('address'), True) elif item_name in self._contracts: 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 from_definition(cls, code, sigs=None, custom_units=None, custom_structs=None, contract_def=False, constants=None, constant_override=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) # Validate default values. for default_value in getattr(code.args, 'defaults', []): validate_default_values(default_value) # 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 const = constant_override payable = False private = False public = False nonreentrant_key = '' # Update function properties from decorators 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 nonreentrant_key: raise StructureException( "Only one @nonreentrant decorator allowed per function", dec) 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( f"Cannot use public and private decorators on the same function: {name}" ) if payable and const: raise StructureException( f"Function {name} cannot be both constant and payable.") if payable and private: raise StructureException( f"Function {name} cannot be both private and payable.") if (not public and not private) and not contract_def: raise StructureException( "Function visibility must be declared (@public or @private)", code, ) if const and nonreentrant_key: raise StructureException( "@nonreentrant makes no sense on a @constant function.", code) # 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( f"Output type invalid or unsupported: {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(keccak256(bytes(sig, 'utf-8'))[:4]) return cls(name, args, output_type, const, payable, private, nonreentrant_key, sig, method_id, custom_units, code)
def add_globals_and_events(self, item): item_attributes = {"public": False} if len(self._globals) > NONRENTRANT_STORAGE_OFFSET: raise ParserException( 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, 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, 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 from_declaration(cls, code, global_ctx): name = code.target.id pos = 0 check_valid_varname( name, global_ctx._custom_units, global_ctx._structs, global_ctx._constants, pos=code, error_prefix="Event name invalid. ", exc=EventDeclarationException ) # Determine the arguments, expects something of the form def foo(arg1: num, arg2: num ... args = [] indexed_list = [] topics_count = 1 if code.annotation.args: keys = code.annotation.args[0].keys values = code.annotation.args[0].values for i in range(len(keys)): typ = values[i] if not isinstance(keys[i], ast.Name): raise EventDeclarationException( 'Invalid key type, expected a valid name.', keys[i], ) if not isinstance(typ, (ast.Name, ast.Call, ast.Subscript)): raise EventDeclarationException('Invalid event argument type.', typ) if isinstance(typ, ast.Call) and not isinstance(typ.func, ast.Name): raise EventDeclarationException('Invalid event argument type', typ) arg = keys[i].id arg_item = keys[i] is_indexed = False # Check to see if argument is a topic if isinstance(typ, ast.Call) and typ.func.id == 'indexed': typ = values[i].args[0] indexed_list.append(True) topics_count += 1 is_indexed = True else: indexed_list.append(False) if isinstance(typ, ast.Subscript) and getattr(typ.value, 'id', None) == 'bytes' and typ.slice.value.n > 32 and is_indexed: # noqa: E501 raise EventDeclarationException("Indexed arguments are limited to 32 bytes") if topics_count > 4: raise EventDeclarationException( "Maximum of 3 topics {} given".format(topics_count - 1), arg, ) if not isinstance(arg, str): raise VariableDeclarationException("Argument name invalid", arg) if not typ: raise InvalidTypeException("Argument must have type", arg) check_valid_varname( arg, global_ctx._custom_units, global_ctx._structs, global_ctx._constants, pos=arg_item, error_prefix="Event argument name invalid or reserved.", ) if arg in (x.name for x in args): raise VariableDeclarationException( "Duplicate function argument name: " + arg, arg_item, ) # Can struct be logged? parsed_type = global_ctx.parse_type(typ, None) args.append(VariableRecord(arg, pos, parsed_type, False)) if isinstance(parsed_type, ByteArrayType): pos += ceil32(typ.slice.value.n) else: pos += get_size_of_type(parsed_type) * 32 sig = name + '(' + ','.join([canonicalize_type(arg.typ, indexed_list[pos]) for pos, arg in enumerate(args)]) + ')' # noqa F812 event_id = bytes_to_int(sha3(bytes(sig, 'utf-8'))) return cls(name, args, indexed_list, event_id, sig)
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, ByteArrayType): mem_pos += 32 else: mem_pos += get_size_of_type(parsed_type) * 32 # Apply decorators const, payable, private, public = 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 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: 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, sig, method_id, custom_units)
def from_definition( cls, code, sigs=None, custom_structs=None, interface_def=False, constants=None, constant_override=False, is_from_json=False, ): if not custom_structs: custom_structs = {} name = code.name mem_pos = 0 valid_name, msg = is_varname_valid(name, custom_structs, constants) if not valid_name and (not name.lower() in FUNCTION_WHITELIST): raise FunctionDeclarationException("Function name invalid. " + msg, code) # Validate default values. for default_value in getattr(code.args, "defaults", []): validate_default_values(default_value) # 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 InvalidType("Argument must have type", arg) # Validate arg name. check_valid_varname( arg.arg, 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_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 mutability = "nonpayable" # Assume nonpayable by default internal = False external = False nonreentrant_key = "" # Update function properties from decorators # NOTE: Can't import enums here because of circular import for dec in code.decorator_list: if isinstance(dec, vy_ast.Name) and dec.id in ("payable", "view", "pure"): mutability = dec.id elif isinstance(dec, vy_ast.Name) and dec.id == "internal": internal = True elif isinstance(dec, vy_ast.Name) and dec.id == "external": external = True elif isinstance(dec, vy_ast.Call) and dec.func.id == "nonreentrant": if nonreentrant_key: raise StructureException( "Only one @nonreentrant decorator allowed per function", dec ) if ( dec.args and len(dec.args) == 1 and isinstance(dec.args[0], vy_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 constant_override: # In case this override is abused, match previous behavior if mutability == "payable": raise StructureException(f"Function {name} cannot be both constant and payable.") mutability = "view" if external and internal: raise StructureException( f"Cannot use external and internal decorators on the same function: {name}" ) if mutability == "payable" and internal: raise StructureException(f"Function {name} cannot be both internal and payable.") if (not external and not internal) and not interface_def: raise StructureException( "Function visibility must be declared (@external or @internal)", code, ) if mutability in ("view", "pure") and nonreentrant_key: raise StructureException( f"@nonreentrant makes no sense on a @{mutability} function.", code ) # 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, (vy_ast.Name, vy_ast.Compare, vy_ast.Subscript, vy_ast.Call, vy_ast.Tuple) ): output_type = parse_type( code.returns, None, sigs, custom_structs=custom_structs, constants=constants, ) else: raise InvalidType( f"Output type invalid or unsupported: {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_structs, constants) # Take the first 4 bytes of the hash of the sig to get the method ID method_id = fourbytes_to_int(keccak256(bytes(sig, "utf-8"))[:4]) return cls( name, args, output_type, mutability, internal, nonreentrant_key, sig, method_id, code, is_from_json, )
def is_valid_varname(self, name, item): """ Valid variable name, checked against global context. """ check_valid_varname(name, self._structs, item) return True