def _validate_arg_types(self, node): num_args = len( self._inputs) # the number of args the signature indicates expect_num_args = num_args if self._has_varargs: # note special meaning for -1 in validate_call_args API expect_num_args = (num_args, -1) validate_call_args(node, expect_num_args, self._kwargs) for arg, (_, expected) in zip(node.args, self._inputs): self._validate_single(arg, expected) for kwarg in node.keywords: kwarg_settings = self._kwargs[kwarg.arg] if kwarg_settings.require_literal and not isinstance( kwarg.value, vy_ast.Constant): raise TypeMismatch("Value for kwarg must be a literal", kwarg.value) self._validate_single(kwarg.value, kwarg_settings.typ) # typecheck varargs. we don't have type info from the signature, # so ensure that the types of the args can be inferred exactly. varargs = node.args[num_args:] if len(varargs) > 0: assert self._has_varargs # double check validate_call_args for arg in varargs: # call get_exact_type_from_node for its side effects - # ensures the type can be inferred exactly. get_exact_type_from_node(arg)
def visit_Log(self, node): if not isinstance(node.value, vy_ast.Call): raise StructureException("Log must call an event", node) event = get_exact_type_from_node(node.value.func) if not isinstance(event, Event): raise StructureException("Value is not an event", node.value) event.fetch_call_return(node.value)
def visit_Expr(self, node): if not isinstance(node.value, vy_ast.Call): raise StructureException( "Expressions without assignment are disallowed", node) fn_type = get_exact_type_from_node(node.value.func) if isinstance(fn_type, Event): raise StructureException( "To call an event you must use the `log` statement", node) if isinstance(fn_type, ContractFunction): if (fn_type.mutability > StateMutability.VIEW and self.func.mutability <= StateMutability.VIEW): raise StateAccessViolation( f"Cannot call a mutating function from a {self.func.mutability.value} function", node, ) if self.func.mutability == StateMutability.PURE: raise StateAccessViolation( f"Cannot call any function from a {self.func.mutability.value} function", node) return_value = fn_type.fetch_call_return(node.value) if (return_value and not isinstance(fn_type, MemberFunctionDefinition) and not isinstance(fn_type, ContractFunction)): raise StructureException( f"Function '{fn_type._id}' cannot be called without assigning the result", node) self.expr_visitor.visit(node.value)
def visit_Subscript(self, node, type_): base_type = get_exact_type_from_node(node.value) if isinstance(base_type, BaseTypeDefinition): # in the vast majority of cases `base_type` is a type definition, # however there are some edge cases with args to builtin functions self.visit(node.slice, base_type.get_index_type(node.slice.value)) self.visit(node.value, base_type)
def visit_Call(self, node, type_): call_type = get_exact_type_from_node(node.func) node_type = type_ or call_type.fetch_call_return(node) node._metadata["type"] = node_type self.visit(node.func) if isinstance(call_type, ContractFunction) and call_type.is_internal: self.func.called_functions.add(call_type) if isinstance(call_type, (Event, ContractFunction)): # events and function calls for arg, arg_type in zip(node.args, list(call_type.arguments.values())): self.visit(arg, arg_type) for kwarg in node.keywords: # We should only see special kwargs self.visit(kwarg.value, call_type.call_site_kwargs[kwarg.arg].typ) elif isinstance(call_type, StructPrimitive): # literal structs for value, arg_type in zip(node.args[0].values, list(call_type.members.values())): self.visit(value, arg_type) elif isinstance(call_type, MemberFunctionDefinition): assert len(node.args) == len(call_type.arg_types) for arg, arg_type in zip(node.args, call_type.arg_types): self.visit(arg, arg_type) else: # builtin functions arg_types = call_type.infer_arg_types(node) for arg, arg_type in zip(node.args, arg_types): self.visit(arg, arg_type) kwarg_types = call_type.infer_kwarg_types(node) for kwarg in node.keywords: self.visit(kwarg.value, kwarg_types[kwarg.arg])
def _is_terminus_node(node: vy_ast.VyperNode) -> bool: if getattr(node, "_is_terminus", None): return True if isinstance(node, vy_ast.Expr) and isinstance(node.value, vy_ast.Call): func = get_exact_type_from_node(node.value.func) if getattr(func, "_is_terminus", None): return True return False
def infer_arg_types(self, node): self._validate_arg_types(node) ret = [expected for (_, expected) in self._inputs] # handle varargs. n_known_args = len(self._inputs) varargs = node.args[n_known_args:] if len(varargs) > 0: assert self._has_varargs ret.extend(get_exact_type_from_node(arg) for arg in varargs) return ret
def check_kwargable(node: vy_ast.VyperNode) -> bool: """ Check if the given node can be used as a default arg """ if _check_literal(node): return True if isinstance(node, (vy_ast.Tuple, vy_ast.List)): return all(check_kwargable(item) for item in node.elements) if isinstance(node, vy_ast.Call): args = node.args if len(args) == 1 and isinstance(args[0], vy_ast.Dict): return all(check_kwargable(v) for v in args[0].values) call_type = get_exact_type_from_node(node.func) if getattr(call_type, "_kwargable", False): return True value_type = get_exact_type_from_node(node) # is_constant here actually means not_assignable, and is to be renamed return getattr(value_type, "is_constant", False)
def visit_AugAssign(self, node): if isinstance(node.value, vy_ast.Tuple): raise StructureException( "Right-hand side of assignment cannot be a tuple", node.value) target = get_exact_type_from_node(node.target) validate_expected_type(node.value, target) target.validate_modification(node, self.func.mutability) self.expr_visitor.visit(node.value)
def visit_AugAssign(self, node): if isinstance(node.value, vy_ast.Tuple): raise StructureException( "Right-hand side of assignment cannot be a tuple", node.value) target = get_exact_type_from_node(node.target) validate_expected_type(node.value, target) if self.func.mutability <= StateMutability.VIEW and target.location == DataLocation.STORAGE: raise StateAccessViolation( f"Cannot modify storage in a {self.func.mutability.value} function", node) target.validate_modification(node)
def check_constant(node: vy_ast.VyperNode) -> bool: """ Check if the given node is a literal or constant value. """ if check_literal(node): return True if isinstance(node, (vy_ast.Tuple, vy_ast.List)): for item in node.elements: if not check_constant(item): return False return True value_type = get_exact_type_from_node(node) return getattr(value_type, "is_constant", False)
def visit_Compare(self, node, type_): if isinstance(node.op, (vy_ast.In, vy_ast.NotIn)): if isinstance(node.right, vy_ast.List): type_ = get_common_types(node.left, *node.right.elements).pop() self.visit(node.left, type_) for element in node.right.elements: self.visit(element, type_) else: type_ = get_exact_type_from_node(node.right) self.visit(node.right, type_) self.visit(node.left, type_.value_type) else: type_ = get_common_types(node.left, node.right).pop() self.visit(node.left, type_) self.visit(node.right, type_)
def visit_Compare(self, node, type_): if isinstance(node.op, (vy_ast.In, vy_ast.NotIn)): if isinstance(node.right, vy_ast.List): type_ = get_common_types(node.left, *node.right.elements).pop() self.visit(node.left, type_) rlen = len(node.right.elements) self.visit(node.right, ArrayDefinition(type_, rlen)) else: type_ = get_exact_type_from_node(node.right) self.visit(node.right, type_) self.visit(node.left, type_.value_type) else: type_ = get_common_types(node.left, node.right).pop() self.visit(node.left, type_) self.visit(node.right, type_)
def _validate_address_code_attribute(node: vy_ast.Attribute) -> None: value_type = get_exact_type_from_node(node.value) if isinstance(value_type, AddressDefinition) and node.attr == "code": # Validate `slice(<address>.code, start, length)` where `length` is constant parent = node.get_ancestor() if isinstance(parent, vy_ast.Call): ok_func = isinstance(parent.func, vy_ast.Name) and parent.func.id == "slice" ok_args = len(parent.args) == 3 and isinstance( parent.args[2], vy_ast.Int) if ok_func and ok_args: return raise StructureException( "(address).code is only allowed inside of a slice function with a constant length", node)
def check_constant(node: vy_ast.VyperNode) -> bool: """ Check if the given node is a literal or constant value. """ if _check_literal(node): return True if isinstance(node, (vy_ast.Tuple, vy_ast.List)): return all(check_constant(item) for item in node.elements) if isinstance(node, vy_ast.Call): args = node.args if len(args) == 1 and isinstance(args[0], vy_ast.Dict): return all(check_constant(v) for v in args[0].values) call_type = get_exact_type_from_node(node.func) if getattr(call_type, "_kwargable", False): return True return False
def visit_Subscript(self, node, type_): if isinstance(node.value, vy_ast.List): possible_base_types = get_possible_types_from_node(node.value) if len(possible_base_types) == 1: base_type = possible_base_types.pop() elif type_ and len(possible_base_types) > 1: for possible_type in possible_base_types: if isinstance(possible_type.value_type, type(type_)): base_type = possible_type break else: base_type = get_exact_type_from_node(node.value) if isinstance(base_type, BaseTypeDefinition): # in the vast majority of cases `base_type` is a type definition, # however there are some edge cases with args to builtin functions self.visit(node.slice, base_type.get_index_type(node.slice.value)) self.visit(node.value, base_type)
def visit_Call(self, node, type_): call_type = get_exact_type_from_node(node.func) node._metadata["type"] = type_ or call_type.fetch_call_return(node) self.visit(node.func) if isinstance(call_type, (Event, ContractFunction)): # events and internal function calls for arg, arg_type in zip(node.args, list(call_type.arguments.values())): self.visit(arg, arg_type) elif isinstance(call_type, StructPrimitive): # literal structs for value, arg_type in zip(node.args[0].values, list(call_type.members.values())): self.visit(value, arg_type) elif node.func.id not in ("empty", "range"): # builtin functions for arg in node.args: self.visit(arg, None) for kwarg in node.keywords: self.visit(kwarg.value, None)
def visit_Subscript(self, node, type_): node._metadata["type"] = type_ if isinstance(type_, TypeTypeDefinition): # don't recurse; can't annotate AST children of type definition return if isinstance(node.value, vy_ast.List): possible_base_types = get_possible_types_from_node(node.value) if len(possible_base_types) == 1: base_type = possible_base_types.pop() elif type_ is not None and len(possible_base_types) > 1: for possible_type in possible_base_types: if isinstance(possible_type.value_type, type(type_)): base_type = possible_type break else: base_type = get_exact_type_from_node(node.value) self.visit(node.slice, base_type.get_index_type()) self.visit(node.value, base_type)
def visit_AugAssign(self, node): type_ = get_exact_type_from_node(node.target) self.expr_visitor.visit(node.target, type_) self.expr_visitor.visit(node.value, type_)
def visit_Name(self, node, type_): node._metadata["type"] = get_exact_type_from_node(node)
def visit_Attribute(self, node, type_): base_type = get_exact_type_from_node(node.value) node._metadata["type"] = base_type.get_member(node.attr, None) self.visit(node.value, None)
def visit_Name(self, node, type_): if isinstance(type_, TypeTypeDefinition): node._metadata["type"] = type_ else: node._metadata["type"] = get_exact_type_from_node(node)