Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 1 | /* Copyright 2006 aQute SARL |
| 2 | * Licensed under the Apache License, Version 2.0, see http://www.apache.org/licenses/LICENSE-2.0 */ |
| 3 | package aQute.libg.classdump; |
| 4 | |
| 5 | import java.io.*; |
| 6 | import java.lang.reflect.*; |
| 7 | |
| 8 | public class ClassDumper { |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 9 | /** |
| 10 | * <pre> |
| 11 | * ACC_PUBLIC 0x0001 Declared public; may be accessed from outside its |
| 12 | * package. |
| 13 | * ACC_FINAL 0x0010 Declared final; no subclasses allowed. |
| 14 | * ACC_SUPER 0x0020 Treat superclass methods specially when invoked by the |
| 15 | * invokespecial instruction. |
| 16 | * ACC_INTERFACE 0x0200 Is an interface, not a |
| 17 | * class. |
| 18 | * ACC_ABSTRACT 0x0400 Declared abstract; may not be instantiated. |
| 19 | * </pre> |
| 20 | * |
| 21 | * @param mod |
| 22 | */ |
| 23 | final static int ACC_PUBLIC = 0x0001; // Declared public; may be |
| 24 | // accessed |
| 25 | // from outside its package. |
| 26 | final static int ACC_FINAL = 0x0010; // Declared final; no |
| 27 | // subclasses |
| 28 | // allowed. |
| 29 | final static int ACC_SUPER = 0x0020; // Treat superclass methods |
| 30 | // specially when invoked by the |
| 31 | // invokespecial instruction. |
| 32 | final static int ACC_INTERFACE = 0x0200; // Is an interface, not a |
| 33 | // classs |
| 34 | final static int ACC_ABSTRACT = 0x0400; // Declared abstract; may |
| 35 | // not be |
| 36 | // instantiated. |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 37 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 38 | final static class Assoc { |
| 39 | Assoc(byte tag, int a, int b) { |
| 40 | this.tag = tag; |
| 41 | this.a = a; |
| 42 | this.b = b; |
| 43 | } |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 44 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 45 | byte tag; |
| 46 | int a; |
| 47 | int b; |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 48 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 49 | } |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 50 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 51 | final String path; |
| 52 | final static String NUM_COLUMN = "%-30s %d\n"; |
| 53 | final static String HEX_COLUMN = "%-30s %x\n"; |
| 54 | final static String STR_COLUMN = "%-30s %s\n"; |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 55 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 56 | PrintStream ps = System.err; |
| 57 | Object[] pool; |
| 58 | InputStream in; |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 59 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 60 | public ClassDumper(String path) throws Exception { |
| 61 | this(path, new FileInputStream(new File(path))); |
| 62 | } |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 63 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 64 | public ClassDumper(String path, InputStream in) throws IOException { |
| 65 | this.path = path; |
| 66 | this.in = in; |
| 67 | } |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 68 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 69 | public void dump(PrintStream ps) throws Exception { |
| 70 | if (ps != null) |
| 71 | this.ps = ps; |
| 72 | DataInputStream din = new DataInputStream(in); |
| 73 | parseClassFile(din); |
| 74 | din.close(); |
| 75 | } |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 76 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 77 | void parseClassFile(DataInputStream in) throws IOException { |
| 78 | int magic = in.readInt(); |
| 79 | if (magic != 0xCAFEBABE) |
| 80 | throw new IOException("Not a valid class file (no CAFEBABE header)"); |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 81 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 82 | ps.printf(HEX_COLUMN, "magic", magic); |
| 83 | int minor = in.readUnsignedShort(); // minor version |
| 84 | int major = in.readUnsignedShort(); // major version |
| 85 | ps.printf(STR_COLUMN, "version", "" + major + "." + minor); |
| 86 | int pool_size = in.readUnsignedShort(); |
| 87 | ps.printf(NUM_COLUMN, "pool size", pool_size); |
| 88 | pool = new Object[pool_size]; |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 89 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 90 | process: for (int poolIndex = 1; poolIndex < pool_size; poolIndex++) { |
| 91 | byte tag = in.readByte(); |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 92 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 93 | switch (tag) { |
| 94 | case 0 : |
| 95 | ps.printf("%30d tag (0)\n", poolIndex); |
| 96 | break process; |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 97 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 98 | case 1 : |
| 99 | String name = in.readUTF(); |
| 100 | pool[poolIndex] = name; |
| 101 | ps.printf("%30d tag(1) utf8 '%s'\n", poolIndex, name); |
| 102 | break; |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 103 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 104 | case 2 : |
| 105 | throw new IOException("Invalid tag " + tag); |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 106 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 107 | case 3 : |
| 108 | int i = in.readInt(); |
| 109 | pool[poolIndex] = Integer.valueOf(i); |
| 110 | ps.printf("%30d tag(3) int %s\n", poolIndex, i); |
| 111 | break; |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 112 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 113 | case 4 : |
| 114 | float f = in.readFloat(); |
| 115 | pool[poolIndex] = new Float(f); |
| 116 | ps.printf("%30d tag(4) float %s\n", poolIndex, f); |
| 117 | break; |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 118 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 119 | // For some insane optimization reason are |
| 120 | // the long and the double two entries in the |
| 121 | // constant pool. See 4.4.5 |
| 122 | case 5 : |
| 123 | long l = in.readLong(); |
| 124 | pool[poolIndex] = Long.valueOf(l); |
| 125 | ps.printf("%30d tag(5) long %s\n", poolIndex, l); |
| 126 | poolIndex++; |
| 127 | break; |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 128 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 129 | case 6 : |
| 130 | double d = in.readDouble(); |
| 131 | pool[poolIndex] = new Double(d); |
| 132 | ps.printf("%30d tag(6) double %s\n", poolIndex, d); |
| 133 | poolIndex++; |
| 134 | break; |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 135 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 136 | case 7 : |
| 137 | int class_index = in.readUnsignedShort(); |
| 138 | pool[poolIndex] = Integer.valueOf(class_index); |
| 139 | ps.printf("%30d tag(7) constant classs %d\n", poolIndex, class_index); |
| 140 | break; |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 141 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 142 | case 8 : |
| 143 | int string_index = in.readUnsignedShort(); |
| 144 | pool[poolIndex] = Integer.valueOf(string_index); |
| 145 | ps.printf("%30d tag(8) constant string %d\n", poolIndex, string_index); |
| 146 | break; |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 147 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 148 | case 9 : // Field ref |
| 149 | class_index = in.readUnsignedShort(); |
| 150 | int name_and_type_index = in.readUnsignedShort(); |
| 151 | pool[poolIndex] = new Assoc((byte) 9, class_index, name_and_type_index); |
| 152 | ps.printf("%30d tag(9) field ref %d/%d\n", poolIndex, class_index, name_and_type_index); |
| 153 | break; |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 154 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 155 | case 10 : // Method ref |
| 156 | class_index = in.readUnsignedShort(); |
| 157 | name_and_type_index = in.readUnsignedShort(); |
| 158 | pool[poolIndex] = new Assoc((byte) 10, class_index, name_and_type_index); |
| 159 | ps.printf("%30d tag(10) method ref %d/%d\n", poolIndex, class_index, name_and_type_index); |
| 160 | break; |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 161 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 162 | case 11 : // Interface and Method ref |
| 163 | class_index = in.readUnsignedShort(); |
| 164 | name_and_type_index = in.readUnsignedShort(); |
| 165 | pool[poolIndex] = new Assoc((byte) 11, class_index, name_and_type_index); |
| 166 | ps.printf("%30d tag(11) interface and method ref %d/%d\n", poolIndex, class_index, |
| 167 | name_and_type_index); |
| 168 | break; |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 169 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 170 | // Name and Type |
| 171 | case 12 : |
| 172 | int name_index = in.readUnsignedShort(); |
| 173 | int descriptor_index = in.readUnsignedShort(); |
| 174 | pool[poolIndex] = new Assoc(tag, name_index, descriptor_index); |
| 175 | ps.printf("%30d tag(12) name and type %d/%d\n", poolIndex, name_index, descriptor_index); |
| 176 | break; |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 177 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 178 | default : |
| 179 | throw new IllegalArgumentException("Unknown tag: " + tag); |
| 180 | } |
| 181 | } |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 182 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 183 | int access = in.readUnsignedShort(); // access |
| 184 | printAccess(access); |
| 185 | int this_class = in.readUnsignedShort(); |
| 186 | int super_class = in.readUnsignedShort(); |
| 187 | ps.printf("%-30s %x %s(#%d)\n", "this_class", access, pool[this_class], this_class); |
| 188 | ps.printf("%-30s %s(#%d)\n", "super_class", pool[super_class], super_class); |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 189 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 190 | int interfaces_count = in.readUnsignedShort(); |
| 191 | ps.printf(NUM_COLUMN, "interface count", interfaces_count); |
| 192 | for (int i = 0; i < interfaces_count; i++) { |
| 193 | int interface_index = in.readUnsignedShort(); |
| 194 | ps.printf("%-30s interface %s(#%d)", "interface count", pool[interface_index], interfaces_count); |
| 195 | } |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 196 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 197 | int field_count = in.readUnsignedShort(); |
| 198 | ps.printf(NUM_COLUMN, "field count", field_count); |
| 199 | for (int i = 0; i < field_count; i++) { |
| 200 | access = in.readUnsignedShort(); // access |
| 201 | printAccess(access); |
| 202 | int name_index = in.readUnsignedShort(); |
| 203 | int descriptor_index = in.readUnsignedShort(); |
| 204 | ps.printf("%-30s %x %s(#%d) %s(#%d)\n", "field def", access, pool[name_index], name_index, |
| 205 | pool[descriptor_index], descriptor_index); |
| 206 | doAttributes(in, " "); |
| 207 | } |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 208 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 209 | int method_count = in.readUnsignedShort(); |
| 210 | ps.printf(NUM_COLUMN, "method count", method_count); |
| 211 | for (int i = 0; i < method_count; i++) { |
| 212 | int access_flags = in.readUnsignedShort(); |
| 213 | printAccess(access_flags); |
| 214 | int name_index = in.readUnsignedShort(); |
| 215 | int descriptor_index = in.readUnsignedShort(); |
| 216 | ps.printf("%-30s %x %s(#%d) %s(#%d)\n", "method def", access_flags, pool[name_index], name_index, |
| 217 | pool[descriptor_index], descriptor_index); |
| 218 | doAttributes(in, " "); |
| 219 | } |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 220 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 221 | doAttributes(in, ""); |
| 222 | if (in.read() >= 0) |
| 223 | ps.printf("Extra bytes follow ..."); |
| 224 | } |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 225 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 226 | /** |
| 227 | * Called for each attribute in the class, field, or method. |
| 228 | * |
| 229 | * @param in |
| 230 | * The stream |
| 231 | * @throws IOException |
| 232 | */ |
| 233 | private void doAttributes(DataInputStream in, String indent) throws IOException { |
| 234 | int attribute_count = in.readUnsignedShort(); |
| 235 | ps.printf(NUM_COLUMN, indent + "attribute count", attribute_count); |
| 236 | for (int j = 0; j < attribute_count; j++) { |
| 237 | doAttribute(in, indent + j + ": "); |
| 238 | } |
| 239 | } |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 240 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 241 | /** |
| 242 | * Process a single attribute, if not recognized, skip it. |
| 243 | * |
| 244 | * @param in |
| 245 | * the data stream |
| 246 | * @throws IOException |
| 247 | */ |
| 248 | private void doAttribute(DataInputStream in, String indent) throws IOException { |
| 249 | int attribute_name_index = in.readUnsignedShort(); |
| 250 | long attribute_length = in.readInt(); |
| 251 | attribute_length &= 0xFFFF; |
| 252 | String attributeName = (String) pool[attribute_name_index]; |
| 253 | ps.printf("%-30s %s(#%d)\n", indent + "attribute", attributeName, attribute_name_index); |
| 254 | if ("RuntimeVisibleAnnotations".equals(attributeName)) |
| 255 | doAnnotations(in, indent); |
| 256 | else if ("SourceFile".equals(attributeName)) |
| 257 | doSourceFile(in, indent); |
| 258 | else if ("Code".equals(attributeName)) |
| 259 | doCode(in, indent); |
| 260 | else if ("LineNumberTable".equals(attributeName)) |
| 261 | doLineNumberTable(in, indent); |
| 262 | else if ("LocalVariableTable".equals(attributeName)) |
| 263 | doLocalVariableTable(in, indent); |
| 264 | else if ("InnerClasses".equals(attributeName)) |
| 265 | doInnerClasses(in, indent); |
| 266 | else if ("Exceptions".equals(attributeName)) |
| 267 | doExceptions(in, indent); |
| 268 | else if ("EnclosingMethod".equals(attributeName)) |
| 269 | doEnclosingMethod(in, indent); |
| 270 | else if ("Signature".equals(attributeName)) |
| 271 | doSignature(in, indent); |
| 272 | else if ("Synthetic".equals(attributeName)) |
| 273 | ; // Is empty! |
| 274 | else if ("Deprecated".equals(attributeName)) |
| 275 | ; // Is Empty |
| 276 | else { |
| 277 | ps.printf("%-30s %d\n", indent + "Unknown attribute, skipping", attribute_length); |
| 278 | if (attribute_length > 0x7FFFFFFF) { |
| 279 | throw new IllegalArgumentException("Attribute > 2Gb"); |
| 280 | } |
| 281 | byte buffer[] = new byte[(int) attribute_length]; |
| 282 | in.readFully(buffer); |
| 283 | printHex(buffer); |
| 284 | } |
| 285 | } |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 286 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 287 | /** |
| 288 | * <pre> |
| 289 | * Signature_attribute { |
| 290 | * u2 attribute_name_index; |
| 291 | * u4 attribute_length; |
| 292 | * u2 signature_index; |
| 293 | * } |
| 294 | * </pre> |
| 295 | * |
| 296 | * @param in |
| 297 | * @param indent |
| 298 | */ |
| 299 | void doSignature(DataInputStream in, String indent) throws IOException { |
| 300 | int signature_index = in.readUnsignedShort(); |
| 301 | ps.printf("%-30s %s(#%d)\n", indent + "signature", pool[signature_index], signature_index); |
| 302 | } |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 303 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 304 | /** |
| 305 | * <pre> |
| 306 | * EnclosingMethod_attribute { |
| 307 | * u2 attribute_name_index; |
| 308 | * u4 attribute_length; |
| 309 | * u2 class_index |
| 310 | * u2 method_index; |
| 311 | * } |
| 312 | * |
| 313 | * </pre> |
| 314 | */ |
| 315 | void doEnclosingMethod(DataInputStream in, String indent) throws IOException { |
| 316 | int class_index = in.readUnsignedShort(); |
| 317 | int method_index = in.readUnsignedShort(); |
| 318 | ps.printf("%-30s %s(#%d/c) %s\n", // |
| 319 | indent + "enclosing method", // |
| 320 | pool[((Integer) pool[class_index]).intValue()], // |
| 321 | class_index, // |
| 322 | (method_index == 0 ? "<>" : pool[method_index])); |
| 323 | } |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 324 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 325 | /** |
| 326 | * <pre> |
| 327 | * Exceptions_attribute { |
| 328 | * u2 attribute_name_index; |
| 329 | * u4 attribute_length; |
| 330 | * u2 number_of_exceptions; |
| 331 | * u2 exception_index_table[number_of_exceptions]; |
| 332 | * } |
| 333 | * </pre> |
| 334 | * |
| 335 | * @param in |
| 336 | * @param indent |
| 337 | */ |
| 338 | private void doExceptions(DataInputStream in, String indent) throws IOException { |
| 339 | int number_of_exceptions = in.readUnsignedShort(); |
| 340 | ps.printf(NUM_COLUMN, indent + "number of exceptions", number_of_exceptions); |
| 341 | StringBuilder sb = new StringBuilder(); |
| 342 | String del = ""; |
| 343 | for (int i = 0; i < number_of_exceptions; i++) { |
| 344 | int exception_index_table = in.readUnsignedShort(); |
| 345 | sb.append(del); |
| 346 | sb.append(pool[((Integer) pool[exception_index_table])]); |
| 347 | sb.append("(#"); |
| 348 | sb.append(exception_index_table); |
| 349 | sb.append("/c)"); |
| 350 | del = ", "; |
| 351 | } |
| 352 | ps.printf("%-30s %d: %s\n", indent + "exceptions", number_of_exceptions, sb); |
| 353 | } |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 354 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 355 | /** |
| 356 | * <pre> |
| 357 | * Code_attribute { |
| 358 | * u2 attribute_name_index; |
| 359 | * u4 attribute_length; |
| 360 | * u2 max_stack; |
| 361 | * u2 max_locals; |
| 362 | * u4 code_length; |
| 363 | * u1 code[code_length]; |
| 364 | * u2 exception_table_length; |
| 365 | * { u2 start_pc; |
| 366 | * u2 end_pc; |
| 367 | * u2 handler_pc; |
| 368 | * u2 catch_type; |
| 369 | * } exception_table[exception_table_length]; |
| 370 | * u2 attributes_count; |
| 371 | * attribute_info attributes[attributes_count]; |
| 372 | * } |
| 373 | * </pre> |
| 374 | * |
| 375 | * @param in |
| 376 | * @param pool |
| 377 | * @throws IOException |
| 378 | */ |
| 379 | private void doCode(DataInputStream in, String indent) throws IOException { |
| 380 | int max_stack = in.readUnsignedShort(); |
| 381 | int max_locals = in.readUnsignedShort(); |
| 382 | int code_length = in.readInt(); |
| 383 | ps.printf(NUM_COLUMN, indent + "max_stack", max_stack); |
| 384 | ps.printf(NUM_COLUMN, indent + "max_locals", max_locals); |
| 385 | ps.printf(NUM_COLUMN, indent + "code_length", code_length); |
| 386 | byte code[] = new byte[code_length]; |
| 387 | in.readFully(code); |
| 388 | printHex(code); |
| 389 | int exception_table_length = in.readUnsignedShort(); |
| 390 | ps.printf(NUM_COLUMN, indent + "exception_table_length", exception_table_length); |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 391 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 392 | for (int i = 0; i < exception_table_length; i++) { |
| 393 | int start_pc = in.readUnsignedShort(); |
| 394 | int end_pc = in.readUnsignedShort(); |
| 395 | int handler_pc = in.readUnsignedShort(); |
| 396 | int catch_type = in.readUnsignedShort(); |
| 397 | ps.printf("%-30s %d/%d/%d/%d\n", indent + "exception_table", start_pc, end_pc, handler_pc, catch_type); |
| 398 | } |
| 399 | doAttributes(in, indent + " "); |
| 400 | } |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 401 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 402 | /** |
| 403 | * We must find Class.forName references ... |
| 404 | * |
| 405 | * @param code |
| 406 | */ |
| 407 | protected void printHex(byte[] code) { |
| 408 | int index = 0; |
| 409 | while (index < code.length) { |
| 410 | StringBuilder sb = new StringBuilder(); |
| 411 | for (int i = 0; i < 16 && index < code.length; i++) { |
| 412 | String s = Integer.toHexString((0xFF & code[index++])).toUpperCase(); |
| 413 | if (s.length() == 1) |
| 414 | sb.append("0"); |
| 415 | sb.append(s); |
| 416 | sb.append(" "); |
| 417 | } |
| 418 | ps.printf(STR_COLUMN, "", sb.toString()); |
| 419 | } |
| 420 | } |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 421 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 422 | private void doSourceFile(DataInputStream in, String indent) throws IOException { |
| 423 | int sourcefile_index = in.readUnsignedShort(); |
| 424 | ps.printf("%-30s %s(#%d)\n", indent + "Source file", pool[sourcefile_index], sourcefile_index); |
| 425 | } |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 426 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 427 | private void doAnnotations(DataInputStream in, String indent) throws IOException { |
| 428 | int num_annotations = in.readUnsignedShort(); // # of annotations |
| 429 | ps.printf(NUM_COLUMN, indent + "Number of annotations", num_annotations); |
| 430 | for (int a = 0; a < num_annotations; a++) { |
| 431 | doAnnotation(in, indent); |
| 432 | } |
| 433 | } |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 434 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 435 | private void doAnnotation(DataInputStream in, String indent) throws IOException { |
| 436 | int type_index = in.readUnsignedShort(); |
| 437 | ps.printf("%-30s %s(#%d)", indent + "type", pool[type_index], type_index); |
| 438 | int num_element_value_pairs = in.readUnsignedShort(); |
| 439 | ps.printf(NUM_COLUMN, indent + "num_element_value_pairs", num_element_value_pairs); |
| 440 | for (int v = 0; v < num_element_value_pairs; v++) { |
| 441 | int element_name_index = in.readUnsignedShort(); |
| 442 | ps.printf(NUM_COLUMN, indent + "element_name_index", element_name_index); |
| 443 | doElementValue(in, indent); |
| 444 | } |
| 445 | } |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 446 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 447 | private void doElementValue(DataInputStream in, String indent) throws IOException { |
| 448 | int tag = in.readUnsignedByte(); |
| 449 | switch (tag) { |
| 450 | case 'B' : |
| 451 | case 'C' : |
| 452 | case 'D' : |
| 453 | case 'F' : |
| 454 | case 'I' : |
| 455 | case 'J' : |
| 456 | case 'S' : |
| 457 | case 'Z' : |
| 458 | case 's' : |
| 459 | int const_value_index = in.readUnsignedShort(); |
| 460 | ps.printf("%-30s %c %s(#%d)\n", indent + "element value", tag, pool[const_value_index], |
| 461 | const_value_index); |
| 462 | break; |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 463 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 464 | case 'e' : |
| 465 | int type_name_index = in.readUnsignedShort(); |
| 466 | int const_name_index = in.readUnsignedShort(); |
| 467 | ps.printf("%-30s %c %s(#%d) %s(#%d)\n", indent + "type+const", tag, pool[type_name_index], |
| 468 | type_name_index, pool[const_name_index], const_name_index); |
| 469 | break; |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 470 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 471 | case 'c' : |
| 472 | int class_info_index = in.readUnsignedShort(); |
| 473 | ps.printf("%-30s %c %s(#%d)\n", indent + "element value", tag, pool[class_info_index], class_info_index); |
| 474 | break; |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 475 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 476 | case '@' : |
| 477 | ps.printf("%-30s %c\n", indent + "sub annotation", tag); |
| 478 | doAnnotation(in, indent); |
| 479 | break; |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 480 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 481 | case '[' : |
| 482 | int num_values = in.readUnsignedShort(); |
| 483 | ps.printf("%-30s %c num_values=%d\n", indent + "sub element value", tag, num_values); |
| 484 | for (int i = 0; i < num_values; i++) { |
| 485 | doElementValue(in, indent); |
| 486 | } |
| 487 | break; |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 488 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 489 | default : |
| 490 | throw new IllegalArgumentException("Invalid value for Annotation ElementValue tag " + tag); |
| 491 | } |
| 492 | } |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 493 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 494 | /** |
| 495 | * <pre> |
| 496 | * LineNumberTable_attribute { |
| 497 | * u2 attribute_name_index; |
| 498 | * u4 attribute_length; |
| 499 | * u2 line_number_table_length; |
| 500 | * { u2 start_pc; |
| 501 | * u2 line_number; |
| 502 | * } line_number_table[line_number_table_length]; |
| 503 | * } |
| 504 | * |
| 505 | * </pre> |
| 506 | */ |
| 507 | void doLineNumberTable(DataInputStream in, String indent) throws IOException { |
| 508 | int line_number_table_length = in.readUnsignedShort(); |
| 509 | ps.printf(NUM_COLUMN, indent + "line number table length", line_number_table_length); |
| 510 | StringBuilder sb = new StringBuilder(); |
| 511 | for (int i = 0; i < line_number_table_length; i++) { |
| 512 | int start_pc = in.readUnsignedShort(); |
| 513 | int line_number = in.readUnsignedShort(); |
| 514 | sb.append(start_pc); |
| 515 | sb.append("/"); |
| 516 | sb.append(line_number); |
| 517 | sb.append(" "); |
| 518 | } |
| 519 | ps.printf("%-30s %d: %s\n", indent + "line number table", line_number_table_length, sb); |
| 520 | } |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 521 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 522 | /** |
| 523 | * <pre> |
| 524 | * LocalVariableTable_attribute { |
| 525 | * u2 attribute_name_index; |
| 526 | * u4 attribute_length; |
| 527 | * u2 local_variable_table_length; |
| 528 | * { u2 start_pc; |
| 529 | * u2 length; |
| 530 | * u2 name_index; |
| 531 | * u2 descriptor_index; |
| 532 | * u2 index; |
| 533 | * } local_variable_table[local_variable_table_length]; |
| 534 | * } |
| 535 | * </pre> |
| 536 | */ |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 537 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 538 | void doLocalVariableTable(DataInputStream in, String indent) throws IOException { |
| 539 | int local_variable_table_length = in.readUnsignedShort(); |
| 540 | ps.printf(NUM_COLUMN, indent + "local variable table length", local_variable_table_length); |
| 541 | for (int i = 0; i < local_variable_table_length; i++) { |
| 542 | int start_pc = in.readUnsignedShort(); |
| 543 | int length = in.readUnsignedShort(); |
| 544 | int name_index = in.readUnsignedShort(); |
| 545 | int descriptor_index = in.readUnsignedShort(); |
| 546 | int index = in.readUnsignedShort(); |
| 547 | ps.printf("%-30s %d: %d/%d %s(#%d) %s(#%d)\n", indent, index, start_pc, length, pool[name_index], |
| 548 | name_index, pool[descriptor_index], descriptor_index); |
| 549 | } |
| 550 | } |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 551 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 552 | /** |
| 553 | * <pre> |
| 554 | * InnerClasses_attribute { |
| 555 | * u2 attribute_name_index; |
| 556 | * u4 attribute_length; |
| 557 | * u2 number_of_classes; |
| 558 | * { u2 inner_class_info_index; |
| 559 | * u2 outer_class_info_index; |
| 560 | * u2 inner_name_index; |
| 561 | * u2 inner_class_access_flags; |
| 562 | * } classes[number_of_classes]; |
| 563 | * } |
| 564 | * </pre> |
| 565 | */ |
| 566 | void doInnerClasses(DataInputStream in, String indent) throws IOException { |
| 567 | int number_of_classes = in.readUnsignedShort(); |
| 568 | ps.printf(NUM_COLUMN, indent + "number of classes", number_of_classes); |
| 569 | for (int i = 0; i < number_of_classes; i++) { |
| 570 | int inner_class_info_index = in.readUnsignedShort(); |
| 571 | int outer_class_info_index = in.readUnsignedShort(); |
| 572 | int inner_name_index = in.readUnsignedShort(); |
| 573 | int inner_class_access_flags = in.readUnsignedShort(); |
| 574 | printAccess(inner_class_access_flags); |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 575 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 576 | String iname = "<>"; |
| 577 | String oname = iname; |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 578 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 579 | if (inner_class_info_index != 0) |
| 580 | iname = (String) pool[((Integer) pool[inner_class_info_index]).intValue()]; |
| 581 | if (outer_class_info_index != 0) |
| 582 | oname = (String) pool[((Integer) pool[outer_class_info_index]).intValue()]; |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 583 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 584 | ps.printf("%-30s %d: %x %s(#%d/c) %s(#%d/c) %s(#%d) \n", indent, i, inner_class_access_flags, iname, |
| 585 | inner_class_info_index, oname, outer_class_info_index, pool[inner_name_index], inner_name_index); |
| 586 | } |
| 587 | } |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 588 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 589 | void printClassAccess(int mod) { |
| 590 | ps.printf("%-30s", "Class Access"); |
| 591 | if ((ACC_PUBLIC & mod) != 0) |
| 592 | ps.print(" public"); |
| 593 | if ((ACC_FINAL & mod) != 0) |
| 594 | ps.print(" final"); |
| 595 | if ((ACC_SUPER & mod) != 0) |
| 596 | ps.print(" super"); |
| 597 | if ((ACC_INTERFACE & mod) != 0) |
| 598 | ps.print(" interface"); |
| 599 | if ((ACC_ABSTRACT & mod) != 0) |
| 600 | ps.print(" abstract"); |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 601 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 602 | ps.println(); |
| 603 | } |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 604 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 605 | void printAccess(int mod) { |
| 606 | ps.printf("%-30s", "Access"); |
| 607 | if (Modifier.isStatic(mod)) |
| 608 | ps.print(" static"); |
| 609 | if (Modifier.isAbstract(mod)) |
| 610 | ps.print(" abstract"); |
| 611 | if (Modifier.isPublic(mod)) |
| 612 | ps.print(" public"); |
| 613 | if (Modifier.isFinal(mod)) |
| 614 | ps.print(" final"); |
| 615 | if (Modifier.isInterface(mod)) |
| 616 | ps.print(" interface"); |
| 617 | if (Modifier.isNative(mod)) |
| 618 | ps.print(" native"); |
| 619 | if (Modifier.isPrivate(mod)) |
| 620 | ps.print(" private"); |
| 621 | if (Modifier.isProtected(mod)) |
| 622 | ps.print(" protected"); |
| 623 | if (Modifier.isStrict(mod)) |
| 624 | ps.print(" strict"); |
| 625 | if (Modifier.isSynchronized(mod)) |
| 626 | ps.print(" synchronized"); |
| 627 | if (Modifier.isTransient(mod)) |
| 628 | ps.print(" transient"); |
| 629 | if (Modifier.isVolatile(mod)) |
| 630 | ps.print(" volatile"); |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 631 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 632 | ps.println(); |
| 633 | } |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 634 | |
Stuart McCulloch | 4482c70 | 2012-06-15 13:27:53 +0000 | [diff] [blame^] | 635 | public static void main(String args[]) throws Exception { |
| 636 | if (args.length == 0) { |
| 637 | System.err.println("clsd <class file>+"); |
| 638 | } |
| 639 | for (int i = 0; i < args.length; i++) { |
| 640 | File f = new File(args[i]); |
| 641 | if (!f.isFile()) |
| 642 | System.err.println("File does not exist or is directory " + f); |
| 643 | else { |
| 644 | ClassDumper cd = new ClassDumper(args[i]); |
| 645 | cd.dump(null); |
| 646 | } |
| 647 | } |
| 648 | } |
Stuart McCulloch | f317322 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 649 | |
| 650 | } |