All Downloads are FREE. Search and download functionalities are using the official Maven repository.

soot.jimple.toolkits.reflection.ReflectiveCallsInliner Maven / Gradle / Ivy

package soot.jimple.toolkits.reflection;

/*-
 * #%L
 * Soot - a J*va Optimization Framework
 * %%
 * Copyright (C) 2010 Eric Bodden
 * %%
 * 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
 * .
 * #L%
 */

import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import soot.ArrayType;
import soot.Body;
import soot.BooleanType;
import soot.Local;
import soot.Modifier;
import soot.PatchingChain;
import soot.PhaseOptions;
import soot.PrimType;
import soot.RefLikeType;
import soot.RefType;
import soot.Scene;
import soot.SceneTransformer;
import soot.SootClass;
import soot.SootField;
import soot.SootFieldRef;
import soot.SootMethod;
import soot.SootMethodRef;
import soot.Type;
import soot.Unit;
import soot.Value;
import soot.VoidType;
import soot.javaToJimple.LocalGenerator;
import soot.jimple.ArrayRef;
import soot.jimple.AssignStmt;
import soot.jimple.ClassConstant;
import soot.jimple.FieldRef;
import soot.jimple.GotoStmt;
import soot.jimple.InstanceFieldRef;
import soot.jimple.InstanceInvokeExpr;
import soot.jimple.IntConstant;
import soot.jimple.InterfaceInvokeExpr;
import soot.jimple.InvokeExpr;
import soot.jimple.InvokeStmt;
import soot.jimple.Jimple;
import soot.jimple.JimpleBody;
import soot.jimple.NopStmt;
import soot.jimple.NullConstant;
import soot.jimple.SpecialInvokeExpr;
import soot.jimple.StaticInvokeExpr;
import soot.jimple.Stmt;
import soot.jimple.StringConstant;
import soot.jimple.VirtualInvokeExpr;
import soot.jimple.toolkits.reflection.ReflectionTraceInfo.Kind;
import soot.jimple.toolkits.scalar.CopyPropagator;
import soot.jimple.toolkits.scalar.DeadAssignmentEliminator;
import soot.jimple.toolkits.scalar.NopEliminator;
import soot.options.CGOptions;
import soot.options.Options;
import soot.rtlib.tamiflex.DefaultHandler;
import soot.rtlib.tamiflex.IUnexpectedReflectiveCallHandler;
import soot.rtlib.tamiflex.OpaquePredicate;
import soot.rtlib.tamiflex.ReflectiveCalls;
import soot.rtlib.tamiflex.SootSig;
import soot.rtlib.tamiflex.UnexpectedReflectiveCall;
import soot.toolkits.scalar.UnusedLocalEliminator;
import soot.util.Chain;
import soot.util.HashChain;

public class ReflectiveCallsInliner extends SceneTransformer {
  // caching currently does not work because it adds fields to Class, Method
  // and Constructor,
  // but such fields cannot currently be added using the Instrumentation API
  private final boolean useCaching = false;

  private static final String ALREADY_CHECKED_FIELDNAME = "SOOT$Reflection$alreadyChecked";
  private ReflectionTraceInfo RTI;
  private SootMethodRef UNINTERPRETED_METHOD;

  private boolean initialized = false;

  private int callSiteId;

  private int callNum;

  private SootClass reflectiveCallsClass;

  private static final List fieldSets
      = Arrays.asList("set", "setBoolean", "setByte", "setChar", "setInt", "setLong", "setFloat", "setDouble", "setShort");

  private static final List fieldGets
      = Arrays.asList("get", "getBoolean", "getByte", "getChar", "getInt", "getLong", "getFloat", "getDouble", "getShort");

