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
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
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
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
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
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
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
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
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
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
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
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
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