Пример #1
0
    def computeStatement(self, constraint_collection):
        outer_constraint_collection = constraint_collection
        constraint_collection = ConstraintCollectionBranch(
            parent = constraint_collection,
            name   = "loop"
        )

        loop_body, break_collections = self.computeLoopBody(constraint_collection)

        # Consider trailing "continue" statements, these have no effect, so we
        # can remove them.
        if loop_body is not None:
            assert loop_body.isStatementsSequence()

            statements = loop_body.getStatements()
            assert statements # Cannot be empty

            # If the last statement is a "continue" statement, it can simply
            # be discarded.
            last_statement = statements[-1]
            if last_statement.isStatementContinueLoop():
                if len(statements) == 1:
                    self.setLoopBody(None)
                    loop_body = None
                else:
                    last_statement.replaceWith(None)

                constraint_collection.signalChange(
                    "new_statements",
                    last_statement.getSourceReference(),
                    """\
Removed useless terminal 'continue' as last statement of loop."""
                )

        if break_collections:
            outer_constraint_collection.mergeMultipleBranches(break_collections)

        # Consider leading "break" statements, they should be the only, and
        # should lead to removing the whole loop statement. Trailing "break"
        # statements could also be handled, but that would need to consider if
        # there are other "break" statements too. Numbering loop exits is
        # nothing we have yet.
        if loop_body is not None:
            assert loop_body.isStatementsSequence()

            statements = loop_body.getStatements()
            assert statements # Cannot be empty

            if len(statements) == 1 and statements[-1].isStatementBreakLoop():
                return None, "new_statements", """\
Removed useless loop with immediate 'break' statement."""

        # Also consider the threading intermission. TODO: We ought to make it
        # explicit, so we can see it potentially disrupting and changing the
        # global variables. It may also raise.
        outer_constraint_collection.onExceptionRaiseExit(BaseException)

        return self, None, None
Пример #2
0
    def computeExpressionRaw(self, constraint_collection):
        # Query the truth value after the expression is evaluated, once it is
        # evaluated in onExpression, it is known.
        constraint_collection.onExpression(
            expression = self.getCondition()
        )
        condition = self.getCondition()

        # No need to look any further, if the condition raises, the branches do
        # not matter at all.
        if condition.willRaiseException(BaseException):
            return condition, "new_raise", """\
Conditional statements already raises implicitly in condition, removing \
branches."""

        # If the condition raises, we let that escape instead, and the
        # branches don't matter at all.
        if condition.willRaiseException(BaseException):
            return condition, "new_raise", """\
Conditional expression raises in condition."""

        # Decide this based on truth value of condition.
        truth_value = condition.getTruthValue()

        # TODO: We now know that condition evaluates to true for the yes branch
        # and to not true for no branch, the branch should know that.
        yes_branch = self.getExpressionYes()

        # Continue to execute for yes branch unless we know it's not going to be
        # relevant.
        if truth_value is not False:
            branch_yes_collection = ConstraintCollectionBranch(
                parent = constraint_collection,
            )

            branch_yes_collection.computeBranch(
                branch = yes_branch
            )

            # May have just gone away, so fetch it again.
            yes_branch = self.getExpressionYes()

            # If it's aborting, it doesn't contribute to merging.
            if yes_branch.willRaiseException(BaseException):
                branch_yes_collection = None
        else:
            branch_yes_collection = None

        no_branch = self.getExpressionNo()

        # Continue to execute for yes branch.
        if truth_value is not True:
            branch_no_collection = ConstraintCollectionBranch(
                parent = constraint_collection,
            )

            branch_no_collection.computeBranch(
                branch = no_branch
            )

            # May have just gone away, so fetch it again.
            no_branch = self.getExpressionNo()

            # If it's aborting, it doesn't contribute to merging.
            if no_branch.willRaiseException(BaseException):
                branch_no_collection = None
        else:
            branch_no_collection = None

        # Merge into parent execution.
        constraint_collection.mergeBranches(
            branch_yes_collection,
            branch_no_collection
        )

        if truth_value is True:
            return (
                wrapExpressionWithNodeSideEffects(
                    new_node = self.getExpressionYes(),
                    old_node = condition
                ),
                "new_expression",
                "Conditional expression predicted to yes case"
            )
        elif truth_value is False:
            return (
                wrapExpressionWithNodeSideEffects(
                    new_node = self.getExpressionNo(),
                    old_node = condition
                ),
                "new_expression",
                "Conditional expression predicted to no case"
            )
        else:
            return self, None, None
