Mercurial > hg > blitz_condensed
view src/com/go/trove/classfile/CodeDisassembler.java @ 0:3dc0c5604566
Initial checkin of blitz 2.0 fcs - no installer yet.
author | Dan Creswell <dan.creswell@gmail.com> |
---|---|
date | Sat, 21 Mar 2009 11:00:06 +0000 |
parents | |
children |
line wrap: on
line source
/* ==================================================================== * Trove - Copyright (c) 1997-2000 Walt Disney Internet Group * ==================================================================== * The Tea Software License, Version 1.1 * * Copyright (c) 2000 Walt Disney Internet Group. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, * if any, must include the following acknowledgment: * "This product includes software developed by the * Walt Disney Internet Group (http://opensource.go.com/)." * Alternately, this acknowledgment may appear in the software itself, * if and wherever such third-party acknowledgments normally appear. * * 4. The names "Tea", "TeaServlet", "Kettle", "Trove" and "BeanDoc" must * not be used to endorse or promote products derived from this * software without prior written permission. For written * permission, please contact opensource@dig.com. * * 5. Products derived from this software may not be called "Tea", * "TeaServlet", "Kettle" or "Trove", nor may "Tea", "TeaServlet", * "Kettle", "Trove" or "BeanDoc" appear in their name, without prior * written permission of the Walt Disney Internet Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE WALT DISNEY INTERNET GROUP OR ITS * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ==================================================================== * * For more information about Tea, please see http://opensource.go.com/. */ package com.go.trove.classfile; import java.util.*; /****************************************************************************** * * @author Brian S O'Neill * @version * <!--$$Revision: 1.1 $-->, <!--$$JustDate:--> 00/12/27 <!-- $--> */ 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 mLocals; // True if the method being decompiled still has a "this" reference. private boolean mHasThis; // Maps Integer address keys to itself, but to Label objects after first // needed. private Map mLabels; // Maps Integer catch locations to Lists of ExceptionHandler objects. private Map mCatchLocations; // Current address being decompiled. private int mAddress; public CodeDisassembler(MethodInfo method) { mMethod = method; mEnclosingClassName = method.getClassFile().getClassName(); mSuperClassName = method.getClassFile().getSuperClassName(); mCode = method.getCodeAttr(); mCp = mCode.getConstantPool(); CodeBuffer buffer = mCode.getCodeBuffer(); mByteCodes = buffer.getByteCodes(); mExceptionHandlers = buffer.getExceptionHandlers(); } /** * Disassemble the MethodInfo into the given assembler. * * @see CodeAssemblerPrinter */ public synchronized void disassemble(CodeAssembler assembler) { mAssembler = assembler; mLocals = new Vector(); mHasThis = !mMethod.getAccessFlags().isStatic(); gatherLabels(); // Gather the local variables of the parameters. LocalVariable[] paramVars = assembler.getParameters(); for (int i=0; i<paramVars.length; i++) { LocalVariable paramVar = paramVars[i]; int number = paramVar.getNumber(); if (number >= mLocals.size()) { mLocals.setSize(number + 1); } mLocals.setElementAt(paramVar, number); } Location currentLoc = new Location() { public int getLocation() { return mAddress; } public int compareTo(Object obj) { if (this == obj) { return 0; } Location other = (Location)obj; 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; Class clazz; ConstantInfo ci; switch (opcode) { default: // TODO: raise an error. break; // Opcodes with no operands... case Opcode.NOP: assembler.nop(); break; case Opcode.BREAKPOINT: assembler.breakpoint(); break; case Opcode.ACONST_NULL: assembler.loadConstant(null); 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(int.class, long.class); break; case Opcode.I2F: assembler.convert(int.class, float.class); break; case Opcode.I2D: assembler.convert(int.class, double.class); break; case Opcode.L2I: assembler.convert(long.class, int.class); break; case Opcode.L2F: assembler.convert(long.class, float.class); break; case Opcode.L2D: assembler.convert(long.class, double.class); break; case Opcode.F2I: assembler.convert(float.class, int.class); break; case Opcode.F2L: assembler.convert(float.class, long.class); break; case Opcode.F2D: assembler.convert(float.class, double.class); break; case Opcode.D2I: assembler.convert(double.class, int.class); break; case Opcode.D2L: assembler.convert(double.class, long.class); break; case Opcode.D2F: assembler.convert(double.class, float.class); break; case Opcode.I2B: assembler.convert(int.class, byte.class); break; case Opcode.I2C: assembler.convert(int.class, char.class); break; case Opcode.I2S: assembler.convert(int.class, short.class); break; case Opcode.IRETURN: assembler.returnValue(int.class); break; case Opcode.LRETURN: assembler.returnValue(long.class); break; case Opcode.FRETURN: assembler.returnValue(float.class); break; case Opcode.DRETURN: assembler.returnValue(double.class); break; case Opcode.ARETURN: assembler.returnValue(Object.class); break; case Opcode.RETURN: assembler.returnVoid(); break; case Opcode.IALOAD: assembler.loadFromArray(int.class); break; case Opcode.LALOAD: assembler.loadFromArray(long.class); break; case Opcode.FALOAD: assembler.loadFromArray(float.class); break; case Opcode.DALOAD: assembler.loadFromArray(double.class); break; case Opcode.AALOAD: assembler.loadFromArray(Object.class); break; case Opcode.BALOAD: assembler.loadFromArray(byte.class); break; case Opcode.CALOAD: assembler.loadFromArray(char.class); break; case Opcode.SALOAD: assembler.loadFromArray(short.class); break; case Opcode.IASTORE: assembler.storeToArray(int.class); break; case Opcode.LASTORE: assembler.storeToArray(long.class); break; case Opcode.FASTORE: assembler.storeToArray(float.class); break; case Opcode.DASTORE: assembler.storeToArray(double.class); break; case Opcode.AASTORE: assembler.storeToArray(Object.class); break; case Opcode.BASTORE: assembler.storeToArray(byte.class); break; case Opcode.CASTORE: assembler.storeToArray(char.class); break; case Opcode.SASTORE: assembler.storeToArray(short.class); 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; } ci = mCp.getConstant(index); if (ci instanceof ConstantStringInfo) { assembler.loadConstant (((ConstantStringInfo)ci).getValue()); } else if (ci instanceof ConstantIntegerInfo) { assembler.loadConstant (((ConstantIntegerInfo)ci).getValue().intValue()); } else if (ci instanceof ConstantLongInfo) { assembler.loadConstant (((ConstantLongInfo)ci).getValue().longValue()); } else if (ci instanceof ConstantFloatInfo) { assembler.loadConstant (((ConstantFloatInfo)ci).getValue().floatValue()); } else if (ci instanceof ConstantDoubleInfo) { assembler.loadConstant (((ConstantDoubleInfo)ci).getValue().doubleValue()); } else { // TODO: raise an error. } break; case Opcode.NEW: ci = mCp.getConstant(readUnsignedShort()); if (ci instanceof ConstantClassInfo) { assembler.newObject (((ConstantClassInfo)ci).getTypeDescriptor()); } else { // TODO: raise an error. } break; case Opcode.ANEWARRAY: ci = mCp.getConstant(readUnsignedShort()); if (ci instanceof ConstantClassInfo) { TypeDescriptor type = ((ConstantClassInfo)ci).getTypeDescriptor(); type = new TypeDescriptor(type, 1); assembler.newObject(type); } else { // TODO: raise an error. } break; case Opcode.MULTIANEWARRAY: ci = mCp.getConstant(readUnsignedShort()); int dims = readUnsignedByte(); if (ci instanceof ConstantClassInfo) { assembler.newObject (((ConstantClassInfo)ci).getTypeDescriptor()); } else { // TODO: raise an error. } break; case Opcode.CHECKCAST: ci = mCp.getConstant(readUnsignedShort()); if (ci instanceof ConstantClassInfo) { assembler.checkCast (((ConstantClassInfo)ci).getTypeDescriptor()); } else { // TODO: raise an error. } break; case Opcode.INSTANCEOF: ci = mCp.getConstant(readUnsignedShort()); if (ci instanceof ConstantClassInfo) { assembler.instanceOf (((ConstantClassInfo)ci).getTypeDescriptor()); } else { // TODO: raise an error. } break; case Opcode.GETSTATIC: case Opcode.PUTSTATIC: case Opcode.GETFIELD: case Opcode.PUTFIELD: ci = mCp.getConstant(readUnsignedShort()); if (!(ci instanceof ConstantFieldInfo)) { // TODO: raise an error. break; } ConstantFieldInfo field = (ConstantFieldInfo)ci; String className = field.getParentClass().getClassName(); if (mEnclosingClassName.equals(className)) { className = null; } String fieldName = field.getNameAndType().getName(); Descriptor type = field.getNameAndType().getType(); if (!(type instanceof TypeDescriptor)) { // TODO: raise an error. break; } // 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, (TypeDescriptor)type); } else { assembler.loadStaticField (className, fieldName, (TypeDescriptor)type); } break; case Opcode.PUTSTATIC: if (className == null) { assembler.storeStaticField (fieldName, (TypeDescriptor)type); } else { assembler.storeStaticField (className, fieldName, (TypeDescriptor)type); } break; case Opcode.GETFIELD: if (className == null) { assembler.loadField (fieldName, (TypeDescriptor)type); } else { assembler.loadField (className, fieldName, (TypeDescriptor)type); } break; case Opcode.PUTFIELD: if (className == null) { assembler.storeField (fieldName, (TypeDescriptor)type); } else { assembler.storeField (className, fieldName, (TypeDescriptor)type); } break; } break; case Opcode.INVOKEVIRTUAL: case Opcode.INVOKESPECIAL: case Opcode.INVOKESTATIC: case Opcode.INVOKEINTERFACE: ci = mCp.getConstant(readUnsignedShort()); ConstantNameAndTypeInfo nameAndType; if (opcode == Opcode.INVOKEINTERFACE) { // Read and ignore nargs and padding byte. readShort(); if (!(ci instanceof ConstantInterfaceMethodInfo)) { // TODO: raise an error. break; } ConstantInterfaceMethodInfo method = (ConstantInterfaceMethodInfo)ci; className = method.getParentClass().getClassName(); nameAndType = method.getNameAndType(); } else { if (!(ci instanceof ConstantMethodInfo)) { // TODO: raise an error. break; } ConstantMethodInfo method = (ConstantMethodInfo)ci; className = method.getParentClass().getClassName(); if (mEnclosingClassName.equals(className)) { className = null; } nameAndType = method.getNameAndType(); } String methodName = nameAndType.getName(); type = nameAndType.getType(); if (!(type instanceof MethodDescriptor)) { // TODO: raise an error. break; } TypeDescriptor ret = ((MethodDescriptor)type).getReturnType(); if (ret.getClassArg() == void.class) { ret = null; } TypeDescriptor[] params = ((MethodDescriptor)type).getParameterTypes(); if (params.length == 0) { params = null; } switch (opcode) { case Opcode.INVOKEVIRTUAL: if (className == null) { assembler.invokeVirtual(methodName, ret, params); } else { assembler.invokeVirtual (className, methodName, ret, params); } break; case Opcode.INVOKESPECIAL: if ("<init>".equals(methodName)) { if (className == null) { assembler.invokeConstructor(params); } else { if (className.equals(mSuperClassName)) { assembler.invokeSuperConstructor(params); } else { assembler.invokeConstructor(className, params); } } } else { if (className == null) { assembler.invokePrivate(methodName, ret, params); } else { assembler.invokeSuper (className, methodName, ret, params); } } break; case Opcode.INVOKESTATIC: if (className == null) { assembler.invokeStatic(methodName, ret, params); } else { assembler.invokeStatic (className, methodName, ret, params); } break; case Opcode.INVOKEINTERFACE: assembler.invokeInterface (className, methodName, ret, params); 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(); clazz = int.class; break; case Opcode.LLOAD: case Opcode.LSTORE: index = readUnsignedByte(); clazz = long.class; break; case Opcode.FLOAD: case Opcode.FSTORE: index = readUnsignedByte(); clazz = float.class; break; case Opcode.DLOAD: case Opcode.DSTORE: index = readUnsignedByte(); clazz = double.class; break; case Opcode.ALOAD: case Opcode.ASTORE: index = readUnsignedByte(); clazz = Object.class; break; case Opcode.ILOAD_0: case Opcode.ISTORE_0: index = 0; clazz = int.class; break; case Opcode.ILOAD_1: case Opcode.ISTORE_1: index = 1; clazz = int.class; break; case Opcode.ILOAD_2: case Opcode.ISTORE_2: index = 2; clazz = int.class; break; case Opcode.ILOAD_3: case Opcode.ISTORE_3: index = 3; clazz = int.class; break; case Opcode.LLOAD_0: case Opcode.LSTORE_0: index = 0; clazz = long.class; break; case Opcode.LLOAD_1: case Opcode.LSTORE_1: index = 1; clazz = long.class; break; case Opcode.LLOAD_2: case Opcode.LSTORE_2: index = 2; clazz = long.class; break; case Opcode.LLOAD_3: case Opcode.LSTORE_3: index = 3; clazz = long.class; break; case Opcode.FLOAD_0: case Opcode.FSTORE_0: index = 0; clazz = float.class; break; case Opcode.FLOAD_1: case Opcode.FSTORE_1: index = 1; clazz = float.class; break; case Opcode.FLOAD_2: case Opcode.FSTORE_2: index = 2; clazz = float.class; break; case Opcode.FLOAD_3: case Opcode.FSTORE_3: index = 3; clazz = float.class; break; case Opcode.DLOAD_0: case Opcode.DSTORE_0: index = 0; clazz = double.class; break; case Opcode.DLOAD_1: case Opcode.DSTORE_1: index = 1; clazz = double.class; break; case Opcode.DLOAD_2: case Opcode.DSTORE_2: index = 2; clazz = double.class; break; case Opcode.DLOAD_3: case Opcode.DSTORE_3: index = 3; clazz = double.class; break; case Opcode.ALOAD_0: case Opcode.ASTORE_0: index = 0; clazz = Object.class; break; case Opcode.ALOAD_1: case Opcode.ASTORE_1: index = 1; clazz = Object.class; break; case Opcode.ALOAD_2: case Opcode.ASTORE_2: index = 2; clazz = Object.class; break; case Opcode.ALOAD_3: case Opcode.ASTORE_3: index = 3; clazz = Object.class; break; default: index = 0; clazz = 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, clazz)); } 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, clazz)); break; } break; case Opcode.RET: LocalVariable local = getLocalVariable (readUnsignedByte(), Object.class); assembler.ret(local); break; case Opcode.IINC: local = getLocalVariable(readUnsignedByte(), int.class); 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(); clazz = null; switch (atype) { case 4: // T_BOOLEAN clazz = boolean.class; break; case 5: // T_CHAR clazz = char.class; break; case 6: // T_FLOAT clazz = float.class; break; case 7: // T_DOUBLE clazz = double.class; break; case 8: // T_BYTE clazz = byte.class; break; case 9: // T_SHORT clazz = short.class; break; case 10: // T_INT clazz = int.class; break; case 11: // T_LONG clazz = long.class; break; } if (clazz == null) { // TODO: raise an error. break; } assembler.newObject (new TypeDescriptor(new TypeDescriptor(clazz), 1)); 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) { // TODO: raise an error. 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) { // TODO: raise an error. 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: // TODO: raise an error. 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: clazz = int.class; break; case Opcode.LLOAD: case Opcode.LSTORE: clazz = long.class; break; case Opcode.FLOAD: case Opcode.FSTORE: clazz = float.class; break; case Opcode.DLOAD: case Opcode.DSTORE: clazz = double.class; break; case Opcode.ALOAD: case Opcode.ASTORE: clazz = Object.class; break; default: clazz = 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, clazz)); } 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, clazz)); break; } break; case Opcode.RET: local = getLocalVariable (readUnsignedShort(), Object.class); assembler.ret(local); break; case Opcode.IINC: local = getLocalVariable(readUnsignedShort(), int.class); assembler.integerIncrement(local, readShort()); break; } break; } // end huge switch } // end for loop } private void gatherLabels() { mLabels = new HashMap(); mCatchLocations = new HashMap(mExceptionHandlers.length * 2 + 1); Integer labelKey; // Gather labels for any exception handlers. for (int i = mExceptionHandlers.length - 1; i >= 0; i--) { ExceptionHandler handler = mExceptionHandlers[i]; labelKey = new Integer(handler.getStartLocation().getLocation()); mLabels.put(labelKey, labelKey); labelKey = new Integer(handler.getEndLocation().getLocation()); mLabels.put(labelKey, labelKey); labelKey = new Integer(handler.getCatchLocation().getLocation()); List list = (List)mCatchLocations.get(labelKey); if (list == null) { list = new ArrayList(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: // TODO: raise an error. 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 = new Integer(mAddress + readShort()); mLabels.put(labelKey, labelKey); break; case Opcode.GOTO_W: case Opcode.JSR_W: labelKey = new Integer(mAddress + readInt()); mLabels.put(labelKey, 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 = new Integer(opcodeAddress + readInt()); mLabels.put(labelKey, 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 = new Integer(opcodeAddress + readInt()); mLabels.put(labelKey, labelKey); } } else { int caseCount = readInt(); for (int i=0; i<caseCount; i++) { // Skip the case value. mAddress += 4; // Read the branch location. labelKey = new Integer(opcodeAddress + readInt()); mLabels.put(labelKey, 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.IINC: 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: mAddress += 2; break; // Opcodes with three operand bytes... case Opcode.MULTIANEWARRAY: mAddress += 3; break; // Opcodes with four operand bytes... case Opcode.INVOKEINTERFACE: 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(int index, Class typeHint) { TypeDescriptor type = new TypeDescriptor(typeHint); LocalVariable local; if (index >= mLocals.size()) { mLocals.setSize(index + 1); local = null; } else { local = (LocalVariable)mLocals.elementAt(index); if (local != null) { TypeDescriptor localType = local.getType(); Class typeClass = type.getClassArg(); Class localTypeClass = localType.getClassArg(); if (typeClass != null && !typeClass.isPrimitive()) { typeClass = null; } if (localTypeClass != null && !localTypeClass.isPrimitive()) { localTypeClass = null; } if (typeClass == null) { if (localTypeClass == null) { // Both types must represent objects. This is ok. } else { // Requesting object type, but existing is primitive. local = null; } } else { if (localTypeClass == null) { // Requesting primitive type, but existing is object. local = null; } else { // Primitive types must exactly match. if (typeClass != localTypeClass) { local = null; } } } } } if (local == null) { local = mAssembler.createLocalVariable(null, type); mLocals.setElementAt(local, index); } return local; } private void locateLabel() { Integer labelKey = new Integer(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 handlers = (List)mCatchLocations.get(labelKey); if (handlers != null) { for (int i=0; i<handlers.size(); i++) { ExceptionHandler handler = (ExceptionHandler)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().getClassName(); } mAssembler.exceptionHandler(start, end, catchClassName); } } } private Label getLabel(int address) { Integer labelKey = new Integer(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; } }