package soot.dava.toolkits.base.AST.transformations;

/*-
 * #%L
 * Soot - a J*va Optimization Framework
 * %%
 * Copyright (C) 2005 - 2006 Nomair A. Naeem (nomair.naeem@mail.mcgill.ca)
 * %%
 * This program 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 program 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 General Lesser Public License for more details.
 * 
 * You should have received a copy of the GNU General Lesser Public
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/lgpl-2.1.html>.
 * #L%
 */

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import soot.BooleanType;
import soot.ByteType;
import soot.CharType;
import soot.DoubleType;
import soot.FloatType;
import soot.G;
import soot.IntType;
import soot.Local;
import soot.LongType;
import soot.PrimType;
import soot.RefType;
import soot.Scene;
import soot.ShortType;
import soot.SootClass;
import soot.SootMethod;
import soot.SootMethodRef;
import soot.Type;
import soot.Unit;
import soot.Value;
import soot.VoidType;
import soot.dava.CorruptASTException;
import soot.dava.Dava;
import soot.dava.DavaBody;
import soot.dava.DecompilationException;
import soot.dava.internal.AST.ASTMethodNode;
import soot.dava.internal.AST.ASTNode;
import soot.dava.internal.AST.ASTStatementSequenceNode;
import soot.dava.internal.asg.AugmentedStmt;
import soot.dava.internal.javaRep.DIntConstant;
import soot.dava.internal.javaRep.DNewInvokeExpr;
import soot.dava.internal.javaRep.DSpecialInvokeExpr;
import soot.dava.internal.javaRep.DStaticInvokeExpr;
import soot.dava.internal.javaRep.DVariableDeclarationStmt;
import soot.dava.internal.javaRep.DVirtualInvokeExpr;
import soot.dava.toolkits.base.AST.analysis.DepthFirstAdapter;
import soot.dava.toolkits.base.AST.traversals.ASTParentNodeFinder;
import soot.dava.toolkits.base.AST.traversals.ASTUsesAndDefs;
import soot.dava.toolkits.base.AST.traversals.AllDefinitionsFinder;
import soot.grimp.internal.GAssignStmt;
import soot.grimp.internal.GCastExpr;
import soot.grimp.internal.GInvokeStmt;
import soot.grimp.internal.GReturnStmt;
import soot.jimple.DefinitionStmt;
import soot.jimple.InstanceInvokeExpr;
import soot.jimple.Stmt;
import soot.jimple.internal.JimpleLocal;

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) {
    for (AugmentedStmt as : node.getStatements()) {
      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<AugmentedStmt> newStmtList = new ArrayList<AugmentedStmt>();

      for (AugmentedStmt augStmt : seqNode.getStatements()) {
        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<AugmentedStmt>()));
    originalASTMethod.replaceBody(new ArrayList<Object>());
    return true;

  }

  private SootMethodRef makeMethodRef(String methodName, ArrayList args) {
    // make MethodRef for methodName

    SootMethod method = Scene.v().makeSootMethod(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 = Scene.v().makeSootMethod("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 = Scene.v().makeSootMethod("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 = Scene.v().makeSootMethod("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 = Scene.v().makeSootMethod("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 = Scene.v().makeSootMethod("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 = Scene.v().makeSootMethod("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 = Scene.v().makeSootMethod("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 = Scene.v().makeSootMethod("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<AugmentedStmt> stmts = ((ASTStatementSequenceNode) tempNode).getStatements();

      // all declaration stmts are allowed
      for (AugmentedStmt as : stmts) {
        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<AugmentedStmt> newStmts = new ArrayList<AugmentedStmt>();
    /*
     * 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<AugmentedStmt> 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);

    Iterator<Object> itOld = oldASTBody.iterator();
    boolean sanity = false;
    while (itOld.hasNext()) {
      // going through originalASTMethodNode's ASTNodes
      ASTNode tempNode = (ASTNode) itOld.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 (itOld.hasNext()) {
      newConstructorBody.add(itOld.next());
    }

    // setDeclarations in newNode
    // The LocalVariableCleaner which is called in the end of DavaBody will
    // clear up any declarations that are not required

    List<AugmentedStmt> newConstructorDeclarations = new ArrayList<AugmentedStmt>();
    for (AugmentedStmt as : originalASTMethod.getDeclarations().getStatements()) {
      DVariableDeclarationStmt varDecStmt = (DVariableDeclarationStmt) as.get_Stmt();
      newConstructorDeclarations.add(new AugmentedStmt((DVariableDeclarationStmt) varDecStmt.clone()));
    }
    ASTStatementSequenceNode newDecs = new ASTStatementSequenceNode(new ArrayList<AugmentedStmt>());
    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 = Scene.v().makeSootMethod(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<AugmentedStmt> newStmts = new ArrayList<AugmentedStmt>();

    for (AugmentedStmt augStmt : initNode.getStatements()) {
      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<AugmentedStmt> newPreinitDeclarations = new ArrayList<AugmentedStmt>();
    for (AugmentedStmt as : originalASTMethod.getDeclarations().getStatements()) {
      DVariableDeclarationStmt varDecStmt = (DVariableDeclarationStmt) as.get_Stmt();
      newPreinitDeclarations.add(new AugmentedStmt((DVariableDeclarationStmt) varDecStmt.clone()));
    }
    ASTStatementSequenceNode newDecs = new ASTStatementSequenceNode(new ArrayList<AugmentedStmt>());
    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 = Scene.v().makeSootMethod(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<AugmentedStmt> davaHandlerStmts = new ArrayList<AugmentedStmt>();

    // 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 = Scene.v().makeSootMethod("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);
        continue;
      }

      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);
    }
  }
}
