def evaluate(self, node, attribute):
        cache = self._cache
        cache_key = (node, attribute)

        if cache_key in cache:
            return cache[cache_key]

        dependency_stack = [(self.PRE, cache_key)]

        dependency_path_set = set()
        dependency_path = []

        while dependency_stack:
            (state, data) = dependency_stack.pop()

            if state == self.PRE:
                if data not in cache:
                    node, attribute = data
                    rule_info = self[node].resolve_rule(attribute)

                    if rule_info is None:
                        cache[data] = getattr(node, attribute, None)
                        continue

                    if data in dependency_path_set:
                        from retector.securify2.grammar.attributes import AttributeGrammarError
                        raise AttributeGrammarError(
                            f"Found cycle during evaluation of '{attribute}'. \n"
                            f"Rule trace: {self.__rule_trace(dependency_path)}"
                        )

                    dependency_path.append(rule_info.rule)
                    dependency_path_set.add(data)

                    dependency_stack.append((self.POST, (rule_info, data)))

                    for target in rule_info.attribute_dependencies:
                        dependency_stack.append((self.PRE, target))

            elif state == self.POST:
                rule_info, current_dependency = data

                rule = rule_info.rule
                arguments = rule_info.node_dependencies

                try:
                    result = super()._execute_rule(rule, arguments)
                except Exception as e:
                    from retector.securify2.grammar.attributes import AttributeGrammarError
                    raise AttributeGrammarError(
                        f"Error during evaluation of rule '{rule.name}'. \n"
                        f"Rule trace: {self.__rule_trace(dependency_path)}"
                    ) from e

                cache[current_dependency] = result

                dependency_path.pop()
                dependency_path_set.remove(current_dependency)

        return cache[cache_key]
Example #2
0
    def __get__(self, instance, owner):
        if instance is None:
            return self

        raise AttributeGrammarError(
            f"Attributes cannot be accessed directly without prior evaluation. "
            f"Use an evaluator. ({self} in {self.owner.__name__}, {self.source_location})")
Example #3
0
def __set_rule(rule, cls=None):
    # Registers a rule in the '__semantic_rules' field of cls.

    # If None is passed via cls, it is assumed that the __set_rule
    # function was called from within a class definition block. In
    # order to prevent shadowing of already declared attributes or
    # rules with identical names, the function traverses the stack
    # of frames in order to find the namespace of the class it has
    # been called from. It then looks for fields whose names match
    # the rule's name and returns their value, if found. The value
    # can be returned by a decorator so that the existing field is
    # effectively not overridden by the new definition.
    if cls is not None:
        _rules(cls).append(rule)
    else:
        class_locals = __class_locals()

        if class_locals is None:
            raise AttributeGrammarError(
                f"Rule definition of {rule} is not applicable outside of a class. \n"
                f"If defining rules outside of a class, please pass the target class "
                f"as first parameter (e.g. @synthesized(MyClass), @inherited(MyClass))."
            )

        _rules(class_locals).append(rule)

        if rule.function_name in class_locals:
            return class_locals[rule.function_name]

    return rule
Example #4
0
    def _execute_rule(self, rule, arguments):
        try:
            access = defaultdict(lambda: set())
            for node_name, attribute in rule.dependencies:
                nodes = arguments[node_name]
                for node in nodes if isinstance(nodes,
                                                (list, tuple)) else [nodes]:
                    access[node].add(attribute)

            self.__allowed_access.append(access)
            self.__current_rule.append(rule)

            for (node, attributes) in access.items():
                if node is not None:
                    for attribute in attributes:
                        self.__evaluate(node, attribute)

            try:
                return super()._execute_rule(rule, arguments)
            except Exception as e:
                from retector.securify2.grammar.attributes import AttributeGrammarError
                raise AttributeGrammarError(
                    f"Error during evaluation of rule '{rule.name}'. \n"
                    f"Rule trace: {self.__rule_trace()}") from e
        finally:
            self.__allowed_access.pop()
            self.__current_rule.pop()
Example #5
0
    def evaluate(self, node, attribute):
        from retector.securify2.grammar.attributes import AttributeGrammarError

        try:
            return self.__evaluate(node, attribute)
        except AttributeGrammarError as e:
            raise AttributeGrammarError(*e.args) from e.__cause__
Example #6
0
    def build_order(self, symbol):
        if not self.grammar.is_absolutely_acyclic:
            raise AttributeGrammarError("Attribute grammar must be absolutely non-circular for static evaluation.")

        dependencies = self.grammar.lower_dependence_combined[symbol].depends_on
        remaining_attributes = set(self.grammar.attributes[symbol])

        inherited_attributes = self.grammar.inherited_attributes[symbol]
        synthesized_attributes = self.grammar.synthesized_attributes[symbol]

        def max_inherited_group():
            sequence = []
            while True:
                inherited = {i for i in remaining_attributes
                             if i in inherited_attributes
                             if remaining_attributes.isdisjoint(dependencies.get(i, set()))}

                if len(inherited) == 0:
                    break

                remaining_attributes.difference_update(inherited)
                sequence.append(inherited)

            return [s for sub_seq in sequence for s in sub_seq]

        def max_synthesized_group():
            sequence = []
            while True:
                synthesized = {i for i in remaining_attributes
                               if i in synthesized_attributes
                               if remaining_attributes.isdisjoint(dependencies.get(i, set()))}

                if len(synthesized) == 0:
                    break

                remaining_attributes.difference_update(synthesized)
                sequence.append(synthesized)

            return [s for sub_seq in sequence for s in sub_seq]

        sequences = []

        synthesized_seq = max_synthesized_group()
        sequences.append(([], synthesized_seq))

        while len(remaining_attributes) > 0:
            inherited_seq = max_inherited_group()
            synthesized_seq = max_synthesized_group()

            sequences.append((inherited_seq, synthesized_seq))

        return sequences
Example #7
0
    def __evaluate(self, node, attribute):
        from retector.securify2.grammar.attributes import AttributeGrammarError

        if (node, attribute) in self.__nodes_evaluating:
            raise AttributeGrammarError(
                f"Found cycle during evaluation of '{attribute}'. \n"
                f"Rule trace: {self.__rule_trace()}")

        cache_key = (node, attribute)
        cache = self.__cache

        if cache_key in cache:
            return cache[cache_key]

        self.__nodes_evaluating.add((node, attribute))
        result = self.__evaluate_inner(node, attribute)
        self.__nodes_evaluating.remove((node, attribute))

        cache[cache_key] = result

        return result
Example #8
0
        def attribute_declarations(self, production):
            if production is None:
                return set(), set()

            syn = getmembers(production, lambda x: isinstance(x, SynthesizedAttribute))
            inh = getmembers(production, lambda x: isinstance(x, InheritedAttribute))

            for super_production in self.grammar.super_productions[production]:
                if super_production in self.grammar.starting_productions:
                    continue

                for i_name, i_attr in inh:
                    if getattr(super_production, i_name, None) != i_attr:
                        raise AttributeDefinitionError(
                            f"Inherited attribute {i_attr} defined in production "
                            f"'{production.__name__}' must also be available in its "
                            f"super production '{super_production.__name__}'. "
                            f"Alternatively '{super_production.__name__}' must be a "
                            f"start production. {i_attr.source_location}")

            syn = set(a for _, a in syn)
            inh = set(a for _, a in inh)

            parent_attributes = set()
            for super_production in self.grammar.super_productions[production]:
                parent_attributes_new = self.attribute_declarations(super_production)
                if parent_attributes_new is None:
                    parent_attributes |= parent_attributes_new[0]
                    parent_attributes |= parent_attributes_new[1]

            local_attributes = (syn | inh)

            if not parent_attributes.issubset(local_attributes):
                raise AttributeGrammarError(local_attributes, parent_attributes)

            return syn, inh
Example #9
0
 def _execute_rule(self, rule, arguments):
     try:
         return rule.func(**{name: node for name, node in arguments.items()})
     except AttributeGrammarError as e:
         raise AttributeGrammarError(f"Error during evaluation of rule '{rule.name}'.") from e