Пример #3
0
    def computeStatement(self, constraint_collection):
        # This is rather complex stuff, pylint: disable=R0912

        # Query the truth value after the expression is evaluated, once it is
        # evaluated in onExpression, it is known.
        constraint_collection.onExpression(
            expression = self.getCondition()
        )
        condition = self.getCondition()

        # No need to look any further, if the condition raises, the branches do
        # not matter at all.
        if condition.willRaiseException(BaseException):
            result = makeStatementExpressionOnlyReplacementNode(
                expression = condition,
                node       = self
            )

            return result, "new_raise", """\
Conditional statements already raises implicitly in condition, removing \
branches."""

        # Consider to not execute branches that we know to be true, but execute
        # the ones that may be true, potentially both.
        truth_value = condition.getTruthValue()

        # TODO: We now know that condition evaluates to true for the yes branch
        # and to not true for no branch, the branch collection should know that.
        yes_branch = self.getBranchYes()

        # Handle branches that became empty behind our back.
        if yes_branch is not None:
            if not yes_branch.getStatements():
                yes_branch = None

        # Continue to execute for yes branch unless we know it's not going to be
        # relevant.
        if yes_branch is not None and truth_value is not False:
            branch_yes_collection = ConstraintCollectionBranch(
                parent = constraint_collection,
            )

            branch_yes_collection.computeBranch(
                branch = yes_branch
            )

            # May have just gone away, so fetch it again.
            yes_branch = self.getBranchYes()

            # If it's aborting, it doesn't contribute to merging.
            if yes_branch is None or yes_branch.isStatementAborting():
                branch_yes_collection = None
        else:
            branch_yes_collection = None

        no_branch = self.getBranchNo()

        # Handle branches that became empty behind our back
        if no_branch is not None:
            if not no_branch.getStatements():
                no_branch = None

        # Continue to execute for yes branch.
        if no_branch is not None and truth_value is not True:
            branch_no_collection = ConstraintCollectionBranch(
                parent = constraint_collection,
            )

            branch_no_collection.computeBranch(
                branch = no_branch
            )


            # May have just gone away, so fetch it again.
            no_branch = self.getBranchNo()

            # If it's aborting, it doesn't contribute to merging.
            if no_branch is None or no_branch.isStatementAborting():
                branch_no_collection = None
        else:
            branch_no_collection = None

        # Merge into parent execution.
        constraint_collection.mergeBranches(
            branch_yes_collection,
            branch_no_collection
        )

        # Both branches may have become empty.
        if yes_branch is None and no_branch is None:
            if truth_value is None:
                condition = ExpressionBuiltinBool(
                    value      = condition,
                    source_ref = condition.getSourceReference()
                )

            # With both branches eliminated, the condition remains as a side
            # effect.
            result = makeStatementExpressionOnlyReplacementNode(
                expression = condition,
                node       = self
            )

            return result, "new_statements", """\
Both branches have no effect, reduced to evaluate condition."""

        if yes_branch is None:
            # Would be eliminated already, if there wasn't any "no" branch
            # either.
            assert no_branch is not None

            from .OperatorNodes import ExpressionOperationNOT

            new_statement = StatementConditional(
                condition  = ExpressionOperationNOT(
                    operand    = condition,
                    source_ref = condition.getSourceReference()
                ),
                yes_branch = no_branch,
                no_branch  = None,
                source_ref = self.getSourceReference()
            )

            return new_statement, "new_statements", """\
Empty 'yes' branch for condition was replaced with inverted condition check."""

        # Note: Checking the condition late, so that the surviving branch got
        # processed already. Returning without doing that, will corrupt the SSA
        # results. TODO: Could pretend the other branch didn't exist to save
        # complexity the merging of processing.
        if truth_value is not None:
            if truth_value is True:
                choice = "true"

                new_statement = self.getBranchYes()
            else:
                choice = "false"

                new_statement = self.getBranchNo()

            new_statement = wrapStatementWithSideEffects(
                new_node   = new_statement,
                old_node   = condition,
                allow_none = True # surviving branch may empty
            )

            return new_statement, "new_statements", """\
Condition for branch was predicted to be always %s.""" % choice

        return self, None, None
Пример #4
0
    def computeExpressionRaw(self, constraint_collection):
        # Query the truth value after the expression is evaluated, once it is
        # evaluated in onExpression, it is known.
        constraint_collection.onExpression(
            expression = self.getLeft()
        )
        left = self.getLeft()

        # No need to look any further, if the condition raises, the branches do
        # not matter at all.
        if left.willRaiseException(BaseException):
            return left, "new_raise", """\
Conditional %s statements already raises implicitly in condition, removing \
branches.""" % self.conditional_kind

        # Decide this based on truth value of condition.
        truth_value = left.getTruthValue()

        truth_value_use_left = self.conditional_kind == "or"
        truth_value_use_right = not truth_value_use_left

        right = self.getRight()

        # Continue to execute for yes branch unless we know it's not going to be
        # relevant.
        if truth_value is not truth_value_use_left:
            # TODO: We now know that left evaluates and we should tell the
            # branch that.
            branch_yes_collection = ConstraintCollectionBranch(
                parent = constraint_collection,
                name   = "boolean %s right branch" % self.conditional_kind
            )

            branch_yes_collection.computeBranch(
                branch = right
            )

            # May have just gone away, so fetch it again.
            right = self.getRight()

            # If it's aborting, it doesn't contribute to merging.
            if right.willRaiseException(BaseException):
                branch_yes_collection = None
        else:
            branch_yes_collection = None


        if branch_yes_collection:
            # Merge into parent execution.
            constraint_collection.mergeBranches(
                branch_yes_collection,
                None
            )

        if truth_value is truth_value_use_left:
            return (
                left,
                "new_expression",
                "Conditional '%s' expression predicted to left value." % self.conditional_kind
            )
        elif truth_value is truth_value_use_right:
            return (
                wrapExpressionWithNodeSideEffects(
                    new_node = right,
                    old_node = left
                ),
                "new_expression",
                "Conditional '%s' expression predicted right value." % self.conditional_kind
            )
        else:
            return self, None, None
