All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.googlecode.jpattern.org.cojen.classfile.AssemblyStylePrinter Maven / Gradle / Ivy

Go to download

This is a copy of the good Cojen project from http://cojen.sourceforge.net/ with package name changed

The newest version!
/*
 *  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 com.googlecode.jpattern.org.cojen.classfile;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import com.googlecode.jpattern.org.cojen.classfile.attribute.Annotation;
import com.googlecode.jpattern.org.cojen.classfile.attribute.CodeAttr;
import com.googlecode.jpattern.org.cojen.classfile.attribute.LocalVariableTableAttr;
import com.googlecode.jpattern.org.cojen.classfile.attribute.SignatureAttr;
import com.googlecode.jpattern.org.cojen.classfile.attribute.StackMapTableAttr;
import com.googlecode.jpattern.org.cojen.classfile.constant.ConstantClassInfo;
import com.googlecode.jpattern.org.cojen.classfile.constant.ConstantDoubleInfo;
import com.googlecode.jpattern.org.cojen.classfile.constant.ConstantFieldInfo;
import com.googlecode.jpattern.org.cojen.classfile.constant.ConstantFloatInfo;
import com.googlecode.jpattern.org.cojen.classfile.constant.ConstantIntegerInfo;
import com.googlecode.jpattern.org.cojen.classfile.constant.ConstantInterfaceMethodInfo;
import com.googlecode.jpattern.org.cojen.classfile.constant.ConstantLongInfo;
import com.googlecode.jpattern.org.cojen.classfile.constant.ConstantMethodInfo;
import com.googlecode.jpattern.org.cojen.classfile.constant.ConstantNameAndTypeInfo;
import com.googlecode.jpattern.org.cojen.classfile.constant.ConstantStringInfo;
import com.googlecode.jpattern.org.cojen.classfile.constant.ConstantUTFInfo;
import com.googlecode.jpattern.org.cojen.util.IntHashMap;

/**
 * Disassembles a ClassFile into a pseudo Java assembly language format.
 *
 * @author Brian S O'Neill
 */
class AssemblyStylePrinter implements DisassemblyTool.Printer {
    private ClassFile mClassFile;
    private ConstantPool mCp;
    private PrintWriter mOut;

    private byte[] mByteCodes;
    // Current address being decompiled.
    private int mAddress;

    // Maps int address keys to String labels.
    private IntHashMap mLabels;

    private ExceptionHandler[] mExceptionHandlers;

    // Maps int catch locations to Lists of ExceptionHandler objects.
    private IntHashMap> mCatchLocations;

    public AssemblyStylePrinter() {
    }

    public void disassemble(ClassFile cf, PrintWriter out) {
        disassemble(cf, out, "");
    }

