package com.hellblazer.primeMover.soot.util;
import static java.util.Arrays.asList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import soot.ArrayType;
import soot.BooleanType;
import soot.ByteType;
import soot.CharType;
import soot.DoubleType;
import soot.FloatType;
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.Type;
import soot.Unit;
import soot.UnitBox;
import soot.Value;
import soot.VoidType;
import soot.jimple.ArrayRef;
import soot.jimple.InstanceFieldRef;
import soot.jimple.IntConstant;
import soot.jimple.Jimple;
import soot.jimple.JimpleBody;
import soot.jimple.StaticFieldRef;
import soot.util.Chain;
public class MethodHelper {
private static List<String> generateParameters(List<? extends Type> parameterTypes) {
ArrayList<String> parameterNames = new ArrayList<String>();
for (int i = 0; i < parameterTypes.size(); i++) {
parameterNames.add("parameter" + i);
}
return parameterNames;
}
private final SootClass hostClass;
private final SootMethod method;
private final JimpleBody body;
private final Chain<Unit> units;
private final Jimple jimple = Jimple.v();
private Local thisLocal;
private Local[] parameters;
private final SootClass boxedBoolean = Scene.v().loadClass(Boolean.class.getCanonicalName(),
SootClass.SIGNATURES);
private final SootClass boxedByte = Scene.v().loadClass(Byte.class.getCanonicalName(),
SootClass.SIGNATURES);
private final SootClass boxedChar = Scene.v().loadClass(Character.class.getCanonicalName(),
SootClass.SIGNATURES);
private final SootClass boxedDouble = Scene.v().loadClass(Double.class.getCanonicalName(),
SootClass.SIGNATURES);
private final SootClass boxedFloat = Scene.v().loadClass(Float.class.getCanonicalName(),
SootClass.SIGNATURES);
private final SootClass boxedInt = Scene.v().loadClass(Integer.class.getCanonicalName(),
SootClass.SIGNATURES);
private final SootClass boxedLong = Scene.v().loadClass(Long.class.getCanonicalName(),
SootClass.SIGNATURES);
private final SootClass boxedShort = Scene.v().loadClass(Short.class.getCanonicalName(),
SootClass.SIGNATURES);
private final SootClass object = Scene.v().loadClass(Object.class.getCanonicalName(),
SootClass.SIGNATURES);
public MethodHelper(SootClass clazz, int modifiers) {
this(clazz, "<init>", modifiers);
}
public MethodHelper(SootClass clazz, List<? extends Type> parameterTypes,
int modifiers) {
this(clazz, "<init>", generateParameters(parameterTypes),
parameterTypes, VoidType.v(), modifiers);
}
public MethodHelper(SootClass clazz, List<? extends Type> parameterTypes,
Type returnType, int modifiers) {
this(clazz, "<init>", generateParameters(parameterTypes),
parameterTypes, returnType, modifiers);
}
public MethodHelper(SootClass clazz, List<String> parameterNames,
List<? extends Type> parameterTypes, int modifiers) {
this(clazz, "<init>", parameterNames, parameterTypes, VoidType.v(),
modifiers);
}
@SuppressWarnings("unchecked")
public MethodHelper(SootClass clazz, String name, int modifiers) {
this(clazz, name, Collections.EMPTY_LIST, Collections.EMPTY_LIST,
VoidType.v(), modifiers);
}
public MethodHelper(SootClass clazz, String name,
List<? extends Type> parameterTypes, Type returnType,
int modifiers) {
this(clazz, name, generateParameters(parameterTypes), parameterTypes,
returnType, modifiers);
}
public MethodHelper(SootClass clazz, String name,
List<String> parameterNames,
List<? extends Type> parameterTypes, Type returnType,
int modifiers) {
if (clazz == null) {
throw new NullPointerException("defining class must not be null");
}
if (name == null) {
throw new NullPointerException("method name must not be null");
}
if (parameterNames == null) {
throw new NullPointerException("parameter names must not be null");
}
if (parameterTypes == null) {
throw new NullPointerException("parameter types must not be null");
}
if (parameterNames.size() != parameterTypes.size()) {
throw new IllegalArgumentException(
"The number of parameter names must be equal to the number of parameters");
}
if (modifiers < 0) {
throw new IllegalArgumentException("Modifier must be > 0");
}
hostClass = clazz;
method = new SootMethod(name, parameterTypes, returnType);
method.setModifiers(modifiers);
body = jimple.newBody(method);
method.setActiveBody(body);
units = body.getUnits();
if (!method.isStatic()) {
thisLocal = newLocal("this", hostClass.getType());
units.add(jimple.newIdentityStmt(thisLocal,
jimple.newThisRef(RefType.v(hostClass))));
}
parameters = new Local[method.getParameterCount()];
int i = 0;
for (String param : parameterNames) {
Local arg = newLocal(param, method.getParameterType(i));
units.add(jimple.newIdentityStmt(arg,
jimple.newParameterRef(method.getParameterType(i),
i)));
parameters[i++] = arg;
}
}
@SuppressWarnings("unchecked")
public MethodHelper(SootClass clazz, String name, Type returnType,
int modifiers) {
this(clazz, name, Collections.EMPTY_LIST, Collections.EMPTY_LIST,
returnType, modifiers);
}
public void add(Unit unit) {
units.add(unit);
}
public void addAll(Collection<Unit> units) {
units.addAll(units);
}
public void assign(Value variable, Value rValue) {
units.add(jimple.newAssignStmt(variable, rValue));
}
public void assignInstanceVariable(String instanceVariable, Value rValue) {
if (method.isStatic()) {
throw new UnsupportedOperationException(
"Cannot assign an instance variable in a static method");
}
InstanceFieldRef ref = getInstanceVariableRef(instanceVariable);
units.add(jimple.newAssignStmt(ref, rValue));
}
public void assignInstanceVariableTo(Local target, String instanceVariable) {
if (method.isStatic()) {
throw new UnsupportedOperationException(
"Cannot retrieve an instance variable in a static method");
}
InstanceFieldRef ref = getInstanceVariableRef(instanceVariable);
units.add(jimple.newAssignStmt(target, ref));
}
public void assignStaticVariable(String staticVariable, Value rValue) {
StaticFieldRef ref = getStaticFieldRef(staticVariable);
units.add(jimple.newAssignStmt(ref, rValue));
}
public void assignStaticVariableTo(Local target, String staticVariable) {
StaticFieldRef ref = getStaticFieldRef(staticVariable);
units.add(jimple.newAssignStmt(target, ref));
}
public Local box(PrimType primType, Local unboxedSource) {
Local boxedResult;
if (primType instanceof BooleanType) {
boxedResult = newLocal(UUID.randomUUID().toString(),
boxedBoolean.getType());
units.add(jimple.newAssignStmt(boxedResult,
jimple.newStaticInvokeExpr(boxedBoolean.getMethod("java.lang.Boolean valueOf(boolean)").makeRef(),
asList(unboxedSource))));
} else if (primType instanceof ByteType) {
boxedResult = newLocal(UUID.randomUUID().toString(),
boxedByte.getType());
units.add(jimple.newAssignStmt(boxedResult,
jimple.newStaticInvokeExpr(boxedByte.getMethod("java.lang.Byte valueOf(byte)").makeRef(),
asList(unboxedSource))));
} else if (primType instanceof CharType) {
boxedResult = newLocal(UUID.randomUUID().toString(),
boxedChar.getType());
units.add(jimple.newAssignStmt(boxedResult,
jimple.newStaticInvokeExpr(boxedChar.getMethod("java.lang.Character valueOf(char)").makeRef(),
asList(unboxedSource))));
} else if (primType instanceof DoubleType) {
boxedResult = newLocal(UUID.randomUUID().toString(),
boxedDouble.getType());
units.add(jimple.newAssignStmt(boxedResult,
jimple.newStaticInvokeExpr(boxedDouble.getMethod("java.lang.Double valueOf(double)").makeRef(),
asList(unboxedSource))));
} else if (primType instanceof FloatType) {
boxedResult = newLocal(UUID.randomUUID().toString(),
boxedFloat.getType());
units.add(jimple.newAssignStmt(boxedResult,
jimple.newStaticInvokeExpr(boxedFloat.getMethod("java.lang.Float valueOf(float)").makeRef(),
asList(unboxedSource))));
} else if (primType instanceof IntType) {
boxedResult = newLocal(UUID.randomUUID().toString(),
boxedInt.getType());
units.add(jimple.newAssignStmt(boxedResult,
jimple.newStaticInvokeExpr(boxedInt.getMethod("java.lang.Integer valueOf(int)").makeRef(),
asList(unboxedSource))));
} else if (primType instanceof LongType) {
boxedResult = newLocal(UUID.randomUUID().toString(),
boxedLong.getType());
units.add(jimple.newAssignStmt(boxedResult,
jimple.newStaticInvokeExpr(boxedLong.getMethod("java.lang.Long valueOf(long)").makeRef(),
asList(unboxedSource))));
} else if (primType instanceof ShortType) {
boxedResult = newLocal(UUID.randomUUID().toString(),
boxedShort.getType());
units.add(jimple.newAssignStmt(boxedResult,
jimple.newStaticInvokeExpr(boxedShort.getMethod("java.lang.Short valueOf(short)").makeRef(),
asList(unboxedSource))));
} else {
throw new IllegalArgumentException(
"Parameter type is not a valid primitive type: "
+ primType);
}
return boxedResult;
}
public UnitBox generateIf(Value condition) {
UnitBox target = jimple.newStmtBox(null);
units.add(jimple.newIfStmt(condition, target));
return target;
}
public InstanceFieldRef getInstanceVariableRef(String instanceVariable) {
if (method.isStatic()) {
throw new UnsupportedOperationException(
"Cannot retrieve an instance variable in a static method");
}
return jimple.newInstanceFieldRef(thisLocal,
hostClass.getFieldByName(instanceVariable).makeRef());
}
public Unit getLast() {
return units.getLast();
}
public SootMethod getMethod() {
return method;
}
public Local getParameter(int index) {
if (index > parameters.length) {
throw new IllegalArgumentException("Invalid parameter index: "
+ index);
}
return parameters[index];
}
public List<Local> getParameters() {
return asList(parameters);
}
public StaticFieldRef getStaticFieldRef(String staticVariable) {
return jimple.newStaticFieldRef(hostClass.getFieldByName(staticVariable).makeRef());
}
public void invoke(Value op) {
units.add(jimple.newInvokeStmt(op));
}
/**
* Load all the method arguments on the stack, as a single object array,
* into the target local;
*/
public void loadArgumentsArray(Local arguments) {
units.add(jimple.newAssignStmt(arguments,
jimple.newNewArrayExpr(object.getType(),
IntConstant.v(parameters.length))));
for (int i = 0; i < method.getParameterCount(); i++) {
Type paramType = method.getParameterType(i);
ArrayRef slot = jimple.newArrayRef(arguments, IntConstant.v(i));
if (paramType instanceof PrimType) {
Local boxedArg = box((PrimType) paramType, parameters[i]);
units.add(jimple.newAssignStmt(slot, boxedArg));
} else {
units.add(jimple.newAssignStmt(slot, parameters[i]));
}
}
}
@SuppressWarnings("unchecked")
public void newInstance(Local target, SootClass clazz) {
newInstance(target, clazz, Collections.EMPTY_LIST, new Value[] {});
}
public void newInstance(Local target, SootClass clazz,
List<Type> parameterTypes, Value... arguments) {
units.add(jimple.newAssignStmt(target,
jimple.newNewExpr(clazz.getType())));
units.add(jimple.newInvokeStmt(jimple.newSpecialInvokeExpr(target,
clazz.getMethod("<init>",
asList(arguments)).makeRef())));
}
public Local newLocal(String name, Type type) {
Local local = jimple.newLocal(name, type);
body.getLocals().add(local);
return local;
}
public void returnBoxed(Type returnType, Local unboxedValue) {
if (returnType instanceof RefType || returnType instanceof ArrayType) {
units.add(jimple.newReturnStmt(unboxedValue));
return;
}
if (!(returnType instanceof PrimType)) {
throw new IllegalStateException("Not a valid boxed return type: "
+ returnType);
}
Local returnValue = box((PrimType) returnType, unboxedValue);
units.add(Jimple.v().newReturnStmt(returnValue));
return;
}
public void returnUnboxed(Type returnType, Local boxedValue) {
if (returnType instanceof VoidType) {
units.add(jimple.newReturnVoidStmt());
return;
}
if (returnType instanceof RefType || returnType instanceof ArrayType) {
Local returnValue = newLocal(UUID.randomUUID().toString(),
returnType);
units.add(jimple.newAssignStmt(returnValue,
jimple.newCastExpr(boxedValue,
returnType)));
units.add(jimple.newReturnStmt(returnValue));
return;
}
if (!(returnType instanceof PrimType)) {
throw new IllegalStateException("Not a valid return type: "
+ returnType);
}
Local returnValue = newLocal(UUID.randomUUID().toString(), returnType);
unbox((PrimType) returnType, returnValue, boxedValue);
units.add(Jimple.v().newReturnStmt(returnValue));
return;
}
public void returnValue(Value returnValue) {
units.add(jimple.newReturnStmt(returnValue));
}
public void returnVoid() {
units.add(jimple.newReturnVoidStmt());
}
public Local thisLocal() {
if (method.isStatic()) {
throw new UnsupportedOperationException(
"Cannot retrieve 'this' in a static method");
}
return thisLocal;
}
public void unbox(PrimType primType, Local castResult, Local boxedSource) {
if (primType instanceof BooleanType) {
Local boxLocal = newLocal(UUID.randomUUID().toString(),
boxedBoolean.getType());
units.add(jimple.newAssignStmt(boxLocal,
jimple.newCastExpr(boxedSource,
boxedBoolean.getType())));
units.add(jimple.newAssignStmt(castResult,
jimple.newVirtualInvokeExpr(boxLocal,
boxedBoolean.getMethod("boolean booleanValue()").makeRef())));
} else if (primType instanceof ByteType) {
Local boxLocal = newLocal(UUID.randomUUID().toString(),
boxedByte.getType());
units.add(jimple.newAssignStmt(boxLocal,
jimple.newCastExpr(boxedSource,
boxedByte.getType())));
units.add(jimple.newAssignStmt(castResult,
jimple.newVirtualInvokeExpr(boxLocal,
boxedByte.getMethod("byte byteValue()").makeRef())));
} else if (primType instanceof CharType) {
Local boxLocal = newLocal(UUID.randomUUID().toString(),
boxedChar.getType());
units.add(jimple.newAssignStmt(boxLocal,
jimple.newCastExpr(boxedSource,
boxedChar.getType())));
units.add(jimple.newAssignStmt(castResult,
jimple.newVirtualInvokeExpr(boxLocal,
boxedChar.getMethod("char charValue()").makeRef())));
} else if (primType instanceof DoubleType) {
Local boxLocal = newLocal(UUID.randomUUID().toString(),
boxedDouble.getType());
units.add(jimple.newAssignStmt(boxLocal,
jimple.newCastExpr(boxedSource,
boxedDouble.getType())));
units.add(jimple.newAssignStmt(castResult,
jimple.newVirtualInvokeExpr(boxLocal,
boxedDouble.getMethod("double doubleValue()").makeRef())));
} else if (primType instanceof FloatType) {
Local boxLocal = newLocal(UUID.randomUUID().toString(),
boxedFloat.getType());
units.add(jimple.newAssignStmt(boxLocal,
jimple.newCastExpr(boxedSource,
boxedFloat.getType())));
units.add(jimple.newAssignStmt(castResult,
jimple.newVirtualInvokeExpr(boxLocal,
boxedFloat.getMethod("float floatValue()").makeRef())));
} else if (primType instanceof IntType) {
Local boxLocal = newLocal(UUID.randomUUID().toString(),
boxedInt.getType());
units.add(jimple.newAssignStmt(boxLocal,
jimple.newCastExpr(boxedSource,
boxedInt.getType())));
units.add(jimple.newAssignStmt(castResult,
jimple.newVirtualInvokeExpr(boxLocal,
boxedInt.getMethod("int intValue()").makeRef())));
} else if (primType instanceof LongType) {
Local boxLocal = newLocal(UUID.randomUUID().toString(),
boxedLong.getType());
units.add(jimple.newAssignStmt(boxLocal,
jimple.newCastExpr(boxedSource,
boxedLong.getType())));
units.add(jimple.newAssignStmt(castResult,
jimple.newVirtualInvokeExpr(boxLocal,
boxedLong.getMethod("long longValue()").makeRef())));
} else if (primType instanceof ShortType) {
Local boxLocal = newLocal(UUID.randomUUID().toString(),
boxedShort.getType());
units.add(jimple.newAssignStmt(boxLocal,
jimple.newCastExpr(boxedSource,
boxedShort.getType())));
units.add(jimple.newAssignStmt(castResult,
jimple.newVirtualInvokeExpr(boxLocal,
boxedShort.getMethod("short shortValue()").makeRef())));
} else {
throw new IllegalArgumentException(
"Parameter type is not a valid primitive type: "
+ primType);
}
}
public void validate() {
method.getActiveBody().validate();
}
}