/* 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 java.util.Iterator; import java.util.List; import soot.G; import soot.dava.DecompilationException; import soot.dava.internal.AST.ASTDoWhileNode; import soot.dava.internal.AST.ASTForLoopNode; import soot.dava.internal.AST.ASTLabeledNode; import soot.dava.internal.AST.ASTMethodNode; import soot.dava.internal.AST.ASTNode; import soot.dava.internal.AST.ASTStatementSequenceNode; import soot.dava.internal.AST.ASTSwitchNode; import soot.dava.internal.AST.ASTTryNode; import soot.dava.internal.AST.ASTUnconditionalLoopNode; import soot.dava.internal.AST.ASTWhileNode; import soot.dava.internal.asg.AugmentedStmt; import soot.dava.internal.javaRep.DAbruptStmt; import soot.dava.toolkits.base.AST.analysis.DepthFirstAdapter; import soot.dava.toolkits.base.AST.traversals.ASTParentNodeFinder; import soot.dava.toolkits.base.AST.traversals.LabelToNodeMapper; import soot.jimple.Stmt; /* * It has been seen that a lot of times there are break statements targeting * a label absolutely unnecessarily (with continues this is rare but to be complete * we will handle them too) * An example: * label1: * if(cond1){ * BodyA * break label1 * } * * As in the above code the break stmt is absolutely unnessary as the code * will itself flow to the required position. * * However, remember that breaks and continues are also used to * exit or repeat a loop in which case we should keep the break and continue!!! * * TODO Also if we do decide to remove an abrupt stmt make sure that the * stmt seq node has not become empty. If it has remove the node and see * if the construct carrying this node has become empty and so on..... */ public class UselessAbruptStmtRemover extends DepthFirstAdapter { public static boolean DEBUG=false; ASTParentNodeFinder finder; ASTMethodNode methodNode; LabelToNodeMapper mapper; public UselessAbruptStmtRemover(){ finder=null; } public UselessAbruptStmtRemover(boolean verbose){ super(verbose); finder=null; } public void inASTMethodNode (ASTMethodNode node){ methodNode=node; mapper = new LabelToNodeMapper(); methodNode.apply(mapper); } public void caseASTStatementSequenceNode(ASTStatementSequenceNode node) { Iterator<Object> it = node.getStatements().iterator(); AugmentedStmt remove = null; ASTLabeledNode target=null; while (it.hasNext()) { AugmentedStmt as = (AugmentedStmt) it.next(); Stmt s = as.get_Stmt(); //we only care about break and continue stmts if(! (s instanceof DAbruptStmt)){ continue; } DAbruptStmt abrupt = (DAbruptStmt)s; String label = abrupt.getLabel().toString(); if(label == null){ //could at some time implement a version of the same //analysis with implicit abrupt flow but not needed currently continue; } if(it.hasNext()){ //there is an abrupt stmt and this stmt seq node has something //afterwards...that is for sure dead code throw new DecompilationException("Dead code detected. Report to developer"); } //get the target node Object temp = mapper.getTarget(label); if(temp == null){ continue; //throw new DecompilationException("Could not find target for abrupt stmt"+abrupt.toString()); } target = (ASTLabeledNode)temp; //will need to find parents of ancestors see if we need to initialize the finder if(finder==null){ finder = new ASTParentNodeFinder(); methodNode.apply(finder); } if(DEBUG) System.out.println("Starting useless check for abrupt stmt: "+abrupt); //start condition is that ancestor is the stmt seq node ASTNode ancestor = node; while(ancestor != target){ Object tempParent = finder.getParentOf(ancestor); if(tempParent == null) throw new DecompilationException("Parent found was null!!. Report to Developer"); ASTNode ancestorsParent = (ASTNode)tempParent; if(DEBUG) System.out.println("\tCurrent ancestorsParent has type"+ancestorsParent.getClass()); //ancestor should be last child of ancestorsParent if(!checkChildLastInParent(ancestor,ancestorsParent)){ if(DEBUG) System.out.println("\t\tCurrent ancestorParent has more children after this ancestor"); //return from the method since this is the last stmt and we cant do anything return; } //ancestorsParent should not be a loop of any kind OR A SWITCH if(ancestorsParent instanceof ASTWhileNode || ancestorsParent instanceof ASTDoWhileNode || ancestorsParent instanceof ASTUnconditionalLoopNode || ancestorsParent instanceof ASTForLoopNode || ancestorsParent instanceof ASTSwitchNode){ if(DEBUG) System.out.println("\t\tAncestorsParent is a loop shouldnt remove abrupt stmt"); return; } ancestor = ancestorsParent; } if(DEBUG) System.out.println("\tGot to target without returning means we can remove stmt"); remove = as; }//end of while going through the statement sequence if(remove != null){ List<Object> stmts = node.getStatements(); stmts.remove(remove); if(DEBUG) System.out.println("\tRemoved abrupt stmt"); if(target!= null){ if(DEBUG) System.out.println("Invoking findAndKill on the target"); UselessLabelFinder.v().findAndKill(target); } //TODO what if we just emptied a stmt seq block?? //not doing this for the moment //set modified flag make finder null G.v().ASTTransformations_modified=true; finder=null; } } public boolean checkChildLastInParent(ASTNode child, ASTNode parent){ List<Object> subBodies = parent.get_SubBodies(); Iterator<Object> it = subBodies.iterator(); while(it.hasNext()){ List subBody = null; if (parent instanceof ASTTryNode) subBody = (List) ((ASTTryNode.container) it.next()).o; else subBody = (List)it.next(); if(subBody.contains(child)){ if(subBody.indexOf(child) != subBody.size()-1) return false; else return true; } } return false; } }