/* Soot - a J*va Optimization Framework
* Copyright (C) 2005-2006 Nomair A. Naeem (nomair.naeem@mail.mcgill.ca)
*
* 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.
*/
/*
* Deal with the problem that super has to be the first stmt in a method body
* The problem is as follows:
* Suppose you had a call to super with some complicated arguments
* or suppose an Aspect added some preinitialization
* In both cases some code gets decompiled BEFORE the call to super
* this is just initializing the arguments.
* However the call to super() has to be the first stmt
*
*
* January 23rd 2006 Writing New Algorithm...
*
* Original constructor ONE:Changed Constructor
*
* B(args1){ B(args1){
* ---------- this(..args1..,B.preInit1(args1));
* X ---------- }
* ----------
* super(args2);
* ----------
* Y ----------
* ----------
*
*
********************************************************************
* New method in Class being Decompiled
*
* private static preInit1(args1){
*
* ----------
* X ----------
* ----------
*
* DavaSuperHandler handler = new DavaSuperHandler();
* //code to evaluate all args in args2
*
* //evaluate 1st arg in args2
* ---------
* handler.store(firstArg);
*
* //evaluate 2nd arg in args2
* ---------
* handler.store(secondArg);
*
* //AND SO ON TILL ALL ARGS ARE FINISHED
*
* return handler;
* }
*
*
********************************************************************
* New Constructor Introduced
*
* B(..args1.., DavaSuperHandler handler){
*
* super(
* (CAST-TYPE)handler.get(0),
* ((CAST-TYPE)handler.get(1)).CONVERSION(),
* .......);
*
* ----------
* Y ----------
* ----------
* }
*
*
***********************************************************************
* New Class Created (Inner class will work best)
*
* class DavaSuperHandler{
* Vector myVector = new Vector();
*
* public Object get(int pos){
* return myVector.elementAt(pos);
* }
*
* public void store(Object obj){
* myVector.add(obj);
* }
* }
*
***********************************************************************
*
*
*
* ONE: Important to check that the parent of the call to super is the ASTMethodNode
* This is important because of the super call is nested within some control flow
* we cannot simply remove the super call (Although this shouldnt happen but just for completness)
*
* TWO: It is necessary to run on AST Analysis on the new SootMethod since
* the DavaBody usually invokes these anlayses
* but our newly created method will never go through that process
*
*/
package soot.dava.toolkits.base.AST.transformations;
import java.util.*;
import soot.*;
import soot.jimple.*;
import soot.jimple.internal.*;
import soot.dava.*;
import soot.grimp.internal.*;
import soot.dava.internal.AST.*;
import soot.dava.internal.asg.*;
import soot.dava.internal.javaRep.*;
import soot.dava.toolkits.base.AST.analysis.*;
import soot.dava.toolkits.base.AST.traversals.*;
public class SuperFirstStmtHandler extends DepthFirstAdapter{
public final boolean DEBUG = false;
ASTMethodNode originalASTMethod; //contains the entire method which was being decompiled
DavaBody originalDavaBody; //originalASTMethod.getDavaBody
Unit originalConstructorUnit; //contains this.init<>
InstanceInvokeExpr originalConstructorExpr; //contains the expr within the ConstructorUni
SootMethod originalSootMethod; //originalDavaBody.getMethod();
SootClass originalSootClass; //originalSootMethod.getDeclaringClass();
Map originalPMap;
List argsOneTypes=null; //initialized to the ParameterTypes of the original method
List argsOneValues=null; //initialized to the values of these parameters of the original method
List argsTwoValues=null; //initialized to the Parameter values of the call to super
List argsTwoTypes=null; //initialized to the ParameterTypes of the call to super
//PreInit fields
SootMethod newSootPreInitMethod=null;//only initialized by createMethod
DavaBody newPreInitDavaBody=null; //only initialized by createMethod
ASTMethodNode newASTPreInitMethod=null; //only initialized by createNewASTPreInitMethod
//Constructor fields
SootMethod newConstructor = null; //initialized by createNewConstructor
DavaBody newConstructorDavaBody=null; //initialized by createNewConstructor
ASTMethodNode newASTConstructorMethod=null; //only initialized by createNewASTConstructor
List<Local> mustInitialize;
int mustInitializeIndex=0;
public SuperFirstStmtHandler(ASTMethodNode AST){
this.originalASTMethod=AST;
initialize();
}
public SuperFirstStmtHandler(boolean verbose,ASTMethodNode AST){
super(verbose);
this.originalASTMethod=AST;
initialize();
}
public void initialize(){
originalDavaBody = originalASTMethod.getDavaBody();
originalConstructorUnit = originalDavaBody.get_ConstructorUnit();
originalConstructorExpr = originalDavaBody.get_ConstructorExpr();
if(originalConstructorExpr != null){
//should get the values and the types of these into lists for later use
argsTwoValues = originalConstructorExpr.getArgs();
argsTwoTypes = new ArrayList();
//get also the types of these args
Iterator valIt = argsTwoValues.iterator();
while(valIt.hasNext()){
Value val = (Value)valIt.next();
Type type = val.getType();
argsTwoTypes.add(type);
}
}
originalSootMethod = originalDavaBody.getMethod();
originalSootClass = originalSootMethod.getDeclaringClass();
originalPMap = originalDavaBody.get_ParamMap();
argsOneTypes = originalSootMethod.getParameterTypes();
//initialize argsOneValues
argsOneValues = new ArrayList();
Iterator typeIt = argsOneTypes.iterator();
int count = 0;
while (typeIt.hasNext()) {
Type t = (Type) typeIt.next();
argsOneValues.add(originalPMap.get(new Integer(count)));
count++;
}
}
/*
* looking for an init stmt
*/
public void inASTStatementSequenceNode(ASTStatementSequenceNode node){
List<Object> stmts = node.getStatements();
Iterator<Object> it = stmts.iterator();
while(it.hasNext()){
AugmentedStmt as = (AugmentedStmt)it.next();
Unit u = as.get_Stmt();
if (u == originalConstructorUnit){
//System.out.println("Found the constructorUnit"+u);
//ONE: make sure the parent of the super() call is an ASTMethodNode
ASTParentNodeFinder parentFinder = new ASTParentNodeFinder();
originalASTMethod.apply(parentFinder);
Object tempParent = parentFinder.getParentOf(node);
if( tempParent != originalASTMethod){
//System.out.println("ASTMethod node is not the parent of constructorUnit");
//notice since we cant remove one call of super there is no point
//in trying to remove any other calls to super
removeInit();
return;
}
//only gets here if the call to super is not nested within some other construct
/**********************************************************/
/****************** CREATING PREINIT METHOD ***************/
/**********************************************************/
//CREATE UNIQUE METHOD
createSootPreInitMethod(); //new method is initalized in newSootPreInitMethod
//Create ASTMethodNode for this SootMethod
createNewASTPreInitMethod(node); //the field newASTPreInitMethod is initialized
//newASTPreInitMethod should be non null if we can go ahead
if(newASTPreInitMethod == null){
//could not create ASTMethodNode for some reason or the other
//just silently return
//System.out.println(">>>>>>>>>>>>>>>>Couldnt not create ASTMethodNode for the new PreInitMethod");
removeInit();
return;
}
if(!finalizePreInitMethod()){
//shouldnt be creating PreInit
//System.out.println(">>>>>>>>>>>>>>SHOULDNT BE CREATING PREINIT");
removeInit();
return;
}
/**********************************************************/
/************** CREATING NEW CONSTRUCTOR ******************/
/**********************************************************/
//create SootMethod for the constructor
createNewConstructor();
createNewASTConstructor(node);
if(!createCallToSuper()){
//could not create call to super
//still safe to simply exit
//System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>Could not create call to super...SuperFirstStmtHandler");
removeInit();
return;
}
finalizeConstructor();
/**********************************************************/
/************** CHANGE ORIGINAL CONSTRUCTOR ***************/
/**********************************************************/
if(changeOriginalAST()){
//System.out.println("Done Done Done");
debug("SuperFirstStmtHandler....inASTStatementSeuqneNode","Added PreInit");
G.v().SootMethodAddedByDava=true;
G.v().SootMethodsAdded.add(newSootPreInitMethod);
G.v().SootMethodsAdded.add(newConstructor);
/**********************************************************/
/****************** CREATING INNER CLASS ******************/
/**********************************************************/
//notice that inner class is created by DavaPrinter in the printTo method
//all we do is set a Global to true and later on when the SootClass is being
//output the inner class will be output also
G.v().SootClassNeedsDavaSuperHandlerClass.add(originalSootClass);
//System.out.println("\n\nSet SootMethodAddedByDava to true\n\n");
}
}
}
}
/*
* When we havent created the indirection to take care of super bug be it cos we dont need to
* or that we CANT cos of some limitation....should atleast remove the jimple this.init call from the
* statements
*/
public void removeInit(){
//remove constructorUnit from originalASTMethod
List<Object> newBody = new ArrayList<Object>();
List<Object> subBody = originalASTMethod.get_SubBodies();
if(subBody.size()!=1)
return;
List oldBody = (List)subBody.get(0);
Iterator oldIt = oldBody.iterator();
while(oldIt.hasNext()){
//going through each node in the old body
ASTNode node = (ASTNode)oldIt.next();
//copy the node as is unless its an ASTStatementSequence
if(!(node instanceof ASTStatementSequenceNode)){
newBody.add(node);
continue;
}
//if the node is an ASTStatementSequenceNode
//copy all stmts unless it is a constructorUnit
ASTStatementSequenceNode seqNode = (ASTStatementSequenceNode)node;
List<Object> newStmtList = new ArrayList<Object>();
List<Object> stmts = seqNode.getStatements();
Iterator<Object> it = stmts.iterator();
while(it.hasNext()){
AugmentedStmt augStmt = (AugmentedStmt)it.next();
Stmt stmtTemp = augStmt.get_Stmt();
if(stmtTemp == originalConstructorUnit){
//do nothing
}
else{
newStmtList.add(augStmt);
}
}
if(newStmtList.size()!=0){
newBody.add(new ASTStatementSequenceNode(newStmtList));
}
}
originalASTMethod.replaceBody(newBody);
}
/*
* Remove the entire body and replace with the statement
* this(args1, B.preInit(args1));
*/
public boolean changeOriginalAST(){
//originalDavaBody has to be changed
//fix up the call within the constructorUnit....the args should be argsOne followed by a method call to preInit
if(originalConstructorExpr == null){
//hmm that means there was no call to super in the original code
//System.out.println("originalConstructorExpr is null");
return false;
}
List thisArgList = new ArrayList();
thisArgList.addAll(argsOneValues);
DStaticInvokeExpr newInvokeExpr = new DStaticInvokeExpr(newSootPreInitMethod.makeRef(),argsOneValues);
thisArgList.add(newInvokeExpr);
//the methodRef of themethod to be called is the new constructor we created
InstanceInvokeExpr tempExpr =
new DSpecialInvokeExpr(originalConstructorExpr.getBase(),newConstructor.makeRef(),thisArgList);
originalDavaBody.set_ConstructorExpr(tempExpr);
//create Invoke Stmt with tempExpr as the expression
GInvokeStmt s = new GInvokeStmt(tempExpr);
originalDavaBody.set_ConstructorUnit(s);
//originalASTMethod has to be made empty
originalASTMethod.setDeclarations(new ASTStatementSequenceNode(new ArrayList<Object>()));
originalASTMethod.replaceBody(new ArrayList<Object>());
return true;
}
private SootMethodRef makeMethodRef(String methodName,ArrayList args){
//make MethodRef for methodName
SootMethod method = new SootMethod(methodName,args,RefType.v("java.lang.Object"));
//set the declaring class of new method to be the DavaSuperHandler class
method.setDeclaringClass(new SootClass("DavaSuperHandler"));
return method.makeRef();
}
/*
* super(
* (CAST-TYPE)handler.get(0),
* ((CAST-TYPE)handler.get(1)).CONVERSION(),
* .......);
*
* returns false if we cant create the call to super
*/
private boolean createCallToSuper(){
//check that whether this call is even to be made or not
if (originalConstructorExpr == null) {
//hmm that means there was no call to super in the original code
//System.out.println("originalConstructorExpr is null");
return false;
}
//System.out.println("ConstructorExpr is non null...call to super has to be made");
//find the parent class of the current method being decompiled
SootClass parentClass =originalSootClass.getSuperclass();
//retrieve the constructor of the super class that we want to call
//remember argsTwoTypes contains the ParameterType to the call to the super method
if(!(parentClass.declaresMethod("<init>",argsTwoTypes))){
//System.out.println("parentClass does not have a constructor with this name and ParamTypes");
return false;
}
SootMethod superConstructor = parentClass.getMethod("<init>",argsTwoTypes);
//create InstanceInvokeExpr
//need Value base: this??
//need SootMethod Ref...make sootmethoref of the super constructor found
//need list of thisLocals.........try empty arrayList since it doesnt seem to be used anywhere??
List argsForConstructor = new ArrayList();
int count=0;
//have to make arg as "handler.get(0)"
//create new ReftType for DavaSuperHandler
RefType type = (new SootClass("DavaSuperHandler")).getType();
//make JimpleLocal to be used in each arg
Local jimpleLocal = new JimpleLocal("handler",type);//takes care of handler
//make reference to a method of name get takes one int arg belongs to DavaSuperHandler
ArrayList tempList = new ArrayList();
tempList.add(IntType.v());
SootMethodRef getMethodRef = makeMethodRef("get",tempList);
List tempArgList = null;
Iterator typeIt = argsTwoTypes.iterator();
while(typeIt.hasNext()){
Type tempType = (Type)typeIt.next();
DIntConstant arg = DIntConstant.v(count,IntType.v());//takes care of the index
count++;
tempArgList = new ArrayList();
tempArgList.add(arg);
DVirtualInvokeExpr tempInvokeExpr =
new DVirtualInvokeExpr(jimpleLocal,getMethodRef,tempArgList,new HashSet<Object>());
//NECESASARY CASTING OR RETRIEVAL OF PRIM TYPES TO BE DONE HERE
Value toAddExpr = getProperCasting(tempType,tempInvokeExpr);
if(toAddExpr == null)
throw new DecompilationException("UNABLE TO CREATE TOADDEXPR:"+tempType);
//the above virtualInvokeExpr is one of the args for the constructor
argsForConstructor.add(toAddExpr);
}
mustInitializeIndex=count;
//we are done with creating all necessary args to the virtualinvoke expr constructor
DVirtualInvokeExpr virtualInvoke = new DVirtualInvokeExpr(originalConstructorExpr.getBase(),superConstructor.makeRef(),
argsForConstructor,new HashSet<Object>());
//set the constructors constructorExpr
newConstructorDavaBody.set_ConstructorExpr(virtualInvoke);
//create Invoke Stmt with virtualInvoke as the expression
GInvokeStmt s = new GInvokeStmt(virtualInvoke);
newConstructorDavaBody.set_ConstructorUnit(s);
//return true if super call created
return true;
}
public Value getProperCasting(Type tempType,DVirtualInvokeExpr tempInvokeExpr){
if(tempType instanceof RefType){
//System.out.println("This is a reftype:"+tempType);
return new GCastExpr(tempInvokeExpr,tempType);
}
else if(tempType instanceof PrimType){
PrimType t = (PrimType)tempType;
//BooleanType, ByteType, CharType, DoubleType, FloatType, IntType, LongType, ShortType
if (t == BooleanType.v()){
Value tempExpr = new GCastExpr(tempInvokeExpr,RefType.v("java.lang.Boolean"));
//booleanValue
SootMethod tempMethod = new SootMethod("booleanValue",new ArrayList(),BooleanType.v());
tempMethod.setDeclaringClass(new SootClass("java.lang.Boolean"));
SootMethodRef tempMethodRef = tempMethod.makeRef();
return new DVirtualInvokeExpr(tempExpr,tempMethodRef,new ArrayList(),new HashSet<Object>());
}
else if (t == ByteType.v()){
Value tempExpr = new GCastExpr(tempInvokeExpr,RefType.v("java.lang.Byte"));
//byteValue
SootMethod tempMethod = new SootMethod("byteValue",new ArrayList(),ByteType.v());
tempMethod.setDeclaringClass(new SootClass("java.lang.Byte"));
SootMethodRef tempMethodRef = tempMethod.makeRef();
return new DVirtualInvokeExpr(tempExpr,tempMethodRef,new ArrayList(),new HashSet<Object>());
}
else if (t == CharType.v()){
Value tempExpr = new GCastExpr(tempInvokeExpr,RefType.v("java.lang.Character"));
//charValue
SootMethod tempMethod = new SootMethod("charValue",new ArrayList(),CharType.v());
tempMethod.setDeclaringClass(new SootClass("java.lang.Character"));
SootMethodRef tempMethodRef = tempMethod.makeRef();
return new DVirtualInvokeExpr(tempExpr,tempMethodRef,new ArrayList(),new HashSet<Object>());
}
else if (t == DoubleType.v()){
Value tempExpr = new GCastExpr(tempInvokeExpr,RefType.v("java.lang.Double"));
//doubleValue
SootMethod tempMethod = new SootMethod("doubleValue",new ArrayList(),DoubleType.v());
tempMethod.setDeclaringClass(new SootClass("java.lang.Double"));
SootMethodRef tempMethodRef = tempMethod.makeRef();
return new DVirtualInvokeExpr(tempExpr,tempMethodRef,new ArrayList(),new HashSet<Object>());
}
else if (t == FloatType.v()){
Value tempExpr = new GCastExpr(tempInvokeExpr,RefType.v("java.lang.Float"));
//floatValue
SootMethod tempMethod = new SootMethod("floatValue",new ArrayList(),FloatType.v());
tempMethod.setDeclaringClass(new SootClass("java.lang.Float"));
SootMethodRef tempMethodRef = tempMethod.makeRef();
return new DVirtualInvokeExpr(tempExpr,tempMethodRef,new ArrayList(),new HashSet<Object>());
}
else if (t == IntType.v()){
Value tempExpr = new GCastExpr(tempInvokeExpr,RefType.v("java.lang.Integer"));
//intValue
SootMethod tempMethod = new SootMethod("intValue",new ArrayList(),IntType.v());
tempMethod.setDeclaringClass(new SootClass("java.lang.Integer"));
SootMethodRef tempMethodRef = tempMethod.makeRef();
return new DVirtualInvokeExpr(tempExpr,tempMethodRef,new ArrayList(),new HashSet<Object>());
}
else if (t == LongType.v()){
Value tempExpr = new GCastExpr(tempInvokeExpr,RefType.v("java.lang.Long"));
//longValue
SootMethod tempMethod = new SootMethod("longValue",new ArrayList(),LongType.v());
tempMethod.setDeclaringClass(new SootClass("java.lang.Long"));
SootMethodRef tempMethodRef = tempMethod.makeRef();
return new DVirtualInvokeExpr(tempExpr,tempMethodRef,new ArrayList(),new HashSet<Object>());
}
else if (t == ShortType.v()){
Value tempExpr = new GCastExpr(tempInvokeExpr,RefType.v("java.lang.Short"));
//shortValue
SootMethod tempMethod = new SootMethod("shortValue",new ArrayList(),ShortType.v());
tempMethod.setDeclaringClass(new SootClass("java.lang.Short"));
SootMethodRef tempMethodRef = tempMethod.makeRef();
return new DVirtualInvokeExpr(tempExpr,tempMethodRef,new ArrayList(),new HashSet<Object>());
}
else {
throw new DecompilationException("Unhandle primType:"+tempType);
}
}
else{
throw new DecompilationException("The type:"+tempType+" was not a reftye or primtype. PLEASE REPORT.");
}
}
private void finalizeConstructor(){
//set davaBody...totally redundant but have to do as this is checked by toString of ASTMethodNode
newASTConstructorMethod.setDavaBody(newConstructorDavaBody);
newConstructorDavaBody.getUnits().clear();
newConstructorDavaBody.getUnits().addLast(newASTConstructorMethod);
System.out.println("Setting declaring class of method"+newConstructor.getSubSignature());
newConstructor.setDeclaringClass(originalSootClass);
}
//method should return false if the PreInit body(ASTBody that is) is empty meaning there is no need to create it all
private boolean finalizePreInitMethod(){
//set davaBody...totally redundant but have to do as this is checked by toString of ASTMethodNode
newASTPreInitMethod.setDavaBody(newPreInitDavaBody);
//newPreInitDavaBody is the active body
newPreInitDavaBody.getUnits().clear();
newPreInitDavaBody.getUnits().addLast(newASTPreInitMethod);
//check whether there is something in side the ASTBody
//if its empty (maybe there were only declarations and that got removed
//then no point in creating the preInit method
List<Object> subBodies = newASTPreInitMethod.get_SubBodies();
if(subBodies.size()!=1)
return false;
List body = (List)subBodies.get(0);
//body is NOT allowed to contain one declaration node with whatever in it
//after that it is NOT allowed all ASTStatement nodes with empty bodies
Iterator it = body.iterator();
boolean empty = true; //indicating that method is empty
while(it.hasNext()){
ASTNode tempNode = (ASTNode)it.next();
if(!(tempNode instanceof ASTStatementSequenceNode)){
//found some node other than stmtseq...body not empty return true
empty = false;
break;
}
List<Object> stmts = ((ASTStatementSequenceNode)tempNode).getStatements();
//all declaration stmts are allowed
Iterator<Object> stmtIt = stmts.iterator();
while(stmtIt.hasNext()){
AugmentedStmt as = (AugmentedStmt)stmtIt.next();
Stmt s = as.get_Stmt();
if(!(s instanceof DVariableDeclarationStmt)){
empty=false;
break;
}
}
if(!empty)
break;
}
if(empty){
//System.out.println("Method is empty not creating it");
return false;//should not be creating the method
}
//about to return true enter all DavaSuperHandler stmts to make it part of the preinit method
createDavaStoreStmts();
return true;
}
public void createNewASTConstructor(ASTStatementSequenceNode initNode){
List<Object> newConstructorBody = new ArrayList<Object>();
List<Object> newStmts = new ArrayList<Object>();
/*
* add any definitions to live variables that might be in body X
*
*/
//we have gotten argsTwoType size() out of the handler so thats the index count
//mustInitialize has the live variables that need to be initialized
// create new ReftType for DavaSuperHandler
RefType type = (new SootClass("DavaSuperHandler")).getType();
//make JimpleLocal to be used in each arg
Local jimpleLocal = new JimpleLocal("handler",type);//takes care of handler
//make reference to a method of name get takes one int arg belongs to DavaSuperHandler
ArrayList tempList = new ArrayList();
tempList.add(IntType.v());
SootMethodRef getMethodRef = makeMethodRef("get",tempList);
//Iterator typeIt = argsTwoTypes.iterator();
if(mustInitialize != null){
Iterator<Local> initIt = mustInitialize.iterator();
while(initIt.hasNext()){
Local initLocal = initIt.next();
Type tempType = initLocal.getType();
DIntConstant arg = DIntConstant.v(mustInitializeIndex,IntType.v());//takes care of the index
mustInitializeIndex++;
ArrayList tempArgList = new ArrayList();
tempArgList.add(arg);
DVirtualInvokeExpr tempInvokeExpr = new DVirtualInvokeExpr(jimpleLocal,getMethodRef,tempArgList,new HashSet<Object>());
//NECESASARY CASTING OR RETRIEVAL OF PRIM TYPES TO BE DONE HERE
Value toAddExpr = getProperCasting(tempType,tempInvokeExpr);
if(toAddExpr == null)
throw new DecompilationException("UNABLE TO CREATE TOADDEXPR:"+tempType);
//need to create a def stmt with the local on the left and toAddExpr on the right
GAssignStmt assign = new GAssignStmt(initLocal,toAddExpr);
newStmts.add(new AugmentedStmt(assign));
}
}
//add any statements following the this.<init> statement
Iterator<Object> it = initNode.getStatements().iterator();
while(it.hasNext()){
AugmentedStmt augStmt = (AugmentedStmt)it.next();
Stmt stmtTemp = augStmt.get_Stmt();
if(stmtTemp == originalConstructorUnit){
break;
}
}
while(it.hasNext()){
/*
notice we dont need to clone these because these will be removed from the other method from which
we are copying these
*/
newStmts.add(it.next());
}
if(newStmts.size()>0){
newConstructorBody.add(new ASTStatementSequenceNode(newStmts));
}
//adding body Y now
List<Object> originalASTMethodSubBodies = originalASTMethod.get_SubBodies();
if(originalASTMethodSubBodies.size() != 1)
throw new CorruptASTException("size of ASTMethodNode subBody not 1");
List<Object> oldASTBody = (List<Object>)originalASTMethodSubBodies.get(0);
it = oldASTBody.iterator();
boolean sanity=false;
while(it.hasNext()){
//going through originalASTMethodNode's ASTNodes
ASTNode tempNode = (ASTNode)it.next();
//enter only if its not the initNode
if(tempNode instanceof ASTStatementSequenceNode){
if( (((ASTStatementSequenceNode)tempNode).getStatements()).equals(initNode.getStatements()) ){
sanity = true;
break;
}
}
}
if(!sanity){
//means we never found the initNode which shouldnt happen
throw new DecompilationException("never found the init node");
}
//so we have found the init node
//Y are all the nodes following the initNode
while(it.hasNext()){
newConstructorBody.add(it.next());
}
//setDeclarations in newNode
//The LocalVariableCleaner which is called in the end of DavaBody will clear up any declarations that are not required
List<Object> newConstructorDeclarations = new ArrayList<Object>();
Iterator<Object> originalDeclarationsIterator = originalASTMethod.getDeclarations().getStatements().iterator();
while(originalDeclarationsIterator.hasNext()){
AugmentedStmt as = (AugmentedStmt)originalDeclarationsIterator.next();
DVariableDeclarationStmt varDecStmt = (DVariableDeclarationStmt)as.get_Stmt();
newConstructorDeclarations.add(new AugmentedStmt((DVariableDeclarationStmt)varDecStmt.clone()));
}
ASTStatementSequenceNode newDecs = new ASTStatementSequenceNode(new ArrayList<Object>());
if(newConstructorDeclarations.size()>0){
newDecs = new ASTStatementSequenceNode(newConstructorDeclarations);
//DONT FORGET TO SET THE DECLARATIONS IN THE METHOD ONCE IT IS CREATED
//newASTConstructorMethod.setDeclarations(newDecs);
//declarations are always the first element
newConstructorBody.add(0,newDecs);
}
//so we have any declarations followed by body Y
//have to put the newConstructorBody into an list of subBodies which goes into the newASTConstructorMethod
newASTConstructorMethod = new ASTMethodNode(newConstructorBody);
//dont forget to set the declarations
newASTConstructorMethod.setDeclarations(newDecs);
}
private void createNewConstructor(){
//the constructor name has to be <init>
String uniqueName = "<init>";
//NOTICE args of this constructor are argsOne followed by the DavaSuperHandler
List args = new ArrayList();
args.addAll(argsOneTypes);
//create new ReftType for DavaSuperHandler
RefType type = (new SootClass("DavaSuperHandler")).getType();
args.add(type);
//create SOOTMETHOD
newConstructor = new SootMethod(uniqueName,args,IntType.v());
//set the declaring class of new method to be the originalSootClass
newConstructor.setDeclaringClass(originalSootClass);
//set method to public
newConstructor.setModifiers(soot.Modifier.PUBLIC);
//initalize a new DavaBody, notice this causes all DavaBody vars to be null
newConstructorDavaBody = Dava.v().newBody(newConstructor);
//setting params is really important if you want the args to have proper names in the new method
//make a copy of the originalHashMap
Map tempMap = new HashMap();
Iterator typeIt = argsOneTypes.iterator();
int count = 0;
while (typeIt.hasNext()) {
Type t = (Type) typeIt.next();
tempMap.put(new Integer(count),originalPMap.get(new Integer(count)));
count++;
}
//add the DavaSuperHandler var name in the Parameters
tempMap.put(new Integer(argsOneTypes.size()),"handler");
//add the ParamMap to the constructor's DavaBody
newConstructorDavaBody.set_ParamMap(tempMap);
//set as activeBody
newConstructor.setActiveBody(newConstructorDavaBody);
}
/*
* January 23rd New Algorithm
* Leave originalASTMethod unchanged
* Clone everything and copy only those which are needed in the newASTPreInitMethod
*/
private void createNewASTPreInitMethod(ASTStatementSequenceNode initNode){
List<Object> newPreinitBody = new ArrayList<Object>();
//start adding ASTNodes into newPreinitBody from the originalASTMethod's body until we reach initNode
List<Object> originalASTMethodSubBodies = originalASTMethod.get_SubBodies();
if(originalASTMethodSubBodies.size() != 1)
throw new CorruptASTException("size of ASTMethodNode subBody not 1");
List<Object> oldASTBody = (List<Object>)originalASTMethodSubBodies.get(0);
Iterator<Object> it = oldASTBody.iterator();
boolean sanity=false;
while(it.hasNext()){
//going through originalASTMethodNode's ASTNodes
ASTNode tempNode = (ASTNode)it.next();
//enter only if its not the initNode
if(tempNode instanceof ASTStatementSequenceNode){
if( (((ASTStatementSequenceNode)tempNode).getStatements()).equals(initNode.getStatements()) ){
sanity = true;
break;
}
else{
//this was not the initNode so we add
newPreinitBody.add(tempNode);
}
}
else{
//not a stmtseq so simply add it
newPreinitBody.add(tempNode);
}
}
if(!sanity){
//means we never found the initNode which shouldnt happen
throw new DecompilationException("never found the init node");
}
//at this moment newPreinitBody contains all of X except for any stmts above the this.init call in the stmtseq node
//copy those
List<Object> newStmts = new ArrayList<Object>();
it = initNode.getStatements().iterator();
while(it.hasNext()){
AugmentedStmt augStmt = (AugmentedStmt)it.next();
Stmt stmtTemp = augStmt.get_Stmt();
if(stmtTemp == originalConstructorUnit){
break;
}
//adding any stmt until constructorUnit into topList for newMethodNode
/*
notice we dont need to clone these because these will be removed from the other method from which
we are copying these
*/
newStmts.add(augStmt);
}
if(newStmts.size()>0){
newPreinitBody.add(new ASTStatementSequenceNode(newStmts));
}
//setDeclarations in newNode
//The LocalVariableCleaner which is called in the end of DavaBody will clear up any declarations that are not required
List<Object> newPreinitDeclarations = new ArrayList<Object>();
Iterator<Object> originalDeclarationsIterator = originalASTMethod.getDeclarations().getStatements().iterator();
while(originalDeclarationsIterator.hasNext()){
AugmentedStmt as = (AugmentedStmt)originalDeclarationsIterator.next();
DVariableDeclarationStmt varDecStmt = (DVariableDeclarationStmt)as.get_Stmt();
newPreinitDeclarations.add(new AugmentedStmt((DVariableDeclarationStmt)varDecStmt.clone()));
}
ASTStatementSequenceNode newDecs = new ASTStatementSequenceNode(new ArrayList<Object>());
if(newPreinitDeclarations.size()>0){
newDecs = new ASTStatementSequenceNode(newPreinitDeclarations);
//DONT FORGET TO SET THE DECLARATIONS IN THE METHOD ONCE IT IS CREATED
//newASTPreInitMethod.setDeclarations(newDecs);
//when we copied the body X the first Node copied was the Declarations from the originalASTMethod
//replace that with this new one
newPreinitBody.remove(0);
newPreinitBody.add(0,newDecs);
}
//At this moment we have the newPreInitBody containing declarations followed by code X
//we need to check whether this actually contains anything cos otherwise super is infact the first stmt
if(newPreinitBody.size()<1){
//System.out.println("Method node empty doing nothing returning");
newASTPreInitMethod = null;//meaning ASTMethodNode for this method not created
return;
}
//so we have any declarations followed by body X
//NEXT THING SHOULD BE CODE TO CREATE A DAVAHANDLER AND STORE THE ARGS TO SUPER IN IT
//HOWEVER WE WILL DELAY THIS TILL UNTIL WE ARE READY TO FINALIZE the PREINIT
//reason for delaying is that even though we know that the body is not empty the body
//could be made empty by the transformations which act in the finalize method
//have to put the newPreinitBody into an list of subBodies which goes into the newASTPreInitMethod
newASTPreInitMethod = new ASTMethodNode(newPreinitBody);
//dont forget to set the declarations
newASTPreInitMethod.setDeclarations(newDecs);
}
/*
* Create a unique private static method name starts with preInit
*/
private void createSootPreInitMethod(){
//get a unique name for the method
String uniqueName = getUniqueName();
//NOTICE WE ARE DEFINING ARGS AS SAME AS THE ORIGINAL METHOD
newSootPreInitMethod = new SootMethod(uniqueName,argsOneTypes,(new SootClass("DavaSuperHandler")).getType());
//set the declaring class of new method to be the originalSootClass
newSootPreInitMethod.setDeclaringClass(originalSootClass);
//set method to private and static
newSootPreInitMethod.setModifiers(soot.Modifier.PRIVATE | soot.Modifier.STATIC);
//initalize a new DavaBody, notice this causes all DavaBody vars to be null
newPreInitDavaBody = Dava.v().newBody(newSootPreInitMethod);
//setting params is really important if you want the args to have proper names in the new method
newPreInitDavaBody.set_ParamMap(originalPMap);
//set as activeBody
newSootPreInitMethod.setActiveBody(newPreInitDavaBody);
}
/*
* Check the sootClass that it doesnt have a name we have suggested
* ALSO VERY IMPORTANT TO CHECK THE NAMES IN THE SOOTMETHODSADDED Variable since these will be added
* to this sootclass by the PackManager
*/
private String getUniqueName(){
String toReturn = "preInit";
int counter=0;
List methodList = originalSootClass.getMethods();
boolean done = false; //havent found the name
while(!done){//as long as name not found
done = true; //assume name found
Iterator it = methodList.iterator();
while(it.hasNext()){
Object temp = it.next();
if(temp instanceof SootMethod){
SootMethod method = (SootMethod)temp;
String name = method.getName();
if(toReturn.compareTo(name)==0){
//method exists with this name so change the name
counter++;
toReturn = "preInit"+counter;
done = false; //name was not found
break;//breaks the inner while since the name has been changed
}
}
else
throw new DecompilationException("SootClass returned a non SootMethod method");
}
//if we get here this means that the orignal names are different
//check the to be added names also
it = G.v().SootMethodsAdded.iterator();
while(it.hasNext()){
//are sure its a sootMethod
SootMethod method = (SootMethod)it.next();
String name = method.getName();
if(toReturn.compareTo(name)==0){
//method exists with this name so change the name
counter++;
toReturn = "preInit"+counter;
done = false; //name was not found
break;//breaks the inner while since the name has been changed
}
}
}// end outer while
return toReturn;
}
/*
* Create the following code:
*
* DavaSuperHandler handler;
* handler = new DavaSuperHandler();
* //code to evaluate all args in args2
*
* //evaluate 1st arg in args2
* ---------
* handler.store(firstArg);
*
* //evaluate 2nd arg in args2
* ---------
* handler.store(secondArg);
*
* //AND SO ON TILL ALL ARGS ARE FINISHED
*
* return handler;
*
*/
private void createDavaStoreStmts(){
List<Object> davaHandlerStmts = new ArrayList<Object>();
//create object of DavaSuperHandler handler
SootClass sootClass = new SootClass("DavaSuperHandler");
Type localType = sootClass.getType();
Local newLocal = new JimpleLocal("handler",localType);
/*
Create * DavaSuperHandler handler; *
*/
DVariableDeclarationStmt varStmt = null;
varStmt = new DVariableDeclarationStmt(localType,newPreInitDavaBody);
varStmt.addLocal(newLocal);
AugmentedStmt as = new AugmentedStmt(varStmt);
davaHandlerStmts.add(as);
/*
* create * handler = new DavaSuperHandler(); *
*/
//create RHS
DNewInvokeExpr invokeExpr =
new DNewInvokeExpr(RefType.v(sootClass),makeMethodRef("DavaSuperHandler",new ArrayList()),new ArrayList());
//create LHS
GAssignStmt initialization = new GAssignStmt(newLocal,invokeExpr);
//add to stmts
davaHandlerStmts.add(new AugmentedStmt(initialization));
/*
* create * handler.store(firstArg); *
*/
//best done in a loop for all args
Iterator typeIt = argsTwoTypes.iterator();
Iterator valIt = argsTwoValues.iterator();
//make reference to a method of name store takes one Object arg belongs to DavaSuperHandler
ArrayList tempList = new ArrayList();
tempList.add(RefType.v("java.lang.Object"));//SHOULD BE OBJECT
SootMethod method = new SootMethod("store",tempList,VoidType.v());
//set the declaring class of new method to be the DavaSuperHandler class
method.setDeclaringClass(sootClass);
SootMethodRef getMethodRef = method.makeRef();
//everything is ready all we need is the object argument before we can create the invokeStmt with the invokeexpr
//once that is done wrap it in augmented stmt and add to davaHandlerStmt
while(typeIt.hasNext() && valIt.hasNext()){
Type tempType = (Type)typeIt.next();
Value tempVal = (Value)valIt.next();
AugmentedStmt toAdd = createStmtAccordingToType(tempType,tempVal,newLocal,getMethodRef);
davaHandlerStmts.add(toAdd);
}//end of going through all the types and vals
//sanity check
if(typeIt.hasNext() || valIt.hasNext())
throw new DecompilationException("Error creating DavaHandler stmts");
/*
* code to add defs
*/
List<Local> uniqueLocals = addDefsToLiveVariables();
Iterator<Local> localIt = uniqueLocals.iterator();
while(localIt.hasNext()){
Local local = localIt.next();
AugmentedStmt toAdd = createStmtAccordingToType(local.getType(),local,newLocal,getMethodRef);
davaHandlerStmts.add(toAdd);
}
//set the mustInitialize field to uniqueLocals so that before Y we can assign these locals
mustInitialize = uniqueLocals;
/*
* create * return handler; *
*/
GReturnStmt returnStmt = new GReturnStmt(newLocal);
davaHandlerStmts.add(new AugmentedStmt(returnStmt));
//the appropriate dava handler stmts are all in place within davaHandlerStmts
//store them in an ASTSTatementSequenceNode
ASTStatementSequenceNode addedNode = new ASTStatementSequenceNode(davaHandlerStmts);
//add to method body
List<Object> subBodies = newASTPreInitMethod.get_SubBodies();
if(subBodies.size()!=1)
throw new CorruptASTException("ASTMethodNode does not have one subBody");
List<Object> body = (List<Object>)subBodies.get(0);
body.add(addedNode);
newASTPreInitMethod.replaceBody(body);
}
public AugmentedStmt createStmtAccordingToType(Type tempType, Value tempVal,Local newLocal, SootMethodRef getMethodRef){
if(tempType instanceof RefType){
//simply add this to the handler using handler.store(tempVal);
//System.out.println("This is a reftype:"+tempType);
return createAugmentedStmtToAdd(newLocal,getMethodRef,tempVal);
}
else if(tempType instanceof PrimType){
//The value is a primitive type
//create wrapper object new Integer(tempVal)
PrimType t = (PrimType)tempType;
//create ArgList to be sent to DNewInvokeExpr constructor
ArrayList argList = new ArrayList();
argList.add(tempVal);
//BooleanType, ByteType, CharType, DoubleType, FloatType, IntType, LongType, ShortType
if (t == BooleanType.v()){
//create TypeList to be sent to makeMethodRef
ArrayList typeList = new ArrayList();
typeList.add(IntType.v());
DNewInvokeExpr argForStore =
new DNewInvokeExpr(RefType.v("java.lang.Boolean"),
makeMethodRef("Boolean",typeList),argList);
return createAugmentedStmtToAdd(newLocal,getMethodRef,argForStore);
}
else if (t == ByteType.v()){
//create TypeList to be sent to makeMethodRef
ArrayList typeList = new ArrayList();
typeList.add(ByteType.v());
DNewInvokeExpr argForStore =
new DNewInvokeExpr(RefType.v("java.lang.Byte"),
makeMethodRef("Byte",typeList),argList);
return createAugmentedStmtToAdd(newLocal,getMethodRef,argForStore);
}
else if (t == CharType.v()){
//create TypeList to be sent to makeMethodRef
ArrayList typeList = new ArrayList();
typeList.add(CharType.v());
DNewInvokeExpr argForStore =
new DNewInvokeExpr(RefType.v("java.lang.Character"),
makeMethodRef("Character",typeList),argList);
return createAugmentedStmtToAdd(newLocal,getMethodRef,argForStore);
}
else if (t == DoubleType.v()){
//create TypeList to be sent to makeMethodRef
ArrayList typeList = new ArrayList();
typeList.add(DoubleType.v());
DNewInvokeExpr argForStore =
new DNewInvokeExpr(RefType.v("java.lang.Double"),
makeMethodRef("Double",typeList),argList);
return createAugmentedStmtToAdd(newLocal,getMethodRef,argForStore);
}
else if (t == FloatType.v()){
//create TypeList to be sent to makeMethodRef
ArrayList typeList = new ArrayList();
typeList.add(FloatType.v());
DNewInvokeExpr argForStore =
new DNewInvokeExpr(RefType.v("java.lang.Float"),
makeMethodRef("Float",typeList),argList);
return createAugmentedStmtToAdd(newLocal,getMethodRef,argForStore);
}
else if (t == IntType.v()){
//create TypeList to be sent to makeMethodRef
ArrayList typeList = new ArrayList();
typeList.add(IntType.v());
DNewInvokeExpr argForStore =
new DNewInvokeExpr(RefType.v("java.lang.Integer"),
makeMethodRef("Integer",typeList),argList);
return createAugmentedStmtToAdd(newLocal,getMethodRef,argForStore);
}
else if (t == LongType.v()){
//create TypeList to be sent to makeMethodRef
ArrayList typeList = new ArrayList();
typeList.add(LongType.v());
DNewInvokeExpr argForStore =
new DNewInvokeExpr(RefType.v("java.lang.Long"),
makeMethodRef("Long",typeList),argList);
return createAugmentedStmtToAdd(newLocal,getMethodRef,argForStore);
}
else if (t == ShortType.v()){
//create TypeList to be sent to makeMethodRef
ArrayList typeList = new ArrayList();
typeList.add(ShortType.v());
DNewInvokeExpr argForStore =
new DNewInvokeExpr(RefType.v("java.lang.Short"),
makeMethodRef("Short",typeList),argList);
return createAugmentedStmtToAdd(newLocal,getMethodRef,argForStore);
}
else {
throw new DecompilationException("UNHANDLED PRIMTYPE:"+tempType);
}
}//end of primitivetypes
else{
throw new DecompilationException("The type:"+tempType+" is neither a reftype or a primtype");
}
}
/*
* newASTPreInitMethod at time of invocation just contains body X
* find all defs for this body
*/
private List<Local> addDefsToLiveVariables(){
//get all defs within x
AllDefinitionsFinder finder = new AllDefinitionsFinder();
newASTPreInitMethod.apply(finder);
List<DefinitionStmt> allDefs = finder.getAllDefs();
List<Local> uniqueLocals = new ArrayList<Local>();
List<DefinitionStmt> uniqueLocalDefs = new ArrayList<DefinitionStmt>();
//remove any defs for fields, and any which are done multiple times
Iterator<DefinitionStmt> it = allDefs.iterator();
while(it.hasNext()){
DefinitionStmt s = it.next();
Value left = s.getLeftOp();
if(left instanceof Local){
if(uniqueLocals.contains(left)){
//a def for this local already encountered
int index = uniqueLocals.indexOf(left);
uniqueLocals.remove(index);
uniqueLocalDefs.remove(index);
}
else{
//no def for this local yet
uniqueLocals.add((Local)left);
uniqueLocalDefs.add(s);
}
}
}
//at this point unique locals contains all locals defined and uniqueLocaldef list has a list of the corresponding definitions
//Now remove those unique locals and localdefs whose stmtseq node does not have the ASTMEthodNode as a parent
//This is a conservative step!!
ASTParentNodeFinder parentFinder = new ASTParentNodeFinder();
newASTPreInitMethod.apply(parentFinder);
List<DefinitionStmt> toRemoveDefs = new ArrayList<DefinitionStmt>();
it = uniqueLocalDefs.iterator();
while(it.hasNext()){
DefinitionStmt s = it.next();
Object parent = parentFinder.getParentOf(s);
if(parent == null || (!(parent instanceof ASTStatementSequenceNode)) ){
//shouldnt happen but if it does add this s to toRemove list
toRemoveDefs.add(s);
}
//parent is an ASTStatementsequence node. check that its parent is the ASTMethodNode
Object grandParent = parentFinder.getParentOf(parent);
if(grandParent == null || (!(grandParent instanceof ASTMethodNode))){
//can happen if obfuscators are really smart. add s to toRemove list
toRemoveDefs.add(s);
}
}
// remove any defs and corresponding locals if present in the toRemoveDefs list
it = toRemoveDefs.iterator();
while(it.hasNext()){
DefinitionStmt s = it.next();
int index = uniqueLocalDefs.indexOf(s);
uniqueLocals.remove(index);
uniqueLocalDefs.remove(index);
}
//the uniqueLocalDefs contains all those definitions to unique locals which are not deeply nested in the X body
//find all the uses of these definitions in the original method body
toRemoveDefs = new ArrayList<DefinitionStmt>();
ASTUsesAndDefs uDdU = new ASTUsesAndDefs(originalASTMethod);
originalASTMethod.apply(uDdU);
it = uniqueLocalDefs.iterator();
while(it.hasNext()){
DefinitionStmt s = it.next();
Object temp = uDdU.getDUChain(s);
if(temp == null){
//couldnt find uses
toRemoveDefs.add(s);
}
ArrayList uses = (ArrayList) temp;
//the uses list contains all stmts / nodes which use the definedLocal
//check if uses is non-empty
if (uses.size() == 0) {
toRemoveDefs.add(s);
}
//check for all the non zero uses
Iterator useIt = uses.iterator();
boolean onlyInConstructorUnit=true;
while (useIt.hasNext()) {
//a use is either a statement or a node(condition, synch, switch , for etc)
Object tempUse = useIt.next();
if(tempUse != originalConstructorUnit){
onlyInConstructorUnit=false;
}
}
if(onlyInConstructorUnit){
//mark it to be removed
toRemoveDefs.add(s);
}
}
//remove any defs and corresponding locals if present in the toRemoveDefs list
it = toRemoveDefs.iterator();
while(it.hasNext()){
DefinitionStmt s = it.next();
int index = uniqueLocalDefs.indexOf(s);
uniqueLocals.remove(index);
uniqueLocalDefs.remove(index);
}
//the remaining uniquelocals are the ones which are needed for body Y
return uniqueLocals;
}
private AugmentedStmt createAugmentedStmtToAdd(Local newLocal,SootMethodRef getMethodRef, Value tempVal){
ArrayList tempArgList = new ArrayList();
tempArgList.add(tempVal);
DVirtualInvokeExpr tempInvokeExpr =
new DVirtualInvokeExpr(newLocal,getMethodRef,tempArgList,new HashSet<Object>());
//create Invoke Stmt with virtualInvoke as the expression
GInvokeStmt s = new GInvokeStmt(tempInvokeExpr);
return new AugmentedStmt(s);
}
public void debug(String methodName, String debug){
if(DEBUG)
System.out.println(methodName+ " DEBUG: "+debug);
}
}