/* * Copyright 2004-2010 Brian S O'Neill * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.cojen.classfile; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Vector; import org.cojen.util.IntHashMap; import org.cojen.classfile.attribute.CodeAttr; import org.cojen.classfile.constant.ConstantClassInfo; import org.cojen.classfile.constant.ConstantDoubleInfo; import org.cojen.classfile.constant.ConstantFieldInfo; import org.cojen.classfile.constant.ConstantFloatInfo; import org.cojen.classfile.constant.ConstantIntegerInfo; import org.cojen.classfile.constant.ConstantInterfaceMethodInfo; import org.cojen.classfile.constant.ConstantLongInfo; import org.cojen.classfile.constant.ConstantMethodInfo; import org.cojen.classfile.constant.ConstantNameAndTypeInfo; import org.cojen.classfile.constant.ConstantStringInfo; /** * Disassembles a method into a CodeAssembler, which acts as a visitor. * * @author Brian S O'Neill */ public class CodeDisassembler { private final MethodInfo mMethod; private final String mEnclosingClassName; private final String mSuperClassName; private final CodeAttr mCode; private final ConstantPool mCp; private final byte[] mByteCodes; private final ExceptionHandler[] mExceptionHandlers; // Current CodeAssembler in use for disassembly. private CodeAssembler mAssembler; // List of all the LocalVariable objects in use. private Vector<Object> mLocals; // True if the method being decompiled still has a "this" reference. private boolean mHasThis; private Location mReturnLocation; // Maps int address keys to itself, but to Label objects after first // needed. private IntHashMap<Object> mLabels; // Maps int catch locations to Lists of ExceptionHandler objects. private IntHashMap<List<ExceptionHandler>> mCatchLocations; // Current address being decompiled. private int mAddress; /** * @throws IllegalArgumentException if method has no code */ public CodeDisassembler(MethodInfo method) throws IllegalArgumentException { mMethod = method; mEnclosingClassName = method.getClassFile().getClassName(); mSuperClassName = method.getClassFile().getSuperClassName(); if ((mCode = method.getCodeAttr()) == null) { throw new IllegalArgumentException("Method defines no code"); } mCp = mCode.getConstantPool(); CodeBuffer buffer = mCode.getCodeBuffer(); mByteCodes = buffer.getByteCodes(); mExceptionHandlers = buffer.getExceptionHandlers(); } /** * Disassemble the MethodInfo into the given assembler. * * @see CodeAssemblerPrinter */ public void disassemble(CodeAssembler assembler) { disassemble(assembler, null, null); } /** * Disassemble the MethodInfo into the given assembler. * * @param params if not null, override the local variables which hold parameter values * @param returnLocation if not null, disassemble will branch to this location upon seeing * a return, leaving any arguments on the stack * @see CodeAssemblerPrinter */ public synchronized void disassemble(CodeAssembler assembler, LocalVariable[] params, Location returnLocation) { mAssembler = assembler; mLocals = new Vector<Object>(); if (mHasThis = !mMethod.getModifiers().isStatic()) { // Reserve a slot for "this" parameter. mLocals.add(null); } gatherLabels(); // Gather the local variables of the parameters. { TypeDesc[] paramTypes = mMethod.getMethodDescriptor().getParameterTypes(); if (params == null) { params = new LocalVariable[assembler.getParameterCount()]; for (int i=params.length; --i>=0; ) { params[i] = assembler.getParameter(i); } } if (paramTypes.length != params.length) { throw new IllegalArgumentException ("Method parameter count doesn't match given parameter count: " + paramTypes.length + " != " + params.length); } for (int i=0; i<paramTypes.length; i++) { LocalVariable paramVar = params[i]; if (!compatibleType(paramTypes[i], paramVar.getType())) { throw new IllegalArgumentException ("Method parameter type is not compatible with given type: " + paramTypes[i] + " != " + paramVar.getType()); } mLocals.add(paramVar); if (paramVar.getType().isDoubleWord()) { // Reserve slot for least significant word. mLocals.add(null); } } } mReturnLocation = returnLocation; Location currentLoc = new Location() { public int getLocation() { return mAddress; } public int compareTo(Location other) { if (this == other) { return 0; } int loca = getLocation(); int locb = other.getLocation(); if (loca < locb) { return -1; } else if (loca > locb) { return 1; } else { return 0; } } }; int currentLine = -1; for (mAddress = 0; mAddress < mByteCodes.length; mAddress++) { int nextLine = mCode.getLineNumber(currentLoc); if (nextLine != currentLine) { if ((currentLine = nextLine) >= 0) { mAssembler.mapLineNumber(currentLine); } } // Check if a label needs to be created and/or located. locateLabel(); byte opcode = mByteCodes[mAddress]; int index; Location loc; TypeDesc type; ConstantInfo ci; switch (opcode) { default: error(opcode, "Unknown opcode: " + (opcode & 0xff)); break; // Opcodes with no operands... case Opcode.NOP: assembler.nop(); break; case Opcode.BREAKPOINT: assembler.breakpoint(); break; case Opcode.ACONST_NULL: assembler.loadNull(); break; case Opcode.ICONST_M1: assembler.loadConstant(-1); break; case Opcode.ICONST_0: assembler.loadConstant(0); break; case Opcode.ICONST_1: assembler.loadConstant(1); break; case Opcode.ICONST_2: assembler.loadConstant(2); break; case Opcode.ICONST_3: assembler.loadConstant(3); break; case Opcode.ICONST_4: assembler.loadConstant(4); break; case Opcode.ICONST_5: assembler.loadConstant(5); break; case Opcode.LCONST_0: assembler.loadConstant(0L); break; case Opcode.LCONST_1: assembler.loadConstant(1L); break; case Opcode.FCONST_0: assembler.loadConstant(0f); break; case Opcode.FCONST_1: assembler.loadConstant(1f); break; case Opcode.FCONST_2: assembler.loadConstant(2f); break; case Opcode.DCONST_0: assembler.loadConstant(0d); break; case Opcode.DCONST_1: assembler.loadConstant(1d); break; case Opcode.POP: assembler.pop(); break; case Opcode.POP2: assembler.pop2(); break; case Opcode.DUP: assembler.dup(); break; case Opcode.DUP_X1: assembler.dupX1(); break; case Opcode.DUP_X2: assembler.dupX2(); break; case Opcode.DUP2: assembler.dup2(); break; case Opcode.DUP2_X1: assembler.dup2X2(); break; case Opcode.DUP2_X2: assembler.dup2X2(); break; case Opcode.SWAP: assembler.swap(); break; case Opcode.IADD: case Opcode.LADD: case Opcode.FADD: case Opcode.DADD: case Opcode.ISUB: case Opcode.LSUB: case Opcode.FSUB: case Opcode.DSUB: case Opcode.IMUL: case Opcode.LMUL: case Opcode.FMUL: case Opcode.DMUL: case Opcode.IDIV: case Opcode.LDIV: case Opcode.FDIV: case Opcode.DDIV: case Opcode.IREM: case Opcode.LREM: case Opcode.FREM: case Opcode.DREM: case Opcode.INEG: case Opcode.LNEG: case Opcode.FNEG: case Opcode.DNEG: case Opcode.ISHL: case Opcode.LSHL: case Opcode.ISHR: case Opcode.LSHR: case Opcode.IUSHR: case Opcode.LUSHR: case Opcode.IAND: case Opcode.LAND: case Opcode.IOR: case Opcode.LOR: case Opcode.IXOR: case Opcode.LXOR: case Opcode.FCMPL: case Opcode.DCMPL: case Opcode.FCMPG: case Opcode.DCMPG: case Opcode.LCMP: assembler.math(opcode); break; case Opcode.I2L: assembler.convert(TypeDesc.INT, TypeDesc.LONG); break; case Opcode.I2F: assembler.convert(TypeDesc.INT, TypeDesc.FLOAT); break; case Opcode.I2D: assembler.convert(TypeDesc.INT, TypeDesc.DOUBLE); break; case Opcode.L2I: assembler.convert(TypeDesc.LONG, TypeDesc.INT); break; case Opcode.L2F: assembler.convert(TypeDesc.LONG, TypeDesc.FLOAT); break; case Opcode.L2D: assembler.convert(TypeDesc.LONG, TypeDesc.DOUBLE); break; case Opcode.F2I: assembler.convert(TypeDesc.FLOAT, TypeDesc.INT); break; case Opcode.F2L: assembler.convert(TypeDesc.FLOAT, TypeDesc.LONG); break; case Opcode.F2D: assembler.convert(TypeDesc.FLOAT, TypeDesc.DOUBLE); break; case Opcode.D2I: assembler.convert(TypeDesc.DOUBLE, TypeDesc.INT); break; case Opcode.D2L: assembler.convert(TypeDesc.DOUBLE, TypeDesc.LONG); break; case Opcode.D2F: assembler.convert(TypeDesc.DOUBLE, TypeDesc.FLOAT); break; case Opcode.I2B: assembler.convert(TypeDesc.INT, TypeDesc.BYTE); break; case Opcode.I2C: assembler.convert(TypeDesc.INT, TypeDesc.CHAR); break; case Opcode.I2S: assembler.convert(TypeDesc.INT, TypeDesc.SHORT); break; case Opcode.IRETURN: case Opcode.LRETURN: case Opcode.FRETURN: case Opcode.DRETURN: case Opcode.ARETURN: case Opcode.RETURN: if (mReturnLocation != null) { assembler.branch(mReturnLocation); } else { switch (opcode) { case Opcode.IRETURN: assembler.returnValue(TypeDesc.INT); break; case Opcode.LRETURN: assembler.returnValue(TypeDesc.LONG); break; case Opcode.FRETURN: assembler.returnValue(TypeDesc.FLOAT); break; case Opcode.DRETURN: assembler.returnValue(TypeDesc.DOUBLE); break; case Opcode.ARETURN: assembler.returnValue(TypeDesc.OBJECT); break; case Opcode.RETURN: assembler.returnVoid(); break; } } break; case Opcode.IALOAD: assembler.loadFromArray(TypeDesc.INT); break; case Opcode.LALOAD: assembler.loadFromArray(TypeDesc.LONG); break; case Opcode.FALOAD: assembler.loadFromArray(TypeDesc.FLOAT); break; case Opcode.DALOAD: assembler.loadFromArray(TypeDesc.DOUBLE); break; case Opcode.AALOAD: assembler.loadFromArray(TypeDesc.OBJECT); break; case Opcode.BALOAD: assembler.loadFromArray(TypeDesc.BYTE); break; case Opcode.CALOAD: assembler.loadFromArray(TypeDesc.CHAR); break; case Opcode.SALOAD: assembler.loadFromArray(TypeDesc.SHORT); break; case Opcode.IASTORE: assembler.storeToArray(TypeDesc.INT); break; case Opcode.LASTORE: assembler.storeToArray(TypeDesc.LONG); break; case Opcode.FASTORE: assembler.storeToArray(TypeDesc.FLOAT); break; case Opcode.DASTORE: assembler.storeToArray(TypeDesc.DOUBLE); break; case Opcode.AASTORE: assembler.storeToArray(TypeDesc.OBJECT); break; case Opcode.BASTORE: assembler.storeToArray(TypeDesc.BYTE); break; case Opcode.CASTORE: assembler.storeToArray(TypeDesc.CHAR); break; case Opcode.SASTORE: assembler.storeToArray(TypeDesc.SHORT); break; case Opcode.ARRAYLENGTH: assembler.arrayLength(); break; case Opcode.ATHROW: assembler.throwObject(); break; case Opcode.MONITORENTER: assembler.monitorEnter(); break; case Opcode.MONITOREXIT: assembler.monitorExit(); break; // End opcodes with no operands. // Opcodes that load a constant from the constant pool... case Opcode.LDC: case Opcode.LDC_W: case Opcode.LDC2_W: switch (opcode) { case Opcode.LDC: index = readUnsignedByte(); break; case Opcode.LDC_W: case Opcode.LDC2_W: index = readUnsignedShort(); break; default: index = 0; break; } try { ci = mCp.getConstant(index); } catch (IndexOutOfBoundsException e) { error(opcode, "Undefined constant at index: " + index); break; } if (ci instanceof ConstantStringInfo) { assembler.loadConstant(((ConstantStringInfo)ci).getValue()); } else if (ci instanceof ConstantIntegerInfo) { assembler.loadConstant(((ConstantIntegerInfo)ci).getValue()); } else if (ci instanceof ConstantLongInfo) { assembler.loadConstant(((ConstantLongInfo)ci).getValue()); } else if (ci instanceof ConstantFloatInfo) { assembler.loadConstant(((ConstantFloatInfo)ci).getValue()); } else if (ci instanceof ConstantDoubleInfo) { assembler.loadConstant(((ConstantDoubleInfo)ci).getValue()); } else if (ci instanceof ConstantClassInfo) { assembler.loadConstant(((ConstantClassInfo)ci).getType()); } else { error(opcode, "Invalid constant type for load: " + ci); } break; case Opcode.NEW: index = readUnsignedShort(); try { ci = mCp.getConstant(index); } catch (IndexOutOfBoundsException e) { error(opcode, "Undefined constant at index: " + index); break; } if (ci instanceof ConstantClassInfo) { assembler.newObject(((ConstantClassInfo)ci).getType()); } else { error(opcode, "Invalid constant type for new: " + ci); } break; case Opcode.ANEWARRAY: index = readUnsignedShort(); try { ci = mCp.getConstant(index); } catch (IndexOutOfBoundsException e) { error(opcode, "Undefined constant at index: " + index); break; } if (ci instanceof ConstantClassInfo) { type = ((ConstantClassInfo)ci).getType().toArrayType(); assembler.newObject(type); } else { error(opcode, "Invalid constant type for new: " + ci); } break; case Opcode.MULTIANEWARRAY: index = readUnsignedShort(); try { ci = mCp.getConstant(index); } catch (IndexOutOfBoundsException e) { error(opcode, "Undefined constant at index: " + index); break; } int dims = readUnsignedByte(); if (ci instanceof ConstantClassInfo) { type = ((ConstantClassInfo)ci).getType(); assembler.newObject(type, dims); } else { error(opcode, "Invalid constant type for new: " + ci); } break; case Opcode.CHECKCAST: index = readUnsignedShort(); try { ci = mCp.getConstant(index); } catch (IndexOutOfBoundsException e) { error(opcode, "Undefined constant at index: " + index); break; } if (ci instanceof ConstantClassInfo) { assembler.checkCast(((ConstantClassInfo)ci).getType()); } else { error(opcode, "Invalid constant type for checkcast: " + ci); } break; case Opcode.INSTANCEOF: index = readUnsignedShort(); try { ci = mCp.getConstant(index); } catch (IndexOutOfBoundsException e) { error(opcode, "Undefined constant at index: " + index); break; } if (ci instanceof ConstantClassInfo) { assembler.instanceOf(((ConstantClassInfo)ci).getType()); } else { error(opcode, "Invalid constant type for instanceof: " + ci); } break; case Opcode.GETSTATIC: case Opcode.PUTSTATIC: case Opcode.GETFIELD: case Opcode.PUTFIELD: index = readUnsignedShort(); try { ci = mCp.getConstant(index); } catch (IndexOutOfBoundsException e) { error(opcode, "Undefined constant at index: " + index); break; } if (!(ci instanceof ConstantFieldInfo)) { error(opcode, "Invalid constant type for field access: " + ci); break; } ConstantFieldInfo field = (ConstantFieldInfo)ci; String className = field.getParentClass().getType().getFullName(); if (mEnclosingClassName.equals(className)) { className = null; } String fieldName = field.getNameAndType().getName(); Descriptor desc = field.getNameAndType().getType(); if (!(desc instanceof TypeDesc)) { error(opcode, "Invalid descriptor for field access: " + desc); break; } else { type = (TypeDesc)desc; } // Implementation note: Although it may seem convenient if the // CodeAssembler had methods that accepted ConstantFieldInfo // objects as parameters, it would cause problems because // ConstantPools are not portable between ClassFiles. switch (opcode) { case Opcode.GETSTATIC: if (className == null) { assembler.loadStaticField(fieldName, type); } else { assembler.loadStaticField(className, fieldName, type); } break; case Opcode.PUTSTATIC: if (className == null) { assembler.storeStaticField(fieldName, type); } else { assembler.storeStaticField(className, fieldName, type); } break; case Opcode.GETFIELD: if (className == null) { assembler.loadField(fieldName, type); } else { assembler.loadField(className, fieldName, type); } break; case Opcode.PUTFIELD: if (className == null) { assembler.storeField(fieldName, type); } else { assembler.storeField(className, fieldName, type); } break; } break; case Opcode.INVOKEVIRTUAL: case Opcode.INVOKESPECIAL: case Opcode.INVOKESTATIC: case Opcode.INVOKEINTERFACE: case Opcode.INVOKEDYNAMIC: index = readUnsignedShort(); try { ci = mCp.getConstant(index); } catch (IndexOutOfBoundsException e) { error(opcode, "Undefined constant at index: " + index); break; } ConstantNameAndTypeInfo nameAndType; if (opcode == Opcode.INVOKEINTERFACE) { // Read and ignore nargs and padding byte. readShort(); if (!(ci instanceof ConstantInterfaceMethodInfo)) { error(opcode, "Invalid constant type for method invocation: " + ci); break; } ConstantInterfaceMethodInfo method = (ConstantInterfaceMethodInfo)ci; className = method.getParentClass().getType().getFullName(); nameAndType = method.getNameAndType(); } else if (opcode == Opcode.INVOKEDYNAMIC) { // Read and ignore extra bytes. readShort(); ConstantInterfaceMethodInfo method = (ConstantInterfaceMethodInfo)ci; className = method.getParentClass().getType().getFullName(); nameAndType = method.getNameAndType(); System.out.println(nameAndType); } else { if (!(ci instanceof ConstantMethodInfo)) { error(opcode, "Invalid constant type for method invocation: " + ci); break; } ConstantMethodInfo method = (ConstantMethodInfo)ci; className = method.getParentClass().getType().getFullName(); if (mEnclosingClassName.equals(className)) { className = null; } nameAndType = method.getNameAndType(); } String methodName = nameAndType.getName(); desc = nameAndType.getType(); if (!(desc instanceof MethodDesc)) { error(opcode, "Invalid descriptor for method invocation: " + desc); break; } TypeDesc ret = ((MethodDesc)desc).getReturnType(); if (ret == TypeDesc.VOID) { ret = null; } TypeDesc[] paramTypes = ((MethodDesc)desc).getParameterTypes(); if (paramTypes.length == 0) { paramTypes = null; } switch (opcode) { case Opcode.INVOKEVIRTUAL: if (className == null) { assembler.invokeVirtual(methodName, ret, paramTypes); } else { assembler.invokeVirtual(className, methodName, ret, paramTypes); } break; case Opcode.INVOKESPECIAL: if ("<init>".equals(methodName)) { if (className == null) { assembler.invokeConstructor(paramTypes); } else { if ("<init>".equals(mMethod.getName()) && className.equals(mSuperClassName)) { assembler.invokeSuperConstructor(paramTypes); } else { assembler.invokeConstructor(className, paramTypes); } } } else { if (className == null) { assembler.invokePrivate(methodName, ret, paramTypes); } else { assembler.invokeSuper(className, methodName, ret, paramTypes); } } break; case Opcode.INVOKESTATIC: if (className == null) { assembler.invokeStatic(methodName, ret, paramTypes); } else { assembler.invokeStatic(className, methodName, ret, paramTypes); } break; case Opcode.INVOKEINTERFACE: assembler.invokeInterface(className, methodName, ret, paramTypes); break; } break; // End opcodes that load a constant from the constant pool. // Opcodes that load or store local variables... case Opcode.ILOAD: case Opcode.ISTORE: case Opcode.LLOAD: case Opcode.LSTORE: case Opcode.FLOAD: case Opcode.FSTORE: case Opcode.DLOAD: case Opcode.DSTORE: case Opcode.ALOAD: case Opcode.ASTORE: case Opcode.ILOAD_0: case Opcode.ISTORE_0: case Opcode.ILOAD_1: case Opcode.ISTORE_1: case Opcode.ILOAD_2: case Opcode.ISTORE_2: case Opcode.ILOAD_3: case Opcode.ISTORE_3: case Opcode.LLOAD_0: case Opcode.LSTORE_0: case Opcode.LLOAD_1: case Opcode.LSTORE_1: case Opcode.LLOAD_2: case Opcode.LSTORE_2: case Opcode.LLOAD_3: case Opcode.LSTORE_3: case Opcode.FLOAD_0: case Opcode.FSTORE_0: case Opcode.FLOAD_1: case Opcode.FSTORE_1: case Opcode.FLOAD_2: case Opcode.FSTORE_2: case Opcode.FLOAD_3: case Opcode.FSTORE_3: case Opcode.DLOAD_0: case Opcode.DSTORE_0: case Opcode.DLOAD_1: case Opcode.DSTORE_1: case Opcode.DLOAD_2: case Opcode.DSTORE_2: case Opcode.DLOAD_3: case Opcode.DSTORE_3: case Opcode.ALOAD_0: case Opcode.ASTORE_0: case Opcode.ALOAD_1: case Opcode.ASTORE_1: case Opcode.ALOAD_2: case Opcode.ASTORE_2: case Opcode.ALOAD_3: case Opcode.ASTORE_3: switch (opcode) { case Opcode.ILOAD: case Opcode.ISTORE: index = readUnsignedByte(); type = TypeDesc.INT; break; case Opcode.LLOAD: case Opcode.LSTORE: index = readUnsignedByte(); type = TypeDesc.LONG; break; case Opcode.FLOAD: case Opcode.FSTORE: index = readUnsignedByte(); type = TypeDesc.FLOAT; break; case Opcode.DLOAD: case Opcode.DSTORE: index = readUnsignedByte(); type = TypeDesc.DOUBLE; break; case Opcode.ALOAD: case Opcode.ASTORE: index = readUnsignedByte(); type = TypeDesc.OBJECT; break; case Opcode.ILOAD_0: case Opcode.ISTORE_0: index = 0; type = TypeDesc.INT; break; case Opcode.ILOAD_1: case Opcode.ISTORE_1: index = 1; type = TypeDesc.INT; break; case Opcode.ILOAD_2: case Opcode.ISTORE_2: index = 2; type = TypeDesc.INT; break; case Opcode.ILOAD_3: case Opcode.ISTORE_3: index = 3; type = TypeDesc.INT; break; case Opcode.LLOAD_0: case Opcode.LSTORE_0: index = 0; type = TypeDesc.LONG; break; case Opcode.LLOAD_1: case Opcode.LSTORE_1: index = 1; type = TypeDesc.LONG; break; case Opcode.LLOAD_2: case Opcode.LSTORE_2: index = 2; type = TypeDesc.LONG; break; case Opcode.LLOAD_3: case Opcode.LSTORE_3: index = 3; type = TypeDesc.LONG; break; case Opcode.FLOAD_0: case Opcode.FSTORE_0: index = 0; type = TypeDesc.FLOAT; break; case Opcode.FLOAD_1: case Opcode.FSTORE_1: index = 1; type = TypeDesc.FLOAT; break; case Opcode.FLOAD_2: case Opcode.FSTORE_2: index = 2; type = TypeDesc.FLOAT; break; case Opcode.FLOAD_3: case Opcode.FSTORE_3: index = 3; type = TypeDesc.FLOAT; break; case Opcode.DLOAD_0: case Opcode.DSTORE_0: index = 0; type = TypeDesc.DOUBLE; break; case Opcode.DLOAD_1: case Opcode.DSTORE_1: index = 1; type = TypeDesc.DOUBLE; break; case Opcode.DLOAD_2: case Opcode.DSTORE_2: index = 2; type = TypeDesc.DOUBLE; break; case Opcode.DLOAD_3: case Opcode.DSTORE_3: index = 3; type = TypeDesc.DOUBLE; break; case Opcode.ALOAD_0: case Opcode.ASTORE_0: index = 0; type = TypeDesc.OBJECT; break; case Opcode.ALOAD_1: case Opcode.ASTORE_1: index = 1; type = TypeDesc.OBJECT; break; case Opcode.ALOAD_2: case Opcode.ASTORE_2: index = 2; type = TypeDesc.OBJECT; break; case Opcode.ALOAD_3: case Opcode.ASTORE_3: index = 3; type = TypeDesc.OBJECT; break; default: index = 0; type = null; break; } switch (opcode) { case Opcode.ILOAD: case Opcode.LLOAD: case Opcode.FLOAD: case Opcode.DLOAD: case Opcode.ALOAD: case Opcode.ILOAD_0: case Opcode.ILOAD_1: case Opcode.ILOAD_2: case Opcode.ILOAD_3: case Opcode.LLOAD_0: case Opcode.LLOAD_1: case Opcode.LLOAD_2: case Opcode.LLOAD_3: case Opcode.FLOAD_0: case Opcode.FLOAD_1: case Opcode.FLOAD_2: case Opcode.FLOAD_3: case Opcode.DLOAD_0: case Opcode.DLOAD_1: case Opcode.DLOAD_2: case Opcode.DLOAD_3: case Opcode.ALOAD_0: case Opcode.ALOAD_1: case Opcode.ALOAD_2: case Opcode.ALOAD_3: if (index == 0 && mHasThis) { assembler.loadThis(); } else { assembler.loadLocal(getLocalVariable(index, type)); } break; case Opcode.ISTORE: case Opcode.LSTORE: case Opcode.FSTORE: case Opcode.DSTORE: case Opcode.ASTORE: case Opcode.ISTORE_0: case Opcode.ISTORE_1: case Opcode.ISTORE_2: case Opcode.ISTORE_3: case Opcode.LSTORE_0: case Opcode.LSTORE_1: case Opcode.LSTORE_2: case Opcode.LSTORE_3: case Opcode.FSTORE_0: case Opcode.FSTORE_1: case Opcode.FSTORE_2: case Opcode.FSTORE_3: case Opcode.DSTORE_0: case Opcode.DSTORE_1: case Opcode.DSTORE_2: case Opcode.DSTORE_3: case Opcode.ASTORE_0: case Opcode.ASTORE_1: case Opcode.ASTORE_2: case Opcode.ASTORE_3: if (index == 0 && mHasThis) { // The "this" reference just got blown away. mHasThis = false; } assembler.storeLocal(getLocalVariable(index, type)); break; } break; case Opcode.RET: LocalVariable local = getLocalVariable (readUnsignedByte(), TypeDesc.OBJECT); assembler.ret(local); break; case Opcode.IINC: local = getLocalVariable(readUnsignedByte(), TypeDesc.INT); assembler.integerIncrement(local, readByte()); break; // End opcodes that load or store local variables. // Opcodes that branch to another address. case Opcode.GOTO: loc = getLabel(mAddress + readShort()); assembler.branch(loc); break; case Opcode.JSR: loc = getLabel(mAddress + readShort()); assembler.jsr(loc); break; case Opcode.GOTO_W: loc = getLabel(mAddress + readInt()); assembler.branch(loc); break; case Opcode.JSR_W: loc = getLabel(mAddress + readInt()); assembler.jsr(loc); break; case Opcode.IFNULL: loc = getLabel(mAddress + readShort()); assembler.ifNullBranch(loc, true); break; case Opcode.IFNONNULL: loc = getLabel(mAddress + readShort()); assembler.ifNullBranch(loc, false); break; case Opcode.IF_ACMPEQ: loc = getLabel(mAddress + readShort()); assembler.ifEqualBranch(loc, true); break; case Opcode.IF_ACMPNE: loc = getLabel(mAddress + readShort()); assembler.ifEqualBranch(loc, false); break; case Opcode.IFEQ: case Opcode.IFNE: case Opcode.IFLT: case Opcode.IFGE: case Opcode.IFGT: case Opcode.IFLE: loc = getLabel(mAddress + readShort()); String choice; switch (opcode) { case Opcode.IFEQ: choice = "=="; break; case Opcode.IFNE: choice = "!="; break; case Opcode.IFLT: choice = "<"; break; case Opcode.IFGE: choice = ">="; break; case Opcode.IFGT: choice = ">"; break; case Opcode.IFLE: choice = "<="; break; default: choice = null; break; } assembler.ifZeroComparisonBranch(loc, choice); break; case Opcode.IF_ICMPEQ: case Opcode.IF_ICMPNE: case Opcode.IF_ICMPLT: case Opcode.IF_ICMPGE: case Opcode.IF_ICMPGT: case Opcode.IF_ICMPLE: loc = getLabel(mAddress + readShort()); switch (opcode) { case Opcode.IF_ICMPEQ: choice = "=="; break; case Opcode.IF_ICMPNE: choice = "!="; break; case Opcode.IF_ICMPLT: choice = "<"; break; case Opcode.IF_ICMPGE: choice = ">="; break; case Opcode.IF_ICMPGT: choice = ">"; break; case Opcode.IF_ICMPLE: choice = "<="; break; default: choice = null; break; } assembler.ifComparisonBranch(loc, choice); break; // End opcodes that branch to another address. // Miscellaneous opcodes... case Opcode.BIPUSH: assembler.loadConstant(readByte()); break; case Opcode.SIPUSH: assembler.loadConstant(readShort()); break; case Opcode.NEWARRAY: int atype = readByte(); type = null; switch (atype) { case 4: // T_BOOLEAN type = TypeDesc.BOOLEAN; break; case 5: // T_CHAR type = TypeDesc.CHAR; break; case 6: // T_FLOAT type = TypeDesc.FLOAT; break; case 7: // T_DOUBLE type = TypeDesc.DOUBLE; break; case 8: // T_BYTE type = TypeDesc.BYTE; break; case 9: // T_SHORT type = TypeDesc.SHORT; break; case 10: // T_INT type = TypeDesc.INT; break; case 11: // T_LONG type = TypeDesc.LONG; break; } if (type == null) { error(opcode, "Unknown primitive type for new array: " + atype); break; } assembler.newObject(type.toArrayType()); break; case Opcode.TABLESWITCH: case Opcode.LOOKUPSWITCH: int opcodeAddress = mAddress; // Read padding until address is 32 bit word aligned. while (((mAddress + 1) & 3) != 0) { ++mAddress; } Location defaultLocation = getLabel(opcodeAddress + readInt()); int[] cases; Location[] locations; if (opcode == Opcode.TABLESWITCH) { int lowValue = readInt(); int highValue = readInt(); int caseCount = highValue - lowValue + 1; try { cases = new int[caseCount]; } catch (NegativeArraySizeException e) { error(opcode, "Negative case count for switch: " + caseCount); break; } locations = new Location[caseCount]; for (int i=0; i<caseCount; i++) { cases[i] = lowValue + i; locations[i] = getLabel(opcodeAddress + readInt()); } } else { int caseCount = readInt(); try { cases = new int[caseCount]; } catch (NegativeArraySizeException e) { error(opcode, "Negative case count for switch: " + caseCount); break; } locations = new Location[caseCount]; for (int i=0; i<caseCount; i++) { cases[i] = readInt(); locations[i] = getLabel(opcodeAddress + readInt()); } } assembler.switchBranch(cases, locations, defaultLocation); break; case Opcode.WIDE: opcode = mByteCodes[++mAddress]; switch (opcode) { default: error(opcode, "Unknown wide instruction"); break; case Opcode.ILOAD: case Opcode.ISTORE: case Opcode.LLOAD: case Opcode.LSTORE: case Opcode.FLOAD: case Opcode.FSTORE: case Opcode.DLOAD: case Opcode.DSTORE: case Opcode.ALOAD: case Opcode.ASTORE: switch (opcode) { case Opcode.ILOAD: case Opcode.ISTORE: type = TypeDesc.INT; break; case Opcode.LLOAD: case Opcode.LSTORE: type = TypeDesc.LONG; break; case Opcode.FLOAD: case Opcode.FSTORE: type = TypeDesc.FLOAT; break; case Opcode.DLOAD: case Opcode.DSTORE: type = TypeDesc.DOUBLE; break; case Opcode.ALOAD: case Opcode.ASTORE: type = TypeDesc.OBJECT; break; default: type = null; break; } index = readUnsignedShort(); switch (opcode) { case Opcode.ILOAD: case Opcode.LLOAD: case Opcode.FLOAD: case Opcode.DLOAD: case Opcode.ALOAD: if (index == 0 && mHasThis) { assembler.loadThis(); } else { assembler.loadLocal(getLocalVariable(index, type)); } break; case Opcode.ISTORE: case Opcode.LSTORE: case Opcode.FSTORE: case Opcode.DSTORE: case Opcode.ASTORE: if (index == 0 && mHasThis) { // The "this" reference just got blown away. mHasThis = false; } assembler.storeLocal(getLocalVariable(index, type)); break; } break; case Opcode.RET: local = getLocalVariable (readUnsignedShort(), TypeDesc.OBJECT); assembler.ret(local); break; case Opcode.IINC: local = getLocalVariable (readUnsignedShort(), TypeDesc.INT); assembler.integerIncrement(local, readShort()); break; } break; } // end huge switch } // end for loop } /** * Invoked on disassembly errors. By default, this method does nothing. */ protected void error(byte opcode, String message) { } private void gatherLabels() { mLabels = new IntHashMap<Object>(); mCatchLocations = new IntHashMap<List<ExceptionHandler>> (mExceptionHandlers.length * 2 + 1); int labelKey; // Gather labels for any exception handlers. for (int i = mExceptionHandlers.length - 1; i >= 0; i--) { ExceptionHandler handler = mExceptionHandlers[i]; labelKey = handler.getStartLocation().getLocation(); mLabels.put(labelKey, (Object) labelKey); labelKey = handler.getEndLocation().getLocation(); mLabels.put(labelKey, (Object) labelKey); labelKey = handler.getCatchLocation().getLocation(); List<ExceptionHandler> list = mCatchLocations.get(labelKey); if (list == null) { list = new ArrayList<ExceptionHandler>(2); mCatchLocations.put(labelKey, list); } list.add(handler); } // Now gather labels that occur within byte code. for (mAddress = 0; mAddress < mByteCodes.length; mAddress++) { byte opcode = mByteCodes[mAddress]; switch (opcode) { default: error(opcode, "Unknown opcode: " + (opcode & 0xff)); break; // Opcodes that use labels. case Opcode.GOTO: case Opcode.JSR: case Opcode.IFNULL: case Opcode.IFNONNULL: case Opcode.IF_ACMPEQ: case Opcode.IF_ACMPNE: case Opcode.IFEQ: case Opcode.IFNE: case Opcode.IFLT: case Opcode.IFGE: case Opcode.IFGT: case Opcode.IFLE: case Opcode.IF_ICMPEQ: case Opcode.IF_ICMPNE: case Opcode.IF_ICMPLT: case Opcode.IF_ICMPGE: case Opcode.IF_ICMPGT: case Opcode.IF_ICMPLE: labelKey = mAddress + readShort(); mLabels.put(labelKey, (Object) labelKey); break; case Opcode.GOTO_W: case Opcode.JSR_W: labelKey = mAddress + readInt(); mLabels.put(labelKey, (Object) labelKey); break; case Opcode.TABLESWITCH: case Opcode.LOOKUPSWITCH: int opcodeAddress = mAddress; // Read padding until address is 32 bit word aligned. while (((mAddress + 1) & 3) != 0) { ++mAddress; } // Read the default location. labelKey = opcodeAddress + readInt(); mLabels.put(labelKey, (Object) labelKey); if (opcode == Opcode.TABLESWITCH) { int lowValue = readInt(); int highValue = readInt(); int caseCount = highValue - lowValue + 1; for (int i=0; i<caseCount; i++) { // Read the branch location. labelKey = opcodeAddress + readInt(); mLabels.put(labelKey, (Object) labelKey); } } else { int caseCount = readInt(); for (int i=0; i<caseCount; i++) { // Skip the case value. mAddress += 4; // Read the branch location. labelKey = opcodeAddress + readInt(); mLabels.put(labelKey, (Object) labelKey); } } break; // All other operations are skipped. The amount to skip // depends on the operand size. // Opcodes with no operands... case Opcode.NOP: case Opcode.BREAKPOINT: case Opcode.ACONST_NULL: case Opcode.ICONST_M1: case Opcode.ICONST_0: case Opcode.ICONST_1: case Opcode.ICONST_2: case Opcode.ICONST_3: case Opcode.ICONST_4: case Opcode.ICONST_5: case Opcode.LCONST_0: case Opcode.LCONST_1: case Opcode.FCONST_0: case Opcode.FCONST_1: case Opcode.FCONST_2: case Opcode.DCONST_0: case Opcode.DCONST_1: case Opcode.POP: case Opcode.POP2: case Opcode.DUP: case Opcode.DUP_X1: case Opcode.DUP_X2: case Opcode.DUP2: case Opcode.DUP2_X1: case Opcode.DUP2_X2: case Opcode.SWAP: case Opcode.IADD: case Opcode.LADD: case Opcode.FADD: case Opcode.DADD: case Opcode.ISUB: case Opcode.LSUB: case Opcode.FSUB: case Opcode.DSUB: case Opcode.IMUL: case Opcode.LMUL: case Opcode.FMUL: case Opcode.DMUL: case Opcode.IDIV: case Opcode.LDIV: case Opcode.FDIV: case Opcode.DDIV: case Opcode.IREM: case Opcode.LREM: case Opcode.FREM: case Opcode.DREM: case Opcode.INEG: case Opcode.LNEG: case Opcode.FNEG: case Opcode.DNEG: case Opcode.ISHL: case Opcode.LSHL: case Opcode.ISHR: case Opcode.LSHR: case Opcode.IUSHR: case Opcode.LUSHR: case Opcode.IAND: case Opcode.LAND: case Opcode.IOR: case Opcode.LOR: case Opcode.IXOR: case Opcode.LXOR: case Opcode.FCMPL: case Opcode.DCMPL: case Opcode.FCMPG: case Opcode.DCMPG: case Opcode.LCMP: case Opcode.I2L: case Opcode.I2F: case Opcode.I2D: case Opcode.L2I: case Opcode.L2F: case Opcode.L2D: case Opcode.F2I: case Opcode.F2L: case Opcode.F2D: case Opcode.D2I: case Opcode.D2L: case Opcode.D2F: case Opcode.I2B: case Opcode.I2C: case Opcode.I2S: case Opcode.IRETURN: case Opcode.LRETURN: case Opcode.FRETURN: case Opcode.DRETURN: case Opcode.ARETURN: case Opcode.RETURN: case Opcode.IALOAD: case Opcode.LALOAD: case Opcode.FALOAD: case Opcode.DALOAD: case Opcode.AALOAD: case Opcode.BALOAD: case Opcode.CALOAD: case Opcode.SALOAD: case Opcode.IASTORE: case Opcode.LASTORE: case Opcode.FASTORE: case Opcode.DASTORE: case Opcode.AASTORE: case Opcode.BASTORE: case Opcode.CASTORE: case Opcode.SASTORE: case Opcode.ARRAYLENGTH: case Opcode.ATHROW: case Opcode.MONITORENTER: case Opcode.MONITOREXIT: case Opcode.ILOAD_0: case Opcode.ISTORE_0: case Opcode.ILOAD_1: case Opcode.ISTORE_1: case Opcode.ILOAD_2: case Opcode.ISTORE_2: case Opcode.ILOAD_3: case Opcode.ISTORE_3: case Opcode.LLOAD_0: case Opcode.LSTORE_0: case Opcode.LLOAD_1: case Opcode.LSTORE_1: case Opcode.LLOAD_2: case Opcode.LSTORE_2: case Opcode.LLOAD_3: case Opcode.LSTORE_3: case Opcode.FLOAD_0: case Opcode.FSTORE_0: case Opcode.FLOAD_1: case Opcode.FSTORE_1: case Opcode.FLOAD_2: case Opcode.FSTORE_2: case Opcode.FLOAD_3: case Opcode.FSTORE_3: case Opcode.DLOAD_0: case Opcode.DSTORE_0: case Opcode.DLOAD_1: case Opcode.DSTORE_1: case Opcode.DLOAD_2: case Opcode.DSTORE_2: case Opcode.DLOAD_3: case Opcode.DSTORE_3: case Opcode.ALOAD_0: case Opcode.ASTORE_0: case Opcode.ALOAD_1: case Opcode.ASTORE_1: case Opcode.ALOAD_2: case Opcode.ASTORE_2: case Opcode.ALOAD_3: case Opcode.ASTORE_3: break; // Opcodes with one operand byte... case Opcode.LDC: case Opcode.ILOAD: case Opcode.ISTORE: case Opcode.LLOAD: case Opcode.LSTORE: case Opcode.FLOAD: case Opcode.FSTORE: case Opcode.DLOAD: case Opcode.DSTORE: case Opcode.ALOAD: case Opcode.ASTORE: case Opcode.RET: case Opcode.BIPUSH: case Opcode.NEWARRAY: mAddress += 1; break; // Opcodes with two operand bytes... case Opcode.LDC_W: case Opcode.LDC2_W: case Opcode.NEW: case Opcode.ANEWARRAY: case Opcode.CHECKCAST: case Opcode.INSTANCEOF: case Opcode.GETSTATIC: case Opcode.PUTSTATIC: case Opcode.GETFIELD: case Opcode.PUTFIELD: case Opcode.INVOKEVIRTUAL: case Opcode.INVOKESPECIAL: case Opcode.INVOKESTATIC: case Opcode.SIPUSH: case Opcode.IINC: mAddress += 2; break; // Opcodes with three operand bytes... case Opcode.MULTIANEWARRAY: mAddress += 3; break; // Opcodes with four operand bytes... case Opcode.INVOKEINTERFACE: case Opcode.INVOKEDYNAMIC: mAddress += 4; break; // Wide opcode has a variable sized operand. case Opcode.WIDE: opcode = mByteCodes[++mAddress]; mAddress += 2; if (opcode == Opcode.IINC) { mAddress += 2; } break; } // end huge switch } // end for loop } private int readByte() { return mByteCodes[++mAddress]; } private int readUnsignedByte() { return mByteCodes[++mAddress] & 0xff; } private int readShort() { return (mByteCodes[++mAddress] << 8) | (mByteCodes[++mAddress] & 0xff); } private int readUnsignedShort() { return ((mByteCodes[++mAddress] & 0xff) << 8) | ((mByteCodes[++mAddress] & 0xff) << 0); } private int readInt() { return (mByteCodes[++mAddress] << 24) | ((mByteCodes[++mAddress] & 0xff) << 16) | ((mByteCodes[++mAddress] & 0xff) << 8) | ((mByteCodes[++mAddress] & 0xff) << 0); } private LocalVariable getLocalVariable(final int index, final TypeDesc type) { LocalVariable local; if (index >= mLocals.size()) { mLocals.setSize(index + 1); local = mAssembler.createLocalVariable(null, type); mLocals.set(index, local); return local; } Object obj = mLocals.get(index); if (obj == null) { local = mAssembler.createLocalVariable(null, type); mLocals.set(index, local); return local; } if (obj instanceof LocalVariable) { local = (LocalVariable)obj; if (compatibleType(type, local.getType())) { return local; } // Variable takes on multiple types, so convert entry to a list. List<LocalVariable> locals = new ArrayList<LocalVariable>(4); locals.add(local); local = mAssembler.createLocalVariable(null, type); locals.add(local); mLocals.set(index, locals); return local; } List<LocalVariable> locals = (List<LocalVariable>)obj; for (int i=locals.size(); --i>=0; ) { local = locals.get(i); if (compatibleType(type, local.getType())) { return local; } } local = mAssembler.createLocalVariable(null, type); locals.add(local); return local; } private boolean compatibleType(TypeDesc a, TypeDesc b) { if (a == b || (!a.isPrimitive() && !b.isPrimitive())) { return true; } if (isIntType(a) && isIntType(b)) { return true; } return false; } private boolean isIntType(TypeDesc type) { switch (type.getTypeCode()) { case TypeDesc.INT_CODE: case TypeDesc.BOOLEAN_CODE: case TypeDesc.BYTE_CODE: case TypeDesc.SHORT_CODE: case TypeDesc.CHAR_CODE: return true; } return false; } private void locateLabel() { int labelKey = mAddress; Object labelValue = mLabels.get(labelKey); if (labelValue != null) { if (labelValue instanceof Label) { ((Label)labelValue).setLocation(); } else { labelValue = mAssembler.createLabel().setLocation(); mLabels.put(labelKey, labelValue); } } List<ExceptionHandler> handlers = mCatchLocations.get(labelKey); if (handlers != null) { for (int i=0; i<handlers.size(); i++) { ExceptionHandler handler = handlers.get(i); Label start = getLabel(handler.getStartLocation().getLocation()); Label end = getLabel(handler.getEndLocation().getLocation()); String catchClassName; if (handler.getCatchType() == null) { catchClassName = null; } else { catchClassName = handler.getCatchType().getType().getFullName(); } mAssembler.exceptionHandler(start, end, catchClassName); } } } private Label getLabel(int address) { int labelKey = address; Object labelValue = mLabels.get(labelKey); // labelValue will never be null unless gatherLabels is broken. if (!(labelValue instanceof Label)) { labelValue = mAssembler.createLabel(); mLabels.put(labelKey, labelValue); } return (Label)labelValue; } }