/******************************************************************************* * Copyright (c) 2002,2006 IBM Corporation. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package com.ibm.wala.shrikeBT.shrikeCT.tools; import java.io.BufferedWriter; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.lang.reflect.Field; import com.ibm.wala.shrikeBT.Constants; import com.ibm.wala.shrikeBT.Decoder.InvalidBytecodeException; import com.ibm.wala.shrikeBT.Disassembler; import com.ibm.wala.shrikeBT.shrikeCT.CTDecoder; import com.ibm.wala.shrikeBT.shrikeCT.ClassInstrumenter; import com.ibm.wala.shrikeBT.shrikeCT.OfflineInstrumenter; import com.ibm.wala.shrikeCT.AnnotationsReader; import com.ibm.wala.shrikeCT.AnnotationsReader.AnnotationAttribute; import com.ibm.wala.shrikeCT.ClassConstants; import com.ibm.wala.shrikeCT.ClassReader; import com.ibm.wala.shrikeCT.CodeReader; import com.ibm.wala.shrikeCT.ConstantPoolParser; import com.ibm.wala.shrikeCT.ConstantValueReader; import com.ibm.wala.shrikeCT.InvalidClassFileException; import com.ibm.wala.shrikeCT.LineNumberTableReader; import com.ibm.wala.shrikeCT.LocalVariableTableReader; import com.ibm.wala.shrikeCT.SignatureReader; import com.ibm.wala.shrikeCT.SourceFileReader; /** * This class prints the contents of a class file. It's like an alternative to javap that shows more information. * * In Unix I run it like this: java -cp ~/dev/shrike/shrike com.ibm.wala.shrikeBT.shrikeCT.tools.ClassPrinter test.jar This will * print the contents of every class in the JAR file. * * @author roca */ public class ClassPrinter { final private PrintWriter w; private boolean printLineNumberInfo = true; private boolean printConstantPool = true; /** * Get ready to print a class to the given output stream. */ public ClassPrinter(PrintWriter w) { this.w = w; } /** * Controls whether to print line number information. The default is 'true'. */ public void setPrintLineNumberInfo(boolean b) { printLineNumberInfo = b; } /** * Controls whether to print all the constant pool entries. The default is 'true'. */ public void setPrintConstantPool(boolean b) { printConstantPool = b; } public static void main(String[] args) throws Exception { OfflineInstrumenter oi = new OfflineInstrumenter(true); args = oi.parseStandardArgs(args); PrintWriter w = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out))); ClassPrinter p = new ClassPrinter(w); ClassInstrumenter ci; oi.beginTraversal(); while ((ci = oi.nextClass()) != null) { try { p.doClass(ci.getReader()); } finally { w.flush(); } } oi.close(); } private static final char[] hexChars = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; private static String makeHex(byte[] bytes, int pos, int len, int padTo) { StringBuffer b = new StringBuffer(); for (int i = pos; i < pos + len; i++) { byte v = bytes[i]; b.append(hexChars[(v >> 4) & 0xF]); b.append(hexChars[v & 0xF]); } while (b.length() < padTo) { b.append(' '); } return b.toString(); } private static String makeChars(byte[] bytes, int pos, int len) { StringBuffer b = new StringBuffer(); for (int i = pos; i < pos + len; i++) { char ch = (char) bytes[i]; if (ch < 32 || ch > 127) { b.append('.'); } else { b.append(ch); } } return b.toString(); } private static String getClassName(ClassReader cr, int index) throws InvalidClassFileException { if (index == 0) { return "any"; } else { return cr.getCP().getCPClass(index); } } private static String dumpFlags(int flags) { StringBuffer buf = new StringBuffer(); Class<Constants> c = Constants.class; Field[] fs = c.getDeclaredFields(); for (int i = 0; i < fs.length; i++) { String name = fs[i].getName(); if (name.startsWith("ACC_")) { int val; try { val = fs[i].getInt(null); } catch (IllegalArgumentException e) { throw new Error(e.getMessage()); } catch (IllegalAccessException e) { throw new Error(e.getMessage()); } if ((flags & val) != 0) { if (buf.length() > 0) { buf.append(" "); } buf.append(name.substring(4).toLowerCase()); } } } return "0x" + Integer.toString(16, flags) + "(" + buf.toString() + ")"; } private void dumpAttributes(ClassReader cr, int i, ClassReader.AttrIterator attrs) throws InvalidClassFileException, InvalidBytecodeException, IOException { for (; attrs.isValid(); attrs.advance()) { String name = attrs.getName(); w.write(" " + name + ": @" + Integer.toString(attrs.getRawOffset(), 16) + "\n"); if (name.equals("Code")) { CodeReader code = new CodeReader(attrs); w.write(" maxstack: " + code.getMaxStack() + "\n"); w.write(" maxlocals: " + code.getMaxLocals() + "\n"); w.write(" bytecode:\n"); int[] rawHandlers = code.getRawHandlers(); CTDecoder decoder = new CTDecoder(code); decoder.decode(); Disassembler disasm = new Disassembler(decoder.getInstructions(), decoder.getHandlers(), decoder .getInstructionsToBytecodes()); disasm.disassembleTo(" ", w); w.write(" exception handlers:\n"); for (int e = 0; e < rawHandlers.length; e += 4) { w.write(" " + rawHandlers[e] + " to " + rawHandlers[e + 1] + " catch " + getClassName(cr, rawHandlers[e + 3]) + " at " + rawHandlers[e + 2] + "\n"); } ClassReader.AttrIterator codeAttrs = new ClassReader.AttrIterator(); code.initAttributeIterator(codeAttrs); for (; codeAttrs.isValid(); codeAttrs.advance()) { String cName = codeAttrs.getName(); w.write(" " + cName + ": " + Integer.toString(codeAttrs.getRawOffset(), 16) + "\n"); } if (printLineNumberInfo) { int[] map = LineNumberTableReader.makeBytecodeToSourceMap(code); if (map != null) { w.write(" line number map:\n"); String line = null; int count = 0; for (int j = 0; j < map.length; j++) { String line2 = " " + j + ": " + map[j]; if (line == null || line2 == null || !line2.substring(line2.indexOf(':')).equals(line.substring(line.indexOf(':')))) { if (count > 1) { w.write(" (" + count + " times)\n"); } else if (count > 0) { w.write("\n"); } count = 0; line = line2; w.write(line); } count++; } if (count > 1) { w.write(" (" + count + " times)\n"); } else if (count > 0) { w.write("\n"); } } } int[][] locals = LocalVariableTableReader.makeVarMap(code); if (locals != null) { w.write(" local variable map:\n"); String line = null; int count = 0; for (int j = 0; j < locals.length; j++) { int[] vars = locals[j]; String line2 = null; if (vars != null) { StringBuffer buf = new StringBuffer(); buf.append(" " + j + ":"); for (int k = 0; k < vars.length; k += 2) { if (vars[k] != 0) { String n = cr.getCP().getCPUtf8(vars[k]) + "(" + cr.getCP().getCPUtf8(vars[k + 1]) + ")"; buf.append(" " + (k / 2) + ":" + n); } } line2 = buf.toString(); } if (line == null || line2 == null || !line2.substring(line2.indexOf(':')).equals(line.substring(line.indexOf(':')))) { if (count > 1) { w.write(" (" + count + " times)\n"); } else if (count > 0) { w.write("\n"); } count = 0; line = line2; if (line != null) { w.write(line); } } if (line != null) { count++; } } if (count > 1) { w.write(" (" + count + " times)\n"); } else if (count > 0) { w.write("\n"); } } } else if (name.equals("ConstantValue")) { ConstantValueReader cv = new ConstantValueReader(attrs); w.write(" value: " + getCPItemString(cr.getCP(), cv.getValueCPIndex()) + "\n"); } else if (name.equals("SourceFile")) { SourceFileReader sr = new SourceFileReader(attrs); w.write(" file: " + cr.getCP().getCPUtf8(sr.getSourceFileCPIndex()) + "\n"); } else if (name.equals("Signature")) { SignatureReader sr = new SignatureReader(attrs); w.write(" signature: " + cr.getCP().getCPUtf8(sr.getSignatureCPIndex()) + "\n"); } else if (AnnotationsReader.isKnownAnnotation(name)) { AnnotationsReader r = new AnnotationsReader(attrs, name); printAnnotations(cr, attrs, r); } else { int len = attrs.getDataSize(); int pos = attrs.getDataOffset(); while (len > 0) { int amount = Math.min(16, len); w.write(" " + makeHex(cr.getBytes(), pos, amount, 32) + " " + makeChars(cr.getBytes(), pos, amount) + "\n"); len -= amount; pos += amount; } } } } private void printAnnotations(ClassReader cr, ClassReader.AttrIterator attrs, AnnotationsReader r) throws InvalidClassFileException { for (AnnotationAttribute annot : r.getAllAnnotations()) { w.write(" Annotation type: " + annot.type + "\n"); } } private static String getCPItemString(ConstantPoolParser cp, int i) throws InvalidClassFileException { int t = cp.getItemType(i); switch (t) { case ClassConstants.CONSTANT_Utf8: return "Utf8 " + quoteString(cp.getCPUtf8(i)); case ClassConstants.CONSTANT_Class: return "Class " + cp.getCPClass(i); case ClassConstants.CONSTANT_String: return "String " + quoteString(cp.getCPString(i)); case ClassConstants.CONSTANT_Integer: return "Integer " + cp.getCPInt(i); case ClassConstants.CONSTANT_Float: return "Float " + cp.getCPFloat(i); case ClassConstants.CONSTANT_Double: return "Double " + cp.getCPDouble(i); case ClassConstants.CONSTANT_Long: return "Long " + cp.getCPLong(i); case ClassConstants.CONSTANT_MethodRef: return "Method " + cp.getCPRefClass(i) + " " + cp.getCPRefName(i) + " " + cp.getCPRefType(i); case ClassConstants.CONSTANT_FieldRef: return "Field " + cp.getCPRefClass(i) + " " + cp.getCPRefName(i) + " " + cp.getCPRefType(i); case ClassConstants.CONSTANT_InterfaceMethodRef: return "InterfaceMethod " + cp.getCPRefClass(i) + " " + cp.getCPRefName(i) + " " + cp.getCPRefType(i); case ClassConstants.CONSTANT_NameAndType: return "NameAndType " + cp.getCPNATType(i) + " " + cp.getCPNATName(i); default: return "Unknown type " + t; } } private static String quoteString(String string) { StringBuffer buf = new StringBuffer(); buf.append('"'); for (int i = 0; i < string.length(); i++) { char ch = string.charAt(i); switch (ch) { case '\r': buf.append("\\r"); break; case '\n': buf.append("\\n"); break; case '\\': buf.append("\\\\"); break; case '\t': buf.append("\\t"); break; case '\"': buf.append("\\\""); break; default: if (ch >= 32 && ch <= 127) { buf.append(ch); } else { buf.append("\\u"); String h = makeHex(new byte[] { (byte) (ch >> 8), (byte) ch }, 0, 2, 0); for (int j = 4 - h.length(); j > 0; j--) { buf.append('0'); } buf.append(h); } } } buf.append('"'); return buf.toString(); } /** * Print a class. * * @throws IllegalArgumentException if cr is null */ public void doClass(final ClassReader cr) throws InvalidClassFileException, InvalidBytecodeException, IOException { if (cr == null) { throw new IllegalArgumentException("cr is null"); } w.write("Class: " + cr.getName() + "\n"); if (printConstantPool) { ConstantPoolParser cp = cr.getCP(); for (int i = 1; i < cp.getItemCount(); i++) { int t = cp.getItemType(i); if (t > 0) { w.write(" Constant pool item " + i + ": "); w.write(getCPItemString(cp, i)); w.write("\n"); } } } ClassReader.AttrIterator attrs = new ClassReader.AttrIterator(); cr.initClassAttributeIterator(attrs); dumpAttributes(cr, 0, attrs); w.write("\n"); int fieldCount = cr.getFieldCount(); w.write(fieldCount + " fields:\n"); for (int i = 0; i < fieldCount; i++) { w.write(cr.getFieldName(i) + " " + cr.getFieldType(i) + " " + dumpFlags(cr.getFieldAccessFlags(i)) + "\n"); cr.initFieldAttributeIterator(i, attrs); dumpAttributes(cr, i, attrs); } w.write("\n"); int methodCount = cr.getMethodCount(); w.write(methodCount + " methods:\n"); for (int i = 0; i < methodCount; i++) { w.write(cr.getMethodName(i) + " " + cr.getMethodType(i) + " " + dumpFlags(cr.getMethodAccessFlags(i)) + "\n"); cr.initMethodAttributeIterator(i, attrs); dumpAttributes(cr, i, attrs); } w.write("\n"); } }