0001: /*
0002: * Janino - An embedded Java[TM] compiler
0003: *
0004: * Copyright (c) 2006, Arno Unkrig
0005: * All rights reserved.
0006: *
0007: * Redistribution and use in source and binary forms, with or without
0008: * modification, are permitted provided that the following conditions
0009: * are met:
0010: *
0011: * 1. Redistributions of source code must retain the above copyright
0012: * notice, this list of conditions and the following disclaimer.
0013: * 2. Redistributions in binary form must reproduce the above
0014: * copyright notice, this list of conditions and the following
0015: * disclaimer in the documentation and/or other materials
0016: * provided with the distribution.
0017: * 3. The name of the author may not be used to endorse or promote
0018: * products derived from this software without specific prior
0019: * written permission.
0020: *
0021: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
0022: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
0023: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0024: * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
0025: * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
0026: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
0027: * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0028: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
0029: * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
0030: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
0031: * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0032: */
0033:
0034: package org.codehaus.janino.tools;
0035:
0036: import java.io.*;
0037: import java.util.*;
0038:
0039: /**
0040: * A Java bytecode disassembler, comparable to JAVAP, which is part of
0041: * Sun's JDK.
0042: * <p>
0043: * Notice that this tool does not depend on any other classes or libraries (other than the
0044: * standard JDK library).
0045: */
0046:
0047: public class Disassembler {
0048: private IndentPrintWriter ipw = new IndentPrintWriter(System.out);
0049: private boolean verbose = false;
0050: private File sourceDirectory = new File(".");
0051:
0052: /**
0053: * Usage:
0054: * <pre>
0055: * java new.janino.tools.Disassembler [ -o <i>output-file</i> ] [ -help ]
0056: * </pre>
0057: * @param args
0058: * @throws IOException
0059: */
0060: public static void main(String[] args) throws IOException {
0061: Disassembler d = new Disassembler();
0062: int i;
0063: for (i = 0; i < args.length; ++i) {
0064: String arg = args[i];
0065: if (arg.charAt(0) != '-')
0066: break;
0067: if (arg.equals("-o")) {
0068: d.setOut(new FileOutputStream(args[++i]));
0069: } else if (arg.equals("-verbose")) {
0070: d.setVerbose(true);
0071: } else if (arg.equals("-src")) {
0072: d.setSourceDirectory(new File(args[++i]));
0073: } else if (arg.equals("-help")) {
0074: System.out
0075: .println("Usage: java jexpr.Jdisasm [ -o <output-file> ] [ -verbose ] [ -src <source-dir> ] <class-file> ...");
0076: System.exit(0);
0077: } else {
0078: System.err
0079: .println("Unrecognized command line option \""
0080: + arg + "\"; try \"-help\".");
0081: }
0082: }
0083: if (i == args.length) {
0084: System.err
0085: .println("Class file name missing, try \"-help\".");
0086: System.exit(1);
0087: }
0088: for (; i < args.length; ++i) {
0089: d.disasm(new File(args[i]));
0090: }
0091: }
0092:
0093: public Disassembler() {
0094: }
0095:
0096: public void setOut(OutputStream os) {
0097: this .ipw = new IndentPrintWriter(os);
0098: }
0099:
0100: public void setVerbose(boolean verbose) {
0101: this .verbose = verbose;
0102: }
0103:
0104: public void setSourceDirectory(File sourceDirectory) {
0105: this .sourceDirectory = sourceDirectory;
0106: }
0107:
0108: // void print(byte b) { this.ipw.print(b); }
0109: // void print(short s) { this.ipw.print(s); }
0110: void print(int i) {
0111: this .ipw.print(i);
0112: }
0113:
0114: // void print(long l) { this.ipw.print(l); }
0115: // void print(float f) { this.ipw.print(f); }
0116: // void print(double d) { this.ipw.print(d); }
0117: void print(String s) {
0118: this .ipw.print(s);
0119: }
0120:
0121: void println() {
0122: this .ipw.println();
0123: }
0124:
0125: void println(String s) {
0126: this .ipw.println(s);
0127: }
0128:
0129: void indent(String s) {
0130: this .ipw.print(s);
0131: this .ipw.indent();
0132: }
0133:
0134: void indentln(String s) {
0135: this .ipw.println(s);
0136: this .ipw.indent();
0137: }
0138:
0139: void unindent(String s) {
0140: this .ipw.unindent();
0141: this .ipw.print(s);
0142: }
0143:
0144: void unindentln(String s) {
0145: this .ipw.unindent();
0146: this .ipw.println(s);
0147: }
0148:
0149: /**
0150: * Disassemble one Java<sup>TM</sup> class file to {@link System#out}.
0151: *
0152: * @param file
0153: * @throws IOException
0154: */
0155: public void disasm(File file) throws IOException {
0156: InputStream is = new FileInputStream(file);
0157: try {
0158: disasm(is);
0159: } finally {
0160: try {
0161: is.close();
0162: } catch (IOException ex) {
0163: }
0164: }
0165: }
0166:
0167: public void disasm(InputStream is) throws IOException {
0168: this .disasmClassFile(new DataInputStream(is));
0169: this .println();
0170: this .ipw.flush();
0171: }
0172:
0173: private void disasmClassFile(DataInputStream dis)
0174: throws IOException {
0175: this .indentln("ClassFile {");
0176: {
0177: this .println("magic = 0x"
0178: + Integer.toHexString(dis.readInt()));
0179: this .println("minor_version = " + dis.readShort());
0180: this .println("major_version = " + dis.readShort());
0181:
0182: this .constantPool = new ConstantPoolInfo[dis.readShort()];
0183: for (short i = 1; i < this .constantPool.length;) {
0184: ConstantPoolInfo cpi = readConstantPoolInfo(dis);
0185: this .constantPool[i++] = cpi;
0186: for (int j = cpi.getSizeInConstantPool(); j > 1; --j) {
0187: this .constantPool[i++] = null;
0188: }
0189: }
0190: this .indentln("constant_pool[] = {");
0191: {
0192: for (short i = 1; i < this .constantPool.length;) {
0193: this .print(i + ": ");
0194: ConstantPoolInfo cpi = this .constantPool[i];
0195: cpi.print(); // Must be invoked only after "this.constantPool" is initialized.
0196: this .println();
0197: i += cpi.getSizeInConstantPool();
0198: }
0199: }
0200: this .unindentln("}");
0201:
0202: this .println("access_flags = "
0203: + decodeAccess(dis.readShort()));
0204:
0205: short this Class = dis.readShort();
0206: this .indent("this_class = " + this Class + " (");
0207: {
0208: this .printConstantPoolEntry(this Class);
0209: }
0210: this .unindentln(")");
0211:
0212: short super Class = dis.readShort();
0213: this .indent("super_class = " + super Class + " (");
0214: {
0215: this .printConstantPoolEntry(super Class);
0216: }
0217: this .unindentln(")");
0218:
0219: this .indentln("interfaces[] = {");
0220: {
0221: short interfacesCount = dis.readShort();
0222: for (short i = 0; i < interfacesCount; ++i) {
0223: this .printConstantPoolEntry(null, dis.readShort());
0224: this .println();
0225: }
0226: }
0227: this .unindentln("}");
0228:
0229: this .indentln("fields[] = {");
0230: {
0231: short fieldsCount = dis.readShort();
0232: for (short i = 0; i < fieldsCount; ++i) {
0233: disasmFieldInfo(dis);
0234: this .println();
0235: }
0236: }
0237: this .unindentln("}");
0238:
0239: MethodInfo[] methodInfos;
0240: {
0241: short methodsCount = dis.readShort();
0242: methodInfos = new MethodInfo[methodsCount];
0243: for (int i = 0; i < methodsCount; ++i)
0244: methodInfos[i] = this .readMethodInfo(dis);
0245: }
0246:
0247: AttributeInfo[] attributes;
0248: {
0249: short attributesCount = dis.readShort();
0250: attributes = new AttributeInfo[attributesCount];
0251: for (short i = 0; i < attributesCount; ++i)
0252: attributes[i] = this .readAttributeInfo(dis);
0253: }
0254:
0255: Map sourceLines = new HashMap();
0256: READ_SOURCE_LINES: {
0257: for (int i = 0; i < attributes.length; ++i) {
0258: if (attributes[i] instanceof SourceFileAttribute) {
0259: ConstantPoolInfo cpi = Disassembler.this
0260: .getConstantPoolEntry(((SourceFileAttribute) attributes[i]).sourceFileIndex);
0261: if (cpi instanceof ConstantUtf8Info) {
0262: String sourceFile = ((ConstantUtf8Info) cpi)
0263: .getValue();
0264: LineNumberReader lnr;
0265: try {
0266: lnr = new LineNumberReader(
0267: new FileReader(new File(
0268: this .sourceDirectory,
0269: sourceFile)));
0270: } catch (FileNotFoundException ex) {
0271: ;
0272: break READ_SOURCE_LINES;
0273: }
0274: try {
0275: for (;;) {
0276: String sl = lnr.readLine();
0277: if (sl == null)
0278: break;
0279: sourceLines.put(new Integer(lnr
0280: .getLineNumber()), sl);
0281: }
0282: } finally {
0283: lnr.close();
0284: }
0285: }
0286: }
0287: }
0288: }
0289:
0290: this .indentln("methods[] = {");
0291: {
0292: for (short i = 0; i < methodInfos.length; ++i) {
0293: methodInfos[i].print(sourceLines);
0294: this .println();
0295: }
0296: }
0297: this .unindentln("}");
0298:
0299: this .indentln("attributes[] = {");
0300: {
0301: for (short i = 0; i < attributes.length; ++i) {
0302: attributes[i].print();
0303: this .println();
0304: }
0305: }
0306: this .unindentln("}");
0307: }
0308: this .unindent("}");
0309: }
0310:
0311: private ConstantPoolInfo readConstantPoolInfo(
0312: final DataInputStream dis) throws IOException {
0313: byte tag = dis.readByte();
0314: switch (tag) {
0315: case 7: {
0316: final short nameIndex = dis.readShort();
0317: return new ConstantPoolInfo() {
0318: public void print() {
0319: Disassembler.this .print("CONSTANT_Class_info { ");
0320: Disassembler.this .printConstantPoolEntry(
0321: "name_index", nameIndex);
0322: Disassembler.this .print(" }");
0323: }
0324: };
0325: }
0326: case 9: {
0327: final short classIndex = dis.readShort();
0328: final short nameAndTypeIndex = dis.readShort();
0329: return new ConstantPoolInfo() {
0330: public void print() {
0331: Disassembler.this
0332: .indentln("CONSTANT_Fieldref_info {");
0333: {
0334: Disassembler.this .printConstantPoolEntry(
0335: "class_index", classIndex);
0336: Disassembler.this .println();
0337: Disassembler.this
0338: .printConstantPoolEntry(
0339: "name_and_type_index",
0340: nameAndTypeIndex);
0341: Disassembler.this .println();
0342: }
0343: Disassembler.this .unindent("}");
0344: }
0345: };
0346: }
0347: case 10: {
0348: final short classIndex = dis.readShort();
0349: final short nameAndTypeIndex = dis.readShort();
0350: return new ConstantPoolInfo() {
0351: public void print() {
0352: Disassembler.this
0353: .indentln("CONSTANT_Methodref_info {");
0354: {
0355: Disassembler.this .printConstantPoolEntry(
0356: "class_index", classIndex);
0357: Disassembler.this .println();
0358: Disassembler.this
0359: .printConstantPoolEntry(
0360: "name_and_type_index",
0361: nameAndTypeIndex);
0362: Disassembler.this .println();
0363: }
0364: Disassembler.this .unindent("}");
0365: }
0366: };
0367: }
0368: case 11: {
0369: final short classIndex = dis.readShort();
0370: final short nameAndTypeIndex = dis.readShort();
0371: return new ConstantPoolInfo() {
0372: public void print() {
0373: Disassembler.this
0374: .indentln("CONSTANT_InterfaceMethodref_info {");
0375: {
0376: Disassembler.this .printConstantPoolEntry(
0377: "class_index", classIndex);
0378: Disassembler.this .println();
0379: Disassembler.this
0380: .printConstantPoolEntry(
0381: "name_and_type_index",
0382: nameAndTypeIndex);
0383: Disassembler.this .println();
0384: }
0385: Disassembler.this .unindent("}");
0386: }
0387: };
0388: }
0389: case 8: {
0390: final short stringIndex = dis.readShort();
0391: return new ConstantPoolInfo() {
0392: public void print() {
0393: ConstantPoolInfo cpi = Disassembler.this
0394: .getConstantPoolEntry(stringIndex);
0395: String s;
0396: if (cpi == null) {
0397: s = "INVALID CONSTANT POOL INDEX";
0398: } else if (!(cpi instanceof ConstantUtf8Info)) {
0399: s = "STRING CONSTANT VALUE IS NOT UTF8";
0400: } else {
0401: s = ((ConstantUtf8Info) cpi).getValue();
0402: if (s.length() > 80) {
0403: s = Disassembler.stringToJavaLiteral(s
0404: .substring(0, 80))
0405: + "... (" + s.length() + " chars)";
0406: } else {
0407: s = Disassembler.stringToJavaLiteral(s);
0408: }
0409: }
0410: if (Disassembler.this .verbose) {
0411: Disassembler.this
0412: .print("CONSTANT_String_info { string_index = "
0413: + stringIndex
0414: + " ("
0415: + s
0416: + ") }");
0417: } else {
0418: Disassembler.this .print(s);
0419: }
0420: }
0421: };
0422: }
0423: case 3: {
0424: final int bytes = dis.readInt();
0425: return new ConstantPoolInfo() {
0426: public void print() {
0427: Disassembler.this
0428: .print("CONSTANT_Integer_info { bytes = "
0429: + bytes + " }");
0430: }
0431: };
0432: }
0433: case 4: {
0434: final float bytes = dis.readFloat();
0435: return new ConstantPoolInfo() {
0436: public void print() {
0437: Disassembler.this
0438: .print("CONSTANT_Float_info { bytes = "
0439: + bytes + " }");
0440: }
0441: };
0442: }
0443: case 5: {
0444: final long bytes = dis.readLong();
0445: return new ConstantPoolInfo() {
0446: public void print() {
0447: Disassembler.this
0448: .print("CONSTANT_Long_info { high_bytes/low_bytes = "
0449: + bytes + " }");
0450: }
0451:
0452: public int getSizeInConstantPool() {
0453: return 2;
0454: }
0455: };
0456: }
0457: case 6: {
0458: final double bytes = dis.readDouble();
0459: return new ConstantPoolInfo() {
0460: public void print() {
0461: Disassembler.this
0462: .print("CONSTANT_Double_info { high_bytes/low_bytes = "
0463: + bytes + " }");
0464: }
0465:
0466: public int getSizeInConstantPool() {
0467: return 2;
0468: }
0469: };
0470: }
0471: case 12: {
0472: final short nameIndex = dis.readShort();
0473: final short descriptorIndex = dis.readShort();
0474: return new ConstantPoolInfo() {
0475: public void print() {
0476: Disassembler.this
0477: .indentln("CONSTANT_NameAndType_info {");
0478: {
0479: Disassembler.this .printConstantPoolEntry(
0480: "name_index", nameIndex);
0481: Disassembler.this .println();
0482: Disassembler.this .printConstantPoolEntry(
0483: "descriptor_index", descriptorIndex);
0484: Disassembler.this .println();
0485: }
0486: Disassembler.this .unindent("}");
0487: }
0488: };
0489: }
0490: case 1:
0491: return new ConstantUtf8Info(dis.readUTF());
0492: default:
0493: throw new RuntimeException("Invalid cp_info tag \""
0494: + (int) tag + "\"");
0495: }
0496: }
0497:
0498: private static String stringToJavaLiteral(String s) {
0499: for (int i = 0; i < s.length();) {
0500: char c = s.charAt(i);
0501: int idx = "\r\n\"\t\b".indexOf(c);
0502: if (idx == -1) {
0503: ++i;
0504: } else {
0505: s = s.substring(0, i) + '\\' + "rn\"tb".charAt(idx)
0506: + s.substring(i + 1);
0507: i += 2;
0508: }
0509: if (i >= 80)
0510: break;
0511: }
0512: return '"' + s + '"';
0513: }
0514:
0515: private void disasmFieldInfo(DataInputStream dis)
0516: throws IOException {
0517: this .indentln("field_info {");
0518: {
0519: this .println("access_flags = "
0520: + decodeAccess(dis.readShort()));
0521:
0522: this .printConstantPoolEntry("name_index", dis.readShort());
0523: this .println();
0524:
0525: this .printConstantPoolEntry("descriptor_index", dis
0526: .readShort());
0527: this .println();
0528:
0529: this .indentln("attributes[] = {");
0530: {
0531: short attributesCount = dis.readShort();
0532: for (short i = 0; i < attributesCount; ++i) {
0533: disasmAttributeInfo(dis);
0534: this .println();
0535: }
0536: }
0537: this .unindentln("}");
0538: }
0539: this .unindent("}");
0540: }
0541:
0542: private MethodInfo readMethodInfo(DataInputStream dis)
0543: throws IOException {
0544: final short accessFlags = dis.readShort();
0545: final short nameIndex = dis.readShort();
0546: final short descriptorIndex = dis.readShort();
0547: final AttributeInfo[] attributeInfos;
0548: {
0549: short attributesCount = dis.readShort();
0550: attributeInfos = new AttributeInfo[attributesCount];
0551: for (short i = 0; i < attributesCount; ++i)
0552: attributeInfos[i] = this .readAttributeInfo(dis);
0553: }
0554: return new MethodInfo() {
0555: public void print(Map sourceLines) {
0556: Disassembler.this .indentln("method_info {");
0557: {
0558: Disassembler.this .println("access_flags = "
0559: + decodeAccess(accessFlags));
0560:
0561: Disassembler.this .printConstantPoolEntry(
0562: "name_index", nameIndex);
0563: Disassembler.this .println();
0564:
0565: Disassembler.this .printConstantPoolEntry(
0566: "descriptor_index", descriptorIndex);
0567: Disassembler.this .println();
0568:
0569: Disassembler.this .indentln("attributes[] = {");
0570: {
0571: for (short i = 0; i < attributeInfos.length; ++i) {
0572: AttributeInfo ai = attributeInfos[i];
0573: if (ai instanceof SourceRelatedAttributeInfo) {
0574: ((SourceRelatedAttributeInfo) ai)
0575: .print(sourceLines);
0576: } else {
0577: ai.print();
0578: }
0579: Disassembler.this .println();
0580: }
0581: }
0582: Disassembler.this .unindentln("}");
0583: }
0584: Disassembler.this .unindent("}");
0585: }
0586: };
0587: }
0588:
0589: private interface MethodInfo {
0590: public void print(Map sourceLines // Integer lineNumber => String line
0591: );
0592: }
0593:
0594: private void disasmAttributeInfo(DataInputStream dis)
0595: throws IOException {
0596: this .readAttributeInfo(dis).print();
0597: }
0598:
0599: private AttributeInfo readAttributeInfo(DataInputStream dis)
0600: throws IOException {
0601:
0602: // Determine attribute name.
0603: short attributeNameIndex = dis.readShort();
0604: String attributeName;
0605: {
0606: ConstantPoolInfo cpi = Disassembler.this
0607: .getConstantPoolEntry(attributeNameIndex);
0608: if (cpi == null) {
0609: attributeName = "INVALID CONSTANT POOL INDEX";
0610: } else if (!(cpi instanceof ConstantUtf8Info)) {
0611: attributeName = "ATTRIBUTE NAME IS NOT UTF8";
0612: } else {
0613: attributeName = ((ConstantUtf8Info) cpi).getValue();
0614: }
0615: }
0616:
0617: // Read attribute body into byte array and create a DataInputStream.
0618: int attributeLength = dis.readInt();
0619: final byte[] ba = new byte[attributeLength];
0620: if (dis.read(ba) != ba.length)
0621: throw new EOFException();
0622: ByteArrayInputStream bais = new ByteArrayInputStream(ba);
0623: DataInputStream dis2 = new DataInputStream(bais);
0624:
0625: // Parse the attribute body.
0626: AttributeInfo res = this .readAttributeBody(attributeName, dis2);
0627:
0628: // Check for extraneous bytes.
0629: int av = bais.available();
0630: if (av > 0)
0631: throw new RuntimeException(av
0632: + " extraneous bytes in attribute \""
0633: + attributeName + "\"");
0634:
0635: return res;
0636: }
0637:
0638: private AttributeInfo readAttributeBody(final String attributeName,
0639: final DataInputStream dis) throws IOException {
0640: if (attributeName.equals("ConstantValue")) {
0641: final short constantValueIndex = dis.readShort();
0642: return new AttributeInfo() {
0643: public void print() {
0644: Disassembler.this .indent("ConstantValue "
0645: + constantValueIndex + " (");
0646: {
0647: Disassembler.this
0648: .printConstantPoolEntry(constantValueIndex);
0649: }
0650: Disassembler.this .unindent(")");
0651: }
0652: };
0653: } else if (attributeName.equals("Code")) {
0654: final short maxStack = dis.readShort();
0655: final short maxLocals = dis.readShort();
0656: final byte[] code = readByteArray(dis, dis.readInt());
0657: final ExceptionTableEntry[] exceptionTable = readExceptionTable(dis);
0658: final AttributeInfo[] attributes = readAttributes(dis);
0659: return new SourceRelatedAttributeInfo() {
0660: public void print(Map sourceLines) {
0661: Disassembler.this .indentln("Code {");
0662: {
0663: Disassembler.this .println("max_stack = "
0664: + maxStack);
0665: Disassembler.this .println("max_locals = "
0666: + maxLocals);
0667:
0668: Disassembler.this .indentln("code = { ");
0669: {
0670: LocalVariableTableAttribute localVariableTableAttribute = null;
0671: LineNumberTableAttribute lineNumberTableAttribute = null;
0672: for (int i = 0; i < attributes.length; ++i) {
0673: AttributeInfo a = attributes[i];
0674: if (a instanceof LocalVariableTableAttribute) {
0675: if (localVariableTableAttribute != null)
0676: throw new RuntimeException(
0677: "Duplicate LocalVariableTable attribute");
0678: localVariableTableAttribute = (LocalVariableTableAttribute) a;
0679: }
0680: if (a instanceof LineNumberTableAttribute) {
0681: if (lineNumberTableAttribute != null)
0682: throw new RuntimeException(
0683: "Duplicate LineNumberTable attribute");
0684: lineNumberTableAttribute = (LineNumberTableAttribute) a;
0685: }
0686: }
0687: try {
0688: disasmBytecode(
0689: new ByteArrayInputStream(code),
0690: localVariableTableAttribute,
0691: lineNumberTableAttribute,
0692: sourceLines);
0693: } catch (IOException ignored) {
0694: ;
0695: }
0696: }
0697: Disassembler.this .unindentln("}");
0698:
0699: Disassembler.this
0700: .indentln("exception_table = {");
0701: {
0702: for (int i = 0; i < exceptionTable.length; ++i) {
0703: exceptionTable[i].print();
0704: Disassembler.this .println();
0705: }
0706: }
0707: Disassembler.this .unindentln("}");
0708:
0709: Disassembler.this .indentln("attributes[] = {");
0710: {
0711: for (int i = 0; i < attributes.length; ++i) {
0712: attributes[i].print();
0713: Disassembler.this .println();
0714: }
0715: }
0716: Disassembler.this .unindentln("}");
0717: }
0718: Disassembler.this .unindent("}");
0719: }
0720: };
0721: } else if (attributeName.equals("Exceptions")) {
0722: final short[] exceptionIndexTable = readShortArray(dis, dis
0723: .readShort());
0724: return new AttributeInfo() {
0725: public void print() {
0726: Disassembler.this .indentln("Exceptions {");
0727: {
0728: Disassembler.this
0729: .indentln("exception_index_table = {");
0730: {
0731: for (short i = 0; i < exceptionIndexTable.length; ++i) {
0732: short exceptionIndex = exceptionIndexTable[i];
0733: Disassembler.this .print(exceptionIndex
0734: + " (");
0735: Disassembler.this
0736: .printConstantPoolEntry(exceptionIndex);
0737: Disassembler.this .println("),");
0738: }
0739: }
0740: Disassembler.this .unindentln("}");
0741: }
0742: Disassembler.this .unindent("}");
0743: }
0744: };
0745: } else if (attributeName.equals("InnerClasses")) {
0746: final short[] data = readShortArray(dis, 4 * dis
0747: .readShort());
0748: return new AttributeInfo() {
0749: public void print() {
0750: Disassembler.this .indentln("InnerClasses {");
0751: {
0752: Disassembler.this .indentln("classes = {");
0753: {
0754: for (int i = 0; i < data.length; i += 4) {
0755: Disassembler.this .indentln("{");
0756: {
0757: Disassembler.this
0758: .printConstantPoolEntry(
0759: "inner_class_info_index",
0760: data[i]);
0761: Disassembler.this .println();
0762:
0763: short outerClassInfoIndex = data[i + 1];
0764: if (outerClassInfoIndex == 0) {
0765: Disassembler.this
0766: .print("(not a member)");
0767: } else {
0768: Disassembler.this
0769: .printConstantPoolEntry(
0770: "outer_class_info_index",
0771: outerClassInfoIndex);
0772: }
0773: Disassembler.this .println();
0774:
0775: short innerNameIndex = data[i + 2];
0776: if (innerNameIndex == 0) {
0777: Disassembler.this
0778: .print("(anonymous)");
0779: } else {
0780: Disassembler.this
0781: .printConstantPoolEntry(
0782: "inner_name_index",
0783: innerNameIndex);
0784: }
0785: Disassembler.this .println();
0786:
0787: Disassembler.this
0788: .println("inner_class_access_flags = "
0789: + decodeAccess(data[i + 3]));
0790: }
0791: Disassembler.this
0792: .unindentln(i == data.length - 1 ? "}"
0793: : "},");
0794: }
0795: }
0796: Disassembler.this .unindentln("}");
0797: }
0798: Disassembler.this .unindent("}");
0799: }
0800: };
0801: } else if (attributeName.equals("Synthetic")) {
0802: return new AttributeInfo() {
0803: public void print() {
0804: Disassembler.this .print("Synthetic");
0805: }
0806: };
0807: } else if (attributeName.equals("SourceFile")) {
0808: return new SourceFileAttribute(dis.readShort());
0809: } else if (attributeName.equals("LineNumberTable")) {
0810: return new LineNumberTableAttribute(readShortArray(dis,
0811: 2 * dis.readShort()));
0812: } else if (attributeName.equals("LocalVariableTable")) {
0813: return new LocalVariableTableAttribute(readShortArray(dis,
0814: 5 * dis.readShort()));
0815: } else if (attributeName.equals("Deprecated")) {
0816: return new AttributeInfo() {
0817: public void print() {
0818: Disassembler.this .print("Deprecated");
0819: }
0820: };
0821: } else {
0822: final ByteArrayOutputStream baos = new ByteArrayOutputStream();
0823: for (;;) {
0824: int c = dis.read();
0825: if (c == -1)
0826: break;
0827: baos.write(c);
0828: }
0829: return new AttributeInfo() {
0830: public void print() {
0831: Disassembler.this .indentln(attributeName + " {");
0832: {
0833: Disassembler.this .print("info = { ");
0834: for (int i = 0; i < this .info.length; ++i) {
0835: Disassembler.this
0836: .print("0x"
0837: + Integer
0838: .toHexString(0xff & this .info[i])
0839: + ", ");
0840: }
0841: Disassembler.this .println("}");
0842: }
0843: Disassembler.this .unindent("}");
0844: }
0845:
0846: private byte[] info = baos.toByteArray();
0847: };
0848: }
0849: }
0850:
0851: private interface AttributeInfo {
0852: public void print();
0853: }
0854:
0855: private abstract static class SourceRelatedAttributeInfo implements
0856: AttributeInfo {
0857: public void print() {
0858: throw new UnsupportedOperationException("print");
0859: }
0860:
0861: public abstract void print(Map sourceLines);
0862: }
0863:
0864: private class SourceFileAttribute implements AttributeInfo {
0865: final short sourceFileIndex;
0866:
0867: SourceFileAttribute(short sourceFileIndex) {
0868: this .sourceFileIndex = sourceFileIndex;
0869: }
0870:
0871: public void print() {
0872: Disassembler.this .indentln("SourceFile {");
0873: {
0874: Disassembler.this .printConstantPoolEntry(
0875: "sourcefile_index", this .sourceFileIndex);
0876: Disassembler.this .println();
0877: }
0878: Disassembler.this .unindent("}");
0879: }
0880: }
0881:
0882: public class LineNumberTableAttribute implements AttributeInfo {
0883: private final short[] data;
0884:
0885: public LineNumberTableAttribute(short[] data) {
0886: this .data = data;
0887: }
0888:
0889: public void print() {
0890: Disassembler.this .indentln("LineNumberTable {");
0891: {
0892: Disassembler.this .indentln("line_number_table = {");
0893: {
0894: for (short i = 0; i < this .data.length; i += 2) {
0895: Disassembler.this .print("start_pc = "
0896: + this .data[i]);
0897: Disassembler.this .print(", ");
0898: Disassembler.this .println("line_number = "
0899: + this .data[i + 1]);
0900: }
0901: }
0902: Disassembler.this .unindentln("}");
0903: }
0904: Disassembler.this .unindent("}");
0905: }
0906:
0907: public short findLineNumber(short offset) {
0908: for (short i = 0; i < this .data.length; i += 2) {
0909: if (this .data[i] == offset)
0910: return this .data[i + 1];
0911: }
0912: return -1;
0913: }
0914: }
0915:
0916: private class LocalVariableTableAttribute implements AttributeInfo {
0917: public LocalVariableTableAttribute(short[] data) {
0918: this .data = data;
0919: }
0920:
0921: public void print() {
0922: Disassembler.this .indentln("LocalVariableTable {");
0923: {
0924: Disassembler.this .indentln("local_variable_table = {");
0925: {
0926: for (int i = 0; i < this .data.length; i += 5) {
0927: Disassembler.this .print("start_pc = "
0928: + this .data[i]);
0929: Disassembler.this .print(", ");
0930: Disassembler.this .print("length = "
0931: + this .data[i + 1]);
0932: Disassembler.this .print(", ");
0933: Disassembler.this .printConstantPoolEntry(
0934: "name_index", this .data[i + 2]);
0935: Disassembler.this .print(", ");
0936: Disassembler.this .printConstantPoolEntry(
0937: "descriptor_index", this .data[i + 3]);
0938: Disassembler.this .print(", ");
0939: Disassembler.this .println("index = "
0940: + this .data[i + 4]);
0941: }
0942: }
0943: Disassembler.this .unindentln("}");
0944: }
0945: Disassembler.this .unindent("}");
0946: }
0947:
0948: public String find(short index, int instructionOffset) {
0949: for (int i = 0; i < this .data.length; i += 5) {
0950: short startPC = this .data[i];
0951: short length = this .data[i + 1];
0952: if (instructionOffset >= startPC
0953: && instructionOffset <= startPC + length
0954: && index == this .data[i + 4]) {
0955: String name = ((ConstantUtf8Info) Disassembler.this
0956: .getConstantPoolEntry(this .data[i + 2]))
0957: .getValue();
0958: String descriptor = ((ConstantUtf8Info) Disassembler.this
0959: .getConstantPoolEntry(this .data[i + 3]))
0960: .getValue();
0961: return Disassembler.decodeDescriptor(descriptor)
0962: + " " + name;
0963: }
0964: }
0965: return "anonymous";
0966: }
0967:
0968: private short[] data;
0969: }
0970:
0971: private static byte[] readByteArray(InputStream is, int size)
0972: throws IOException {
0973: byte[] res = new byte[size];
0974: if (is.read(res) != size)
0975: throw new EOFException("EOF in byte array");
0976: return res;
0977: }
0978:
0979: private ExceptionTableEntry[] readExceptionTable(DataInputStream dis)
0980: throws IOException {
0981: ExceptionTableEntry[] res = new ExceptionTableEntry[dis
0982: .readShort()];
0983: for (short i = 0; i < res.length; ++i) {
0984: res[i] = new ExceptionTableEntry(dis.readShort(), dis
0985: .readShort(), dis.readShort(), dis.readShort());
0986: }
0987: return res;
0988: }
0989:
0990: private class ExceptionTableEntry {
0991: ExceptionTableEntry(short startPC, short endPC,
0992: short handlerPC, short catchType) {
0993: this .startPC = startPC;
0994: this .endPC = endPC;
0995: this .handlerPC = handlerPC;
0996: this .catchType = catchType;
0997: }
0998:
0999: public void print() {
1000: Disassembler.this .print("start_pc = " + this .startPC
1001: + ", end_pc = " + this .endPC + ", handler_pc = "
1002: + this .handlerPC + ", catch_type = "
1003: + this .catchType + " (");
1004: if (this .catchType == 0) {
1005: Disassembler.this .print("finally");
1006: } else {
1007: Disassembler.this
1008: .printConstantPoolEntry(this .catchType);
1009: }
1010: Disassembler.this .print(")");
1011: }
1012:
1013: private short startPC, endPC, handlerPC, catchType;
1014: }
1015:
1016: private AttributeInfo[] readAttributes(DataInputStream dis)
1017: throws IOException {
1018: AttributeInfo[] res = new AttributeInfo[dis.readShort()];
1019: for (int i = 0; i < res.length; ++i) {
1020: res[i] = Disassembler.this .readAttributeInfo(dis);
1021: }
1022: return res;
1023: }
1024:
1025: private static short[] readShortArray(DataInputStream dis, int size)
1026: throws IOException {
1027: short[] res = new short[size];
1028: for (int i = 0; i < res.length; ++i)
1029: res[i] = dis.readShort();
1030: return res;
1031: }
1032:
1033: private void disasmBytecode(InputStream is,
1034: LocalVariableTableAttribute localVariableTableAttribute,
1035: LineNumberTableAttribute lineNumberTableAttribute,
1036: Map sourceLines // Integer lineNumber => String sourceLine
1037: ) throws IOException {
1038: CountingInputStream cis = new CountingInputStream(is);
1039: DataInputStream dis = new DataInputStream(cis);
1040:
1041: for (;;) {
1042: short instructionOffset = (short) cis.getCount();
1043:
1044: int opcode = dis.read();
1045: if (opcode == -1)
1046: return; // EOF
1047:
1048: if (lineNumberTableAttribute != null) {
1049: short lineNumber = lineNumberTableAttribute
1050: .findLineNumber(instructionOffset);
1051: if (lineNumber != -1) {
1052: String sourceLine = (String) sourceLines
1053: .get(new Integer(lineNumber));
1054: if (sourceLine == null)
1055: sourceLine = "(Source line not available)";
1056: this .println(" Line " + lineNumber
1057: + ": " + sourceLine);
1058: }
1059: }
1060: this .print(instructionOffset + ": ");
1061:
1062: Instruction instruction = opcodeToInstruction[opcode];
1063: if (instruction == null) {
1064: this .println("??? (invalid opcode \"" + opcode + "\")");
1065: continue;
1066: }
1067: disasmInstruction(instruction, dis, instructionOffset,
1068: localVariableTableAttribute);
1069: this .println();
1070: }
1071: }
1072:
1073: private void disasmInstruction(Instruction instruction,
1074: DataInputStream dis, int instructionOffset,
1075: LocalVariableTableAttribute localVariableTableAttribute)
1076: throws IOException {
1077: this .print(instruction.getMnemonic());
1078: Operand[] operands = instruction.getOperands();
1079: if (operands != null) {
1080: for (int i = 0; i < operands.length; ++i) {
1081: Operand operand = operands[i];
1082: this .print(" ");
1083: operand.disasm(dis, instructionOffset,
1084: localVariableTableAttribute, this );
1085: }
1086: }
1087: }
1088:
1089: private static final String[] instructions = new String[] {
1090: "50 aaload",
1091: "83 aastore",
1092: "1 aconst_null",
1093: "25 aload localvariablearrayindex1",
1094: "42 aload_0 localvariablearrayindex_0",
1095: "43 aload_1 localvariablearrayindex_1",
1096: "44 aload_2 localvariablearrayindex_2",
1097: "45 aload_3 localvariablearrayindex_3",
1098: "189 anewarray constantpoolindex2",
1099: "176 areturn",
1100: "190 arraylength",
1101: "58 astore localvariablearrayindex1",
1102: "75 astore_0 localvariablearrayindex_0",
1103: "76 astore_1 localvariablearrayindex_1",
1104: "77 astore_2 localvariablearrayindex_2",
1105: "78 astore_3 localvariablearrayindex_3",
1106: "191 athrow",
1107: "51 baload",
1108: "84 bastore",
1109: "16 bipush signedbyte",
1110: "52 caload",
1111: "85 castore",
1112: "192 checkcast constantpoolindex2",
1113: "144 d2f",
1114: "142 d2i",
1115: "143 d2l",
1116: "99 dadd",
1117: "49 daload",
1118: "82 dastore",
1119: "152 dcmpg",
1120: "151 dcmpl",
1121: "14 dconst_0",
1122: "15 dconst_1",
1123: "111 ddiv",
1124: "24 dload localvariablearrayindex1",
1125: "38 dload_0 localvariablearrayindex_0",
1126: "39 dload_1 localvariablearrayindex_1",
1127: "40 dload_2 localvariablearrayindex_2",
1128: "41 dload_3 localvariablearrayindex_3",
1129: "107 dmul",
1130: "119 dneg",
1131: "115 drem",
1132: "175 dreturn",
1133: "57 dstore localvariablearrayindex1",
1134: "71 dstore_0 localvariablearrayindex_0",
1135: "72 dstore_1 localvariablearrayindex_1",
1136: "73 dstore_2 localvariablearrayindex_2",
1137: "74 dstore_3 localvariablearrayindex_3",
1138: "103 dsub",
1139: "89 dup",
1140: "90 dup_x1",
1141: "91 dup_x2",
1142: "92 dup2",
1143: "93 dup2_x1",
1144: "94 dup2_x2",
1145: "141 f2d",
1146: "139 f2i",
1147: "140 f2l",
1148: "98 fadd",
1149: "48 faload",
1150: "81 fastore",
1151: "150 fcmpg",
1152: "149 fcmpl",
1153: "11 fconst_0",
1154: "12 fconst_1",
1155: "13 fconst_2",
1156: "110 fdiv",
1157: "23 fload localvariablearrayindex1",
1158: "34 fload_0 localvariablearrayindex_0",
1159: "35 fload_1 localvariablearrayindex_1",
1160: "36 fload_2 localvariablearrayindex_2",
1161: "37 fload_3 localvariablearrayindex_3",
1162: "106 fmul",
1163: "118 fneg",
1164: "114 frem",
1165: "174 freturn",
1166: "56 fstore localvariablearrayindex1",
1167: "67 fstore_0 localvariablearrayindex_0",
1168: "68 fstore_1 localvariablearrayindex_1",
1169: "69 fstore_2 localvariablearrayindex_2",
1170: "70 fstore_3 localvariablearrayindex_3",
1171: "102 fsub",
1172: "180 getfield constantpoolindex2",
1173: "178 getstatic constantpoolindex2",
1174: "167 goto branchoffset2",
1175: "200 goto_w branchoffset4",
1176: "145 i2b",
1177: "146 i2c",
1178: "135 i2d",
1179: "134 i2f",
1180: "133 i2l",
1181: "147 i2s",
1182: "96 iadd",
1183: "46 iaload",
1184: "126 iand",
1185: "79 iastore",
1186: "2 iconst_m1",
1187: "3 iconst_0",
1188: "4 iconst_1",
1189: "5 iconst_2",
1190: "6 iconst_3",
1191: "7 iconst_4",
1192: "8 iconst_5",
1193: "108 idiv",
1194: "165 if_acmpeq",
1195: "166 if_acmpne",
1196: "159 if_icmpeq branchoffset2",
1197: "160 if_icmpne branchoffset2",
1198: "161 if_icmplt branchoffset2",
1199: "162 if_icmpge branchoffset2",
1200: "163 if_icmpgt branchoffset2",
1201: "164 if_icmple branchoffset2",
1202: "153 ifeq branchoffset2",
1203: "154 ifne branchoffset2",
1204: "155 iflt branchoffset2",
1205: "156 ifge branchoffset2",
1206: "157 ifgt branchoffset2",
1207: "158 ifle branchoffset2",
1208: "199 ifnonnull branchoffset2",
1209: "198 ifnull branchoffset2",
1210: "132 iinc localvariablearrayindex1 signedbyte",
1211: "21 iload localvariablearrayindex1",
1212: "26 iload_0 localvariablearrayindex_0",
1213: "27 iload_1 localvariablearrayindex_1",
1214: "28 iload_2 localvariablearrayindex_2",
1215: "29 iload_3 localvariablearrayindex_3",
1216: "104 imul",
1217: "116 ineg",
1218: "193 instanceof constantpoolindex2",
1219: "185 invokeinterface constantpoolindex2 signedbyte signedbyte",
1220: "183 invokespecial constantpoolindex2",
1221: "184 invokestatic constantpoolindex2",
1222: "182 invokevirtual constantpoolindex2", "128 ior",
1223: "112 irem", "172 ireturn", "120 ishl", "122 ishr",
1224: "54 istore localvariablearrayindex1",
1225: "59 istore_0 localvariablearrayindex_0",
1226: "60 istore_1 localvariablearrayindex_1",
1227: "61 istore_2 localvariablearrayindex_2",
1228: "62 istore_3 localvariablearrayindex_3",
1229: "100 isub", "124 iushr", "130 ixor",
1230: "168 jsr branchoffset2",
1231: "201 jsr_w branchoffset4", "138 l2d", "137 l2f",
1232: "136 l2i", "97 ladd", "47 laload", "127 land",
1233: "80 lastore", "148 lcmp", "9 lconst_0", "10 lconst_1",
1234: "18 ldc constantpoolindex1",
1235: "19 ldc_w constantpoolindex2",
1236: "20 ldc2_w constantpoolindex2", "109 ldiv",
1237: "22 lload localvariablearrayindex1",
1238: "30 lload_0 localvariablearrayindex_0",
1239: "31 lload_1 localvariablearrayindex_1",
1240: "32 lload_2 localvariablearrayindex_2",
1241: "33 lload_3 localvariablearrayindex_3", "105 lmul",
1242: "117 lneg", "171 lookupswitch lookupswitch", "129 lor",
1243: "113 lrem", "173 lreturn", "121 lshl", "123 lshr",
1244: "55 lstore localvariablearrayindex1",
1245: "63 lstore_0 localvariablearrayindex_0",
1246: "64 lstore_1 localvariablearrayindex_1",
1247: "65 lstore_2 localvariablearrayindex_2",
1248: "66 lstore_3 localvariablearrayindex_3", "101 lsub",
1249: "125 lushr", "131 lxor", "194 monitorenter",
1250: "195 monitorexit",
1251: "197 multianewarray constantpoolindex2 unsignedbyte",
1252: "187 new constantpoolindex2",
1253: "188 newarray atype", "0 nop", "87 pop",
1254: "88 pop2", "181 putfield constantpoolindex2",
1255: "179 putstatic constantpoolindex2",
1256: "169 ret localvariablearrayindex1", "177 return",
1257: "53 saload", "86 sastore",
1258: "17 sipush signedshort", "95 swap",
1259: "170 tableswitch tableswitch", "196 wide wide", };
1260: private static final String[] wideInstructions = new String[] {
1261: "21 iload localvariablearrayindex2",
1262: "23 fload localvariablearrayindex2",
1263: "25 aload localvariablearrayindex2",
1264: "22 lload localvariablearrayindex2",
1265: "24 dload localvariablearrayindex2",
1266: "54 istore localvariablearrayindex2",
1267: "56 fstore localvariablearrayindex2",
1268: "58 astore localvariablearrayindex2",
1269: "55 lstore localvariablearrayindex2",
1270: "57 dstore localvariablearrayindex2",
1271: "169 ret localvariablearrayindex2",
1272: "132 iinc localvariablearrayindex2 signedshort", };
1273: private static final Instruction[] opcodeToInstruction = new Instruction[256];
1274: private static final Instruction[] opcodeToWideInstruction = new Instruction[256];
1275: static {
1276: compileInstructions(instructions, opcodeToInstruction);
1277: compileInstructions(wideInstructions, opcodeToWideInstruction);
1278: }
1279:
1280: private static void compileInstructions(String[] instructions,
1281: Instruction[] opcodeToInstruction) {
1282: for (int j = 0; j < instructions.length; ++j) {
1283: StringTokenizer st = new StringTokenizer(instructions[j]);
1284: String os = st.nextToken();
1285: int opcode = Integer.parseInt(os);
1286: String mnemonic = st.nextToken();
1287: Operand[] operands = null;
1288: if (st.hasMoreTokens()) {
1289: List l = new ArrayList();
1290: while (st.hasMoreTokens()) {
1291: String s = st.nextToken();
1292: Operand operand;
1293: if (s.equals("constantpoolindex1")) {
1294: operand = new Operand() {
1295: public void disasm(
1296: DataInputStream dis,
1297: int instructionOffset,
1298: LocalVariableTableAttribute localVariableTableAttribute,
1299: Disassembler d) throws IOException {
1300: short index = (short) (0xff & dis
1301: .readByte());
1302: d.printConstantPoolEntry(index);
1303: }
1304: };
1305: } else if (s.equals("constantpoolindex2")) {
1306: operand = new Operand() {
1307: public void disasm(
1308: DataInputStream dis,
1309: int instructionOffset,
1310: LocalVariableTableAttribute localVariableTableAttribute,
1311: Disassembler d) throws IOException {
1312: d.printConstantPoolEntry(dis
1313: .readShort());
1314: }
1315: };
1316: } else if (s.equals("localvariablearrayindex1")) {
1317: operand = new Operand() {
1318: public void disasm(
1319: DataInputStream dis,
1320: int instructionOffset,
1321: LocalVariableTableAttribute localVariableTableAttribute,
1322: Disassembler d) throws IOException {
1323: short index = dis.readByte();
1324: d.print(index);
1325: if (localVariableTableAttribute != null) {
1326: d
1327: .print(" ("
1328: + localVariableTableAttribute
1329: .find(
1330: index,
1331: instructionOffset + 2)
1332: + ")");
1333: }
1334: }
1335: };
1336: } else if (s.equals("localvariablearrayindex2")) {
1337: operand = new Operand() {
1338: public void disasm(
1339: DataInputStream dis,
1340: int instructionOffset,
1341: LocalVariableTableAttribute localVariableTableAttribute,
1342: Disassembler d) throws IOException {
1343: short index = dis.readShort();
1344: d.print(index);
1345: if (localVariableTableAttribute != null) {
1346: d
1347: .print(" ("
1348: + localVariableTableAttribute
1349: .find(
1350: index,
1351: instructionOffset + 3)
1352: + ")");
1353: }
1354: }
1355: };
1356: } else if (s.startsWith("localvariablearrayindex_")) {
1357: final short index = Short.parseShort(s
1358: .substring(s.length() - 1));
1359: operand = new Operand() {
1360: public void disasm(
1361: DataInputStream dis,
1362: int instructionOffset,
1363: LocalVariableTableAttribute localVariableTableAttribute,
1364: Disassembler d) throws IOException {
1365: if (localVariableTableAttribute != null) {
1366: d
1367: .print("("
1368: + localVariableTableAttribute
1369: .find(
1370: index,
1371: instructionOffset + 1)
1372: + ")");
1373: }
1374: }
1375: };
1376: } else if (s.equals("branchoffset2")) {
1377: operand = new Operand() {
1378: public void disasm(
1379: DataInputStream dis,
1380: int instructionOffset,
1381: LocalVariableTableAttribute localVariableTableAttribute,
1382: Disassembler d) throws IOException {
1383: d.print(instructionOffset
1384: + dis.readShort());
1385: }
1386: };
1387: } else if (s.equals("branchoffset4")) {
1388: operand = new Operand() {
1389: public void disasm(
1390: DataInputStream dis,
1391: int instructionOffset,
1392: LocalVariableTableAttribute localVariableTableAttribute,
1393: Disassembler d) throws IOException {
1394: d.print(instructionOffset
1395: + dis.readInt());
1396: }
1397: };
1398: } else if (s.equals("signedbyte")) {
1399: operand = new Operand() {
1400: public void disasm(
1401: DataInputStream dis,
1402: int instructionOffset,
1403: LocalVariableTableAttribute localVariableTableAttribute,
1404: Disassembler d) throws IOException {
1405: d.print(dis.readByte());
1406: }
1407: };
1408: } else if (s.equals("unsignedbyte")) {
1409: operand = new Operand() {
1410: public void disasm(
1411: DataInputStream dis,
1412: int instructionOffset,
1413: LocalVariableTableAttribute localVariableTableAttribute,
1414: Disassembler d) throws IOException {
1415: d.print(0xff & dis.readByte());
1416: }
1417: };
1418: } else if (s.equals("atype")) {
1419: operand = new Operand() {
1420: public void disasm(
1421: DataInputStream dis,
1422: int instructionOffset,
1423: LocalVariableTableAttribute localVariableTableAttribute,
1424: Disassembler d) throws IOException {
1425: byte b = dis.readByte();
1426: d
1427: .print(b == 4 ? "BOOLEAN"
1428: : b == 5 ? "CHAR"
1429: : b == 6 ? "FLOAT"
1430: : b == 7 ? "DOUBLE"
1431: : b == 8 ? "BYTE"
1432: : b == 9 ? "SHORT"
1433: : b == 10 ? "INT"
1434: : b == 11 ? "LONG"
1435: : new Integer(
1436: 0xff & b)
1437: .toString());
1438: }
1439: };
1440: } else if (s.equals("signedshort")) {
1441: operand = new Operand() {
1442: public void disasm(
1443: DataInputStream dis,
1444: int instructionOffset,
1445: LocalVariableTableAttribute localVariableTableAttribute,
1446: Disassembler d) throws IOException {
1447: d.print(dis.readShort());
1448: }
1449: };
1450: } else if (s.equals("tableswitch")) {
1451: operand = new Operand() {
1452: public void disasm(
1453: DataInputStream dis,
1454: int instructionOffset,
1455: LocalVariableTableAttribute localVariableTableAttribute,
1456: Disassembler d) throws IOException {
1457: int npads = 3 - (instructionOffset % 4);
1458: for (int i = 0; i < npads; ++i)
1459: if (dis.readByte() != (byte) 0)
1460: throw new RuntimeException(
1461: "Non-zero pad byte in \"tableswitch\"");
1462: d.print("default => "
1463: + (instructionOffset + dis
1464: .readInt()));
1465: int low = dis.readInt();
1466: int high = dis.readInt();
1467: for (int i = low; i <= high; ++i) {
1468: int offset = dis.readInt();
1469: d
1470: .print(", "
1471: + i
1472: + " => "
1473: + (instructionOffset + offset));
1474: }
1475: }
1476: };
1477: } else if (s.equals("lookupswitch")) {
1478: operand = new Operand() {
1479: public void disasm(
1480: DataInputStream dis,
1481: int instructionOffset,
1482: LocalVariableTableAttribute localVariableTableAttribute,
1483: Disassembler d) throws IOException {
1484: int npads = 3 - (instructionOffset % 4);
1485: for (int i = 0; i < npads; ++i) {
1486: byte b = dis.readByte();
1487: if (b != (byte) 0)
1488: d.print("Padding byte #" + i
1489: + " is " + (b & 0xff));
1490: }
1491: d.print("default => "
1492: + (instructionOffset + dis
1493: .readInt()));
1494: int npairs = dis.readInt();
1495: for (int i = 0; i < npairs; ++i) {
1496: int match = dis.readInt();
1497: int offset = dis.readInt();
1498: d
1499: .print(", "
1500: + match
1501: + " => "
1502: + (instructionOffset + offset));
1503: }
1504: }
1505: };
1506: } else if (s.equals("wide")) {
1507: operand = new Operand() {
1508: public void disasm(
1509: DataInputStream dis,
1510: int instructionOffset,
1511: LocalVariableTableAttribute localVariableTableAttribute,
1512: Disassembler d) throws IOException {
1513: int subopcode = 0xff & dis.readByte();
1514: Instruction wideInstruction = opcodeToWideInstruction[subopcode];
1515: if (wideInstruction == null)
1516: throw new RuntimeException(
1517: "Invalid opcode "
1518: + subopcode
1519: + " after opcode WIDE");
1520: d.disasmInstruction(wideInstruction,
1521: dis, instructionOffset,
1522: localVariableTableAttribute);
1523: }
1524: };
1525: } else {
1526: throw new RuntimeException("Unknown operand \""
1527: + s + "\"");
1528: }
1529: l.add(operand);
1530: }
1531: operands = (Operand[]) l.toArray(new Operand[l.size()]);
1532: }
1533: opcodeToInstruction[opcode] = new Instruction(mnemonic,
1534: operands);
1535: }
1536: }
1537:
1538: private ConstantPoolInfo getConstantPoolEntry(short index) {
1539: if ((index & 0xffff) < this .constantPool.length) {
1540: return this .constantPool[index & 0xffff];
1541: } else {
1542: return null;
1543: }
1544: }
1545:
1546: private void printConstantPoolEntry(short index) {
1547: if (this .verbose)
1548: this .print(index + " (");
1549: if ((index & 0xffff) < this .constantPool.length) {
1550: ConstantPoolInfo cpi = this .constantPool[index];
1551: if (cpi == null) {
1552: this .print("NULL CONSTANT POOL ENTRY");
1553: } else {
1554: cpi.print();
1555: }
1556: } else {
1557: this .print("CONSTANT POOL INDEX OUT OF RANGE");
1558: }
1559: if (this .verbose)
1560: this .print(")");
1561: }
1562:
1563: private void printConstantPoolEntry(String label, short index) {
1564: if (this .verbose) {
1565: if (label != null)
1566: this .print(label + " = ");
1567: this .print(index + " (");
1568: this .printConstantPoolEntry(index);
1569: this .print(")");
1570: } else {
1571: if (label != null) {
1572: if (label.endsWith("_index"))
1573: label = label.substring(0, label.length() - 6);
1574: this .print(label + " = ");
1575: }
1576: this .printConstantPoolEntry(index);
1577: }
1578: }
1579:
1580: private static class Instruction {
1581:
1582: /**
1583: *
1584: * @param mnemonic
1585: * @param operands <code>null</code> is equivalent to "zero operands"
1586: */
1587: public Instruction(String mnemonic, Operand[] operands) {
1588: this .mnemonic = mnemonic;
1589: this .operands = operands;
1590: }
1591:
1592: public String getMnemonic() {
1593: return this .mnemonic;
1594: }
1595:
1596: public Operand[] getOperands() {
1597: return this .operands;
1598: }
1599:
1600: private final String mnemonic;
1601: private final Operand[] operands;
1602: }
1603:
1604: private interface Operand {
1605: void disasm(
1606: DataInputStream dis,
1607: int instructionOffset,
1608: LocalVariableTableAttribute localVariableTableAttribute,
1609: Disassembler d) throws IOException;
1610: }
1611:
1612: /**
1613: * A variant of {@link PrintWriter} that allows for convenient indentation
1614: * of output lines.
1615: */
1616: private static class IndentPrintWriter extends PrintWriter {
1617: public IndentPrintWriter(OutputStream os) {
1618: super (os);
1619: }
1620:
1621: public void write(char[] cbuf, int off, int len) {
1622: this .handleIndentation();
1623: super .write(cbuf, off, len);
1624: }
1625:
1626: public void write(String str, int off, int len) {
1627: this .handleIndentation();
1628: super .write(str, off, len);
1629: }
1630:
1631: public void println() {
1632: super .println();
1633: this .atBOL = true;
1634: }
1635:
1636: public void indent() {
1637: ++this .indentation;
1638: }
1639:
1640: public void unindent() {
1641: --this .indentation;
1642: }
1643:
1644: private void handleIndentation() {
1645: if (this .atBOL) {
1646: for (int i = 0; i < this .indentation; ++i) {
1647: super .write(INDENTATION_CHARS, 0,
1648: INDENTATION_CHARS.length);
1649: }
1650: this .atBOL = false;
1651: }
1652: }
1653:
1654: private final static char[] INDENTATION_CHARS = new char[] {
1655: ' ', ' ' };
1656:
1657: private boolean atBOL = true;
1658: private int indentation = 0;
1659: }
1660:
1661: private abstract static class ConstantPoolInfo {
1662: public abstract void print();
1663:
1664: public int getSizeInConstantPool() {
1665: return 1;
1666: }
1667: }
1668:
1669: private class ConstantUtf8Info extends ConstantPoolInfo {
1670: public ConstantUtf8Info(String value) {
1671: this .value = value;
1672: }
1673:
1674: public void print() {
1675: Disassembler.this .print(this .value);
1676: }
1677:
1678: public String getValue() {
1679: return this .value;
1680: }
1681:
1682: private String value;
1683: }
1684:
1685: private ConstantPoolInfo[] constantPool = null;
1686:
1687: private static class CountingInputStream extends InputStream {
1688: public CountingInputStream(InputStream is) {
1689: this .is = is;
1690: }
1691:
1692: public int read() throws IOException {
1693: int res = this .is.read();
1694: if (res != -1)
1695: ++this .count;
1696: return res;
1697: }
1698:
1699: public int read(byte[] b, int off, int len) throws IOException {
1700: int res = super .read(b, off, len);
1701: if (res != -1)
1702: this .count += res;
1703: return res;
1704: }
1705:
1706: public long getCount() {
1707: return this .count;
1708: }
1709:
1710: private InputStream is;
1711: private long count = 0L;
1712: }
1713:
1714: private static String decodeAccess(short n) {
1715: StringBuffer sb = new StringBuffer();
1716: if ((n & 0x0007) == 0) {
1717: sb.append("package ");
1718: }
1719: if ((n & 0x0007) == 1) {
1720: sb.append("public ");
1721: n &= ~0x0007;
1722: }
1723: if ((n & 0x0007) == 2) {
1724: sb.append("private ");
1725: n &= ~0x0007;
1726: }
1727: if ((n & 0x0007) == 4) {
1728: sb.append("protected ");
1729: n &= ~0x0007;
1730: }
1731: if ((n & 0x0008) != 0) {
1732: sb.append("static ");
1733: n &= ~0x0008;
1734: }
1735: if ((n & 0x0010) != 0) {
1736: sb.append("final ");
1737: n &= ~0x0010;
1738: }
1739: if ((n & 0x0020) != 0) {
1740: sb.append("super/synchronized ");
1741: n &= ~0x0020;
1742: }
1743: if ((n & 0x0040) != 0) {
1744: sb.append("volatile ");
1745: n &= ~0x0040;
1746: }
1747: if ((n & 0x0080) != 0) {
1748: sb.append("transient ");
1749: n &= ~0x0080;
1750: }
1751: if ((n & 0x0100) != 0) {
1752: sb.append("native ");
1753: n &= ~0x0100;
1754: }
1755: if ((n & 0x0200) != 0) {
1756: sb.append("interface ");
1757: n &= ~0x0200;
1758: }
1759: if ((n & 0x0400) != 0) {
1760: sb.append("abstract ");
1761: n &= ~0x0400;
1762: }
1763: if ((n & 0x0800) != 0) {
1764: sb.append("strict ");
1765: n &= ~0x0800;
1766: }
1767: if (n != 0)
1768: sb.append("+ " + n + " ");
1769: return sb.substring(0, sb.length() - 1);
1770: }
1771:
1772: private static String decodeDescriptor(String d) {
1773: int brackets = 0;
1774: while (d.charAt(0) == '[') {
1775: ++brackets;
1776: d = d.substring(1);
1777: }
1778: if (d.length() == 1) {
1779: int idx = "BCDFIJSZ".indexOf(d.charAt(0));
1780: if (idx != -1)
1781: d = PRIMITIVES[idx];
1782: } else if (d.charAt(0) == 'L'
1783: && d.charAt(d.length() - 1) == ';') {
1784: d = d.substring(1, d.length() - 1);
1785: }
1786: for (; brackets > 0; --brackets)
1787: d += "[]";
1788: return d;
1789: }
1790:
1791: private static final String[] PRIMITIVES = { "byte", "char",
1792: "double", "float", "int", "long", "short", "boolean" };
1793: }
|