Пример #5
0
    def computeStatement(self, constraint_collection):
        # This is rather complex stuff, pylint: disable=R0912,R0915

        constraint_collection.onExpression(
            expression = self.getCondition()
        )
        condition = self.getCondition()

        condition_may_raise = condition.mayRaiseException(BaseException)

        if condition_may_raise:
            constraint_collection.onExceptionRaiseExit(
                BaseException
            )

        # No need to look any further, if the condition raises, the branches do
        # not matter at all.
        if condition.willRaiseException(BaseException):
            result = makeStatementExpressionOnlyReplacementNode(
                expression = condition,
                node       = self
            )

            return result, "new_raise", """\
Conditional statements already raises implicitly in condition, removing \
branches."""

        if not condition_may_raise and condition.mayRaiseExceptionBool(BaseException):
            constraint_collection.onExceptionRaiseExit(
                BaseException
            )


        # Query the truth value after the expression is evaluated, once it is
        # evaluated in onExpression, it is known.
        truth_value = condition.getTruthValue()

        # TODO: We now know that condition evaluates to true for the yes branch
        # and to not true for no branch, the branch collection should know that.
        yes_branch = self.getBranchYes()
        no_branch = self.getBranchNo()

        # Handle branches that became empty behind our back.
        if yes_branch is not None:
            if not yes_branch.getStatements():
                yes_branch = None
        if no_branch is not None:
            if not no_branch.getStatements():
                no_branch = None

        # Consider to not remove branches that we know won't be taken.
        if yes_branch is not None and truth_value is False:
            constraint_collection.signalChange(
                tags       = "new_statements",
                source_ref = yes_branch.source_ref,
                message    = "Removed conditional branch not taken due to false condition value."
            )

            self.setBranchYes(None)
            yes_branch = None

        if no_branch is not None and truth_value is True:
            constraint_collection.signalChange(
                tags       = "new_statements",
                source_ref = no_branch.source_ref,
                message    = "Removed 'else' branch not taken due to true condition value."
            )

            self.setBranchNo(None)
            no_branch = None

        # Continue to execute for yes branch unless we know it's not going to be
        # relevant.
        if yes_branch is not None:
            branch_yes_collection = ConstraintCollectionBranch(
                parent = constraint_collection,
                name   = "conditional yes branch",
            )

            branch_yes_collection.computeBranch(
                branch = yes_branch
            )

            # May have just gone away, so fetch it again.
            yes_branch = self.getBranchYes()

            # If it's aborting, it doesn't contribute to merging.
            if yes_branch is None or yes_branch.isStatementAborting():
                branch_yes_collection = None
        else:
            branch_yes_collection = None

        # Continue to execute for yes branch.
        if no_branch is not None:
            branch_no_collection = ConstraintCollectionBranch(
                parent = constraint_collection,
                name   = "conditional no branch"
            )

            branch_no_collection.computeBranch(
                branch = no_branch
            )

            # May have just gone away, so fetch it again.
            no_branch = self.getBranchNo()

            # If it's aborting, it doesn't contribute to merging.
            if no_branch is None or no_branch.isStatementAborting():
                branch_no_collection = None
        else:
            branch_no_collection = None

        # Merge into parent execution.
        constraint_collection.mergeBranches(
            branch_yes_collection,
            branch_no_collection
        )

        # Both branches may have become empty, which case, the statement needs
        # not remain.
        if yes_branch is None and no_branch is None:
            # Need to keep the boolean check.
            if truth_value is None:
                condition = ExpressionBuiltinBool(
                    value      = condition,
                    source_ref = condition.getSourceReference()
                )

            if condition.mayHaveSideEffects():
                # With both branches eliminated, the condition remains as a side
                # effect.
                result = makeStatementExpressionOnlyReplacementNode(
                    expression = condition,
                    node       = self
                )

                return result, "new_statements", """\
Both branches have no effect, reduced to evaluate condition."""
            else:
                return None, "new_statements", """\
Removed conditional statement without effect."""

        # Note: Checking the condition late, so that the surviving branch got
        # processed already. Returning without doing that, will corrupt the SSA
        # results. TODO: Could pretend the other branch didn't exist to save
        # complexity the merging of processing.
        if truth_value is not None:
            if truth_value is True:
                choice = "true"

                new_statement = self.getBranchYes()
            else:
                choice = "false"

                new_statement = self.getBranchNo()

            new_statement = wrapStatementWithSideEffects(
                new_node   = new_statement,
                old_node   = condition,
                allow_none = True # surviving branch may empty
            )

            return new_statement, "new_statements", """\
Condition for branch was predicted to be always %s.""" % choice

        # If there is no "yes" branch, remove that. Maybe a bad idea though.
        if yes_branch is None:
            # Would be eliminated already, if there wasn't any "no" branch
            # either.
            assert no_branch is not None

            from .OperatorNodes import ExpressionOperationNOT

            new_statement = StatementConditional(
                condition  = ExpressionOperationNOT(
                    operand    = condition,
                    source_ref = condition.getSourceReference()
                ),
                yes_branch = no_branch,
                no_branch  = None,
                source_ref = self.getSourceReference()
            )

            return new_statement, "new_statements", """\
Empty 'yes' branch for conditional statement treated with inverted condition check."""

        return self, None, None
