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 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 # 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.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 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.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): # 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