    private void disassemble(ClassFile cf, PrintWriter out, String indent) {
        mClassFile = cf;
        mCp = cf.getConstantPool();
        mOut = out;

        if (indent.length() == 0 || mClassFile.getSourceFile() != null ||
            mClassFile.isDeprecated() || mClassFile.isSynthetic()) {

            println(indent, "/**");

            boolean addBreak = false;

            if (indent.length() == 0) {
                print(indent, " * Disassembled on ");
                print(new Date());
                println(".");
                addBreak = true;
            }

            if (indent.length() == 0 && mClassFile.getTarget() != null) {
                if (addBreak) {
                    println(indent, " * ");
                    addBreak = false;
                }
                print(indent, " * @target ");
                println(CodeAssemblerPrinter.escape(mClassFile.getTarget()));
            }

            if (mClassFile.getSourceFile() != null) {
                if (addBreak) {
                    println(indent, " * ");
                    addBreak = false;
                }
                print(indent, " * @source ");
                println(CodeAssemblerPrinter.escape(mClassFile.getSourceFile()));
            }

            if (mClassFile.isInnerClass()) {
                if (addBreak) {
                    println(indent, " * ");
                    addBreak = false;
                }
                if (mClassFile.getInnerClassName() == null) {
                    println(indent, " * @anonymous");
                } else {
                    print(indent, " * @name ");
                    println(CodeAssemblerPrinter.escape(mClassFile.getInnerClassName()));
                }
            }

            if (mClassFile.isDeprecated()) {
                if (addBreak) {
                    println(indent, " * ");
                    addBreak = false;
                }
                println(indent, " * @deprecated");
            }

            if (mClassFile.isSynthetic()) {
                if (addBreak) {
                    println(indent, " * ");
                    addBreak = false;
                }
                println(indent, " * @synthetic");
            }

            // TODO: Just testing
            SignatureAttr sig = mClassFile.getSignatureAttr();
            if (sig != null) {
                if (addBreak) {
                    println(indent, " * ");
                    addBreak = false;
                }
                println(indent, " * @signature " + sig.getSignature().getValue());
            }

            println(indent, " */");
        }

        disassemble(indent, mClassFile.getRuntimeVisibleAnnotations());
        disassemble(indent, mClassFile.getRuntimeInvisibleAnnotations());

        print(indent);

        disassemble(mClassFile.getModifiers());
        boolean isInterface = mClassFile.getModifiers().isInterface();
        if (!isInterface) {
            print("class ");
        }
        print(mClassFile.getClassName());

        if (mClassFile.getSuperClassName() != null) {
            print(" extends ");
            print(mClassFile.getSuperClassName());
        }

        String innerIndent = indent + "    ";

        String[] interfaces = mClassFile.getInterfaces();
        if (interfaces.length == 0) {
            println(" {");
        } else {
            println();
            for (int i=0; i 0) {
                println();
            }
            if (members[i] instanceof FieldInfo) {
                disassemble(innerIndent, (FieldInfo)members[i]);
            } else {
                disassemble(innerIndent, (MethodInfo)members[i]);
            }
        }

        mByteCodes = null;
        mLabels = null;
        mExceptionHandlers = null;
        mCatchLocations = null;

        ClassFile[] innerClasses = mClassFile.getInnerClasses();

        for (int i=0; i 0 || members.length > 0) {
                println();
            }
            AssemblyStylePrinter printer = new AssemblyStylePrinter();
            printer.disassemble(innerClasses[i], mOut, innerIndent);
        }

        println(indent, "}");

