Beispiel #1
0
    def __init__(self,
                 variables: Set[VariableIdentifier],
                 precursory: State = None):
        """Map each program variable to the type representing its value.

        :param variables: set of program variables
        """
        lattices = defaultdict(lambda: TypeLattice)
        arguments = defaultdict(
            lambda: {'type_status': TypeLattice.Status.String})
        arguments[BooleanLyraType()] = {
            'type_status': TypeLattice.Status.Boolean
        }
        arguments[ListLyraType(BooleanLyraType())] = {
            'type_status': TypeLattice.Status.Boolean
        }
        arguments[IntegerLyraType()] = {
            'type_status': TypeLattice.Status.Integer
        }
        arguments[ListLyraType(IntegerLyraType())] = {
            'type_status': TypeLattice.Status.Integer
        }
        arguments[FloatLyraType()] = {'type_status': TypeLattice.Status.Float}
        arguments[ListLyraType(FloatLyraType())] = {
            'type_status': TypeLattice.Status.Float
        }

        super().__init__(variables, lattices, arguments)
        InputMixin.__init__(self, precursory)
Beispiel #2
0
    def visit_While(self, node, types=None, typ=None):
        header_node = Loop(self._id_gen.next)

        cfg = self._translate_body(node.body, types, typ)
        body_in_node = cfg.in_node
        body_out_node = cfg.out_node

        pp = ProgramPoint(node.test.lineno, node.test.col_offset)
        test = self.visit(node.test, types, BooleanLyraType())
        neg_test = Call(pp, "not", [test], BooleanLyraType())

        cfg.add_node(header_node)
        cfg.in_node = header_node

        cfg.add_edge(Conditional(header_node, test, body_in_node, Edge.Kind.LOOP_IN))
        cfg.add_edge(Conditional(header_node, neg_test, None))
        if body_out_node:
            # if control flow can exit the body at all, add an unconditional LOOP_OUT edge
            cfg.add_edge(Unconditional(body_out_node, header_node, Edge.Kind.LOOP_OUT))

        if node.orelse:  # if there is else branch
            orelse_cfg = self._translate_body(node.orelse, types)
            if orelse_cfg.out_node:
                # if control flow can exit the else at all, add an unconditional DEFAULT edge
                orelse_cfg.add_edge(Unconditional(orelse_cfg.out_node, None, Edge.Kind.DEFAULT))
            cfg.append(orelse_cfg)

        for special_edge, edge_type in cfg.special_edges:
            if edge_type == LooseControlFlowGraph.SpecialEdgeType.CONTINUE:
                cfg.add_edge(Unconditional(special_edge.source, header_node, Edge.Kind.LOOP_OUT))
            elif edge_type == LooseControlFlowGraph.SpecialEdgeType.BREAK:
                cfg.add_edge(Unconditional(special_edge.source, None, Edge.Kind.LOOP_OUT))
        cfg.special_edges.clear()

        return cfg
Beispiel #3
0
    def visit_If(self, node, types=None, typ=None):
        body_cfg = self._translate_body(node.body, types)

        pp = ProgramPoint(node.test.lineno, node.test.col_offset)
        test = self.visit(node.test, types, BooleanLyraType())
        neg_test = Call(pp, "not", [test], BooleanLyraType())

        body_cfg.add_edge(Conditional(None, test, body_cfg.in_node, Edge.Kind.IF_IN))
        if body_cfg.out_node:
            # if control flow can exit the body at all, add an unconditional IF_OUT edge
            body_cfg.add_edge(Unconditional(body_cfg.out_node, None, Edge.Kind.IF_OUT))
        if node.orelse:  # if there is else branch
            orelse_cfg = self._translate_body(node.orelse, types)
            orelse_cfg.add_edge(Conditional(None, neg_test, orelse_cfg.in_node, Edge.Kind.IF_IN))
            if orelse_cfg.out_node:
                # if control flow can exit the else at all, add an unconditional IF_OUT edge
                orelse_cfg.add_edge(Unconditional(orelse_cfg.out_node, None, Edge.Kind.IF_OUT))
        else:
            orelse_cfg = LooseControlFlowGraph()
            orelse_cfg.add_edge(Conditional(None, neg_test, None, Edge.Kind.DEFAULT))

        # extend special edges with IF_OUT edges and additional necessary dummy nodes
        for special_edge, edge_type in body_cfg.special_edges:
            dummy = _dummy(self._id_gen)
            body_cfg.add_node(dummy)

            # add a new IF_OUT edge where the special edge is at the moment,
            # ending in new dummy node
            body_cfg.add_edge(Unconditional(special_edge.source, dummy, Edge.Kind.IF_OUT))

            # change position of special edge to be AFTER the new dummy
            special_edge._source = dummy

        cfg = body_cfg.combine(orelse_cfg)
        return cfg