Пример #6
0
    def computeExpressionRaw(self, constraint_collection):
        # Query the truth value after the expression is evaluated, once it is
        # evaluated in onExpression, it is known.
        constraint_collection.onExpression(
            expression = self.getCondition()
        )
        condition = self.getCondition()

        condition_may_raise = condition.mayRaiseException(BaseException)

        if condition_may_raise:
            constraint_collection.onExceptionRaiseExit(
                BaseException
            )

        # No need to look any further, if the condition raises, the branches do
        # not matter at all.
        if condition.willRaiseException(BaseException):
            return condition, "new_raise", """\
Conditional expression already raises implicitly in condition, removing \
branches."""

        if not condition_may_raise and condition.mayRaiseExceptionBool(BaseException):
            constraint_collection.onExceptionRaiseExit(
                BaseException
            )

        # Decide this based on truth value of condition.
        truth_value = condition.getTruthValue()

        # TODO: We now know that condition evaluates to true for the yes branch
        # and to not true for no branch, the branch should know that.
        yes_branch = self.getExpressionYes()

        # Continue to execute for yes branch unless we know it's not going to be
        # relevant.
        if truth_value is not False:
            branch_yes_collection = ConstraintCollectionBranch(
                parent = constraint_collection,
                name   = "conditional expression yes branch"
            )

            branch_yes_collection.computeBranch(
                branch = yes_branch
            )

            # May have just gone away, so fetch it again.
            yes_branch = self.getExpressionYes()

            # If it's aborting, it doesn't contribute to merging.
            if yes_branch.willRaiseException(BaseException):
                branch_yes_collection = None
        else:
            branch_yes_collection = None

        no_branch = self.getExpressionNo()

        # Continue to execute for yes branch.
        if truth_value is not True:
            branch_no_collection = ConstraintCollectionBranch(
                parent = constraint_collection,
                name   = "conditional expression no branch"
            )

            branch_no_collection.computeBranch(
                branch = no_branch
            )

            # May have just gone away, so fetch it again.
            no_branch = self.getExpressionNo()

            # If it's aborting, it doesn't contribute to merging.
            if no_branch.willRaiseException(BaseException):
                branch_no_collection = None
        else:
            branch_no_collection = None

        # Merge into parent execution.
        constraint_collection.mergeBranches(
            branch_yes_collection,
            branch_no_collection
        )

        if truth_value is True:
            return (
                wrapExpressionWithNodeSideEffects(
                    new_node = self.getExpressionYes(),
                    old_node = condition
                ),
                "new_expression",
                "Conditional expression predicted to yes case"
            )
        elif truth_value is False:
            return (
                wrapExpressionWithNodeSideEffects(
                    new_node = self.getExpressionNo(),
                    old_node = condition
                ),
                "new_expression",
                "Conditional expression predicted to no case"
            )
        else:
            return self, None, None
Пример #7
0
    def computeStatement(self, constraint_collection):
        # This node has many children to handle, pylint: disable=R0912,R0914,R0915
        tried = self.getBlockTry()

        except_handler = self.getBlockExceptHandler()
        break_handler = self.getBlockBreakHandler()
        continue_handler = self.getBlockContinueHandler()
        return_handler = self.getBlockReturnHandler()

        # The tried block must be considered as a branch, if it is not empty
        # already.
        collection_start = ConstraintCollectionBranch(
            parent = constraint_collection,
            name   = "try start"
        )

        abort_context = constraint_collection.makeAbortStackContext(
            catch_breaks    = break_handler is not None,
            catch_continues = continue_handler is not None,
            catch_returns   = return_handler is not None
        )

        with abort_context:
            # As a branch point for the many types of handlers.

            result = tried.computeStatementsSequence(
                constraint_collection = constraint_collection
            )

            # We might be done entirely already.
            if result is None:
                return None, "new_statements", "Removed now empty try statement."

            # Might be changed.
            if result is not tried:
                self.setBlockTry(result)
                tried = result

            break_collections = constraint_collection.getLoopBreakCollections()
            continue_collections = constraint_collection.getLoopContinueCollections()
            return_collections = constraint_collection.getFunctionReturnCollections()

        tried_may_raise = tried.mayRaiseException(BaseException)
        # Exception handling is useless if no exception is to be raised.
        # TODO: signal the change.
        if not tried_may_raise:
            if except_handler is not None:
                self.setBlockExceptHandler(None)
                except_handler = None

        # If tried may raise, even empty exception handler has a meaning to
        # ignore that exception.
        if tried_may_raise:
            collection_exception_handling = ConstraintCollectionBranch(
                parent = collection_start,
                name   = "except handler"
            )

            collection_exception_handling.degradePartiallyFromTriedCode(tried)

            if except_handler is not None:
                result = except_handler.computeStatementsSequence(
                    constraint_collection = collection_exception_handling
                )

                # Might be changed.
                if result is not except_handler:
                    self.setBlockExceptHandler(result)
                    except_handler = result

        if break_handler is not None:
            if not tried.mayBreak():
                self.setBlockBreakHandler(None)
                break_handler = None

        if break_handler is not None:
            collection_break = ConstraintCollectionBranch(
                parent = collection_start,
                name   = "break handler"
            )

            collection_break.mergeMultipleBranches(break_collections)

            result = break_handler.computeStatementsSequence(
                constraint_collection = collection_break
            )

            # Might be changed.
            if result is not break_handler:
                self.setBlockBreakHandler(result)
                break_handler = result

        if continue_handler is not None:
            if not tried.mayContinue():
                self.setBlockContinueHandler(None)
                continue_handler = None

        if continue_handler is not None:
            collection_continue = ConstraintCollectionBranch(
                parent = collection_start,
                name   = "continue handler"
            )

            collection_continue.mergeMultipleBranches(continue_collections)

            result = continue_handler.computeStatementsSequence(
                constraint_collection = collection_continue
            )

            # Might be changed.
            if result is not continue_handler:
                self.setBlockContinueHandler(result)
                continue_handler = result

        if return_handler is not None:
            if not tried.mayReturn():
                self.setBlockReturnHandler(None)
                return_handler = None

        if return_handler is not None:
            collection_return = ConstraintCollectionBranch(
                parent = collection_start,
                name   = "return handler"
            )

            collection_return.mergeMultipleBranches(return_collections)

            result = return_handler.computeStatementsSequence(
                constraint_collection = collection_return
            )

            # Might be changed.
            if result is not return_handler:
                self.setBlockReturnHandler(result)
                return_handler = result

        if return_handler is not None:
            if return_handler.getStatements()[0].isStatementReturn() and \
               return_handler.getStatements()[0].getExpression().isExpressionReturnedValueRef():
                self.setBlockReturnHandler(None)
                return_handler = None


        # Merge exception handler only if it is used. Empty means it is not
        # aborting, as it swallows the exception.
        if tried_may_raise and (
               except_handler is None or \
               not except_handler.isStatementAborting()
            ):
            constraint_collection.mergeBranches(
                collection_yes = collection_exception_handling,
                collection_no  = None
            )

        # An empty exception handler means we have to swallow exception.
        if not tried_may_raise and \
           break_handler is None and \
           continue_handler is None and \
           return_handler is None:
            return tried, "new_statements", "Removed all try handlers."

        tried_statements = tried.getStatements()

        pre_statements = []

        while tried_statements:
            tried_statement = tried_statements[0]

            if tried_statement.mayRaiseException(BaseException):
                break

            if break_handler is not None and \
               tried_statement.mayBreak():
                break

            if continue_handler is not None and \
               tried_statement.mayContinue():
                break

            if return_handler is not None and \
               tried_statement.mayReturn():
                break

            pre_statements.append(tried_statement)
            tried_statements = list(tried_statements)

            del tried_statements[0]

        post_statements = []

        if except_handler is not None and except_handler.isStatementAborting():
            while tried_statements:
                tried_statement = tried_statements[-1]

                if tried_statement.mayRaiseException(BaseException):
                    break

                if break_handler is not None and \
                   tried_statement.mayBreak():
                    break

                if continue_handler is not None and \
                   tried_statement.mayContinue():
                    break

                if return_handler is not None and \
                   tried_statement.mayReturn():
                    break

                post_statements.insert(0, tried_statement)
                tried_statements = list(tried_statements)

                del tried_statements[-1]

        if pre_statements or post_statements:
            assert tried_statements # Should be dealt with already

            tried.setStatements(tried_statements)

            result = StatementsSequence(
                statements = pre_statements + [self] + post_statements,
                source_ref = self.getSourceReference()
            )

            # TODO: We probably don't want to say this for re-formulation ones.
            return result, "new_statements", "Reduced scope of tried block."

        return self, None, None
