package com.googlecode.d2j.converter; import com.googlecode.dex2jar.ir.IrMethod; import com.googlecode.dex2jar.ir.Trap; import com.googlecode.dex2jar.ir.expr.Exprs; import com.googlecode.dex2jar.ir.expr.Local; import com.googlecode.dex2jar.ir.expr.Value; import com.googlecode.dex2jar.ir.stmt.LabelStmt; import com.googlecode.dex2jar.ir.stmt.StmtList; import com.googlecode.dex2jar.ir.stmt.Stmts; import org.objectweb.asm.Handle; import org.objectweb.asm.Label; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.tree.*; import org.objectweb.asm.tree.analysis.*; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Convert Instruction in Asm to IRMethod, * please set <code>ClassReader.EXPAND_FRAMES | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES</code> where build * the ClassNode * * @author bob */ public class J2IRConverter implements Opcodes { public static final Value PLACEHOLDER = Exprs.nByte((byte) 0); Map<Label, LabelStmt> map = new HashMap<Label, LabelStmt>(); public static String[] toDescArray(Type[] ts) { String[] ds = new String[ts.length]; for (int i = 0; i < ts.length; i++) { ds[i] = ts[i].getDescriptor(); } return ds; } LabelStmt getLabel(LabelNode labelNode) { Label label = labelNode.getLabel(); LabelStmt ls = map.get(label); if (ls == null) { ls = Stmts.nLabel(); map.put(label, ls); } return ls; } /** * convert the asm code into ir * * @param owner class internal name * @param source src method node * @return the converted IR */ @SuppressWarnings({"rawtypes", "unchecked"}) public IrMethod convert(String owner, final MethodNode source) { IrMethod target = populate(owner, source); if (source.instructions.size() == 0) { return target; } for (TryCatchBlockNode tc : source.tryCatchBlocks) { target.traps.add(new Trap(getLabel(tc.start), getLabel(tc.end), new LabelStmt[] { getLabel(tc.handler) }, new String[] { tc.type })); } Analyzer an = new Analyzer(new BasicInterpreter()); Frame[] frames; try { frames = an.analyze(owner, source); } catch (AnalyzerException ex) { throw new RuntimeException(ex); } final Value[] values = new Value[source.maxLocals + source.maxStack]; for (int i = 0; i < values.length; i++) { Local local = Exprs.nLocal("a" + i); target.locals.add(local); values[i] = local; } int x = 0; final StmtList stmts = target.stmts; if (!target.isStatic) {// not static stmts.add(Stmts.nIdentity(values[x++], Exprs.nThisRef(target.owner))); } for (int i = 0; i < target.args.length; i++) { stmts.add(Stmts.nIdentity(values[x++], Exprs.nParameterRef(target.args[i], i))); } final Local voidLocal = Exprs.nLocal("ignore"); target.locals.add(voidLocal); // FIXME clean all Label.info Frame fx = new Frame(source.maxLocals, source.maxStack) { public XValue pop() throws IndexOutOfBoundsException { org.objectweb.asm.tree.analysis.Value v = super.pop(); int top = super.getStackSize(); return new XValue(v.getSize(), values[top + super.getLocals()]); } @Override public org.objectweb.asm.tree.analysis.Value getStack(int i) throws IndexOutOfBoundsException { org.objectweb.asm.tree.analysis.Value v = super.getStack(i); return new XValue(v.getSize(), values[i + super.getLocals()]); } @Override public XValue getLocal(int i) throws IndexOutOfBoundsException { org.objectweb.asm.tree.analysis.Value v = super.getLocal(i); return new XValue(v.getSize(), values[i]); } @Override public void execute(AbstractInsnNode insn, Interpreter interpreter) throws AnalyzerException { switch (insn.getOpcode()) { case DUP_X1: // BEFORE: ..AB // AFTER: ..BAB int stack = getStackSize() + getLocals(); Value A = values[stack - 2]; Value B = values[stack - 1]; // tos now Value C = values[stack - 0]; // next stack position stmts.add(Stmts.nAssign(C, B)); stmts.add(Stmts.nAssign(B, A)); stmts.add(Stmts.nAssign(A, C)); return; case DUP_X2: case DUP2: case DUP2_X1: case DUP2_X2: throw new RuntimeException("not support opcode" + insn.getOpcode()); } super.execute(insn, interpreter); } @Override public void setLocal(int i, org.objectweb.asm.tree.analysis.Value value) throws IndexOutOfBoundsException { Value v = ((XValue) value).value; if (v != PLACEHOLDER) { stmts.add(Stmts.nAssign(values[i], v)); } } public void push(org.objectweb.asm.tree.analysis.Value v) throws IndexOutOfBoundsException { int top = super.getStackSize(); stmts.add(Stmts.nAssign(values[top + super.getLocals()], ((XValue) v).value)); super.push(v); } public String toString() { return "[Frame]"; } }; Interpreter<XValue> interpreter = new Interpreter<XValue>(Opcodes.ASM4) { @Override public XValue newValue(Type type) { return new XValue(1, PLACEHOLDER); } @Override public XValue newOperation(AbstractInsnNode insn) throws AnalyzerException { switch (insn.getOpcode()) { case ACONST_NULL: return new XValue(1, Exprs.nNull()); case ICONST_M1: case ICONST_0: case ICONST_1: case ICONST_2: case ICONST_3: case ICONST_4: case ICONST_5: return new XValue(1, Exprs.nInt(insn.getOpcode() - ICONST_0)); case LCONST_0: case LCONST_1: return new XValue(2, Exprs.nLong(insn.getOpcode() - LCONST_0)); case FCONST_0: case FCONST_1: case FCONST_2: return new XValue(1, Exprs.nFloat(insn.getOpcode() - FCONST_0)); case DCONST_0: case DCONST_1: return new XValue(2, Exprs.nDouble(insn.getOpcode() - DCONST_0)); case BIPUSH: case SIPUSH: return new XValue(1, Exprs.nInt(((IntInsnNode) insn).operand)); case LDC: Object cst = ((LdcInsnNode) insn).cst; if (cst instanceof Integer) { return new XValue(1, Exprs.nInt((Integer) cst)); } else if (cst instanceof Float) { return new XValue(1, Exprs.nFloat((Float) cst)); } else if (cst instanceof Long) { return new XValue(2, Exprs.nLong((Long) cst)); } else if (cst instanceof Double) { return new XValue(2, Exprs.nDouble((Double) cst)); } else if (cst instanceof String) { return new XValue(1, Exprs.nString((String) cst)); } else if (cst instanceof Type) { Type type = (Type) cst; int sort = type.getSort(); if (sort == Type.OBJECT || sort == Type.ARRAY) { return new XValue(1, Exprs.nType(type.getDescriptor())); } else if (sort == Type.METHOD) { throw new UnsupportedOperationException("Not supported yet."); } else { throw new IllegalArgumentException("Illegal LDC constant " + cst); } } else if (cst instanceof Handle) { throw new UnsupportedOperationException("Not supported yet."); } else { throw new IllegalArgumentException("Illegal LDC constant " + cst); } case JSR: throw new UnsupportedOperationException("Not supported yet."); case GETSTATIC: FieldInsnNode fin = (FieldInsnNode) insn; return new XValue(Type.getType(fin.desc).getSize(), Exprs.nStaticField("L" + fin.owner + ";", fin.name, fin.desc)); case NEW: return new XValue(1, Exprs.nNew("L" + ((TypeInsnNode) insn).desc + ";")); default: throw new Error("Internal error."); } } @Override public XValue copyOperation(AbstractInsnNode insn, XValue value) throws AnalyzerException { return value; } @Override public XValue unaryOperation(AbstractInsnNode insn, XValue value0) throws AnalyzerException { XValue value = (XValue) value0; switch (insn.getOpcode()) { case INEG: return new XValue(1, Exprs.nNeg(value.value, "I")); case IINC: return new XValue(1, Exprs.nAdd(value.value, Exprs.nInt(((IincInsnNode) insn).incr), "I")); case L2I: return new XValue(1, Exprs.nCast(value.value, "J", "I")); case F2I: return new XValue(1, Exprs.nCast(value.value, "F", "I")); case D2I: return new XValue(1, Exprs.nCast(value.value, "D", "I")); case I2B: return new XValue(1, Exprs.nCast(value.value, "I", "B")); case I2C: return new XValue(1, Exprs.nCast(value.value, "I", "C")); case I2S: return new XValue(1, Exprs.nCast(value.value, "I", "S")); case FNEG: return new XValue(1, Exprs.nNeg(value.value, "F")); case I2F: return new XValue(1, Exprs.nCast(value.value, "I", "F")); case L2F: return new XValue(1, Exprs.nCast(value.value, "J", "F")); case D2F: return new XValue(1, Exprs.nCast(value.value, "D", "F")); case LNEG: return new XValue(2, Exprs.nNeg(value.value, "J")); case I2L: return new XValue(2, Exprs.nCast(value.value, "I", "J")); case F2L: return new XValue(2, Exprs.nCast(value.value, "F", "J")); case D2L: return new XValue(2, Exprs.nCast(value.value, "D", "J")); case DNEG: return new XValue(2, Exprs.nNeg(value.value, "D")); case I2D: return new XValue(2, Exprs.nCast(value.value, "I", "D")); case L2D: return new XValue(2, Exprs.nCast(value.value, "J", "D")); case F2D: return new XValue(2, Exprs.nCast(value.value, "F", "D")); case IFEQ: stmts.add(Stmts.nIf(Exprs.nEq(value.value, Exprs.nInt(0), "I"), getLabel(((JumpInsnNode) insn).label))); return null; case IFNE: stmts.add(Stmts.nIf(Exprs.nNe(value.value, Exprs.nInt(0), "I"), getLabel(((JumpInsnNode) insn).label))); return null; case IFLT: stmts.add(Stmts.nIf(Exprs.nLt(value.value, Exprs.nInt(0), "I"), getLabel(((JumpInsnNode) insn).label))); return null; case IFGE: stmts.add(Stmts.nIf(Exprs.nGe(value.value, Exprs.nInt(0), "I"), getLabel(((JumpInsnNode) insn).label))); return null; case IFGT: stmts.add(Stmts.nIf(Exprs.nGt(value.value, Exprs.nInt(0), "I"), getLabel(((JumpInsnNode) insn).label))); return null; case IFLE: stmts.add(Stmts.nIf(Exprs.nLe(value.value, Exprs.nInt(0), "I"), getLabel(((JumpInsnNode) insn).label))); return null; case TABLESWITCH: { TableSwitchInsnNode ts = (TableSwitchInsnNode) insn; LabelStmt targets[] = new LabelStmt[ts.labels.size()]; for (int i = 0; i < ts.labels.size(); i++) { targets[i] = getLabel((LabelNode) ts.labels.get(i)); } stmts.add(Stmts.nTableSwitch(value.value, ts.min, targets, getLabel(ts.dflt))); return null; } case LOOKUPSWITCH: { LookupSwitchInsnNode ls = (LookupSwitchInsnNode) insn; LabelStmt targets[] = new LabelStmt[ls.labels.size()]; int[] lookupValues = new int[ls.labels.size()]; for (int i = 0; i < ls.labels.size(); i++) { targets[i] = getLabel((LabelNode) ls.labels.get(i)); lookupValues[i] = (Integer) ls.keys.get(i); } stmts.add(Stmts.nLookupSwitch(value.value, lookupValues, targets, getLabel(ls.dflt))); return null; } case IRETURN: case LRETURN: case FRETURN: case DRETURN: case ARETURN: stmts.add(Stmts.nReturn(value.value)); return null; case PUTSTATIC: { FieldInsnNode fin = (FieldInsnNode) insn; stmts.add(Stmts.nAssign(Exprs.nStaticField("L" + fin.owner + ";", fin.name, fin.desc), value.value)); return null; } case GETFIELD: { FieldInsnNode fin = (FieldInsnNode) insn; Type fieldType = Type.getType(fin.desc); return new XValue(fieldType.getSize(), Exprs.nField(value.value, "L" + fin.owner + ";", fin.name, fin.desc)); } case NEWARRAY: switch (((IntInsnNode) insn).operand) { case T_BOOLEAN: return new XValue(1, Exprs.nNewArray("Z", value.value)); case T_CHAR: return new XValue(1, Exprs.nNewArray("C", value.value)); case T_BYTE: return new XValue(1, Exprs.nNewArray("B", value.value)); case T_SHORT: return new XValue(1, Exprs.nNewArray("S", value.value)); case T_INT: return new XValue(1, Exprs.nNewArray("I", value.value)); case T_FLOAT: return new XValue(1, Exprs.nNewArray("F", value.value)); case T_DOUBLE: return new XValue(1, Exprs.nNewArray("D", value.value)); case T_LONG: return new XValue(1, Exprs.nNewArray("D", value.value)); default: throw new AnalyzerException(insn, "Invalid array type"); } case ANEWARRAY: String desc = "L" + ((TypeInsnNode) insn).desc + ";"; return new XValue(1, Exprs.nNewArray(desc, value.value)); case ARRAYLENGTH: return new XValue(1, Exprs.nLength(value.value)); case ATHROW: stmts.add(Stmts.nThrow(value.value)); return null; case CHECKCAST: desc = "L" + ((TypeInsnNode) insn).desc + ";"; return new XValue(1, Exprs.nCheckCast(value.value, desc)); case INSTANCEOF: return new XValue(1, Exprs.nInstanceOf(value.value, "L" + ((TypeInsnNode) insn).desc + ";")); case MONITORENTER: stmts.add(Stmts.nLock(value.value)); return null; case MONITOREXIT: stmts.add(Stmts.nUnLock(value.value)); return null; case IFNULL: stmts.add(Stmts.nIf(Exprs.nEq(value.value, Exprs.nNull(), "L"), getLabel(((JumpInsnNode) insn).label))); return null; case IFNONNULL: stmts.add(Stmts.nIf(Exprs.nNe(value.value, Exprs.nNull(), "L"), getLabel(((JumpInsnNode) insn).label))); return null; default: throw new Error("Internal error."); } } @Override public XValue binaryOperation(AbstractInsnNode insn, XValue value10, XValue value20) throws AnalyzerException { XValue value1 = (XValue) value10; XValue value2 = (XValue) value20; switch (insn.getOpcode()) { case IALOAD: return new XValue(1, Exprs.nArray(value1.value, value2.value, "I")); case BALOAD: return new XValue(1, Exprs.nArray(value1.value, value2.value, "B")); case CALOAD: return new XValue(1, Exprs.nArray(value1.value, value2.value, "C")); case SALOAD: return new XValue(1, Exprs.nArray(value1.value, value2.value, "S")); case FALOAD: return new XValue(1, Exprs.nArray(value1.value, value2.value, "F")); case AALOAD: return new XValue(1, Exprs.nArray(value1.value, value2.value, "L")); case DALOAD: return new XValue(1, Exprs.nArray(value1.value, value2.value, "D")); case LALOAD: return new XValue(1, Exprs.nArray(value1.value, value2.value, "J")); case IADD: return new XValue(1, Exprs.nAdd(value1.value, value2.value, "I")); case ISUB: return new XValue(1, Exprs.nSub(value1.value, value2.value, "I")); case IMUL: return new XValue(1, Exprs.nMul(value1.value, value2.value, "I")); case IDIV: return new XValue(1, Exprs.nDiv(value1.value, value2.value, "I")); case IREM: return new XValue(1, Exprs.nRem(value1.value, value2.value, "I")); case ISHL: return new XValue(1, Exprs.nShl(value1.value, value2.value, "I")); case ISHR: return new XValue(1, Exprs.nShr(value1.value, value2.value, "I")); case IUSHR: return new XValue(1, Exprs.nUshr(value1.value, value2.value, "I")); case IAND: return new XValue(1, Exprs.nAnd(value1.value, value2.value, "I")); case IOR: return new XValue(1, Exprs.nOr(value1.value, value2.value, "I")); case IXOR: return new XValue(1, Exprs.nXor(value1.value, value2.value, "I")); case FADD: return new XValue(1, Exprs.nAdd(value1.value, value2.value, "F")); case FSUB: return new XValue(1, Exprs.nSub(value1.value, value2.value, "F")); case FMUL: return new XValue(1, Exprs.nMul(value1.value, value2.value, "F")); case FDIV: return new XValue(1, Exprs.nDiv(value1.value, value2.value, "F")); case FREM: return new XValue(1, Exprs.nRem(value1.value, value2.value, "F")); case LADD: return new XValue(2, Exprs.nAdd(value1.value, value2.value, "J")); case LSUB: return new XValue(2, Exprs.nSub(value1.value, value2.value, "J")); case LMUL: return new XValue(2, Exprs.nMul(value1.value, value2.value, "J")); case LDIV: return new XValue(2, Exprs.nDiv(value1.value, value2.value, "J")); case LREM: return new XValue(2, Exprs.nRem(value1.value, value2.value, "J")); case LSHL: return new XValue(2, Exprs.nShl(value1.value, value2.value, "J")); case LSHR: return new XValue(2, Exprs.nShr(value1.value, value2.value, "J")); case LUSHR: return new XValue(2, Exprs.nUshr(value1.value, value2.value, "J")); case LAND: return new XValue(2, Exprs.nAnd(value1.value, value2.value, "J")); case LOR: return new XValue(2, Exprs.nOr(value1.value, value2.value, "J")); case LXOR: return new XValue(2, Exprs.nXor(value1.value, value2.value, "J")); case DADD: return new XValue(2, Exprs.nAdd(value1.value, value2.value, "D")); case DSUB: return new XValue(2, Exprs.nSub(value1.value, value2.value, "D")); case DMUL: return new XValue(2, Exprs.nMul(value1.value, value2.value, "D")); case DDIV: return new XValue(2, Exprs.nDiv(value1.value, value2.value, "D")); case DREM: return new XValue(2, Exprs.nRem(value1.value, value2.value, "D")); case LCMP: return new XValue(2, Exprs.nLCmp(value1.value, value2.value)); case FCMPL: return new XValue(1, Exprs.nFCmpl(value1.value, value2.value)); case FCMPG: return new XValue(1, Exprs.nFCmpg(value1.value, value2.value)); case DCMPL: return new XValue(2, Exprs.nDCmpl(value1.value, value2.value)); case DCMPG: return new XValue(2, Exprs.nDCmpg(value1.value, value2.value)); case IF_ICMPEQ: stmts.add(Stmts.nIf(Exprs.nEq(value1.value, value2.value, "I"), getLabel(((JumpInsnNode) insn).label))); return null; case IF_ICMPNE: stmts.add(Stmts.nIf(Exprs.nNe(value1.value, value2.value, "I"), getLabel(((JumpInsnNode) insn).label))); return null; case IF_ICMPLT: stmts.add(Stmts.nIf(Exprs.nLt(value1.value, value2.value, "I"), getLabel(((JumpInsnNode) insn).label))); return null; case IF_ICMPGE: stmts.add(Stmts.nIf(Exprs.nGe(value1.value, value2.value, "I"), getLabel(((JumpInsnNode) insn).label))); return null; case IF_ICMPGT: stmts.add(Stmts.nIf(Exprs.nGt(value1.value, value2.value, "I"), getLabel(((JumpInsnNode) insn).label))); return null; case IF_ICMPLE: stmts.add(Stmts.nIf(Exprs.nLe(value1.value, value2.value, "I"), getLabel(((JumpInsnNode) insn).label))); return null; case IF_ACMPEQ: stmts.add(Stmts.nIf(Exprs.nEq(value1.value, value2.value, "L"), getLabel(((JumpInsnNode) insn).label))); return null; case IF_ACMPNE: stmts.add(Stmts.nIf(Exprs.nNe(value1.value, value2.value, "L"), getLabel(((JumpInsnNode) insn).label))); return null; case PUTFIELD: FieldInsnNode fin = (FieldInsnNode) insn; stmts.add(Stmts.nAssign(Exprs.nField(value1.value, "L" + fin.owner + ";", fin.name, fin.desc), value2.value)); return null; default: throw new Error("Internal error."); } } @Override public XValue ternaryOperation(AbstractInsnNode insn, XValue value1, XValue value2, XValue value3) throws AnalyzerException { switch (insn.getOpcode()) { case IASTORE: stmts.add(Stmts.nAssign(Exprs.nArray(((XValue) value1).value, ((XValue) value2).value, "I"), ((XValue) value3).value)); break; case LASTORE: stmts.add(Stmts.nAssign(Exprs.nArray(((XValue) value1).value, ((XValue) value2).value, "J"), ((XValue) value3).value)); break; case FASTORE: stmts.add(Stmts.nAssign(Exprs.nArray(((XValue) value1).value, ((XValue) value2).value, "F"), ((XValue) value3).value)); break; case DASTORE: stmts.add(Stmts.nAssign(Exprs.nArray(((XValue) value1).value, ((XValue) value2).value, "D"), ((XValue) value3).value)); break; case AASTORE: stmts.add(Stmts.nAssign(Exprs.nArray(((XValue) value1).value, ((XValue) value2).value, "L"), ((XValue) value3).value)); break; case BASTORE: stmts.add(Stmts.nAssign(Exprs.nArray(((XValue) value1).value, ((XValue) value2).value, "B"), ((XValue) value3).value)); break; case CASTORE: stmts.add(Stmts.nAssign(Exprs.nArray(((XValue) value1).value, ((XValue) value2).value, "C"), ((XValue) value3).value)); break; case SASTORE: stmts.add(Stmts.nAssign(Exprs.nArray(((XValue) value1).value, ((XValue) value2).value, "S"), ((XValue) value3).value)); break; } return null; } @Override public XValue naryOperation(AbstractInsnNode insn, List xvalues) throws AnalyzerException { Value values[] = new Value[xvalues.size()]; for (int i = 0; i < xvalues.size(); i++) { values[i] = ((XValue) xvalues.get(i)).value; } if (insn.getOpcode() == MULTIANEWARRAY) { throw new UnsupportedOperationException("Not supported yet."); } else { MethodInsnNode mi = (MethodInsnNode) insn; Value v = null; String ret = Type.getReturnType(mi.desc).getDescriptor(); String owner = "L" + mi.owner + ";"; String ps[] = toDescArray(Type.getArgumentTypes(mi.desc)); switch (insn.getOpcode()) { case INVOKEVIRTUAL: v = Exprs.nInvokeVirtual(values, owner, mi.name, ps, ret); break; case INVOKESPECIAL: v = Exprs.nInvokeSpecial(values, owner, mi.name, ps, ret); break; case INVOKESTATIC: v = Exprs.nInvokeStatic(values, owner, mi.name, ps, ret); break; case INVOKEINTERFACE: v = Exprs.nInvokeInterface(values, owner, mi.name, ps, ret); break; case INVOKEDYNAMIC: throw new UnsupportedOperationException("Not supported yet."); } if ("V".equals(ret)) { stmts.add(Stmts.nVoidInvoke(v)); return null; } else { return new XValue(Type.getReturnType(mi.desc).getSize(), v); } } } @Override public XValue merge(XValue v, XValue w) { throw new UnsupportedOperationException("Not supported yet."); } @Override public void returnOperation(AbstractInsnNode insn, XValue value, XValue expected) throws AnalyzerException { stmts.add(Stmts.nReturn(((XValue) value).value)); } }; for (AbstractInsnNode p = source.instructions.getFirst(); p != null; p = p.getNext()) { if (p.getType() == AbstractInsnNode.LABEL) { stmts.add(getLabel((LabelNode) p)); } else if (p.getOpcode() == GOTO) { stmts.add(Stmts.nGoto(getLabel(((JumpInsnNode) p).label))); } else if (p.getOpcode() == RETURN) { stmts.add(Stmts.nReturnVoid()); } else { fx.init(frames[source.instructions.indexOf(p)]); fx.setReturn(null); // we don't need return value, cause ClassCastException if return not set to null try { fx.execute(p, interpreter); } catch (Exception ex) { throw new RuntimeException(ex); } } } return target; } private IrMethod populate(String owner, MethodNode source) { IrMethod target = new IrMethod(); target.name = source.name; target.owner = "L" + owner + ";"; target.ret = Type.getReturnType(source.desc).getDescriptor(); Type[] args = Type.getArgumentTypes(source.desc); String sArgs[] = new String[args.length]; target.args = sArgs; for (int i = 0; i < args.length; i++) { sArgs[i] = args[i].getDescriptor(); } target.isStatic = 0 != (source.access & Opcodes.ACC_STATIC); return target; } static class XValue implements org.objectweb.asm.tree.analysis.Value { public Value value; int size; public XValue(int size, Value value) { this.size = size; this.value = value; } @Override public int getSize() { return size; } } }