/* Soot - a J*va Optimization Framework * Copyright (C) 2006 Nomair A. Naeem * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ package soot.dava.toolkits.base.AST.transformations; import soot.BooleanType; import soot.Value; import soot.dava.internal.AST.ASTAggregatedCondition; import soot.dava.internal.AST.ASTAndCondition; import soot.dava.internal.AST.ASTBinaryCondition; import soot.dava.internal.AST.ASTCondition; import soot.dava.internal.AST.ASTControlFlowNode; import soot.dava.internal.AST.ASTDoWhileNode; import soot.dava.internal.AST.ASTForLoopNode; import soot.dava.internal.AST.ASTIfElseNode; import soot.dava.internal.AST.ASTIfNode; import soot.dava.internal.AST.ASTOrCondition; import soot.dava.internal.AST.ASTUnaryCondition; import soot.dava.internal.AST.ASTWhileNode; import soot.dava.internal.javaRep.DIntConstant; import soot.dava.internal.javaRep.DNotExpr; import soot.dava.toolkits.base.AST.analysis.DepthFirstAdapter; import soot.jimple.ConditionExpr; import soot.jimple.DoubleConstant; import soot.jimple.FloatConstant; import soot.jimple.IntConstant; import soot.jimple.LongConstant; /* * 5 == 5 true DONE * 5 != 5 false DONE (all other relational operators done) * * !true --> false DONE * !false --> true DONE * * DONE WHEN one or both are constants (did all combinations) * true || b ----> true * true && b -----> b * false || b -----> b * false && b -------> false * * if ( (z0 && z1) || ( ! ( ! (z2) || ! (z3)) ) ) * ---> if ( (z0 && z1) || (z2 && z3) ) DONE * * * * TODO currently only doing primtype comparison of same types not handled are following types * long <= int * int <=long * bla bla * * * TODO IDEA if(io==0 && io==0) --> if(io==0) */ public class SimplifyConditions extends DepthFirstAdapter { public static boolean DEBUG=false; public boolean changed=false; public SimplifyConditions() { super(); } public SimplifyConditions(boolean verbose) { super(verbose); } public void fixedPoint(ASTControlFlowNode node){ ASTCondition returned; do{ if(DEBUG) System.out.println("Invoking simplify"); changed=false; ASTCondition cond = node.get_Condition(); returned = simplifyTheCondition(cond); if(returned != null) node.set_Condition(returned); }while(changed); } public void outASTIfNode(ASTIfNode node){ fixedPoint(node); } public void outASTIfElseNode(ASTIfElseNode node){ fixedPoint(node); } public void outASTWhileNode(ASTWhileNode node){ fixedPoint(node); } public void outASTDoWhileNode(ASTDoWhileNode node){ fixedPoint(node); } public void outASTForLoopNode(ASTForLoopNode node){ fixedPoint(node); } /* * !z0 && !z1 ----> !(z0 || z1) * !z0 || !z1 ----> !(z0 && z1) * * Send null if no change else send new condition CONDITION */ public ASTCondition applyDeMorgans(ASTAggregatedCondition aggCond){ ASTCondition left = aggCond.getLeftOp(); ASTCondition right = aggCond.getRightOp(); if(aggCond.isNotted() && left instanceof ASTBinaryCondition && right instanceof ASTBinaryCondition){ //we can remove the not sign by simply flipping the two conditions // ! ( x==y && a<b ) left.flip(); right.flip(); if(aggCond instanceof ASTAndCondition) aggCond = new ASTOrCondition(left,right); else aggCond = new ASTAndCondition(left,right); return aggCond; } if( ( left.isNotted() && right.isNotted() && ( !(left instanceof ASTBinaryCondition) && !(right instanceof ASTBinaryCondition) ) ) || (left.isNotted() && aggCond.isNotted() && !(left instanceof ASTBinaryCondition) ) || (right.isNotted() && aggCond.isNotted() && !(right instanceof ASTBinaryCondition) ) ){ //both are notted and atleast one is not a binaryCondition left.flip(); right.flip(); ASTAggregatedCondition newCond; if(aggCond instanceof ASTAndCondition) newCond = new ASTOrCondition(left,right); else newCond = new ASTAndCondition(left,right); if(aggCond.isNotted()) return newCond; else{ newCond.flip(); return newCond; } } return null; } /* * When this method is invoked we are sure that there are no occurences of !true or !false since * this is AFTER doing depth first of the children so the unaryCondition must have simplified the above * * Return Null if no change else return changed condition */ public ASTCondition simplifyIfAtleastOneConstant(ASTAggregatedCondition aggCond){ ASTCondition left = aggCond.getLeftOp(); ASTCondition right = aggCond.getRightOp(); Boolean leftBool = null; Boolean rightBool = null; if (left instanceof ASTUnaryCondition) leftBool = isBooleanConstant(((ASTUnaryCondition) left).getValue()); if (right instanceof ASTUnaryCondition) rightBool = isBooleanConstant(((ASTUnaryCondition) right).getValue()); /* * a && b NOCHANGE DONE * b && a NOCHANGE DONE * * a || b NOCHANGE DONE * b || a NOCHANGE DONE * */ if (leftBool == null && rightBool == null) { // meaning both are not constants return null; } if(aggCond instanceof ASTAndCondition){ /* * true && true ---> true DONE * true && false --> false DONE * false && false ---> false DONE * false && true --> false DONE * * true && b -----> b DONE * false && b -------> false DONE * * b && true ---> b DONE * b && false ---> b && false (since b could have side effects and the overall condition has to be false) DONE * */ if (leftBool != null && rightBool != null) { // meaning both are constants if (leftBool.booleanValue() && rightBool.booleanValue()) { // both are true return new ASTUnaryCondition(DIntConstant.v(1, BooleanType.v())); } else { // atleast one of the two is false return new ASTUnaryCondition(DIntConstant.v(0, BooleanType.v())); } } if (leftBool != null) { // implicityly means that rigthBool is null since the above // condition passed if (leftBool.booleanValue()) { // left bool is a true meaning we have to evaluate right // condition.......just return the right condition return right; } else { // left bool is false meaning no need to continue since we // will never execute the right condition // return a unary false return new ASTUnaryCondition(DIntConstant.v(0, BooleanType.v())); } } if (rightBool != null) { // implicityly means that the leftBool is null if (rightBool.booleanValue()) { // rightBool is true so it all depends on left return left; } else { // although we know the condition overall is false we cant // remove the leftBool since there might be side effects return aggCond; } } } else if(aggCond instanceof ASTOrCondition){ /* * * true || false ---> true DONE * true || true --> true DONE * false || true --> true DONE * false || false ---> false DONE * * * true || b ----> true DONE * false || b -----> b DONE * * b || true ---> b || true .... although we know the condition is true we have to evaluate b because of possible side effects DONE * b || false ---> b DONE * */ if (leftBool != null && rightBool != null) { // meaning both are constants if ( !leftBool.booleanValue() && !rightBool.booleanValue()) { // both are false return new ASTUnaryCondition(DIntConstant.v(0, BooleanType.v())); } else { // atleast one of the two is true return new ASTUnaryCondition(DIntConstant.v(1, BooleanType.v())); } } if (leftBool != null) { // implicityly means that rigthBool is null since the above // condition passed if (leftBool.booleanValue()) { //left bool is true that means we will stop evaluation of condition, just return true return new ASTUnaryCondition(DIntConstant.v(1, BooleanType.v())); } else{ //left bool is false so we have to continue evaluating right return right; } } if (rightBool != null) { // implicityly means that the leftBool is null if (rightBool.booleanValue()) { // rightBool is true but leftBool must be evaluated beforehand return aggCond; } else { //rightBool is false so everything depends on left return left; } } } else throw new RuntimeException("Found unknown aggregated condition"); return null; } /* * Method returns null if the Value is not a constant or not a boolean constant * return true if the constant is true * return false if the constant is false */ public Boolean isBooleanConstant(Value internal){ if(! (internal instanceof DIntConstant)) return null; if(DEBUG) System.out.println("Found Constant"); DIntConstant intConst = (DIntConstant)internal; if(! (intConst.type instanceof BooleanType) ) return null; //either true or false if(DEBUG) System.out.println("Found Boolean Constant"); if(intConst.value == 1){ return new Boolean(true); } else if(intConst.value == 0){ return new Boolean(false); } else throw new RuntimeException("BooleanType found with value different than 0 or 1"); } /* * In a loop keep simplifying the condition as much as possible * */ public ASTCondition simplifyTheCondition(ASTCondition cond){ if(cond instanceof ASTAggregatedCondition){ ASTAggregatedCondition aggCond = (ASTAggregatedCondition)cond; ASTCondition leftCond = simplifyTheCondition(aggCond.getLeftOp()); ASTCondition rightCond = simplifyTheCondition(aggCond.getRightOp()); //if returned values are non null then set leftop /rightop to new condition if(leftCond != null){ aggCond.setLeftOp(leftCond); } if(rightCond != null){ aggCond.setRightOp(rightCond); } ASTCondition returned = simplifyIfAtleastOneConstant(aggCond); if(returned != null){ changed=true; return returned; } returned = applyDeMorgans(aggCond); if(returned != null){ changed = true; return returned; } return aggCond; } else if(cond instanceof ASTUnaryCondition){ //dont do anything with unary conditions ASTUnaryCondition unary = (ASTUnaryCondition)cond; /* * if unary is a noted constant simplify it * !true to be converted to false * !false to be converted to true */ Value unaryVal = unary.getValue(); if(unaryVal instanceof DNotExpr){ if(DEBUG) System.out.println("Found NotExpr in unary COndition"+unaryVal); DNotExpr notted = (DNotExpr)unaryVal; Value internal = notted.getOp(); Boolean isIt = isBooleanConstant(internal); if(isIt != null){ //is a boolean constant truth value will give whether its true or false //convert !true to false if(isIt.booleanValue()){ //true if(DEBUG) System.out.println("CONVERTED !true to false"); changed=true; return new ASTUnaryCondition( DIntConstant.v(0,BooleanType.v())); } else if(!isIt.booleanValue()){ //false if(DEBUG) System.out.println("CONVERTED !false to true"); changed=true; return new ASTUnaryCondition( DIntConstant.v(1,BooleanType.v())); } else throw new RuntimeException("BooleanType found with value different than 0 or 1"); } else{ if(DEBUG)System.out.println("Not boolean type"); } } return unary; } else if(cond instanceof ASTBinaryCondition){ ASTBinaryCondition binary = (ASTBinaryCondition)cond; ConditionExpr expr = binary.getConditionExpr(); //returns null if no change ASTUnaryCondition temp = evaluateBinaryCondition(expr); if(DEBUG) System.out.println("changed binary condition "+cond +" to" + temp); if(temp != null) changed=true; return temp; } else{ throw new RuntimeException("Method getUseList in ASTUsesAndDefs encountered unknown condition type"); } } //return condition if was able to simplify (convert to a boolean true or false) else null public ASTUnaryCondition evaluateBinaryCondition(ConditionExpr expr){ String symbol = expr.getSymbol(); int op =-1; if(symbol.indexOf("==")>-1){ if(DEBUG) System.out.println("=="); op=1; } else if(symbol.indexOf(">=")>-1){ if(DEBUG) System.out.println(">="); op=2; } else if(symbol.indexOf('>')>-1){ if(DEBUG) System.out.println(">"); op=3; } else if(symbol.indexOf("<=")>-1){ if(DEBUG) System.out.println("<="); op=4; } else if(symbol.indexOf('<')>-1){ if(DEBUG) System.out.println("<"); op=5; } else if(symbol.indexOf("!=")>-1){ if(DEBUG) System.out.println("!="); op=6; } Value leftOp = expr.getOp1(); Value rightOp = expr.getOp2(); Boolean result=null; if(leftOp instanceof LongConstant && rightOp instanceof LongConstant){ if(DEBUG) System.out.println("long constants!!"); long left = ((LongConstant)leftOp).value; long right = ((LongConstant)rightOp).value; result = longSwitch(op,left,right); } else if(leftOp instanceof DoubleConstant && rightOp instanceof DoubleConstant){ double left = ((DoubleConstant)leftOp).value; double right = ((DoubleConstant)rightOp).value; result = doubleSwitch(op,left,right); } else if(leftOp instanceof FloatConstant && rightOp instanceof FloatConstant){ float left = ((FloatConstant)leftOp).value; float right = ((FloatConstant)rightOp).value; result = floatSwitch(op,left,right); } else if(leftOp instanceof IntConstant && rightOp instanceof IntConstant){ int left = ((IntConstant)leftOp).value; int right = ((IntConstant)rightOp).value; result = intSwitch(op,left,right); } if(result!=null){ if(result.booleanValue()) return new ASTUnaryCondition( DIntConstant.v(1,BooleanType.v())); else return new ASTUnaryCondition( DIntConstant.v(0,BooleanType.v())); } return null; } public Boolean longSwitch(int op,long l,long r){ switch(op){ case 1: // == if (l == r) return new Boolean(true); else return new Boolean(false); case 2: // >= if(l >= r) return new Boolean(true); else return new Boolean(false); case 3: // > if(l > r) return new Boolean(true); else return new Boolean(false); case 4: // <= if(l <= r) return new Boolean(true); else return new Boolean(false); case 5: // < if(l < r) return new Boolean(true); else return new Boolean(false); case 6: // != if(l != r) return new Boolean(true); else return new Boolean(false); default: if(DEBUG) System.out.println("got here"); return null; } } public Boolean doubleSwitch(int op,double l,double r){ switch(op){ case 1: // == if (l == r) return new Boolean(true); else return new Boolean(false); case 2: // >= if(l >= r) return new Boolean(true); else return new Boolean(false); case 3: // > if(l > r) return new Boolean(true); else return new Boolean(false); case 4: // <= if(l <= r) return new Boolean(true); else return new Boolean(false); case 5: // < if(l < r) return new Boolean(true); else return new Boolean(false); case 6: // != if(l != r) return new Boolean(true); else return new Boolean(false); default: return null; } } public Boolean floatSwitch(int op,float l,float r){ switch(op){ case 1: // == if (l == r) return new Boolean(true); else return new Boolean(false); case 2: // >= if(l >= r) return new Boolean(true); else return new Boolean(false); case 3: // > if(l > r) return new Boolean(true); else return new Boolean(false); case 4: // <= if(l <= r) return new Boolean(true); else return new Boolean(false); case 5: // < if(l < r) return new Boolean(true); else return new Boolean(false); case 6: // != if(l != r) return new Boolean(true); else return new Boolean(false); default: return null; } } public Boolean intSwitch(int op,int l,int r){ switch(op){ case 1: // == if (l == r) return new Boolean(true); else return new Boolean(false); case 2: // >= if(l >= r) return new Boolean(true); else return new Boolean(false); case 3: // > if(l > r) return new Boolean(true); else return new Boolean(false); case 4: // <= if(l <= r) return new Boolean(true); else return new Boolean(false); case 5: // < if(l < r) return new Boolean(true); else return new Boolean(false); case 6: // != if(l != r) return new Boolean(true); else return new Boolean(false); default: return null; } } }