Пример #1
0
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()
Пример #2
0
    def __init__(
        self, module_node: vy_ast.Module, interface_codes: InterfaceDict, namespace: dict
    ) -> None:
        self.ast = module_node
        self.interface_codes = interface_codes or {}
        self.namespace = namespace

        module_nodes = module_node.body.copy()
        while module_nodes:
            count = len(module_nodes)
            err_list = ExceptionList()
            for node in list(module_nodes):
                try:
                    self.visit(node)
                    module_nodes.remove(node)
                except (InvalidLiteral, InvalidType, VariableDeclarationException):
                    # these exceptions cannot be caused by another statement not yet being
                    # parsed, so we raise them immediately
                    raise
                except VyperException as e:
                    err_list.append(e)

            # Only raise if no nodes were successfully processed. This allows module
            # level logic to parse regardless of the ordering of code elements.
            if count == len(module_nodes):
                err_list.raise_if_not_empty()

        # check for collisions between 4byte function selectors
        # internal functions are intentionally included in this check, to prevent breaking
        # changes in in case of a future change to their calling convention
        self_members = namespace["self"].members
        functions = [i for i in self_members.values() if isinstance(i, ContractFunction)]
        validate_unique_method_ids(functions)

        # generate an `InterfacePrimitive` from the top-level node - used for building the ABI
        interface = namespace["interface"].build_primitive_from_node(module_node)
        module_node._metadata["type"] = interface

        # get list of internal function calls made by each function
        function_defs = self.ast.get_children(vy_ast.FunctionDef)
        function_names = set(node.name for node in function_defs)
        for node in function_defs:
            calls_to_self = set(
                i.func.attr for i in node.get_descendants(vy_ast.Call, {"func.value.id": "self"})
            )
            # anything that is not a function call will get semantically checked later
            calls_to_self = calls_to_self.intersection(function_names)
            self_members[node.name].internal_calls = calls_to_self
            if node.name in self_members[node.name].internal_calls:
                self_node = node.get_descendants(
                    vy_ast.Attribute, {"value.id": "self", "attr": node.name}
                )[0]
                raise CallViolation(f"Function '{node.name}' calls into itself", self_node)

        for fn_name in sorted(function_names):

            if fn_name not in self_members:
                # the referenced function does not exist - this is an issue, but we'll report
                # it later when parsing the function so we can give more meaningful output
                continue

            # check for circular function calls
            sequence = _find_cyclic_call([fn_name], self_members)
            if sequence is not None:
                nodes = []
                for i in range(len(sequence) - 1):
                    fn_node = self.ast.get_children(vy_ast.FunctionDef, {"name": sequence[i]})[0]
                    call_node = fn_node.get_descendants(
                        vy_ast.Attribute, {"value.id": "self", "attr": sequence[i + 1]}
                    )[0]
                    nodes.append(call_node)

                raise CallViolation("Contract contains cyclic function call", *nodes)

            # get complete list of functions that are reachable from this function
            function_set = set(i for i in self_members[fn_name].internal_calls if i in self_members)
            while True:
                expanded = set(x for i in function_set for x in self_members[i].internal_calls)
                expanded |= function_set
                if expanded == function_set:
                    break
                function_set = expanded

            self_members[fn_name].recursive_calls = function_set
Пример #3
0
    def __init__(
        self,
        module_node: vy_ast.Module,
        interface_codes: InterfaceDict,
        namespace: dict,
    ) -> None:
        self.ast = module_node
        self.interface_codes = interface_codes or {}
        self.namespace = namespace

        module_nodes = module_node.body.copy()
        while module_nodes:
            count = len(module_nodes)
            err_list = ExceptionList()
            for node in list(module_nodes):
                try:
                    self.visit(node)
                    module_nodes.remove(node)
                except VyperException as e:
                    err_list.append(e)

            # Only raise if no nodes were successfully processed. This allows module
            # level logic to parse regardless of the ordering of code elements.
            if count == len(module_nodes):
                err_list.raise_if_not_empty()

        # get list of internal function calls made by each function
        call_function_names = set()
        self_members = namespace["self"].members
        for node in self.ast.get_children(vy_ast.FunctionDef):
            call_function_names.add(node.name)
            self_members[node.name].internal_calls = set(
                i.func.attr for i in node.get_descendants(
                    vy_ast.Call, {"func.value.id": "self"}))
            if node.name in self_members[node.name].internal_calls:
                self_node = node.get_descendants(vy_ast.Attribute, {
                    "value.id": "self",
                    "attr": node.name
                })[0]
                raise CallViolation(
                    f"Function '{node.name}' calls into itself", self_node)

        for fn_name in sorted(call_function_names):

            if fn_name not in self_members:
                # the referenced function does not exist - this is an issue, but we'll report
                # it later when parsing the function so we can give more meaningful output
                continue

            # check for circular function calls
            sequence = _find_cyclic_call([fn_name], self_members)
            if sequence is not None:
                nodes = []
                for i in range(len(sequence) - 1):
                    fn_node = self.ast.get_children(vy_ast.FunctionDef,
                                                    {"name": sequence[i]})[0]
                    call_node = fn_node.get_descendants(
                        vy_ast.Attribute, {
                            "value.id": "self",
                            "attr": sequence[i + 1]
                        })[0]
                    nodes.append(call_node)

                raise CallViolation("Contract contains cyclic function call",
                                    *nodes)

            # get complete list of functions that are reachable from this function
            function_set = set(i for i in self_members[fn_name].internal_calls
                               if i in self_members)
            while True:
                expanded = set(x for i in function_set
                               for x in self_members[i].internal_calls)
                expanded |= function_set
                if expanded == function_set:
                    break
                function_set = expanded

            self_members[fn_name].recursive_calls = function_set