Пример #8
0
    def computeExpressionRaw(self, constraint_collection):
        # Query the truth value after the expression is evaluated, once it is
        # evaluated in onExpression, it is known.
        constraint_collection.onExpression(
            expression = self.getLeft()
        )
        left = self.getLeft()

        left_may_raise = left.mayRaiseException(BaseException)

        if left_may_raise:
            constraint_collection.onExceptionRaiseExit(
                BaseException
            )
        # No need to look any further, if the condition raises, the branches do
        # not matter at all.
        if left.willRaiseException(BaseException):
            return left, "new_raise", """\
Conditional %s statements already raises implicitly in condition, removing \
branches.""" % self.conditional_kind

        if not left_may_raise and left.mayRaiseExceptionBool(BaseException):
            constraint_collection.onExceptionRaiseExit(
                BaseException
            )

        # Decide this based on truth value of condition.
        truth_value = left.getTruthValue()

        truth_value_use_left = self.conditional_kind == "or"
        truth_value_use_right = not truth_value_use_left

        right = self.getRight()

        # Continue to execute for yes branch unless we know it's not going to be
        # relevant.
        if truth_value is not truth_value_use_left:
            # TODO: We now know that left evaluates and we should tell the
            # branch that.
            branch_yes_collection = ConstraintCollectionBranch(
                parent = constraint_collection,
                name   = "boolean %s right branch" % self.conditional_kind
            )

            branch_yes_collection.computeBranch(
                branch = right
            )

            # May have just gone away, so fetch it again.
            right = self.getRight()

            # If it's aborting, it doesn't contribute to merging.
            if right.willRaiseException(BaseException):
                branch_yes_collection = None
        else:
            branch_yes_collection = None


        if branch_yes_collection:
            # Merge into parent execution.
            constraint_collection.mergeBranches(
                branch_yes_collection,
                None
            )

        if truth_value is truth_value_use_left:
            return (
                left,
                "new_expression",
                "Conditional '%s' expression predicted to left value." % self.conditional_kind
            )
        elif truth_value is truth_value_use_right:
            return (
                wrapExpressionWithNodeSideEffects(
                    new_node = right,
                    old_node = left
                ),
                "new_expression",
                "Conditional '%s' expression predicted right value." % self.conditional_kind
            )
        else:
            return self, None, None
