/*
* Copyright 2012 Phil Pratt-Szeliga and other contributors
* http://chirrup.org/
*
* See the file LICENSE for copying permission.
*/
package org.trifort.rootbeer.generate.bytecode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Stack;
import soot.*;
import soot.jimple.*;
import soot.rbclassload.ClassHierarchy;
import soot.rbclassload.HierarchyGraph;
import soot.rbclassload.MethodSignatureUtil;
import soot.rbclassload.RootbeerClassLoader;
import soot.rbclassload.TypeToString;
public class BytecodeLanguage {
private Jimple m_jimple;
private SootClass m_currClass;
//method fields
private SootMethod m_currMethod;
private JimpleBody m_currBody;
private List<Type> m_parameterTypes;
private UnitAssembler m_assembler;
private Stack<SootMethod> m_methodStack;
public BytecodeLanguage(){
m_jimple = Jimple.v();
m_methodStack = new Stack<SootMethod>();
}
public SootClass makeClass(String name){
SootClass ret = new SootClass(name, Modifier.PUBLIC);
SootClass object_soot_class = Scene.v().getSootClass("java.lang.Object");
ret.setSuperclass(object_soot_class);
Scene.v().addClass(ret);
ret.setApplicationClass();
m_currClass = ret;
return ret;
}
public SootClass makeClass(String name, String parent){
SootClass ret = new SootClass(name, Modifier.PUBLIC);
//set superclass
SootClass parent_class = Scene.v().getSootClass(parent);
ret.setSuperclass(parent_class);
Scene.v().addClass(ret);
ret.setApplicationClass();
m_currClass = ret;
return ret;
}
public void addFieldToClass(Local local){
SootField field = new SootField(local.getName(), local.getType(), Modifier.PUBLIC);
m_currClass.addField(field);
}
public void addFieldToClass(Local local, String name){
SootField field = new SootField(name, local.getType(), Modifier.PUBLIC);
m_currClass.addField(field);
}
public void openClass(String name){
m_currClass = Scene.v().getSootClass(name);
}
public void openClass(SootClass soot_class){
m_currClass = soot_class;
}
public void startMethod(String method_name, Type return_type, Type... arg_types){
doStartMethod(method_name, return_type, Modifier.PUBLIC, arg_types);
}
private void doStartMethod(String method_name, Type return_type, int modifiers, Type... arg_types){
m_assembler = new UnitAssembler();
m_parameterTypes = convertTypeArrayToList(arg_types);
m_currMethod = new SootMethod(method_name, m_parameterTypes, return_type, modifiers);
m_currMethod.setDeclaringClass(m_currClass);
m_currBody = m_jimple.newBody(m_currMethod);
m_currMethod.setActiveBody(m_currBody);
m_currClass.addMethod(m_currMethod);
RootbeerClassLoader.v().addGeneratedMethod(m_currMethod.getSignature());
}
public void startStaticMethod(String method_name, Type return_type, Type... arg_types){
doStartMethod(method_name, return_type, Modifier.PUBLIC | Modifier.STATIC, arg_types);
}
public void continueMethod(UnitAssembler assembler){
m_assembler = assembler;
}
public Local refThis(){
String name = "this0";
RefType type = m_currClass.getType();
Local thislocal = m_jimple.newLocal(name, type);
Unit u = m_jimple.newIdentityStmt(thislocal, m_jimple.newThisRef(type));
m_assembler.add(u);
return thislocal;
}
public Local refParameter(int index){
Type type = m_parameterTypes.get(index);
String name = "parameter"+Integer.toString(index);
Local parameterI = m_jimple.newLocal(name, type);
Unit u = m_jimple.newIdentityStmt(parameterI, m_jimple.newParameterRef(type, index));
m_assembler.add(u);
return parameterI;
}
public Local binOp(Value lhs, String op, Value rhs){
Value binop = null;
if(op.equals("*")){
binop = m_jimple.newMulExpr(lhs, rhs);
}
Local ret = m_jimple.newLocal(getLocalName(), lhs.getType());
Unit u = m_jimple.newAssignStmt(ret, binop);
m_assembler.add(u);
return ret;
}
public void setInstanceField(SootField field, Local field_instance, Value value){
Value lhs;
if(field.isStatic() == false)
lhs = m_jimple.newInstanceFieldRef(field_instance, field.makeRef());
else
lhs = m_jimple.newStaticFieldRef(field.makeRef());
Unit u = m_jimple.newAssignStmt(lhs, value);
m_assembler.add(u);
}
public void setInstanceField(String field_name, Local field_instance, Value value){
Type type = field_instance.getType();
if(type instanceof RefType == false)
throw new RuntimeException("How do we handle this case?");
RefType ref_type = (RefType) type;
SootClass soot_class = ref_type.getSootClass();
SootField soot_field = soot_class.getFieldByName(field_name);
setInstanceField(soot_field, field_instance, value);
}
public void setStaticField(SootField field, Value value) {
Value lhs;
lhs = m_jimple.newStaticFieldRef(field.makeRef());
Unit u = m_jimple.newAssignStmt(lhs, value);
m_assembler.add(u);
}
public void endMethod(){
//System.out.println("Ending method: "+m_currMethod.getName());
m_assembler.assemble(m_currBody);
//System.out.println(m_assembler.toString());
}
private List<Type> convertTypeArrayToList(Type[] type_array){
List<Type> ret = new ArrayList<Type>();
for(int i = 0; i < type_array.length; ++i){
ret.add(type_array[i]);
}
return ret;
}
public void pushMethod(Local class_instance, String method_name, Type return_type, Type... arg_types){
String class_name = getTypeString(class_instance);
pushMethod(class_name, method_name, return_type, arg_types);
}
public void pushMethod(SootClass soot_class, String method_name, Type return_type, Type... arg_types){
String class_name = soot_class.getName();
pushMethod(class_name, method_name, return_type, arg_types);
}
public void pushMethod(String class_name, String method_name, Type return_type, Type... arg_types){
SootClass soot_class = Scene.v().getSootClass(class_name);
SootClass org_class = soot_class;
TypeToString converter = new TypeToString();
MethodSignatureUtil util = new MethodSignatureUtil();
util.setClassName(class_name);
util.setMethodName(method_name);
util.setReturnType(converter.convert(return_type));
List<String> parameter_types = new ArrayList<String>();
for(Type arg_type : arg_types){
parameter_types.add(converter.convert(arg_type));
}
util.setParameterTypes(parameter_types);
SootMethod soot_method = util.getSootMethod();
m_methodStack.push(soot_method);
}
private List<Value> convertValueArrayToList(Value[] local_array){
List<Value> ret = new ArrayList<Value>();
new ArrayList<Type>();
for(int i = 0; i < local_array.length; ++i){
ret.add(local_array[i]);
}
return ret;
}
public void invokeMethodNoRet(Local base, Value... args){
SootMethod method = m_methodStack.pop();
List<Value> args_list = convertValueArrayToList(args);
Value invoke_expr;
if(method.getName().equals("<init>")){
invoke_expr = m_jimple.newSpecialInvokeExpr(base, method.makeRef(), args_list);
} else {
//I can't find any way to distinguish between an interface and non-interface
//method. let's just try both and use whatever works.
try {
invoke_expr = m_jimple.newVirtualInvokeExpr(base, method.makeRef(), args_list);
} catch(RuntimeException ex){
invoke_expr = m_jimple.newInterfaceInvokeExpr(base, method.makeRef(), args_list);
}
}
Unit u = m_jimple.newInvokeStmt(invoke_expr);
m_assembler.add(u);
}
public void invokeStaticMethodNoRet(Value... args){
SootMethod method = m_methodStack.pop();
List<Value> args_list = convertValueArrayToList(args);
Value invoke_expr;
invoke_expr = m_jimple.newStaticInvokeExpr(method.makeRef(), args_list);
Unit u = m_jimple.newInvokeStmt(invoke_expr);
m_assembler.add(u);
}
public void invokeSpecialNoRet(Local base, Value... args){
SootMethod method = m_methodStack.pop();
List<Value> args_list = convertValueArrayToList(args);
Value invoke_expr;
invoke_expr = m_jimple.newSpecialInvokeExpr(base, method.makeRef(), args_list);
Unit u = m_jimple.newInvokeStmt(invoke_expr);
m_assembler.add(u);
}
public Local invokeMethodRet(Local base, Value... args){
SootMethod method = m_methodStack.pop();
List<Value> args_list = convertValueArrayToList(args);
Value invoke_expr;
if(method.getName().equals("<init>")){
invoke_expr = m_jimple.newSpecialInvokeExpr(base, method.makeRef(), args_list);
} else {
//I can't find any way to distinguish between an interface and non-interface
//method. let's just try both and use whatever works.
try {
invoke_expr = m_jimple.newVirtualInvokeExpr(base, method.makeRef(), args_list);
} catch(RuntimeException ex){
invoke_expr = m_jimple.newInterfaceInvokeExpr(base, method.makeRef(), args_list);
}
}
String name = getLocalName();
Local ret = m_jimple.newLocal(name, method.getReturnType());
Unit u = m_jimple.newAssignStmt(ret, invoke_expr);
m_assembler.add(u);
return ret;
}
private String getLocalName(){
return RegisterNamer.v().getName();
}
public Local local(Type type){
Local ret = m_jimple.newLocal(getLocalName(), type);
return ret;
}
public void ifStmt(Value lhs, String op, Value rhs, String target_label){
Value condition;
if(op.equals("==")){
condition = m_jimple.newEqExpr(lhs, rhs);
} else if(op.equals("!=")){
condition = m_jimple.newNeExpr(lhs, rhs);
} else if(op.equals(">=")){
condition = m_jimple.newGeExpr(lhs, rhs);
} else {
throw new UnsupportedOperationException();
}
m_assembler.addIf(condition, target_label);
}
public void ifInstanceOfStmt(Value lhs, Type rhs, String target){
//Local lhs_instanceof_rhs_local = lhs instanceof rhs;
Value lhs_instanceof_rhs;
lhs_instanceof_rhs = m_jimple.newInstanceOfExpr(lhs, rhs);
Local lhs_instance_of_rhs_local = m_jimple.newLocal(getLocalName(), BooleanType.v());
Unit u1 = m_jimple.newAssignStmt(lhs_instance_of_rhs_local, lhs_instanceof_rhs);
m_assembler.add(u1);
Value eq = m_jimple.newEqExpr(lhs_instance_of_rhs_local, IntConstant.v(0));
m_assembler.addIf(eq, target);
}
public void label(String label_string){
m_assembler.addLabel(label_string);
}
public void returnVoid() {
m_assembler.add(m_jimple.newReturnVoidStmt());
}
public void returnValue(Value value){
Unit u = m_jimple.newReturnStmt(value);
m_assembler.add(u);
}
public Local refInstanceField(Local base, String field_name){
Type base_type = base.getType();
SootClass base_class = Scene.v().getSootClass(base_type.toString());
SootField field = getFieldByName(base_class, field_name);
Local ret = m_jimple.newLocal(getLocalName(), field.getType());
Value rhs = m_jimple.newInstanceFieldRef(base, field.makeRef());
Unit u = m_jimple.newAssignStmt(ret, rhs);
m_assembler.add(u);
return ret;
}
Local refStaticField(Local base, String field_name) {
Type base_type = base.getType();
return refStaticField(base_type, field_name);
}
Local refStaticField(Type base_type, String field_name) {
SootClass base_class = Scene.v().getSootClass(base_type.toString());
SootField field = getFieldByName(base_class, field_name);
Local ret = m_jimple.newLocal(getLocalName(), field.getType());
Value rhs = m_jimple.newStaticFieldRef(field.makeRef());
Unit u = m_jimple.newAssignStmt(ret, rhs);
m_assembler.add(u);
return ret;
}
public void refInstanceFieldToInput(Local base, String field_name, Local input){
Type base_type = base.getType();
SootClass base_class = Scene.v().getSootClass(base_type.toString());
SootField field = getFieldByName(base_class, field_name);
Value rhs = m_jimple.newInstanceFieldRef(base, field.makeRef());
Unit u = m_jimple.newAssignStmt(input, rhs);
m_assembler.add(u);
}
public void refInstanceFieldFromInput(Local base, String field_name, Local input){
Type base_type = base.getType();
SootClass base_class = Scene.v().getSootClass(base_type.toString());
SootField field = getFieldByName(base_class, field_name);
Value rhs = m_jimple.newInstanceFieldRef(base, field.makeRef());
Unit u = m_jimple.newAssignStmt(rhs, input);
m_assembler.add(u);
}
public String getTypeString(Local local){
Type type = local.getType();
return type.toString();
}
public Local cast(Type type, Local rhs){
Local ret = m_jimple.newLocal(getLocalName(), type);
Value rhs_value = m_jimple.newCastExpr(rhs, type);
Unit u = m_jimple.newAssignStmt(ret, rhs_value);
m_assembler.add(u);
return ret;
}
public Local newInstance(String mClassName, Value... params) {
SootClass soot_class = Scene.v().getSootClass(mClassName);
Local u1_lhs = m_jimple.newLocal(getLocalName(), soot_class.getType());
Value u1_rhs = m_jimple.newNewExpr(soot_class.getType());
Unit u1 = m_jimple.newAssignStmt(u1_lhs, u1_rhs);
m_assembler.add(u1);
Type[] arg_types = new Type[params.length];
for(int i = 0; i < params.length; ++i){
arg_types[i] = params[i].getType();
}
pushMethod(u1_lhs, "<init>", VoidType.v(), arg_types);
invokeMethodNoRet(u1_lhs, params);
Local u2_lhs = m_jimple.newLocal(getLocalName(), soot_class.getType());
Unit u2 = m_jimple.newAssignStmt(u2_lhs, u1_lhs);
m_assembler.add(u2);
return u2_lhs;
}
public Local newInstanceValueOf(String mClassName, Value param) {
// Generate mClassName.valueOf(param)
SootClass soot_class = Scene.v().getSootClass(mClassName);
Local l_res = m_jimple.newLocal(getLocalName(), soot_class.getType());
SootMethodRef classForNameRef = soot.Scene.v().makeMethodRef(soot_class,
"valueOf", Arrays.asList(param.getType()), soot_class.getType(), true);
Unit u1 = m_jimple.newAssignStmt(l_res, m_jimple.newStaticInvokeExpr(classForNameRef, Arrays.asList(new Value[]{param})));
m_assembler.add(u1);
return l_res;
}
public Value newArray(Type type, Value size) {
ArrayType atype = (ArrayType) type;
if(atype.numDimensions == 1)
return m_jimple.newNewArrayExpr(atype.baseType, size);
else {
ArrayType to_create = ArrayType.v(atype.baseType, atype.numDimensions-1);
return m_jimple.newNewArrayExpr(to_create, size);
}
}
public void assign(Value lhs, Value rhs) {
Unit u = m_jimple.newAssignStmt(lhs, rhs);
m_assembler.add(u);
}
public Unit getLastUnitCreated(){
return m_assembler.getLastUnitCreated();
}
public void gotoLabel(String label2) {
m_assembler.addGoto(label2);
}
public void makeVoidCtor() {
startMethod("<init>", VoidType.v());
SootClass super_soot_class = m_currClass.getSuperclass();
Local thisref = refThis();
pushMethod(super_soot_class.getName(), "<init>", VoidType.v());
invokeMethodNoRet(thisref);
returnVoid();
endMethod();
}
public UnitAssembler getAssembler() {
return m_assembler;
}
Local lengthof(Value array) {
Value rhs = m_jimple.newLengthExpr(array);
Local lhs = m_jimple.newLocal(getLocalName(), IntType.v());
Unit u = m_jimple.newAssignStmt(lhs, rhs);
m_assembler.add(u);
return lhs;
}
Local indexArray(Local base, Value i) {
Value rhs = m_jimple.newArrayRef(base, i);
Type type = base.getType();
if(type instanceof ArrayType == false)
throw new RuntimeException("How do we handle this case?");
ArrayType atype = (ArrayType) type;
Local lhs;
if(atype.numDimensions == 1){
lhs = m_jimple.newLocal(getLocalName(), atype.baseType);
} else {
Type lhs_type = ArrayType.v(atype.baseType, atype.numDimensions-1);
lhs = m_jimple.newLocal(getLocalName(), lhs_type);
}
Unit u = m_jimple.newAssignStmt(lhs, rhs);
m_assembler.add(u);
return lhs;
}
public void assignArray(Local base, Value i, Local value){
Value lhs = m_jimple.newArrayRef(base, i);
Unit u = m_jimple.newAssignStmt(lhs, value);
m_assembler.add(u);
}
void plus(Local i, int add_value) {
Value rhs = m_jimple.newAddExpr(i, IntConstant.v(add_value));
Unit u = m_jimple.newAssignStmt(i, rhs);
m_assembler.add(u);
}
void plus(Local i, Value add_value) {
Value rhs = m_jimple.newAddExpr(i, add_value);
Unit u = m_jimple.newAssignStmt(i, rhs);
m_assembler.add(u);
}
void mult(Local lhs, Value mult_value){
Value rhs = m_jimple.newMulExpr(lhs, mult_value);
Unit u = m_jimple.newAssignStmt(lhs, rhs);
m_assembler.add(u);
}
void noOp() {
Unit u = m_jimple.newNopStmt();
m_assembler.add(u);
}
void assignElementToArray(Local base, Value rhs, Value i) {
Value lhs = m_jimple.newArrayRef(base, i);
Unit u = m_jimple.newAssignStmt(lhs, rhs);
m_assembler.add(u);
}
public void println(String message){
Type system = RefType.v("java.lang.System");
Local out = refStaticField(system, "out");
Type string = RefType.v("java.lang.String");
pushMethod(out, "println", VoidType.v(), string);
invokeMethodNoRet(out, StringConstant.v(message));
}
void println(Value number) {
Type system = RefType.v("java.lang.System");
Local out = refStaticField(system, "out");
pushMethod(out, "println", VoidType.v(), IntType.v());
invokeMethodNoRet(out, number);
}
void printlnLong(Value number) {
Type system = RefType.v("java.lang.System");
Local out = refStaticField(system, "out");
pushMethod(out, "println", VoidType.v(), LongType.v());
invokeMethodNoRet(out, number);
}
SootClass getSootClass() {
return m_currClass;
}
Local classConstant(Type type) {
String class_name = convertToConstant(type);
Value curr = ClassConstant.v(class_name);
Local ret = m_jimple.newLocal(getLocalName(), curr.getType());
Unit u = m_jimple.newAssignStmt(ret, curr);
m_assembler.add(u);
return ret;
}
private String convertToConstant(Type type) {
if(type instanceof ArrayType){
ArrayType array_type = (ArrayType) type;
String prefix = "";
int dims = array_type.numDimensions;
for(int i = 0; i < dims; ++i){
prefix += "[";
}
String base_string = convertToConstant(array_type.baseType);
if(array_type.baseType instanceof RefType){
return prefix + "L" + base_string + ";";
} else {
return prefix + base_string;
}
} else if(type instanceof RefType){
RefType ref_type = (RefType) type;
return ref_type.getSootClass().getName().replace(".", "/");
} else if(type instanceof PrimType){
if(type.equals(BooleanType.v())){
return "Z";
} else if(type.equals(ByteType.v())){
return "B";
} else if(type.equals(ShortType.v())){
return "S";
} else if(type.equals(CharType.v())){
return "C";
} else if(type.equals(IntType.v())){
return "I";
} else if(type.equals(LongType.v())){
return "J";
} else if(type.equals(FloatType.v())){
return "F";
} else if(type.equals(DoubleType.v())){
return "D";
}
}
throw new RuntimeException("please report bug in BytecodeLanguage.convertToConstant");
}
private SootField getFieldByName(SootClass base_class, String field_name) {
List<String> queue = new LinkedList<String>();
queue.add(base_class.getName());
while(queue.isEmpty() == false){
String curr_class = queue.get(0);
queue.remove(0);
SootClass soot_class = Scene.v().getSootClass(curr_class);
if(soot_class.declaresFieldByName(field_name)){
return soot_class.getFieldByName(field_name);
}
if(soot_class.hasSuperclass()){
queue.add(soot_class.getSuperclass().getName());
}
if(soot_class.hasOuterClass()){
queue.add(soot_class.getOuterClass().getName());
}
}
throw new RuntimeException("cannot find field: "+field_name+" in "+base_class.getName());
}
}