Beispiel #4
0
 def _assume(self, condition: Expression) -> 'IntervalState':
     normal = NegationFreeNormalExpression().visit(condition)
     if isinstance(normal, VariableIdentifier) and isinstance(normal.typ, BooleanLyraType):
         evaluation = self._evaluation.visit(normal, self, dict())
         return self._refinement.visit(normal, evaluation, IntervalLattice(1, 1), self)
     elif isinstance(normal, UnaryBooleanOperation):
         if normal.operator == UnaryBooleanOperation.Operator.Neg:
             expression = normal.expression
             if isinstance(expression, VariableIdentifier):
                 if isinstance(expression.typ, BooleanLyraType):
                     evaluation = self._evaluation.visit(normal, self, dict())
                     false = IntervalLattice(0, 0)
                     return self._refinement.visit(expression, evaluation, false, self)
     elif isinstance(normal, BinaryBooleanOperation):
         if normal.operator == BinaryBooleanOperation.Operator.And:
             right = deepcopy(self)._assume(normal.right)
             return self._assume(normal.left).meet(right)
         if normal.operator == BinaryBooleanOperation.Operator.Or:
             right = deepcopy(self)._assume(normal.right)
             return self._assume(normal.left).join(right)
     elif isinstance(normal, BinaryComparisonOperation):
         if normal.operator == BinaryComparisonOperation.Operator.Is:
             error = f"Assumption of a comparison with {normal.operator} is unsupported!"
             raise ValueError(error)
         elif normal.operator == BinaryComparisonOperation.Operator.IsNot:
             error = f"Assumption of a comparison with {normal.operator} is unsupported!"
             raise ValueError(error)
         elif normal.operator == BinaryComparisonOperation.Operator.In:
             if isinstance(normal.right, Range):
                 typ = BooleanLyraType()
                 left = normal.left
                 lower_operator = BinaryComparisonOperation.Operator.GtE
                 lower_right = normal.right.start
                 lower = BinaryComparisonOperation(typ, left, lower_operator, lower_right)
                 upper_operator = BinaryComparisonOperation.Operator.Lt
                 upper_right = normal.right.stop
                 upper = BinaryComparisonOperation(typ, left, upper_operator, upper_right)
                 right = deepcopy(self)._assume(upper)
                 return self._assume(lower).meet(right)
         elif normal.operator == BinaryComparisonOperation.Operator.NotIn:
             if isinstance(normal.right, Range):
                 typ = BooleanLyraType()
                 left = normal.left
                 lower_operator = BinaryComparisonOperation.Operator.Lt
                 lower_right = normal.right.start
                 lower = BinaryComparisonOperation(typ, left, lower_operator, lower_right)
                 upper_operator = BinaryComparisonOperation.Operator.GtE
                 upper_right = normal.right.stop
                 upper = BinaryComparisonOperation(typ, left, upper_operator, upper_right)
                 right = deepcopy(self)._assume(upper)
                 return self._assume(lower).join(right)
         evaluation = self._evaluation.visit(normal.left, self, dict())
         return self._refinement.visit(normal.left, evaluation, IntervalLattice(upper=0), self)
     error = f"Assumption of a {normal.__class__.__name__} expression is unsupported!"
     raise ValueError(error)
Beispiel #5
0
 def visit_Compare(self, node, types=None, typ=None):
     pp = ProgramPoint(node.lineno, node.col_offset)
     last_comp = self.visit(node.comparators[0], types, None)
     result = Call(pp,
                   type(node.ops[0]).__name__.lower(),
                   [self.visit(node.left, types, None), last_comp],
                   BooleanLyraType())
     for op, comp in list(zip(node.ops, node.comparators))[1:]:
         cur_call = Call(
             pp,
             type(op).__name__.lower(),
             [last_comp, self.visit(comp, types, None)], BooleanLyraType())
         result = Call(pp, 'and', [result, cur_call], BooleanLyraType())
     return result