Пример #9
0
    def computeStatement(self, constraint_collection):
        # This node has many children to handle, pylint: disable=R0912,R0914
        tried = self.getBlockTry()

        except_handler = self.getBlockExceptHandler()
        break_handler = self.getBlockBreakHandler()
        continue_handler = self.getBlockContinueHandler()
        return_handler = self.getBlockReturnHandler()

        # The tried block must be considered as a branch, if it is not empty
        # already.
        collection_start = ConstraintCollectionBranch(
            parent=constraint_collection, name="try start")

        abort_context = constraint_collection.makeAbortStackContext(
            catch_breaks=break_handler is not None,
            catch_continues=continue_handler is not None,
            catch_returns=return_handler is not None,
            catch_exceptions=True,
        )

        with abort_context:
            # As a branch point for the many types of handlers.

            result = tried.computeStatementsSequence(
                constraint_collection=constraint_collection)

            # We might be done entirely already.
            if result is None:
                return None, "new_statements", "Removed now empty try statement."

            # Might be changed.
            if result is not tried:
                self.setBlockTry(result)
                tried = result

            break_collections = constraint_collection.getLoopBreakCollections()
            continue_collections = constraint_collection.getLoopContinueCollections(
            )
            return_collections = constraint_collection.getFunctionReturnCollections(
            )
            exception_collections = constraint_collection.getExceptionRaiseCollections(
            )

        tried_may_raise = tried.mayRaiseException(BaseException)
        # Exception handling is useless if no exception is to be raised.
        # TODO: signal the change.
        if not tried_may_raise:
            if except_handler is not None:
                self.setBlockExceptHandler(None)
                except_handler = None

        # If tried may raise, even empty exception handler has a meaning to
        # ignore that exception.
        if tried_may_raise:
            collection_exception_handling = ConstraintCollectionBranch(
                parent=collection_start, name="except handler")

            if not exception_collections:
                for statement in tried.getStatements():
                    if statement.mayRaiseException(BaseException):
                        assert False, statement.asXmlText()

                assert False

            collection_exception_handling.mergeMultipleBranches(
                exception_collections)

            if except_handler is not None:
                result = except_handler.computeStatementsSequence(
                    constraint_collection=collection_exception_handling)

                # Might be changed.
                if result is not except_handler:
                    self.setBlockExceptHandler(result)
                    except_handler = result

        if break_handler is not None:
            if not tried.mayBreak():
                self.setBlockBreakHandler(None)
                break_handler = None

        if break_handler is not None:
            collection_break = ConstraintCollectionBranch(
                parent=collection_start, name="break handler")

            collection_break.mergeMultipleBranches(break_collections)

            result = break_handler.computeStatementsSequence(
                constraint_collection=collection_break)

            # Might be changed.
            if result is not break_handler:
                self.setBlockBreakHandler(result)
                break_handler = result

        if continue_handler is not None:
            if not tried.mayContinue():
                self.setBlockContinueHandler(None)
                continue_handler = None

        if continue_handler is not None:
            collection_continue = ConstraintCollectionBranch(
                parent=collection_start, name="continue handler")

            collection_continue.mergeMultipleBranches(continue_collections)

            result = continue_handler.computeStatementsSequence(
                constraint_collection=collection_continue)

            # Might be changed.
            if result is not continue_handler:
                self.setBlockContinueHandler(result)
                continue_handler = result

        if return_handler is not None:
            if not tried.mayReturn():
                self.setBlockReturnHandler(None)
                return_handler = None

        if return_handler is not None:
            collection_return = ConstraintCollectionBranch(
                parent=collection_start, name="return handler")

            collection_return.mergeMultipleBranches(return_collections)

            result = return_handler.computeStatementsSequence(
                constraint_collection=collection_return)

            # Might be changed.
            if result is not return_handler:
                self.setBlockReturnHandler(result)
                return_handler = result

        if return_handler is not None:
            if return_handler.getStatements()[0].isStatementReturn() and \
               return_handler.getStatements()[0].getExpression().isExpressionReturnedValueRef():
                self.setBlockReturnHandler(None)
                return_handler = None

        # Merge exception handler only if it is used. Empty means it is not
        # aborting, as it swallows the exception.
        if tried_may_raise and (
               except_handler is None or \
               not except_handler.isStatementAborting()
            ):
            constraint_collection.mergeBranches(
                collection_yes=collection_exception_handling,
                collection_no=None)

        # An empty exception handler means we have to swallow exception.
        if not tried_may_raise and \
           break_handler is None and \
           continue_handler is None and \
           return_handler is None:
            return tried, "new_statements", "Removed all try handlers."

        tried_statements = tried.getStatements()

        pre_statements = []

        while tried_statements:
            tried_statement = tried_statements[0]

            if tried_statement.mayRaiseException(BaseException):
                break

            if break_handler is not None and \
               tried_statement.mayBreak():
                break

            if continue_handler is not None and \
               tried_statement.mayContinue():
                break

            if return_handler is not None and \
               tried_statement.mayReturn():
                break

            pre_statements.append(tried_statement)
            tried_statements = list(tried_statements)

            del tried_statements[0]

        post_statements = []

        if except_handler is not None and except_handler.isStatementAborting():
            while tried_statements:
                tried_statement = tried_statements[-1]

                if tried_statement.mayRaiseException(BaseException):
                    break

                if break_handler is not None and \
                   tried_statement.mayBreak():
                    break

                if continue_handler is not None and \
                   tried_statement.mayContinue():
                    break

                if return_handler is not None and \
                   tried_statement.mayReturn():
                    break

                post_statements.insert(0, tried_statement)
                tried_statements = list(tried_statements)

                del tried_statements[-1]

        if pre_statements or post_statements:
            assert tried_statements  # Should be dealt with already

            tried.setStatements(tried_statements)

            result = StatementsSequence(statements=pre_statements + [self] +
                                        post_statements,
                                        source_ref=self.getSourceReference())

            def explain():
                # TODO: We probably don't want to say this for re-formulation ones.
                result = "Reduced scope of tried block."

                if pre_statements:
                    result += " Leading statements at %s." % (','.join(
                        x.getSourceReference().getAsString() + '/' + str(x)
                        for x in pre_statements))

                if post_statements:
                    result += " Trailing statements at %s." % (','.join(
                        x.getSourceReference().getAsString() + '/' + str(x)
                        for x in post_statements))

                return result

            return (result, "new_statements", explain)

        return self, None, None
