Package org.cojen.classfile

Source Code of org.cojen.classfile.CodeDisassembler

/*
*  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();
                } 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;
    }
}
TOP

Related Classes of org.cojen.classfile.CodeDisassembler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.