  @Override
  protected void internalTransform(String phaseName, @SuppressWarnings("rawtypes") Map options) {
    if (!initialized) {
      CGOptions cgOptions = new CGOptions(PhaseOptions.v().getPhaseOptions("cg"));
      String logFilePath = cgOptions.reflection_log();
      RTI = new ReflectionTraceInfo(logFilePath);

      Scene.v().getSootClass(SootSig.class.getName()).setApplicationClass();
      Scene.v().getSootClass(UnexpectedReflectiveCall.class.getName()).setApplicationClass();
      Scene.v().getSootClass(IUnexpectedReflectiveCallHandler.class.getName()).setApplicationClass();
      Scene.v().getSootClass(DefaultHandler.class.getName()).setApplicationClass();
      Scene.v().getSootClass(OpaquePredicate.class.getName()).setApplicationClass();
      Scene.v().getSootClass(ReflectiveCalls.class.getName()).setApplicationClass();

      reflectiveCallsClass = new SootClass("soot.rtlib.tamiflex.ReflectiveCallsWrapper", Modifier.PUBLIC);
      Scene.v().addClass(reflectiveCallsClass);
      reflectiveCallsClass.setApplicationClass();

      UNINTERPRETED_METHOD = Scene.v().makeMethodRef(Scene.v().getSootClass("soot.rtlib.tamiflex.OpaquePredicate"),
          "getFalse", Collections.emptyList(), BooleanType.v(), true);

      if (useCaching) {
        addCaching();
      }

      initializeReflectiveCallsTable();

      callSiteId = 0;
      callNum = 0;

      initialized = true;
    }

    for (SootMethod m : RTI.methodsContainingReflectiveCalls()) {
      m.retrieveActiveBody();
      Body b = m.getActiveBody();
      {
        Set classForNameClassNames = RTI.classForNameClassNames(m);
        if (!classForNameClassNames.isEmpty()) {
          inlineRelectiveCalls(m, classForNameClassNames, ReflectionTraceInfo.Kind.ClassForName);
          if (Options.v().validate()) {
            b.validate();
          }
        }
      }
      {
        Set classNewInstanceClassNames = RTI.classNewInstanceClassNames(m);
        if (!classNewInstanceClassNames.isEmpty()) {
          inlineRelectiveCalls(m, classNewInstanceClassNames, ReflectionTraceInfo.Kind.ClassNewInstance);
          if (Options.v().validate()) {
            b.validate();
          }
        }
      }
      {
        Set constructorNewInstanceSignatures = RTI.constructorNewInstanceSignatures(m);
        if (!constructorNewInstanceSignatures.isEmpty()) {
          inlineRelectiveCalls(m, constructorNewInstanceSignatures, ReflectionTraceInfo.Kind.ConstructorNewInstance);
          if (Options.v().validate()) {
            b.validate();
          }
        }
      }
      {
        Set methodInvokeSignatures = RTI.methodInvokeSignatures(m);
        if (!methodInvokeSignatures.isEmpty()) {
          inlineRelectiveCalls(m, methodInvokeSignatures, ReflectionTraceInfo.Kind.MethodInvoke);
          if (Options.v().validate()) {
            b.validate();
          }
        }
      }
      {
        Set fieldSetSignatures = RTI.fieldSetSignatures(m);
        if (!fieldSetSignatures.isEmpty()) {
          inlineRelectiveCalls(m, fieldSetSignatures, ReflectionTraceInfo.Kind.FieldSet);
          if (Options.v().validate()) {
            b.validate();
          }
        }
      }
      {
        Set fieldGetSignatures = RTI.fieldGetSignatures(m);
        if (!fieldGetSignatures.isEmpty()) {
          inlineRelectiveCalls(m, fieldGetSignatures, ReflectionTraceInfo.Kind.FieldGet);
          if (Options.v().validate()) {
            b.validate();
          }
        }
      }
      // clean up after us
      cleanup(b);
    }
  }

  private void cleanup(Body b) {
    CopyPropagator.v().transform(b);
    DeadAssignmentEliminator.v().transform(b);
    UnusedLocalEliminator.v().transform(b);
    NopEliminator.v().transform(b);
  }