Пример #10
0
    def computeExpressionRaw(self, constraint_collection):
        # The tried block must be considered as a branch, if it is not empty
        # already.
        tried_statement_sequence = self.getBlockTry()

        # May be "None" from the outset, so guard against that, later in this
        # function we are going to remove it.
        if tried_statement_sequence is not None:
            result = tried_statement_sequence.computeStatementsSequence(
                constraint_collection = constraint_collection
            )

            # Might be changed.
            if result is not tried_statement_sequence:
                self.setBlockTry(result)
                tried_statement_sequence = result

        # The main expression itself.
        constraint_collection.onExpression(self.getExpression())

        final_statement_sequence = self.getBlockFinal()

        # TODO: The final must not assume that all of tried was executed,
        # instead it may have aborted after any part of it, which is a rather
        # complex definition.

        if final_statement_sequence is not None:
            collection_exception_handling = ConstraintCollectionBranch(
                parent = constraint_collection,
            )

            if tried_statement_sequence is not None:
                # Mark all variables as unknown that are written in the tried
                # block, so it destroys the assumptions for loop turn around.
                collection_exception_handling.degradePartiallyFromCode(tried_statement_sequence)

            # In case there are assignments hidden in the expression too.
            collection_exception_handling.degradePartiallyFromCode(self.getExpression())

            # Then assuming no exception, the no raise block if present.
            result = final_statement_sequence.computeStatementsSequence(
                constraint_collection = collection_exception_handling
            )

            if result is not final_statement_sequence:
                self.setBlockFinal(result)

                final_statement_sequence = result

        if tried_statement_sequence is None and \
           final_statement_sequence is None:
            # If the tried and final block is empty, go to the expression
            # directly.
            return self.getExpression(), "new_expression", """\
Removed try/finally expression with empty tried and final block."""
        else:
            if final_statement_sequence is not None:
                # Otherwise keep it as it, merging the finally block back into
                # the constraint collection.
                constraint_collection.mergeBranches(
                    collection_yes = collection_exception_handling,
                    collection_no  = None
                )


            # Otherwise keep it as it.
            return self, None, None
Пример #11
0
    def computeStatement(self, constraint_collection):
        # The tried block must be considered as a branch, if it is not empty
        # already.
        tried_statement_sequence = self.getBlockTry()

        # May be "None" from the outset, so guard against that, later in this
        # function we are going to remove it.
        if tried_statement_sequence is not None:
            result = tried_statement_sequence.computeStatementsSequence(
                constraint_collection = constraint_collection
            )

            # Might be changed.
            if result is not tried_statement_sequence:
                self.setBlockTry(result)
                tried_statement_sequence = result

        final_statement_sequence = self.getBlockFinal()

        if final_statement_sequence is not None:
            # TODO: The must not assume that all of tried was executed, instead
            # it may have aborted after any part of it, which is a rather
            # a complex situation, so we just invalidate all writes.

            collection_exception_handling = ConstraintCollectionBranch(
                parent = constraint_collection,
            )

            if tried_statement_sequence is not None:
                collection_exception_handling.degradePartiallyFromCode(tried_statement_sequence)

            # Then assuming no exception, the no raise block if present.
            result = final_statement_sequence.computeStatementsSequence(
                constraint_collection = collection_exception_handling
            )

            if result is not final_statement_sequence:
                self.setBlockFinal(result)
                final_statement_sequence = result

            # If we are not aborting, we need to merge.
            if not self.isStatementAborting():
                # Merge back as a branch.
                constraint_collection.mergeBranches(
                    collection_yes = collection_exception_handling,
                    collection_no  = None
                )

        if tried_statement_sequence is None:
            # If the tried block is empty, go to the final block directly, if
            # any.
            return final_statement_sequence, "new_statements", """\
Removed try/finally with empty tried block."""
        elif final_statement_sequence is None:
            # If the final block is empty, just need to execute the tried block
            # then.
            return tried_statement_sequence, "new_statements", """\
Removed try/finally with empty final block."""
        elif not tried_statement_sequence.mayRaiseExceptionOrAbort(
                BaseException
            ):
            # There may be no need to try/finally at all then. TODO: The code
            # below, which reduces the scope, could do this too, but would be
            # less elegant in its report.

            tried_statement_sequence.setChild(
                "statements",
                tried_statement_sequence.getStatements() +
                final_statement_sequence.getStatements()
            )

            return tried_statement_sequence, "new_statements", """\
Removed try/finally with try block that cannot raise."""
        else:
            new_statements = tried_statement_sequence.getStatements()
            # Determine statements inside the exception guard, that need not be in
            # a handler, because they wouldn't raise an exception. TODO: This
            # actual exception being watched for should be considered, by look
            # for any now.
            outside_pre = []
            while new_statements and \
                  not new_statements[0].mayRaiseException(BaseException) and \
                  not new_statements[0].mayReturn() and \
                  not new_statements[0].mayBreak() and \
                  not new_statements[0].mayContinue():

                outside_pre.append(new_statements[0])
                new_statements = list(new_statements)[1:]

            if outside_pre:
                tried_statement_sequence.setStatements(new_statements)

                from .NodeMakingHelpers import makeStatementsSequenceReplacementNode

                result = makeStatementsSequenceReplacementNode(
                    statements = outside_pre + [self],
                    node       = self
                )

                return result, "new_statements", """\
Moved statements of tried block that cannot abort to the outside."""

            return self, None, None
