/* Soot - a J*va Optimization Framework
* Copyright (C) 2010 Eric Bodden
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
package soot.jimple.toolkits.reflection;
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<String> fieldSets = Arrays.asList("set", "setBoolean", "setByte", "setChar", "setInt", "setLong", "setFloat", "setDouble", "setShort");
private static final List<String> 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.<Type>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<String> classForNameClassNames = RTI.classForNameClassNames(m);
if(!classForNameClassNames.isEmpty()) {
inlineRelectiveCalls(m,classForNameClassNames, ReflectionTraceInfo.Kind.ClassForName);
if(Options.v().validate()) b.validate();
}
}{
Set<String> classNewInstanceClassNames = RTI.classNewInstanceClassNames(m);
if(!classNewInstanceClassNames.isEmpty()) {
inlineRelectiveCalls(m,classNewInstanceClassNames, ReflectionTraceInfo.Kind.ClassNewInstance);
if(Options.v().validate()) b.validate();
}
}{
Set<String> constructorNewInstanceSignatures = RTI.constructorNewInstanceSignatures(m);
if(!constructorNewInstanceSignatures.isEmpty()) {
inlineRelectiveCalls(m, constructorNewInstanceSignatures, ReflectionTraceInfo.Kind.ConstructorNewInstance);
if(Options.v().validate()) b.validate();
}
}{
Set<String> methodInvokeSignatures = RTI.methodInvokeSignatures(m);
if(!methodInvokeSignatures.isEmpty()) {
inlineRelectiveCalls(m, methodInvokeSignatures, ReflectionTraceInfo.Kind.MethodInvoke);
if(Options.v().validate()) b.validate();
}
}{
Set<String> fieldSetSignatures = RTI.fieldSetSignatures(m);
if(!fieldSetSignatures.isEmpty()) {
inlineRelectiveCalls(m, fieldSetSignatures, ReflectionTraceInfo.Kind.FieldSet);
if(Options.v().validate()) b.validate();
}
}{
Set<String> 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<Unit> units = body.getUnits();
LocalGenerator localGen = new LocalGenerator(body);
Chain<Unit> newUnits = new HashChain<Unit>();
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(new SootField(ALREADY_CHECKED_FIELDNAME, BooleanType.v()));
SootClass constructor = Scene.v().getSootClass("java.lang.reflect.Constructor");
constructor.addField(new SootField(ALREADY_CHECKED_FIELDNAME, BooleanType.v()));
SootClass clazz = Scene.v().getSootClass("java.lang.Class");
clazz.addField(new SootField(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<Unit> newUnits = new HashChain<Unit>();
//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<String> targets, Kind callKind) {
if(!m.hasActiveBody()) m.retrieveActiveBody();
Body b = m.getActiveBody();
PatchingChain<Unit> units = b.getUnits();
Iterator<Unit> iter = units.snapshotIterator();
LocalGenerator localGen = new LocalGenerator(b);
//for all units
while(iter.hasNext()) {
Chain<Unit> newUnits = new HashChain<Unit>();
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("<java.lang.Class: java.lang.Class forName(java.lang.String)>") ||
ie.getMethodRef().getSignature().equals("<java.lang.Class: java.lang.Class forName(java.lang.String,boolean,java.lang.ClassLoader)>"))) {
found = true;
Value classNameValue = ie.getArg(0);
newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newStaticInvokeExpr(Scene.v().getMethod("<soot.rtlib.tamiflex.ReflectiveCalls: void knownClassForName(int,java.lang.String)>").makeRef(),IntConstant.v(callSiteId),classNameValue)));
} else if(callKind==Kind.ClassNewInstance && ie.getMethodRef().getSignature().equals("<java.lang.Class: java.lang.Object newInstance()>")) {
found = true;
Local classLocal = (Local) ((InstanceInvokeExpr)ie).getBase();
newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newStaticInvokeExpr(Scene.v().getMethod("<soot.rtlib.tamiflex.ReflectiveCalls: void knownClassNewInstance(int,java.lang.Class)>").makeRef(),IntConstant.v(callSiteId),classLocal)));
} else if(callKind==Kind.ConstructorNewInstance && ie.getMethodRef().getSignature().equals("<java.lang.reflect.Constructor: java.lang.Object newInstance(java.lang.Object[])>")) {
found = true;
Local constrLocal = (Local) ((InstanceInvokeExpr)ie).getBase();
newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newStaticInvokeExpr(Scene.v().getMethod("<soot.rtlib.tamiflex.ReflectiveCalls: void knownConstructorNewInstance(int,java.lang.reflect.Constructor)>").makeRef(),IntConstant.v(callSiteId),constrLocal)));
} else if(callKind==Kind.MethodInvoke && ie.getMethodRef().getSignature().equals("<java.lang.reflect.Method: java.lang.Object invoke(java.lang.Object,java.lang.Object[])>")) {
found = true;
Local methodLocal = (Local) ((InstanceInvokeExpr)ie).getBase();
Value recv = ie.getArg(0);
newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newStaticInvokeExpr(Scene.v().getMethod("<soot.rtlib.tamiflex.ReflectiveCalls: void knownMethodInvoke(int,java.lang.Object,java.lang.reflect.Method)>").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("<soot.rtlib.tamiflex.ReflectiveCalls: void knownFieldSet(int,java.lang.Object,java.lang.reflect.Field)>").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("<soot.rtlib.tamiflex.ReflectiveCalls: void knownFieldSet(int,java.lang.Object,java.lang.reflect.Field)>").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 <original reflective call>
newUnits.add(Jimple.v().newIfStmt(Jimple.v().newEqExpr(IntConstant.v(0), predLocal), jumpTarget));
SootMethod newMethod = createNewMethod(callKind, target, fieldSetGetType);
List<Value> args = new LinkedList<Value>();
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<Type> parameterTypes = new LinkedList<Type>();
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 = new SootMethod("reflectiveCall"+(callNum++), parameterTypes, returnType, Modifier.PUBLIC | Modifier.STATIC);
Body newBody = Jimple.v().newBody(newMethod);
newMethod.setActiveBody(newBody);
reflectiveCallsClass.addMethod(newMethod);
PatchingChain<Unit> newUnits = newBody.getUnits();
LocalGenerator localGen = new LocalGenerator(newBody);
Local freshLocal;
Value replacement=null;
Local[] paramLocals=null;
switch(callKind) {
case ClassForName:
{
//replace by: <Class constant for <target>>
freshLocal = localGen.generateLocal(RefType.v("java.lang.Class"));
replacement = ClassConstant.v(target.replace('.','/'));
break;
}
case ClassNewInstance:
{
//replace by: new <target>
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<Type>)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<Type>)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.<init>()
SootClass targetClass = Scene.v().getSootClass(target);
SpecialInvokeExpr constrCallExpr = Jimple.v().newSpecialInvokeExpr(freshLocal, Scene.v().makeMethodRef(targetClass, SootMethod.constructorName, Collections.<Type>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.<target>(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.<target>(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.<f> = 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.<f>;
* 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<Unit> 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.<Type>emptyList(),
lhs.getType(),
false
);
newUnits.add(Jimple.v().newAssignStmt(lhs, Jimple.v().newVirtualInvokeExpr(rhs, ref)));
}
}
}
private void insertCastOrBoxingCode(Local lhs, Local rhs, Chain<Unit> 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.<Type>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<Unit> 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.<Type>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);
}
}