  private void initializeReflectiveCallsTable() {
    int callSiteId = 0;

    SootClass reflCallsClass = Scene.v().getSootClass("soot.rtlib.tamiflex.ReflectiveCalls");
    SootMethod clinit = reflCallsClass.getMethodByName(SootMethod.staticInitializerName);
    Body body = clinit.retrieveActiveBody();
    PatchingChain units = body.getUnits();
    LocalGenerator localGen = new LocalGenerator(body);
    Chain newUnits = new HashChain();
    SootClass setClass = Scene.v().getSootClass("java.util.Set");
    SootMethodRef addMethodRef = setClass.getMethodByName("add").makeRef();
    for (SootMethod m : RTI.methodsContainingReflectiveCalls()) {
      {
        if (!RTI.classForNameClassNames(m).isEmpty()) {
          SootFieldRef fieldRef = Scene.v().makeFieldRef(reflCallsClass, "classForName", RefType.v("java.util.Set"), true);
          Local setLocal = localGen.generateLocal(RefType.v("java.util.Set"));
          newUnits.add(Jimple.v().newAssignStmt(setLocal, Jimple.v().newStaticFieldRef(fieldRef)));
          for (String className : RTI.classForNameClassNames(m)) {
            InterfaceInvokeExpr invokeExpr
                = Jimple.v().newInterfaceInvokeExpr(setLocal, addMethodRef, StringConstant.v(callSiteId + className));
            newUnits.add(Jimple.v().newInvokeStmt(invokeExpr));
          }
          callSiteId++;
        }
      }
      {
        if (!RTI.classNewInstanceClassNames(m).isEmpty()) {
          SootFieldRef fieldRef
              = Scene.v().makeFieldRef(reflCallsClass, "classNewInstance", RefType.v("java.util.Set"), true);
          Local setLocal = localGen.generateLocal(RefType.v("java.util.Set"));
          newUnits.add(Jimple.v().newAssignStmt(setLocal, Jimple.v().newStaticFieldRef(fieldRef)));
          for (String className : RTI.classNewInstanceClassNames(m)) {
            InterfaceInvokeExpr invokeExpr
                = Jimple.v().newInterfaceInvokeExpr(setLocal, addMethodRef, StringConstant.v(callSiteId + className));
            newUnits.add(Jimple.v().newInvokeStmt(invokeExpr));
          }
          callSiteId++;
        }
      }
      {
        if (!RTI.constructorNewInstanceSignatures(m).isEmpty()) {
          SootFieldRef fieldRef
              = Scene.v().makeFieldRef(reflCallsClass, "constructorNewInstance", RefType.v("java.util.Set"), true);
          Local setLocal = localGen.generateLocal(RefType.v("java.util.Set"));
          newUnits.add(Jimple.v().newAssignStmt(setLocal, Jimple.v().newStaticFieldRef(fieldRef)));
          for (String constrSig : RTI.constructorNewInstanceSignatures(m)) {
            InterfaceInvokeExpr invokeExpr
                = Jimple.v().newInterfaceInvokeExpr(setLocal, addMethodRef, StringConstant.v(callSiteId + constrSig));
            newUnits.add(Jimple.v().newInvokeStmt(invokeExpr));
          }
          callSiteId++;
        }
      }
      {
        if (!RTI.methodInvokeSignatures(m).isEmpty()) {
          SootFieldRef fieldRef = Scene.v().makeFieldRef(reflCallsClass, "methodInvoke", RefType.v("java.util.Set"), true);
          Local setLocal = localGen.generateLocal(RefType.v("java.util.Set"));
          newUnits.add(Jimple.v().newAssignStmt(setLocal, Jimple.v().newStaticFieldRef(fieldRef)));
          for (String methodSig : RTI.methodInvokeSignatures(m)) {
            InterfaceInvokeExpr invokeExpr
                = Jimple.v().newInterfaceInvokeExpr(setLocal, addMethodRef, StringConstant.v(callSiteId + methodSig));
            newUnits.add(Jimple.v().newInvokeStmt(invokeExpr));
          }
          callSiteId++;
        }
      }
    }

    Unit secondLastStmt = units.getPredOf(units.getLast());
    units.insertAfter(newUnits, secondLastStmt);

    if (Options.v().validate()) {
      body.validate();
    }
  }

  private void addCaching() {
    SootClass method = Scene.v().getSootClass("java.lang.reflect.Method");
    method.addField(Scene.v().makeSootField(ALREADY_CHECKED_FIELDNAME, BooleanType.v()));
    SootClass constructor = Scene.v().getSootClass("java.lang.reflect.Constructor");
    constructor.addField(Scene.v().makeSootField(ALREADY_CHECKED_FIELDNAME, BooleanType.v()));
    SootClass clazz = Scene.v().getSootClass("java.lang.Class");
    clazz.addField(Scene.v().makeSootField(ALREADY_CHECKED_FIELDNAME, BooleanType.v()));

    for (Kind k : Kind.values()) {
      addCaching(k);
    }
  }