        mOut.flush();
        mOut = null;
    }

    private void disassemble(String indent, FieldInfo field) {
        SignatureAttr sig = field.getSignatureAttr();
        if (field.isDeprecated() || field.isSynthetic() || sig != null) {
            println(indent, "/**");
            if (field.isDeprecated()) {
                println(indent, " * @deprecated");
            }
            if (field.isSynthetic()) {
                println(indent, " * @synthetic");
            }
            if (sig != null) {
                println(indent, " * @signature " + sig.getSignature().getValue());
            }
            println(indent, " */");
        }

        disassemble(indent, field.getRuntimeVisibleAnnotations());
        disassemble(indent, field.getRuntimeInvisibleAnnotations());

        print(indent);
        disassemble(field.getModifiers());
        disassemble(field.getType());
        print(" ");
        print(field.getName());
        ConstantInfo constant = field.getConstantValue();
        if (constant != null) {
            print(" = ");
            disassemble(constant);
        }
        println(";");
    }

    private void disassemble(String indent, MethodInfo method) {
        SignatureAttr sig = method.getSignatureAttr();
        if (method.isDeprecated() || method.isSynthetic() || sig != null) {
            println(indent, "/**");
            if (method.isDeprecated()) {
                println(indent, " * @deprecated");
            }
            if (method.isSynthetic()) {
                println(indent, " * @synthetic");
            }
            if (sig != null) {
                println(indent, " * @signature " + sig.getSignature().getValue());
            }
            println(indent, " */");
        }

        disassemble(indent, method.getRuntimeVisibleAnnotations());
        disassemble(indent, method.getRuntimeInvisibleAnnotations());

        print(indent);

        MethodDesc md = method.getMethodDescriptor();

        if ("".equals(method.getName()) &&
            md.getReturnType() == TypeDesc.VOID &&
            md.getParameterCount() == 0 &&
            (method.getModifiers().isStatic()) &&
            (!method.getModifiers().isAbstract()) &&
            method.getExceptions().length == 0) {

            // Static initializer.
            print("static");
        } else {
            Modifiers modifiers = method.getModifiers();
            boolean varargs = modifiers.isVarArgs();
            if (varargs) {
                // Don't display the modifier.
                modifiers = modifiers.toVarArgs(false);
            }
            disassemble(modifiers);
            print(md.toMethodSignature(method.getName(), varargs));
        }

        CodeAttr code = method.getCodeAttr();

        TypeDesc[] exceptions = method.getExceptions();
        if (exceptions.length == 0) {
            if (code == null) {
                println(";");
            } else {
                println(" {");
            }
        } else {
            println();
            for (int i=0; i 0) {
                print(", ");
            }
            Map.Entry entry = (Map.Entry)it.next();
            String name = (String)entry.getKey();
            if (!"value".equals(name)) {
                print(name);
                print("=");
            }
            disassemble(indent, (Annotation.MemberValue)entry.getValue());
        }
        print(")");
    }

    private void disassemble(String indent, Annotation.MemberValue mv) {
        Object value = mv.getValue();
        switch (mv.getTag()) {
        default:
            print("?");
            break;
        case Annotation.MEMBER_TAG_BOOLEAN:
            ConstantIntegerInfo ci = (ConstantIntegerInfo)value;
            print(ci.getValue() == 0 ? "false" : "true");
            break;
        case Annotation.MEMBER_TAG_BYTE:
        case Annotation.MEMBER_TAG_SHORT:
        case Annotation.MEMBER_TAG_INT:
        case Annotation.MEMBER_TAG_LONG:
        case Annotation.MEMBER_TAG_FLOAT:
        case Annotation.MEMBER_TAG_DOUBLE:
        case Annotation.MEMBER_TAG_STRING:
        case Annotation.MEMBER_TAG_CLASS:
            disassemble((ConstantInfo)value, true);
            break;
        case Annotation.MEMBER_TAG_CHAR: {
            print("'");
            char c = (char) ((ConstantIntegerInfo)value).getValue();
            print(CodeAssemblerPrinter.escape(String.valueOf(c), true));
            print("'");
            break;
        }
        case Annotation.MEMBER_TAG_ENUM:
            Annotation.EnumConstValue ecv = (Annotation.EnumConstValue)value;
            print(TypeDesc.forDescriptor(ecv.getTypeName().getValue()).getFullName());
            print(".");
            print(ecv.getConstName().getValue());
            break;
        case Annotation.MEMBER_TAG_ANNOTATION:
            disassemble(indent, (Annotation)value);
            break;
        case Annotation.MEMBER_TAG_ARRAY:
            Annotation.MemberValue[] mvs = (Annotation.MemberValue[])value;

            String originalIndent = indent;
            boolean multiLine = false;
            if (mvs.length > 0) {
                if (mvs.length > 4 ||
                    (mvs.length > 1 && mvs[0].getTag() == Annotation.MEMBER_TAG_ENUM) ||
                    mvs[0].getTag() == Annotation.MEMBER_TAG_ARRAY ||
                    mvs[0].getTag() == Annotation.MEMBER_TAG_ANNOTATION) {

                    multiLine = true;
                    indent = indent + "    ";
                }
            }

            if (multiLine || mvs.length != 1) {
                print("{");
                if (multiLine) {
                    println();
                }
            }
            
            for (int j=0; j locb) {
                    return 1;
                } else {
                    return 0;
                }
            }
        };

        StackMapTableAttr.StackMapFrame frame;
        if (code.getStackMapTable() == null) {
            frame = null;
        } else {
            frame = code.getStackMapTable().getInitialFrame();
        }

        int currentLine = -1;

        for (mAddress = 0; mAddress < mByteCodes.length; mAddress++) {
            int nextLine = code.getLineNumber(currentLoc);
            if (nextLine != currentLine) {
                if ((currentLine = nextLine) >= 0) {
                    println(indent, "// line " + currentLine);
                }
            }

            // Check if a label needs to be created and/or located.
            locateLabel(indent);

            frame = stackMap(indent, frame);

            byte opcode = mByteCodes[mAddress];

            String mnemonic;
            try {
                mnemonic = Opcode.getMnemonic(opcode);
            } catch (IllegalArgumentException e) {
                mnemonic = String.valueOf(opcode & 0xff);
            }

            print(indent, mnemonic);
            
            switch (opcode) {
                
            default:
                break;

                // 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:
                println();
                continue;

            case Opcode.ILOAD_0:
            case Opcode.LLOAD_0:
            case Opcode.FLOAD_0:
            case Opcode.DLOAD_0:
            case Opcode.ALOAD_0:
                disassemble(code.getLocalVariable(mAddress, 0));
                println();
                continue;

            case Opcode.ISTORE_0:
            case Opcode.LSTORE_0:
            case Opcode.FSTORE_0:
            case Opcode.DSTORE_0:
            case Opcode.ASTORE_0:
                disassemble(code.getLocalVariable(mAddress + 1, 0));
                println();
                continue;

            case Opcode.ILOAD_1:
            case Opcode.LLOAD_1:
            case Opcode.FLOAD_1:
            case Opcode.DLOAD_1:
            case Opcode.ALOAD_1:
                disassemble(code.getLocalVariable(mAddress, 1));
                println();
                continue;

            case Opcode.ISTORE_1:
            case Opcode.LSTORE_1:
            case Opcode.FSTORE_1:
            case Opcode.DSTORE_1:
            case Opcode.ASTORE_1:
                disassemble(code.getLocalVariable(mAddress + 1, 1));
                println();
                continue;

            case Opcode.ILOAD_2:
            case Opcode.LLOAD_2:
            case Opcode.FLOAD_2:
            case Opcode.DLOAD_2:
            case Opcode.ALOAD_2:
                disassemble(code.getLocalVariable(mAddress, 2));
                println();
                continue;

            case Opcode.ISTORE_2:
            case Opcode.LSTORE_2:
            case Opcode.FSTORE_2:
            case Opcode.DSTORE_2:
            case Opcode.ASTORE_2:
                disassemble(code.getLocalVariable(mAddress + 1, 2));
                println();
                continue;

            case Opcode.ILOAD_3:
            case Opcode.LLOAD_3:
            case Opcode.FLOAD_3:
            case Opcode.DLOAD_3:
            case Opcode.ALOAD_3:
                disassemble(code.getLocalVariable(mAddress, 3));
                println();
                continue;

            case Opcode.ISTORE_3:
            case Opcode.LSTORE_3:
            case Opcode.FSTORE_3:
            case Opcode.DSTORE_3:
            case Opcode.ASTORE_3:
                disassemble(code.getLocalVariable(mAddress + 1, 3));
                println();
                continue;

                // End opcodes with no operands.
            }

            // Space to separate operands.
            print(" ");

            int index;
            ConstantInfo constant;

            switch (opcode) {
            default:
                break;

                // 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;
                }

                disassemble(getConstant(index), true);
                break;

            case Opcode.NEW:
            case Opcode.ANEWARRAY:
            case Opcode.CHECKCAST:
            case Opcode.INSTANCEOF:
                constant = getConstant(readUnsignedShort());
                if (constant instanceof ConstantClassInfo) {
                    disassemble(constant);
                } else {
                    print(constant);
                }
                break;
            case Opcode.MULTIANEWARRAY:
                constant = getConstant(readUnsignedShort());
                int dims = readUnsignedByte();
                if (constant instanceof ConstantClassInfo) {
                    disassemble(constant);
                } else {
                    print(constant);
                }
                print(" ");
                print(String.valueOf(dims));
                break;

            case Opcode.GETSTATIC:
            case Opcode.PUTSTATIC:
            case Opcode.GETFIELD:
            case Opcode.PUTFIELD:
                constant = getConstant(readUnsignedShort());
                if (constant instanceof ConstantFieldInfo) {
                    ConstantFieldInfo field = (ConstantFieldInfo)constant;
                    Descriptor type = field.getNameAndType().getType();
                    if (type instanceof TypeDesc) {
                        disassemble((TypeDesc)type);
                    } else {
                        print(type);
                    }
                    print(" ");
                    print(field.getParentClass().getType().getFullName());
                    print(".");
                    print(field.getNameAndType().getName());
                } else {
                    print(constant);
                }
                break;

            case Opcode.INVOKEVIRTUAL:
            case Opcode.INVOKESPECIAL:
            case Opcode.INVOKESTATIC:
            case Opcode.INVOKEINTERFACE:
            case Opcode.INVOKEDYNAMIC:
                constant = getConstant(readUnsignedShort());

                String className;
                ConstantNameAndTypeInfo nameAndType;

                if (opcode == Opcode.INVOKEINTERFACE) {
                    // Read and ignore nargs and padding byte.
                    readShort();
                    if (!(constant instanceof ConstantInterfaceMethodInfo)) {
                        print(constant);
                        break;
                    }
                    ConstantInterfaceMethodInfo method = 
                        (ConstantInterfaceMethodInfo)constant;
                    className =
                        method.getParentClass().getType().getFullName();
                    nameAndType = method.getNameAndType();
                } else if (opcode == Opcode.INVOKEDYNAMIC) {
                    // Read and ignore extra bytes.
                    readShort();
                    className = null;
                    nameAndType = (ConstantNameAndTypeInfo)constant;
                } else {
                    if (!(constant instanceof ConstantMethodInfo)) {
                        print(constant);
                        break;
                    }
                    ConstantMethodInfo method = (ConstantMethodInfo)constant;
                    className =
                        method.getParentClass().getType().getFullName();
                    nameAndType = method.getNameAndType();
                }

                Descriptor type = nameAndType.getType();
                if (!(type instanceof MethodDesc)) {
                    print(type);
                    break;
                }
                disassemble(((MethodDesc)type).getReturnType());
                print(" ");
                if (className != null) {
                    print(className);
                    print(".");
                }
                print(nameAndType.getName());

                print("(");
                TypeDesc[] params = ((MethodDesc)type).getParameterTypes();
                for (int i=0; i 0) {
                        print(", ");
                    }
                    disassemble(params[i]);
                }
                print(")");
                break;

                // End opcodes that load a constant from the constant pool.

                // Opcodes that load or store local variables...

            case Opcode.ILOAD:
            case Opcode.LLOAD:
            case Opcode.FLOAD:
            case Opcode.DLOAD:
            case Opcode.ALOAD:
            case Opcode.RET:
                int varNum = readUnsignedByte();
                print(String.valueOf(varNum));
                disassemble(code.getLocalVariable(mAddress, varNum));
                break;
            case Opcode.ISTORE:
            case Opcode.LSTORE:
            case Opcode.FSTORE:
            case Opcode.DSTORE:
            case Opcode.ASTORE:
                varNum = readUnsignedByte();
                print(String.valueOf(varNum));
                disassemble(code.getLocalVariable(mAddress + 1, varNum));
                break;
            case Opcode.IINC:
                print(String.valueOf(varNum = readUnsignedByte()));
                print(" ");
                int incValue = readByte();
                if (incValue >= 0) {
                    print("+");
                }
                print(String.valueOf(incValue));
                disassemble(code.getLocalVariable(mAddress, varNum));
                break;

                // End opcodes that load or store local variables.

                // Opcodes that branch to another address.
            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:
                print(getLabel(mAddress + readShort()));
                break;
            case Opcode.GOTO_W:
            case Opcode.JSR_W:
                print(getLabel(mAddress + readInt()));
                break;

                // End opcodes that branch to another address.

                // Miscellaneous opcodes...
            case Opcode.BIPUSH:
                int value = readByte();
                print(String.valueOf(value));
                printCharLiteral(value);
                break;
            case Opcode.SIPUSH:
                value = readShort();
                print(String.valueOf(value));
                printCharLiteral(value);
                break;

            case Opcode.NEWARRAY:
                int atype = readByte();
                switch (atype) {
                case 4: // T_BOOLEAN
                    print("boolean");
                    break;
                case 5: // T_CHAR
                    print("char");
                    break;
                case 6: // T_FLOAT
                    print("float");
                    break;
                case 7: // T_DOUBLE
                    print("double");
                    break;
                case 8: // T_BYTE
                    print("byte");
                    break;
                case 9: // T_SHORT
                    print("short");
                    break;
                case 10: // T_INT
                    print("int");
                    break;
                case 11: // T_LONG
                    print("long");
                    break;
                default:
                    print("T_" + atype);
                    break;
                }
                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;
                }
                String defaultLocation = getLabel(opcodeAddress + readInt());
                int[] cases;
                String[] locations;
                
                if (opcode == Opcode.TABLESWITCH) {
                    int lowValue = readInt();
                    int highValue = readInt();
                    int caseCount = highValue - lowValue + 1;
                    print("// " + caseCount + " cases");
                    try {
                        cases = new int[caseCount];
                    } catch (NegativeArraySizeException e) {
                        break;
                    }
                    locations = new String[caseCount];
                    for (int i=0; i= 0) {
                        print("+");
                    }
                    print(String.valueOf(incValue));
                    break;
                }

                break;
            } // end huge switch

            println();
        } // end for loop
    }

    private void gatherLabels() {
        mLabels = new IntHashMap();
        mCatchLocations = new IntHashMap>
            (mExceptionHandlers.length * 2 + 1);

        // Gather labels for any exception handlers.
        for (int i = mExceptionHandlers.length - 1; i >= 0; i--) {
            ExceptionHandler handler = mExceptionHandlers[i];
            createLabel(new Integer(handler.getStartLocation().getLocation()));
            createLabel(new Integer(handler.getEndLocation().getLocation()));
            int labelKey = handler.getCatchLocation().getLocation();
            createLabel(labelKey);
            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:
                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:
                createLabel(new Integer(mAddress + readShort()));
                break;

            case Opcode.GOTO_W:
            case Opcode.JSR_W:
                createLabel(new Integer(mAddress + readInt()));
                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.
                

                createLabel(new Integer(opcodeAddress + readInt()));
                
                if (opcode == Opcode.TABLESWITCH) {
                    int lowValue = readInt();
                    int highValue = readInt();
                    int caseCount = highValue - lowValue + 1;

                    for (int i=0; i 0) {
            print(indent.substring(0, len));
        }

        print(labelValue);
        println(":");

        List handlers = mCatchLocations.get(labelKey);

        if (handlers != null) {
            for (int i=0; i 0) {
                print(", ");
            }
            print(num);
            print('=');
            StackMapTableAttr.VerificationTypeInfo info = infos[i];
            print(info.toString());
            if (info.getType() == null || !info.getType().isDoubleWord()) {
                num += 1;
            } else {
                num += 2;
            }
        }
        print('}');
    }

    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 void print(Object text) {
        mOut.print(text);
    }

    private void println(Object text) {
        mOut.println(text);
    }

    private void print(String indent, Object text) {
        mOut.print(indent);
        mOut.print(text);
    }

    private void println(String indent, Object text) {
        mOut.print(indent);
        mOut.println(text);
    }

    private void println() {
        mOut.println();
    }

    private void printCharLiteral(int value) {
        if (value >= 0 && value <= 65535) {
            int type = Character.getType((char)value);
            switch (type) {
            case Character.UPPERCASE_LETTER:
            case Character.LOWERCASE_LETTER:
            case Character.TITLECASE_LETTER:
            case Character.MODIFIER_LETTER:
            case Character.OTHER_LETTER:
            case Character.NON_SPACING_MARK:
            case Character.ENCLOSING_MARK:
            case Character.COMBINING_SPACING_MARK:
            case Character.DECIMAL_DIGIT_NUMBER:
            case Character.LETTER_NUMBER:
            case Character.OTHER_NUMBER:
            case Character.DASH_PUNCTUATION:
            case Character.START_PUNCTUATION:
            case Character.END_PUNCTUATION:
            case Character.CONNECTOR_PUNCTUATION:
            case Character.OTHER_PUNCTUATION:
            case Character.MATH_SYMBOL:
            case Character.CURRENCY_SYMBOL:
            case Character.MODIFIER_SYMBOL:
            case Character.OTHER_SYMBOL:
                print(" // '");
                print(String.valueOf((char)value));
                print("'");
            }
        }
    }

    private void sortMembers(Object[] members) {
        Arrays.sort(members, new MemberComparator());
    }

    /**
     * Orders members in this canonical sequence:
     *
     * - statics
     *   - fields
     *   - initializer
     *   - methods
     * - non-statics
     *   - fields
     *   - constructors
     *   - methods
     *
     * Fields, constructors, and methods are sorted:
     * - public
     *   - final
     *   - non-final
     *   - transient
     * - protected
     *   - final
     *   - non-final
     *   - transient
     * - package
     *   - final
     *   - non-final
     *   - transient
     * - private
     *   - final
     *   - non-final
     *   - transient
     */
    private class MemberComparator implements Comparator {
        public int compare(Object a, Object b) {
            Modifiers aFlags, bFlags;

            if (a instanceof FieldInfo) {
                aFlags = ((FieldInfo)a).getModifiers();
            } else {
                aFlags = ((MethodInfo)a).getModifiers();
            }

            if (b instanceof FieldInfo) {
                bFlags = ((FieldInfo)b).getModifiers();
            } else {
                bFlags = ((MethodInfo)b).getModifiers();
            }

            // static before non-static
            if (aFlags.isStatic()) {
                if (!bFlags.isStatic()) {
                    return -1;
                }
            } else {
                if (bFlags.isStatic()) {
                    return 1;
                }
            }

            // fields before methods
            if (a instanceof FieldInfo) {
                if (b instanceof MethodInfo) {
                    return -1;
                }
            } else {
                if (!(b instanceof MethodInfo)) {
                    return 1;
                }

                // initializers and constructors before regular methods
                String aName = ((MethodInfo)a).getName();
                String bName = ((MethodInfo)b).getName();
                
                if ("".equals(aName) || "".equals(aName)) {
                    if ("".equals(bName) || "".equals(bName)) {
                    } else {
                        return -1;
                    }
                } else {
                    if ("".equals(bName) || "".equals(bName)) {
                        return 1;
                    }
                }
            }

            // public, protected, package, private order
            int aValue, bValue;
            if (aFlags.isPublic()) {
                aValue = 0;
            } else if (aFlags.isProtected()) {
                aValue = 4;
            } else if (!aFlags.isPrivate()) {
                aValue = 8;
            } else {
                aValue = 12;
            }

            if (bFlags.isPublic()) {
                bValue = 0;
            } else if (bFlags.isProtected()) {
                bValue = 4;
            } else if (!bFlags.isPrivate()) {
                bValue = 8;
            } else {
                bValue = 12;
            }

            // final before non-final
            aValue += (aFlags.isFinal()) ? 0 : 2;
            bValue += (bFlags.isFinal()) ? 0 : 2;

            // transient after non-transient
            aValue += (aFlags.isTransient()) ? 1 : 0;
            bValue += (bFlags.isTransient()) ? 1 : 0;

            if (aValue < bValue) {
                return -1;
            } else if (aValue > bValue) {
                return 1;
            }

            return 0;
        }
    }
}