def get_type_from_abi( abi_type: Dict, location: DataLocation = DataLocation.UNSET, is_constant: bool = False, is_public: bool = False, ) -> BaseTypeDefinition: """ Return a type object from an ABI type definition. Arguments --------- abi_type : Dict A type definition taken from the `input` or `output` field of an ABI. Returns ------- BaseTypeDefinition Type definition object. """ type_string = abi_type["type"] if type_string == "fixed168x10": type_string = "decimal" if type_string in ("string", "bytes"): type_string = type_string.capitalize() namespace = get_namespace() if "[" in type_string: value_type_string, length_str = type_string.rsplit("[", maxsplit=1) try: length = int(length_str.rstrip("]")) except ValueError: raise UnknownType( f"ABI type has an invalid length: {type_string}") from None try: value_type = get_type_from_abi({"type": value_type_string}, location=location, is_constant=is_constant) except UnknownType: raise UnknownType( f"ABI contains unknown type: {type_string}") from None try: return ArrayDefinition(value_type, length, location=location, is_constant=is_constant, is_public=is_public) except InvalidType: raise UnknownType( f"ABI contains unknown type: {type_string}") from None else: try: return namespace[type_string]._type(location=location, is_constant=is_constant, is_public=is_public) except KeyError: raise UnknownType( f"ABI contains unknown type: {type_string}") from None
def get_type_from_annotation( node: vy_ast.VyperNode, location: DataLocation, is_constant: bool = False, is_public: bool = False, is_immutable: bool = False, ) -> BaseTypeDefinition: """ Return a type object for the given AST node. Arguments --------- node : VyperNode Vyper ast node from the `annotation` member of a `VariableDef` or `AnnAssign` node. Returns ------- BaseTypeDefinition Type definition object. """ namespace = get_namespace() if isinstance(node, vy_ast.Tuple): values = node.elements types = tuple( get_type_from_annotation(v, DataLocation.UNSET) for v in values) return TupleDefinition(types) try: # get id of leftmost `Name` node from the annotation type_name = next( i.id for i in node.get_descendants(vy_ast.Name, include_self=True)) except StopIteration: raise StructureException("Invalid syntax for type declaration", node) try: type_obj = namespace[type_name] except UndeclaredDefinition: suggestions_str = get_levenshtein_error_suggestions( type_name, namespace, 0.3) raise UnknownType( f"No builtin or user-defined type named '{type_name}'. {suggestions_str}", node) from None if (getattr(type_obj, "_as_array", False) and isinstance(node, vy_ast.Subscript) and node.value.get("id") != "DynArray"): # TODO: handle `is_immutable` for arrays # if type can be an array and node is a subscript, create an `ArrayDefinition` length = get_index_value(node.slice) value_type = get_type_from_annotation(node.value, location, is_constant, False, is_immutable) return ArrayDefinition(value_type, length, location, is_constant, is_public, is_immutable) try: return type_obj.from_annotation(node, location, is_constant, is_public, is_immutable) except AttributeError: raise InvalidType(f"'{type_name}' is not a valid type", node) from None
def namespace(): """ Yields a clean `Namespace` object. """ obj = get_namespace() obj.clear() yield obj obj.clear()
def validate_functions(vy_module: vy_ast.Module) -> None: """Analyzes a vyper ast and validates the function-level namespaces.""" err_list = ExceptionList() namespace = get_namespace() for node in vy_module.get_children(vy_ast.FunctionDef): with namespace.enter_scope(): try: FunctionNodeVisitor(vy_module, node, namespace) except VyperException as e: err_list.append(e) err_list.raise_if_not_empty()
def get_type_from_annotation( node: vy_ast.VyperNode, location: DataLocation, is_immutable: bool = False, is_public: bool = False, ) -> BaseTypeDefinition: """ Return a type object for the given AST node. Arguments --------- node : VyperNode Vyper ast node from the `annotation` member of an `AnnAssign` node. Returns ------- BaseTypeDefinition Type definition object. """ namespace = get_namespace() try: # get id of leftmost `Name` node from the annotation type_name = next( i.id for i in node.get_descendants(vy_ast.Name, include_self=True)) except StopIteration: raise StructureException("Invalid syntax for type declaration", node) try: type_obj = namespace[type_name] except UndeclaredDefinition: raise UnknownType( f"No builtin or user-defined type named '{type_name}'", node) from None if getattr(type_obj, "_as_array", False) and isinstance( node, vy_ast.Subscript): # if type can be an array and node is a subscript, create an `ArrayDefinition` length = get_index_value(node.slice) value_type = get_type_from_annotation(node.value, location, is_immutable, False) return ArrayDefinition(value_type, length, location, is_immutable, is_public) try: return type_obj.from_annotation(node, location, is_immutable, is_public) except AttributeError: raise InvalidType(f"'{type_name}' is not a valid type", node) from None
def validate_implements(self, node: vy_ast.AnnAssign) -> None: namespace = get_namespace() # check for missing functions unimplemented = [ name for name, type_ in self.members.items() if name not in namespace["self"].members or not hasattr(namespace["self"].members[name], "compare_signature") or not namespace["self"].members[name].compare_signature(type_) ] # check for missing events unimplemented += [ name for name, event in self.events.items() if name not in namespace or not isinstance(namespace[name], Event) or namespace[name].event_id != event.event_id ] if unimplemented: missing_str = ", ".join(sorted(unimplemented)) raise InterfaceViolation( f"Contract does not implement all interface functions or events: {missing_str}", node, )
def test_get_namespace(): ns = get_namespace() ns2 = get_namespace() assert ns == ns2
def add_module_namespace(vy_module: vy_ast.Module, interface_codes: InterfaceDict) -> None: """Analyzes a Vyper ast and adds all module-level objects to the namespace.""" namespace = get_namespace() ModuleNodeVisitor(vy_module, interface_codes, namespace)
def from_FunctionDef( cls, node: vy_ast.FunctionDef, is_interface: Optional[bool] = False) -> "ContractFunction": """ Generate a `ContractFunction` object from a `FunctionDef` node. Arguments --------- node : FunctionDef Vyper ast node to generate the function definition from. is_interface: bool, optional Boolean indicating if the function definition is part of an interface. Returns ------- ContractFunction """ kwargs: Dict[str, Any] = {} if is_interface: # FunctionDef with stateMutability in body (Interface defintions) if (len(node.body) == 1 and isinstance(node.body[0], vy_ast.Expr) and isinstance(node.body[0].value, vy_ast.Name) and StateMutability.is_valid_value(node.body[0].value.id)): # Interfaces are always public kwargs["function_visibility"] = FunctionVisibility.EXTERNAL kwargs["state_mutability"] = StateMutability( node.body[0].value.id) elif len(node.body) == 1 and node.body[0].get("value.id") in ( "constant", "modifying"): if node.body[0].value.id == "constant": expected = "view or pure" else: expected = "payable or nonpayable" raise StructureException( f"State mutability should be set to {expected}", node.body[0]) else: raise StructureException( "Body must only contain state mutability label", node.body[0]) else: # FunctionDef with decorators (normal functions) for decorator in node.decorator_list: if isinstance(decorator, vy_ast.Call): if "nonreentrant" in kwargs: raise StructureException( "nonreentrant decorator is already set with key: " f"{kwargs['nonreentrant']}", node, ) if decorator.get("func.id") != "nonreentrant": raise StructureException("Decorator is not callable", decorator) if len(decorator.args) != 1 or not isinstance( decorator.args[0], vy_ast.Str): raise StructureException( "@nonreentrant name must be given as a single string literal", decorator) if node.name == "__init__": msg = "Nonreentrant decorator disallowed on `__init__`" raise FunctionDeclarationException(msg, decorator) kwargs["nonreentrant"] = decorator.args[0].value elif isinstance(decorator, vy_ast.Name): if FunctionVisibility.is_valid_value(decorator.id): if "function_visibility" in kwargs: raise FunctionDeclarationException( f"Visibility is already set to: {kwargs['function_visibility']}", node, ) kwargs["function_visibility"] = FunctionVisibility( decorator.id) elif StateMutability.is_valid_value(decorator.id): if "state_mutability" in kwargs: raise FunctionDeclarationException( f"Mutability is already set to: {kwargs['state_mutability']}", node) kwargs["state_mutability"] = StateMutability( decorator.id) else: if decorator.id == "constant": warnings.warn( "'@constant' decorator has been removed (see VIP2040). " "Use `@view` instead.", DeprecationWarning, ) raise FunctionDeclarationException( f"Unknown decorator: {decorator.id}", decorator) else: raise StructureException("Bad decorator syntax", decorator) if "function_visibility" not in kwargs: raise FunctionDeclarationException( f"Visibility must be set to one of: {', '.join(FunctionVisibility.values())}", node) if node.name == "__default__": if kwargs["function_visibility"] != FunctionVisibility.EXTERNAL: raise FunctionDeclarationException( "Default function must be marked as `@external`", node) if node.args.args: raise FunctionDeclarationException( "Default function may not receive any arguments", node.args.args[0]) if "state_mutability" not in kwargs: # Assume nonpayable if not set at all (cannot accept Ether, but can modify state) kwargs["state_mutability"] = StateMutability.NONPAYABLE if kwargs[ "state_mutability"] == StateMutability.PURE and "nonreentrant" in kwargs: raise StructureException( "Cannot use reentrancy guard on pure functions", node) # call arguments if node.args.defaults and node.name == "__init__": raise FunctionDeclarationException( "Constructor may not use default arguments", node.args.defaults[0]) arguments = OrderedDict() max_arg_count = len(node.args.args) min_arg_count = max_arg_count - len(node.args.defaults) defaults = [None] * min_arg_count + node.args.defaults namespace = get_namespace() for arg, value in zip(node.args.args, defaults): if arg.arg in ("gas", "value", "skip_contract_check", "default_return_value"): raise ArgumentException( f"Cannot use '{arg.arg}' as a variable name in a function input", arg) if arg.arg in arguments: raise ArgumentException( f"Function contains multiple inputs named {arg.arg}", arg) if arg.arg in namespace: raise NamespaceCollision(arg.arg, arg) if arg.annotation is None: raise ArgumentException( f"Function argument '{arg.arg}' is missing a type", arg) type_definition = get_type_from_annotation( arg.annotation, location=DataLocation.CALLDATA, is_constant=True) if value is not None: if not check_kwargable(value): raise StateAccessViolation( "Value must be literal or environment variable", value) validate_expected_type(value, type_definition) arguments[arg.arg] = type_definition # return types if node.returns is None: return_type = None elif node.name == "__init__": raise FunctionDeclarationException( "Constructor may not have a return type", node.returns) elif isinstance(node.returns, (vy_ast.Name, vy_ast.Call, vy_ast.Subscript)): return_type = get_type_from_annotation( node.returns, location=DataLocation.MEMORY) elif isinstance(node.returns, vy_ast.Tuple): tuple_types: Tuple = () for n in node.returns.elements: tuple_types += (get_type_from_annotation( n, location=DataLocation.MEMORY), ) return_type = TupleDefinition(tuple_types) else: raise InvalidType( "Function return value must be a type name or tuple", node.returns) return cls(node.name, arguments, min_arg_count, max_arg_count, return_type, **kwargs)
def __init__(self): self.namespace = get_namespace()