  private void addCaching(Kind kind) {

    SootClass c;
    String methodName;
    switch (kind) {
      case ClassNewInstance:
        c = Scene.v().getSootClass("java.lang.Class");
        methodName = "knownClassNewInstance";
        break;
      case ConstructorNewInstance:
        c = Scene.v().getSootClass("java.lang.reflect.Constructor");
        methodName = "knownConstructorNewInstance";
        break;
      case MethodInvoke:
        c = Scene.v().getSootClass("java.lang.reflect.Method");
        methodName = "knownMethodInvoke";
        break;
      case ClassForName:
        // Cannot implement caching in this case because we can add no field
        // to the String argument
        return;
      default:
        throw new IllegalStateException("unknown kind: " + kind);
    }

    SootClass reflCallsClass = Scene.v().getSootClass("soot.rtlib.tamiflex.ReflectiveCalls");

    SootMethod m = reflCallsClass.getMethodByName(methodName);
    JimpleBody body = (JimpleBody) m.retrieveActiveBody();
    LocalGenerator localGen = new LocalGenerator(body);
    Unit firstStmt = body.getFirstNonIdentityStmt();
    firstStmt = body.getUnits().getPredOf(firstStmt);

    Stmt jumpTarget = Jimple.v().newNopStmt();

    Chain newUnits = new HashChain();

    // alreadyCheckedLocal = m.alreadyChecked
    InstanceFieldRef fieldRef = Jimple.v().newInstanceFieldRef(body.getParameterLocal(m.getParameterCount() - 1),
        Scene.v().makeFieldRef(c, ALREADY_CHECKED_FIELDNAME, BooleanType.v(), false));
    Local alreadyCheckedLocal = localGen.generateLocal(BooleanType.v());
    newUnits.add(Jimple.v().newAssignStmt(alreadyCheckedLocal, fieldRef));

    // if(!alreadyChecked) goto jumpTarget
    newUnits.add(Jimple.v().newIfStmt(Jimple.v().newEqExpr(alreadyCheckedLocal, IntConstant.v(0)), jumpTarget));

    // return
    newUnits.add(Jimple.v().newReturnVoidStmt());

    // jumpTarget: nop
    newUnits.add(jumpTarget);

    // m.alreadyChecked = true
    InstanceFieldRef fieldRef2 = Jimple.v().newInstanceFieldRef(body.getParameterLocal(m.getParameterCount() - 1),
        Scene.v().makeFieldRef(c, ALREADY_CHECKED_FIELDNAME, BooleanType.v(), false));
    newUnits.add(Jimple.v().newAssignStmt(fieldRef2, IntConstant.v(1)));

    body.getUnits().insertAfter(newUnits, firstStmt);

    if (Options.v().validate()) {
      body.validate();
    }
  }