Пример #12
0
    def computeStatement(self, constraint_collection):
        # The tried block can be processed normally.
        tried_statement_sequence = self.getBlockTry()

        # May be "None" from the outset, so guard against that, later we are
        # going to remove it.
        if tried_statement_sequence is not None:
            result = tried_statement_sequence.computeStatementsSequence(
                constraint_collection = constraint_collection
            )

            if result is not tried_statement_sequence:
                self.setBlockTry(result)

                tried_statement_sequence = result

        if tried_statement_sequence is None:
            return None, "new_statements", """\
Removed try/except with empty tried block."""


        collection_exception_handling = ConstraintCollectionBranch(
            parent = constraint_collection,
        )

        collection_exception_handling.degradePartiallyFromCode(tried_statement_sequence)

        if self.getExceptionHandling() is not None:
            collection_exception_handling.computeBranch(
                branch = self.getExceptionHandling()
            )


        # Merge only, if the exception handling itself does exit.
        if self.getExceptionHandling() is None or \
           not self.getExceptionHandling().isStatementAborting():

            constraint_collection.mergeBranches(
                collection_yes = collection_exception_handling,
                collection_no  = None
            )

        # Without exception handlers remaining, nothing else to do. They may
        # e.g. be removed as only re-raising.
        if self.getExceptionHandling() and \
           self.getExceptionHandling().getStatements()[0].\
             isStatementReraiseException():
            return tried_statement_sequence, "new_statements", """\
Removed try/except without any remaining handlers."""

        # Remove exception handling, if it cannot happen.
        if not tried_statement_sequence.mayRaiseException(BaseException):
            return tried_statement_sequence, "new_statements", """\
Removed try/except with tried block that cannot raise."""

        new_statements = tried_statement_sequence.getStatements()
        # Determine statements inside the exception guard, that need not be in
        # a handler, because they wouldn't raise an exception. TODO: This
        # actual exception being watched for should be considered, by look
        # for any now.
        outside_pre = []
        while new_statements and \
              not new_statements[0].mayRaiseException(BaseException):
            outside_pre.append(new_statements[0])
            new_statements = list(new_statements)[1:]

        outside_post = []
        if self.getExceptionHandling() is not None and \
           self.getExceptionHandling().isStatementAborting():
            while new_statements and \
                  not new_statements[-1].mayRaiseException(BaseException):
                outside_post.insert(0, new_statements[-1])
                new_statements = list(new_statements)[:-1]

        if outside_pre or outside_post:
            tried_statement_sequence.setStatements(new_statements)

            from .NodeMakingHelpers import makeStatementsSequenceReplacementNode

            result = makeStatementsSequenceReplacementNode(
                statements = outside_pre + [self] + outside_post,
                node       = self
            )

            return result, "new_statements", """\
Moved statements of tried block that cannot raise."""

        return self, None, None
Пример #13
0
    def computeStatement(self, constraint_collection):
        # The tried block can be processed normally.
        tried_statement_sequence = self.getBlockTry()

        # May be "None" from the outset, so guard against that, later we are
        # going to remove it.
        if tried_statement_sequence is not None:
            result = tried_statement_sequence.computeStatementsSequence(constraint_collection=constraint_collection)

            if result is not tried_statement_sequence:
                self.setBlockTry(result)

                tried_statement_sequence = result

        if tried_statement_sequence is None:
            return (
                None,
                "new_statements",
                """\
Removed try/except with empty tried block.""",
            )

        collection_exception_handling = ConstraintCollectionBranch(parent=constraint_collection)

        collection_exception_handling.degradePartiallyFromCode(tried_statement_sequence)

        if self.getExceptionHandling() is not None:
            collection_exception_handling.computeBranch(branch=self.getExceptionHandling())

        # Merge only, if the exception handling itself does exit.
        if self.getExceptionHandling() is None or not self.getExceptionHandling().isStatementAborting():

            constraint_collection.mergeBranches(collection_yes=collection_exception_handling, collection_no=None)

        # Without exception handlers remaining, nothing else to do. They may
        # e.g. be removed as only re-raising.
        if self.getExceptionHandling() and self.getExceptionHandling().getStatements()[0].isStatementReraiseException():
            return (
                tried_statement_sequence,
                "new_statements",
                """\
Removed try/except without any remaining handlers.""",
            )

        # Remove exception handling, if it cannot happen.
        if not tried_statement_sequence.mayRaiseException(BaseException):
            return (
                tried_statement_sequence,
                "new_statements",
                """\
Removed try/except with tried block that cannot raise.""",
            )

        new_statements = tried_statement_sequence.getStatements()
        # Determine statements inside the exception guard, that need not be in
        # a handler, because they wouldn't raise an exception. TODO: This
        # actual exception being watched for should be considered, by look
        # for any now.
        outside_pre = []
        while new_statements and not new_statements[0].mayRaiseException(BaseException):
            outside_pre.append(new_statements[0])
            new_statements = list(new_statements)[1:]

        outside_post = []
        if self.getExceptionHandling() is not None and self.getExceptionHandling().isStatementAborting():
            while new_statements and not new_statements[-1].mayRaiseException(BaseException):
                outside_post.insert(0, new_statements[-1])
                new_statements = list(new_statements)[:-1]

        if outside_pre or outside_post:
            tried_statement_sequence.setStatements(new_statements)

            from .NodeMakingHelpers import makeStatementsSequenceReplacementNode

            result = makeStatementsSequenceReplacementNode(statements=outside_pre + [self] + outside_post, node=self)

            return (
                result,
                "new_statements",
                """\
Moved statements of tried block that cannot raise.""",
            )

        return self, None, None