Beispiel #6
0
    def visit_For(self, node, types=None, typ=None):
        header_node = Loop(self._id_gen.next)

        cfg = self._translate_body(node.body, types, typ)
        body_in_node = cfg.in_node
        body_out_node = cfg.out_node

        pp = ProgramPoint(node.target.lineno, node.target.col_offset)
        iteration = self.visit(node.iter, types,
                               ListLyraType(IntegerLyraType()))
        if isinstance(iteration, Call) and iteration.name == "range":
            target_type = IntegerLyraType()
        else:
            error = f"The for loop iteration statment {node.iter} is not yet translatable to CFG!"
            raise NotImplementedError(error)
        target = self.visit(node.target, types, target_type)

        test = Call(pp, "in", [target, iteration], BooleanLyraType())
        neg_test = Call(pp, "not", [test], BooleanLyraType())

        cfg.add_node(header_node)
        cfg.in_node = header_node

        cfg.add_edge(
            Conditional(header_node, test, body_in_node, Edge.Kind.LOOP_IN))
        cfg.add_edge(Conditional(header_node, neg_test, None))
        if body_out_node:  # if control flow can exit the body at all, add an unconditional LOOP_OUT edge
            cfg.add_edge(
                Unconditional(body_out_node, header_node, Edge.Kind.LOOP_OUT))

        if node.orelse:  # if there is else branch
            orelse_cfg = self._translate_body(node.orelse, types)
            if orelse_cfg.out_node:  # if control flow can exit the else at all, add an unconditional DEFAULT edge
                orelse_cfg.add_edge(
                    Unconditional(orelse_cfg.out_node, None,
                                  Edge.Kind.DEFAULT))
            cfg.append(orelse_cfg)

        for special_edge, edge_type in cfg.special_edges:
            if edge_type == LooseControlFlowGraph.SpecialEdgeType.CONTINUE:
                cfg.add_edge(
                    Unconditional(special_edge.source, header_node,
                                  Edge.Kind.LOOP_OUT))
            elif edge_type == LooseControlFlowGraph.SpecialEdgeType.BREAK:
                cfg.add_edge(
                    Unconditional(special_edge.source, None,
                                  Edge.Kind.LOOP_OUT))
        cfg.special_edges.clear()

        return cfg
Beispiel #7
0
    def visit_If(self, node, types=None, typ=None):
        """Visitor function for an if statement.
        The attribute test stores a single AST node.
        The attributes body and orelse each store a list of AST nodes to be executed."""
        pp = ProgramPoint(node.test.lineno, node.test.col_offset)

        body = self._visit_body(node.body, types, typ)
        test = self.visit(node.test, types, BooleanLyraType())
        body.add_edge(Conditional(None, test, body.in_node, Edge.Kind.IF_IN))
        if body.out_node:   # control flow can exit the body
            # add an unconditional IF_OUT edge
            body.add_edge(Unconditional(body.out_node, None, Edge.Kind.IF_OUT))

        if node.orelse:  # there is an else branch
            orelse = self._visit_body(node.orelse, types, typ)
            not_test = Call(pp, 'not', [test], BooleanLyraType())
            orelse.add_edge(Conditional(None, not_test, orelse.in_node, Edge.Kind.IF_IN))
            if orelse.out_node:     # control flow can exit the else
                # add an unconditional IF_OUT edge
                orelse.add_edge(Unconditional(orelse.out_node, None, Edge.Kind.IF_OUT))

            # handle special edges
            for special, _ in orelse.special_edges:
                # create dummy node
                dummy = _dummy_node(self._id_gen)
                orelse.add_node(dummy)
                # add an unconditional IF_OUT edge to the newly created dummy node
                orelse.add_edge(Unconditional(special.source, dummy, Edge.Kind.IF_OUT))
                # move the special edge after the dummy node
                special._source = dummy

        else:   # there is no else branch
            orelse = LooseControlFlowGraph()
            not_test = Call(pp, 'not', [test], BooleanLyraType())
            orelse.add_edge(Conditional(None, not_test, None, Edge.Kind.DEFAULT))

        # handle special edges
        for special, edge_type in body.special_edges:
            # create dummy node
            dummy = _dummy_node(self._id_gen)
            body.add_node(dummy)
            # add an unconditional IF_OUT edge to the newly created dummy node
            body.add_edge(Unconditional(special.source, dummy, Edge.Kind.IF_OUT))
            # move the special edge after the dummy node
            special._source = dummy

        cfg = body.combine(orelse)
        return cfg
Beispiel #8
0
 def visit_VariableIdentifier(self,
                              expr: 'VariableIdentifier',
                              invert=False):
     if isinstance(expr.typ, BooleanLyraType) and invert:
         operator = UnaryBooleanOperation.Operator.Neg
         return UnaryBooleanOperation(BooleanLyraType(), operator, expr)
     return expr  # nothing to be done
Beispiel #9
0
 def visit_NameConstant(self, node, types=None, typ=None):
     if isinstance(node.value, bool):
         pp = ProgramPoint(node.lineno, node.col_offset)
         expr = Literal(BooleanLyraType(), str(node.value))
         return LiteralEvaluation(pp, expr)
     raise NotImplementedError(
         f"Name constant {node.value.__class__.__name__} is not yet supported!")
Beispiel #10
0
    def bool_call_semantics(self, stmt: Call, state: State) -> State:
        """Semantics of a call to 'bool'.

        :param stmt: call to 'bool' to be executed
        :param state: state before executing the call statement
        :return: state modified by the call statement
        """
        return self._cast_call_semantics(stmt, state, BooleanLyraType())
Beispiel #11
0
 def visit_NameConstant(self, node, types=None, typ=None):
     """Visitor function for True, False or None.
     The value attribute stores the constant."""
     if isinstance(node.value, bool):
         pp = ProgramPoint(node.lineno, node.col_offset)
         expr = Literal(BooleanLyraType(), str(node.value))
         return LiteralEvaluation(pp, expr)
     raise NotImplementedError(f"Constant {node.value.__class__.__name__} is unsupported!")
Beispiel #12
0
 def visit_IfExp(self, node, targets, op=None, types=None, typ=None):
     """Visitor function for an if expression.
     The components of the expression are stored in the attributes test, body, and orelse."""
     pp = ProgramPoint(node.lineno, node.col_offset)
     then = CFGFactory(self._id_gen)
     body = self.visit(node.body, types, typ)
     assignments = list()
     for target in targets:
         if op:
             left = self.visit(target, types, typ)
             name = type(op).__name__.lower()
             value = Call(pp, name, [left, body], left.typ)
             assignments.append(Assignment(pp, left, value))
         else:
             assignments.append(Assignment(pp, self.visit(target, types, typ), body))
     then.add_stmts(assignments)
     then.complete_basic_block()
     then = then.cfg
     test = self.visit(node.test, types, BooleanLyraType())
     then.add_edge(Conditional(None, test, then.in_node, Edge.Kind.IF_IN))
     then.add_edge(Unconditional(then.out_node, None, Edge.Kind.IF_OUT))
     orelse = CFGFactory(self._id_gen)
     body = self.visit(node.orelse, types, typ)
     assignments = list()
     for target in targets:
         if op:
             left = self.visit(target, types, typ)
             name = type(op).__name__.lower()
             value = Call(pp, name, [left, body], left.typ)
             assignments.append(Assignment(pp, left, value))
         else:
             assignments.append(Assignment(pp, self.visit(target, types, typ), body))
     orelse.add_stmts(assignments)
     orelse.complete_basic_block()
     orelse = orelse.cfg
     not_test = Call(pp, 'not', [test], BooleanLyraType())
     orelse.add_edge(Conditional(None, not_test, orelse.in_node, Edge.Kind.IF_IN))
     orelse.add_edge(Unconditional(orelse.out_node, None, Edge.Kind.IF_OUT))
     return then.combine(orelse)
Beispiel #13
0
 def visit_BinaryArithmeticOperation(self,
                                     expr,
                                     evaluation=None,
                                     value=None,
                                     state=None):
     updated = super().visit_BinaryArithmeticOperation(
         expr, evaluation, value, state)
     if expr.operator == BinaryArithmeticOperation.Operator.Div:
         left = expr.right
         operator = BinaryComparisonOperation.Operator.NotEq
         right = Literal(IntegerLyraType(), "0")
         condition = BinaryComparisonOperation(BooleanLyraType(), left,
                                               operator, right)
         return updated.assume({condition})
     return updated