  private void inlineRelectiveCalls(SootMethod m, Set targets, Kind callKind) {
    if (!m.hasActiveBody()) {
      m.retrieveActiveBody();
    }
    Body b = m.getActiveBody();
    PatchingChain units = b.getUnits();
    Iterator iter = units.snapshotIterator();
    LocalGenerator localGen = new LocalGenerator(b);

    // for all units
    while (iter.hasNext()) {
      Chain newUnits = new HashChain();
      Stmt s = (Stmt) iter.next();

      // if we have an invoke expression, test to see if it is a
      // reflective invoke expression
      if (s.containsInvokeExpr()) {
        InvokeExpr ie = s.getInvokeExpr();
        boolean found = false;
        Type fieldSetGetType = null;

        if (callKind == Kind.ClassForName
            && (ie.getMethodRef().getSignature().equals("")
                || ie.getMethodRef().getSignature()
                    .equals(""))) {
          found = true;
          Value classNameValue = ie.getArg(0);
          newUnits.add(Jimple.v()
              .newInvokeStmt(Jimple.v()
                  .newStaticInvokeExpr(Scene.v()
                      .getMethod("")
                      .makeRef(), IntConstant.v(callSiteId), classNameValue)));
        } else if (callKind == Kind.ClassNewInstance
            && ie.getMethodRef().getSignature().equals("")) {
          found = true;
          Local classLocal = (Local) ((InstanceInvokeExpr) ie).getBase();
          newUnits.add(Jimple.v()
              .newInvokeStmt(Jimple.v()
                  .newStaticInvokeExpr(Scene.v()
                      .getMethod("")
                      .makeRef(), IntConstant.v(callSiteId), classLocal)));
        } else if (callKind == Kind.ConstructorNewInstance && ie.getMethodRef().getSignature()
            .equals("")) {
          found = true;
          Local constrLocal = (Local) ((InstanceInvokeExpr) ie).getBase();
          newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newStaticInvokeExpr(Scene.v().getMethod(
              "")
              .makeRef(), IntConstant.v(callSiteId), constrLocal)));
        } else if (callKind == Kind.MethodInvoke && ie.getMethodRef().getSignature()
            .equals("")) {
          found = true;
          Local methodLocal = (Local) ((InstanceInvokeExpr) ie).getBase();
          Value recv = ie.getArg(0);
          newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newStaticInvokeExpr(Scene.v().getMethod(
              "")
              .makeRef(), IntConstant.v(callSiteId), recv, methodLocal)));
        } else if (callKind == Kind.FieldSet) {
          SootMethod sootMethod = ie.getMethodRef().resolve();
          if (sootMethod.getDeclaringClass().getName().equals("java.lang.reflect.Field")
              && fieldSets.contains(sootMethod.getName())) {
            found = true;
            fieldSetGetType = sootMethod.getParameterType(1); // assign
            // type
            // of
            // 2nd
            // parameter
            // (1st
            // is
            // receiver
            // object)
            Value recv = ie.getArg(0);
            Value field = ((InstanceInvokeExpr) ie).getBase();
            newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newStaticInvokeExpr(Scene.v().getMethod(
                "")
                .makeRef(), IntConstant.v(callSiteId), recv, field)));
          }
        } else if (callKind == Kind.FieldGet) {
          SootMethod sootMethod = ie.getMethodRef().resolve();
          if (sootMethod.getDeclaringClass().getName().equals("java.lang.reflect.Field")
              && fieldGets.contains(sootMethod.getName())) {
            found = true;
            fieldSetGetType = sootMethod.getReturnType(); // assign
            // return
            // type
            // of
            // get
            Value recv = ie.getArg(0);
            Value field = ((InstanceInvokeExpr) ie).getBase();
            newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newStaticInvokeExpr(Scene.v().getMethod(
                "")
                .makeRef(), IntConstant.v(callSiteId), recv, field)));
          }
        }

        if (!found) {
          continue;
        }

        NopStmt endLabel = Jimple.v().newNopStmt();

        // for all recorded targets
        for (String target : targets) {

          NopStmt jumpTarget = Jimple.v().newNopStmt();

          // boolean predLocal = Opaque.getFalse();
          Local predLocal = localGen.generateLocal(BooleanType.v());
          StaticInvokeExpr staticInvokeExpr = Jimple.v().newStaticInvokeExpr(UNINTERPRETED_METHOD);
          newUnits.add(Jimple.v().newAssignStmt(predLocal, staticInvokeExpr));
          // if predLocal == 0 goto 
          newUnits.add(Jimple.v().newIfStmt(Jimple.v().newEqExpr(IntConstant.v(0), predLocal), jumpTarget));

          SootMethod newMethod = createNewMethod(callKind, target, fieldSetGetType);

          List args = new LinkedList();
          switch (callKind) {
            case ClassForName:
            case ClassNewInstance:
              // no arguments
              break;
            case ConstructorNewInstance:
              // add Object[] argument
              args.add((Value) ie.getArgs().get(0));
              break;
            case MethodInvoke:
              // add Object argument
              args.add((Value) ie.getArgs().get(0));
              // add Object[] argument
              args.add((Value) ie.getArgs().get(1));
              break;
            case FieldSet:
              // add Object argument
              args.add((Value) ie.getArgs().get(0));
              // add value argument
              args.add((Value) ie.getArgs().get(1));
              break;
            case FieldGet:
              // add Object argument
              args.add((Value) ie.getArgs().get(0));
              break;
            default:
              throw new IllegalStateException();
          }

          StaticInvokeExpr methodInvokeExpr = Jimple.v().newStaticInvokeExpr(newMethod.makeRef(), args);
          Local retLocal = localGen.generateLocal(newMethod.getReturnType());
          newUnits.add(Jimple.v().newAssignStmt(retLocal, methodInvokeExpr));

          if (s instanceof AssignStmt) {
            AssignStmt assignStmt = (AssignStmt) s;
            Value leftOp = assignStmt.getLeftOp();
            AssignStmt newAssignStmt = Jimple.v().newAssignStmt(leftOp, retLocal);
            newUnits.add(newAssignStmt);
          }

          GotoStmt gotoStmt = Jimple.v().newGotoStmt(endLabel);
          newUnits.add(gotoStmt);

          newUnits.add(jumpTarget);
        }

        Unit end = newUnits.getLast();
        units.insertAfter(newUnits, s);
        units.remove(s);
        units.insertAfter(s, end);
        units.insertAfter(endLabel, s);

      }
    }
    callSiteId++;
  }

  @SuppressWarnings("unchecked")
  private SootMethod createNewMethod(Kind callKind, String target, Type fieldSetGetType) {
    List parameterTypes = new LinkedList();
    Type returnType = null;
    switch (callKind) {
      case ClassForName:
        returnType = RefType.v("java.lang.Class");
        break;
      case ClassNewInstance:
        returnType = RefType.v("java.lang.Object");
        break;
      case ConstructorNewInstance:
        parameterTypes.add(ArrayType.v(RefType.v("java.lang.Object"), 1));
        returnType = RefType.v("java.lang.Object");
        break;
      case MethodInvoke:
        parameterTypes.add(RefType.v("java.lang.Object"));
        parameterTypes.add(ArrayType.v(RefType.v("java.lang.Object"), 1));
        returnType = RefType.v("java.lang.Object");
        break;
      case FieldSet:
        parameterTypes.add(RefType.v("java.lang.Object"));
        parameterTypes.add(fieldSetGetType);
        returnType = VoidType.v();
        break;
      case FieldGet:
        parameterTypes.add(RefType.v("java.lang.Object"));
        returnType = fieldSetGetType;
        break;
      default:
        throw new IllegalStateException();
    }

    SootMethod newMethod = Scene.v().makeSootMethod("reflectiveCall" + (callNum++), parameterTypes, returnType,
        Modifier.PUBLIC | Modifier.STATIC);
    Body newBody = Jimple.v().newBody(newMethod);
    newMethod.setActiveBody(newBody);
    reflectiveCallsClass.addMethod(newMethod);

    PatchingChain newUnits = newBody.getUnits();

    LocalGenerator localGen = new LocalGenerator(newBody);

    Local freshLocal;
    Value replacement = null;
    Local[] paramLocals = null;
    switch (callKind) {
      case ClassForName: {
        // replace by: >
        freshLocal = localGen.generateLocal(RefType.v("java.lang.Class"));
        replacement = ClassConstant.v(target.replace('.', '/'));
        break;
      }
      case ClassNewInstance: {
        // replace by: new 
        RefType targetType = RefType.v(target);
        freshLocal = localGen.generateLocal(targetType);
        replacement = Jimple.v().newNewExpr(targetType);
        break;
      }
      case ConstructorNewInstance: {
        /*
         * replace r=constr.newInstance(args) by: Object p0 = args[0]; ... Object pn = args[n]; T0 a0 = (T0)p0; ... Tn an =
         * (Tn)pn;
         */

        SootMethod constructor = Scene.v().getMethod(target);
        paramLocals = new Local[constructor.getParameterCount()];
        if (constructor.getParameterCount() > 0) {
          // argArrayLocal = @parameter-0
          ArrayType arrayType = ArrayType.v(RefType.v("java.lang.Object"), 1);
          Local argArrayLocal = localGen.generateLocal(arrayType);
          newUnits.add(Jimple.v().newIdentityStmt(argArrayLocal, Jimple.v().newParameterRef(arrayType, 0)));
          int i = 0;
          for (Type paramType : ((Collection) constructor.getParameterTypes())) {
            paramLocals[i] = localGen.generateLocal(paramType);
            unboxParameter(argArrayLocal, i, paramLocals, paramType, newUnits, localGen);
            i++;
          }
        }
        RefType targetType = constructor.getDeclaringClass().getType();
        freshLocal = localGen.generateLocal(targetType);
        replacement = Jimple.v().newNewExpr(targetType);

        break;
      }
      case MethodInvoke: {
        /*
         * replace r=m.invoke(obj,args) by: T recv = (T)obj; Object p0 = args[0]; ... Object pn = args[n]; T0 a0 = (T0)p0;
         * ... Tn an = (Tn)pn;
         */
        SootMethod method = Scene.v().getMethod(target);
        // recvObject = @parameter-0
        RefType objectType = RefType.v("java.lang.Object");
        Local recvObject = localGen.generateLocal(objectType);
        newUnits.add(Jimple.v().newIdentityStmt(recvObject, Jimple.v().newParameterRef(objectType, 0)));
        paramLocals = new Local[method.getParameterCount()];
        if (method.getParameterCount() > 0) {
          // argArrayLocal = @parameter-1
          ArrayType arrayType = ArrayType.v(RefType.v("java.lang.Object"), 1);
          Local argArrayLocal = localGen.generateLocal(arrayType);
          newUnits.add(Jimple.v().newIdentityStmt(argArrayLocal, Jimple.v().newParameterRef(arrayType, 1)));
          int i = 0;
          for (Type paramType : ((Collection) method.getParameterTypes())) {
            paramLocals[i] = localGen.generateLocal(paramType);
            unboxParameter(argArrayLocal, i, paramLocals, paramType, newUnits, localGen);
            i++;
          }
        }
        RefType targetType = method.getDeclaringClass().getType();
        freshLocal = localGen.generateLocal(targetType);
        replacement = Jimple.v().newCastExpr(recvObject, method.getDeclaringClass().getType());

        break;
      }
      case FieldSet:
      case FieldGet: {
        /*
         * replace f.set(o,v) by: Object obj = @parameter-0; T freshLocal = (T)obj;
         */
        RefType objectType = RefType.v("java.lang.Object");
        Local recvObject = localGen.generateLocal(objectType);
        newUnits.add(Jimple.v().newIdentityStmt(recvObject, Jimple.v().newParameterRef(objectType, 0)));

        SootField field = Scene.v().getField(target);
        freshLocal = localGen.generateLocal(field.getDeclaringClass().getType());
        replacement = Jimple.v().newCastExpr(recvObject, field.getDeclaringClass().getType());

        break;
      }
      default:
        throw new InternalError("Unknown kind of reflective call " + callKind);
    }

    AssignStmt replStmt = Jimple.v().newAssignStmt(freshLocal, replacement);
    newUnits.add(replStmt);

    Local retLocal = localGen.generateLocal(returnType);

    switch (callKind) {
      case ClassForName: {
        // add: retLocal = freshLocal;
        newUnits.add(Jimple.v().newAssignStmt(retLocal, freshLocal));
        break;
      }
      case ClassNewInstance: {
        // add: freshLocal.()
        SootClass targetClass = Scene.v().getSootClass(target);
        SpecialInvokeExpr constrCallExpr = Jimple.v().newSpecialInvokeExpr(freshLocal, Scene.v().makeMethodRef(targetClass,
            SootMethod.constructorName, Collections.emptyList(), VoidType.v(), false));
        InvokeStmt constrCallStmt2 = Jimple.v().newInvokeStmt(constrCallExpr);
        newUnits.add(constrCallStmt2);
        // add: retLocal = freshLocal
        newUnits.add(Jimple.v().newAssignStmt(retLocal, freshLocal));
        break;
      }
      case ConstructorNewInstance: {
        // add: freshLocal.(a0,...,an);
        SootMethod constructor = Scene.v().getMethod(target);
        SpecialInvokeExpr constrCallExpr
            = Jimple.v().newSpecialInvokeExpr(freshLocal, constructor.makeRef(), Arrays.asList(paramLocals));
        InvokeStmt constrCallStmt2 = Jimple.v().newInvokeStmt(constrCallExpr);
        newUnits.add(constrCallStmt2);
        // add: retLocal = freshLocal
        newUnits.add(Jimple.v().newAssignStmt(retLocal, freshLocal));
        break;
      }
      case MethodInvoke: {
        // add: freshLocal=recv.(a0,...,an);
        SootMethod method = Scene.v().getMethod(target);
        InvokeExpr invokeExpr;
        if (method.isStatic()) {
          invokeExpr = Jimple.v().newStaticInvokeExpr(method.makeRef(), Arrays.asList(paramLocals));
        } else {
          invokeExpr = Jimple.v().newVirtualInvokeExpr(freshLocal, method.makeRef(), Arrays.asList(paramLocals));
        }
        if (method.getReturnType().equals(VoidType.v())) {
          // method returns null; simply invoke it and return null
          InvokeStmt invokeStmt = Jimple.v().newInvokeStmt(invokeExpr);
          newUnits.add(invokeStmt);
          AssignStmt assignStmt = Jimple.v().newAssignStmt(retLocal, NullConstant.v());
          newUnits.add(assignStmt);
        } else {
          AssignStmt assignStmt = Jimple.v().newAssignStmt(retLocal, invokeExpr);
          newUnits.add(assignStmt);
        }
        break;
      }
      case FieldSet: {
        // add freshLocal. = v;
        Local value = localGen.generateLocal(fieldSetGetType);
        newUnits.insertBeforeNoRedirect(Jimple.v().newIdentityStmt(value, Jimple.v().newParameterRef(fieldSetGetType, 1)),
            replStmt);

        SootField field = Scene.v().getField(target);

        Local boxedOrCasted = localGen.generateLocal(field.getType());

        insertCastOrUnboxingCode(boxedOrCasted, value, newUnits);

        FieldRef fieldRef;
        if (field.isStatic()) {
          fieldRef = Jimple.v().newStaticFieldRef(field.makeRef());
        } else {
          fieldRef = Jimple.v().newInstanceFieldRef(freshLocal, field.makeRef());
        }
        newUnits.add(Jimple.v().newAssignStmt(fieldRef, boxedOrCasted));
        break;
      }
      case FieldGet: {
        /*
         * add: T2 temp = recv.; return temp;
         */
        SootField field = Scene.v().getField(target);

        Local value = localGen.generateLocal(field.getType());

        FieldRef fieldRef;
        if (field.isStatic()) {
          fieldRef = Jimple.v().newStaticFieldRef(field.makeRef());
        } else {
          fieldRef = Jimple.v().newInstanceFieldRef(freshLocal, field.makeRef());
        }
        newUnits.add(Jimple.v().newAssignStmt(value, fieldRef));

        insertCastOrBoxingCode(retLocal, value, newUnits);

        break;
      }
    }

    if (!returnType.equals(VoidType.v())) {
      newUnits.add(Jimple.v().newReturnStmt(retLocal));
    }

    if (Options.v().validate()) {
      newBody.validate();
    }

    cleanup(newBody);

    return newMethod;
  }

  private void insertCastOrUnboxingCode(Local lhs, Local rhs, Chain newUnits) {
    // if assigning to a reference type then there's nothing to do
    if (lhs.getType() instanceof PrimType) {
      if ((rhs.getType() instanceof PrimType)) {
        // insert cast
        newUnits.add(Jimple.v().newAssignStmt(lhs, Jimple.v().newCastExpr(rhs, lhs.getType())));
      } else {
        // reference type in rhs; insert unboxing code
        RefType boxedType = (RefType) rhs.getType();
        SootMethodRef ref = Scene.v().makeMethodRef(boxedType.getSootClass(), lhs.getType().toString() + "Value",
            Collections.emptyList(), lhs.getType(), false);
        newUnits.add(Jimple.v().newAssignStmt(lhs, Jimple.v().newVirtualInvokeExpr(rhs, ref)));
      }
    }
  }

  private void insertCastOrBoxingCode(Local lhs, Local rhs, Chain newUnits) {
    // if assigning to a primitive type then there's nothing to do
    if (lhs.getType() instanceof RefLikeType) {
      if ((rhs.getType() instanceof RefLikeType)) {
        // insert cast
        newUnits.add(Jimple.v().newAssignStmt(lhs, Jimple.v().newCastExpr(rhs, lhs.getType())));
      } else {
        // primitive type in rhs; insert boxing code
        RefType boxedType = ((PrimType) rhs.getType()).boxedType();
        SootMethodRef ref = Scene.v().makeMethodRef(boxedType.getSootClass(), "valueOf",
            Collections.singletonList(rhs.getType()), boxedType, true);
        newUnits.add(Jimple.v().newAssignStmt(lhs, Jimple.v().newStaticInvokeExpr(ref, rhs)));
      }
    }
  }

  /**
   * Auto-unboxes an argument array.
   *
   * @param argsArrayLocal
   *          a local holding the argument Object[] array
   * @param paramIndex
   *          the index of the parameter to unbox
   * @param paramType
   *          the (target) type of the parameter
   * @param newUnits
   *          the Unit chain to which the unboxing code will be appended
   * @param localGen
   *          a {@link LocalGenerator} for the body holding the units
   */
  private void unboxParameter(Local argsArrayLocal, int paramIndex, Local[] paramLocals, Type paramType,
      Chain newUnits, LocalGenerator localGen) {
    ArrayRef arrayRef = Jimple.v().newArrayRef(argsArrayLocal, IntConstant.v(paramIndex));
    AssignStmt assignStmt;
    if (paramType instanceof PrimType) {
      PrimType primType = (PrimType) paramType;
      // Unbox the value if needed
      RefType boxedType = primType.boxedType();
      SootMethodRef ref = Scene.v().makeMethodRef(boxedType.getSootClass(), paramType + "Value",
          Collections.emptyList(), paramType, false);
      Local boxedLocal = localGen.generateLocal(RefType.v("java.lang.Object"));
      AssignStmt arrayLoad = Jimple.v().newAssignStmt(boxedLocal, arrayRef);
      newUnits.add(arrayLoad);
      Local castedLocal = localGen.generateLocal(boxedType);
      AssignStmt cast = Jimple.v().newAssignStmt(castedLocal, Jimple.v().newCastExpr(boxedLocal, boxedType));
      newUnits.add(cast);
      VirtualInvokeExpr unboxInvokeExpr = Jimple.v().newVirtualInvokeExpr(castedLocal, ref);
      assignStmt = Jimple.v().newAssignStmt(paramLocals[paramIndex], unboxInvokeExpr);
    } else {
      Local boxedLocal = localGen.generateLocal(RefType.v("java.lang.Object"));
      AssignStmt arrayLoad = Jimple.v().newAssignStmt(boxedLocal, arrayRef);
      newUnits.add(arrayLoad);
      Local castedLocal = localGen.generateLocal(paramType);
      AssignStmt cast = Jimple.v().newAssignStmt(castedLocal, Jimple.v().newCastExpr(boxedLocal, paramType));
      newUnits.add(cast);
      assignStmt = Jimple.v().newAssignStmt(paramLocals[paramIndex], castedLocal);
    }
    newUnits.add(assignStmt);
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy