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.ASTCondition;
import soot.dava.internal.AST.ASTIfElseNode;
import soot.dava.internal.AST.ASTIfNode;
import soot.dava.internal.AST.ASTMethodNode;
import soot.dava.internal.AST.ASTNode;
import soot.dava.internal.AST.ASTStatementSequenceNode;
import soot.dava.internal.AST.ASTTryNode;
import soot.dava.internal.SET.SETNodeLabel;
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.jimple.ReturnStmt;
import soot.jimple.ReturnVoidStmt;
import soot.jimple.Stmt;
/*
* look for patterns of the form
* if(cond1){ if(cond1){
* BodyA BodyA
* abrupt ----> abrupt
* } }
* else{ BodyB
* BodyB
* }
*
* Things to ensure:
* If abrupt then check BodyA does not target a label on the ifelse
*
* ALWAYS Make sure BodyB does not target a label on the ifelse
*
* If the pattern is NOT matched check the reverse i.e. maybe BodyB
* has the abrupt statement in that case we just negated the condition
*/
public class IfElseSplitter extends DepthFirstAdapter {
public static boolean DEBUG=false;
boolean targeted=false;
ASTMethodNode methodNode;
ASTNode parent;
ASTIfElseNode toReplace;
ASTIfNode toInsert;
List<Object> bodyAfterInsert;
boolean transform=false;
public IfElseSplitter(){
}
public IfElseSplitter(boolean verbose){
super(verbose);
}
public void inASTMethodNode(ASTMethodNode node){
methodNode = node;
}
public void outASTMethodNode(ASTMethodNode a){
if(!transform)
return;
List<Object> parentBodies = parent.get_SubBodies();
Iterator<Object> it = parentBodies.iterator();
while(it.hasNext()){
List<Object> subBody = null;
if (parent instanceof ASTTryNode)
subBody = (List<Object>) ((ASTTryNode.container) it.next()).o;
else
subBody = (List<Object>)it.next();
if(subBody.indexOf(toReplace)>-1){
//in the subBody list the node is present
subBody.add(subBody.indexOf(toReplace),toInsert);
subBody.addAll(subBody.indexOf(toReplace),bodyAfterInsert);
subBody.remove(toReplace);
G.v().ASTTransformations_modified=true;
}
}
}
public void outASTIfElseNode(ASTIfElseNode node){
//if some pattern has already matched cant do another one in this go
if(transform)
return;
List<Object> subBodies = node.get_SubBodies();
if(subBodies.size()!=2)
throw new DecompilationException("IfelseNode without two subBodies. report to developer");
List<Object> ifBody = (List<Object>)subBodies.get(0);
List<Object> elseBody = (List<Object>)subBodies.get(1);
boolean patternMatched = tryBodyPattern(ifBody,node.get_Label(),elseBody);
List<Object> newIfBody = null;
List<Object> outerScopeBody = null;
boolean negateIfCondition=false;
if(patternMatched){
if(DEBUG)
System.out.println("First pattern matched");
newIfBody = ifBody;
outerScopeBody=elseBody;
negateIfCondition=false;
}
else{
patternMatched = tryBodyPattern(elseBody,node.get_Label(),ifBody);
if(patternMatched){
if(DEBUG)
System.out.println("Second pattern matched");
newIfBody = elseBody;
outerScopeBody = ifBody;
negateIfCondition=true;
}
}
//if at this point newIfBody and outerScopeBody are non null we got ourselves a transformation :)
if(newIfBody!= null && outerScopeBody!= null){
ASTCondition cond = node.get_Condition();
if(negateIfCondition)
cond.flip();
ASTIfNode newNode = new ASTIfNode(node.get_Label(),cond,newIfBody);
if(DEBUG){
System.out.println("New IF Node is: "+newNode.toString());
System.out.println("Outer scope body list is:\n");
for(int i=0;i<outerScopeBody.size();i++)
System.out.println("\n\n "+outerScopeBody.get(i).toString());
}
ASTParentNodeFinder finder = new ASTParentNodeFinder();
methodNode.apply(finder);
Object returned = finder.getParentOf(node);
if(returned == null){
//coundnt find parent so cant do anything
return;
}
/*
* Setting globals since everything is ready for transformation
* BECAUSE we cant modify the parent here we are going to do some
* bad coding style
* store the information needed for this into globals
* set a flag
* and the outASTMethod checks for this
*/
parent = (ASTNode)returned;
toReplace=node;
toInsert = newNode;
bodyAfterInsert = outerScopeBody;
transform=true;
}
}
public boolean tryBodyPattern(List<Object> body,SETNodeLabel label, List<Object> otherBody){
Stmt lastStmt = getLastStmt(body);
if(lastStmt == null){
//dont have a last stmt so cant match pattern
return false;
}
if(! (lastStmt instanceof ReturnStmt || lastStmt instanceof ReturnVoidStmt || lastStmt instanceof DAbruptStmt)){
//lastStmt is not an abrupt stmt
return false;
}
if(bodyTargetsLabel(label,body) || bodyTargetsLabel(label,otherBody)){
//one of the bodies targets the label on the ifelse cant match pattern
return false;
}
//pattern matched
return true;
}
/*
* Check that label is non null and the string inside is non null... if yes return false
* Check that the given list (sequeneof ASTNodes have no abrupt edge targeting the label.
*
*/
public boolean bodyTargetsLabel(SETNodeLabel label, List<Object> body){
//no SETNodeLabel is good
if(label == null)
return false;
//SETNodeLabel but with no string is also good
if(label.toString() == null)
return false;
final String strLabel = label.toString();
//go through the body use traversal to find whether there is an abrupt stmt targeting this
Iterator<Object> it = body.iterator();
targeted=false;
while (it.hasNext()) {
ASTNode temp = (ASTNode) it.next();
temp.apply( new DepthFirstAdapter(){
//set targeted to true if DAbruptStmt targets it
public void inStmt(Stmt s){
//only interested in abrupt stmts
if(!(s instanceof DAbruptStmt))
return;
DAbruptStmt abrupt = (DAbruptStmt)s;
SETNodeLabel label = abrupt.getLabel();
if(label != null && label.toString()!= null && label.toString().equals(strLabel)){
targeted=true;
}
}
});
if(targeted)
break;
}
return targeted;
}
/*
* Given a list of ASTNodes see if the last astnode is a StatementSequenceNode
* if not return null
* else, return the last statement in this node
*/
public Stmt getLastStmt(List<Object> body){
if(body.size()==0)
return null;
ASTNode lastNode = (ASTNode)body.get(body.size()-1);
if(!(lastNode instanceof ASTStatementSequenceNode))
return null;
ASTStatementSequenceNode stmtNode = (ASTStatementSequenceNode)lastNode;
List<Object> stmts = stmtNode.getStatements();
if(stmts.size()==0)
return null;
AugmentedStmt lastStmt = (AugmentedStmt)stmts.get(stmts.size()-1);
return lastStmt.get_Stmt();
}
}