Beispiel #14
0
    def visit_While(self, node, types=None, typ=None):
        """Visitor function for an while statement.
        The attribute test stores a single AST node.
        The attributes body and orelse each store a list of AST nodes to be executed."""
        pp = ProgramPoint(node.test.lineno, node.test.col_offset)

        body = self._visit_body(node.body, types, typ)
        test = self.visit(node.test, types, BooleanLyraType())
        header = Loop(self._id_gen.next)
        body_in_node = body.in_node
        body_out_node = body.out_node
        body.add_node(header)
        body.in_node = header
        body.add_edge(Conditional(header, test, body_in_node, Edge.Kind.LOOP_IN))
        not_test = Call(pp, 'not', [test], BooleanLyraType())
        body.add_edge(Conditional(header, not_test, None))
        if body_out_node:   # control flow can exit the body
            # add an unconditional LOOP_OUT edge
            body.add_edge(Unconditional(body_out_node, header, Edge.Kind.LOOP_OUT))

        if node.orelse:  # there is an else branch
            orelse = self._visit_body(node.orelse, types)
            if orelse.out_node:     # control flow can exit the else
                # add an unconditional DEFAULT edge
                orelse.add_edge(Unconditional(orelse.out_node, None, Edge.Kind.DEFAULT))
            body.append(orelse)

        # handle special edges
        for special, kind in body.special_edges:
            if kind == LooseControlFlowGraph.SpecialEdgeType.CONTINUE:
                body.add_edge(Unconditional(special.source, header, Edge.Kind.LOOP_OUT))
            elif kind == LooseControlFlowGraph.SpecialEdgeType.BREAK:
                body.add_edge(Unconditional(special.source, None, Edge.Kind.LOOP_OUT))
        body.special_edges.clear()

        return body
Beispiel #15
0
    def visit_For(self, node, types=None, typ=None):
        """Visitor function for a for statement.
        The attribute target stores the variable(s) the loop assigns to
        (as a single Name, Tuple, or List node).
        The attribute iter stores a single AST node representing the item to be looped over.
        The attributes body and orelse each store a list of AST nodes to be executed."""
        pp = ProgramPoint(node.target.lineno, node.target.col_offset)

        iterated = self.visit(node.iter, types, None)
        target_typ = None
        if isinstance(iterated, VariableAccess):
            if isinstance(iterated.typ, ListLyraType):      # iteration over list items
                target_typ = iterated.typ.typ
            elif isinstance(iterated.typ, SetLyraType):     # iteration over set items
                target_typ = iterated.typ.typ
            elif isinstance(iterated.typ, DictLyraType):    # iteration over dictionary keys
                iterated = Call(iterated.pp, 'keys', [iterated], SetLyraType(iterated.typ.key_typ))
                target_typ = iterated.typ.typ
        elif isinstance(iterated, Call):
            if iterated.name == 'range':
                assert isinstance(iterated.typ, ListLyraType)
                target_typ = iterated.typ.typ
            elif iterated.name == 'items' or iterated.name == 'keys' or iterated.name == 'values':
                assert isinstance(iterated.typ, SetLyraType)
                target_typ = iterated.typ.typ
            elif iterated.name == 'list':
                assert len(iterated.arguments) == 1
                if isinstance(iterated.arguments[0].typ, ListLyraType):
                    target_typ = iterated.arguments[0].typ.typ
            else:
                error = "The type of the target {} is not yet determinable!".format(iterated)
                raise NotImplementedError(error)
        else:
            error = "The iteration attribute {} is not yet translatable to CFG!".format(iterated)
            raise NotImplementedError(error)
        target = self.visit(node.target, types, target_typ)

        body = self._visit_body(node.body, types, typ)
        test = Call(pp, 'in', [target, iterated], BooleanLyraType(), forloop=True)
        header = Loop(self._id_gen.next)
        body_in_node = body.in_node
        body_out_node = body.out_node
        body.add_node(header)
        body.in_node = header
        body.add_edge(Conditional(header, test, body_in_node, Edge.Kind.LOOP_IN))
        not_test = Call(pp, 'notin', [target, iterated], BooleanLyraType(), forloop=True)
        body.add_edge(Conditional(header, not_test, None))
        if body_out_node:   # control flow can exit the body
            # add an unconditional LOOP_OUT edge
            body.add_edge(Unconditional(body_out_node, header, Edge.Kind.LOOP_OUT))

        if node.orelse:  # there is an else branch
            orelse = self._visit_body(node.orelse, types)
            if orelse.out_node:     # control flow can exit the else
                # add an unconditional DEFAULT edge
                orelse.add_edge(Unconditional(orelse.out_node, None, Edge.Kind.DEFAULT))
            body.append(orelse)

        # handle special edges
        for special, kind in body.special_edges:
            if kind == LooseControlFlowGraph.SpecialEdgeType.CONTINUE:
                body.add_edge(Unconditional(special.source, header, Edge.Kind.LOOP_OUT))
            elif kind == LooseControlFlowGraph.SpecialEdgeType.BREAK:
                body.add_edge(Unconditional(special.source, None, Edge.Kind.LOOP_OUT))
        body.special_edges.clear()

        return body