1 // ASM: a very small and fast Java bytecode manipulation framework 2 // Copyright (c) 2000-2011 INRIA, France Telecom 3 // All rights reserved. 4 // 5 // Redistribution and use in source and binary forms, with or without 6 // modification, are permitted provided that the following conditions 7 // are met: 8 // 1. Redistributions of source code must retain the above copyright 9 // notice, this list of conditions and the following disclaimer. 10 // 2. Redistributions in binary form must reproduce the above copyright 11 // notice, this list of conditions and the following disclaimer in the 12 // documentation and/or other materials provided with the distribution. 13 // 3. Neither the name of the copyright holders nor the names of its 14 // contributors may be used to endorse or promote products derived from 15 // this software without specific prior written permission. 16 // 17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27 // THE POSSIBILITY OF SUCH DAMAGE. 28 package org.objectweb.asm.test; 29 30 import java.io.ByteArrayInputStream; 31 import java.io.DataInputStream; 32 import java.io.IOException; 33 import java.lang.reflect.Array; 34 import java.lang.reflect.Constructor; 35 import java.lang.reflect.Modifier; 36 import java.util.ArrayList; 37 import java.util.Collections; 38 import java.util.HashMap; 39 40 /** 41 * A Java class file, whose content can be returned as a verbose, human "readable" string. As an 42 * example, the string representation of the HelloWorld class, obtained with the {@link #toString()} 43 * method, is: 44 * 45 * <pre> 46 * magic: -889275714 47 * minor_version: 0 48 * major_version: 49 49 * access_flags: 33 50 * this_class: ConstantClassInfo HelloWorld 51 * super_class: ConstantClassInfo java/lang/Object 52 * interfaces_count: 0 53 * fields_count: 0 54 * methods_count: 2 55 * access_flags: 1 56 * name_index: <init> 57 * descriptor_index: ()V 58 * attributes_count: 1 59 * attribute_name_index: Code 60 * max_stack: 1 61 * max_locals: 1 62 * 0: 25 0 63 * 1: 183 ConstantMethodRefInfo java/lang/Object.<init>()V 64 * 2: 177 65 * exception_table_length: 0 66 * attributes_count: 2 67 * attribute_name_index: LineNumberTable 68 * line_number_table_length: 1 69 * start_pc: <0> 70 * line_number: 31 71 * attribute_name_index: LocalVariableTable 72 * local_variable_table_length: 1 73 * start_pc: <0> 74 * length: <3> 75 * name_index: this 76 * descriptor_index: LHelloWorld; 77 * index: 0 78 * access_flags: 9 79 * name_index: main 80 * descriptor_index: ([Ljava/lang/String;)V 81 * attributes_count: 1 82 * attribute_name_index: Code 83 * max_stack: 2 84 * max_locals: 1 85 * 0: 178 ConstantFieldRefInfo java/lang/System.outLjava/io/PrintStream; 86 * 1: 18 ConstantStringInfo Hello, world! 87 * 2: 182 ConstantMethodRefInfo java/io/PrintStream.println(Ljava/lang/String;)V 88 * 3: 177 89 * exception_table_length: 0 90 * attributes_count: 2 91 * attribute_name_index: LineNumberTable 92 * line_number_table_length: 2 93 * start_pc: <0> 94 * line_number: 33 95 * start_pc: <3> 96 * line_number: 34 97 * attribute_name_index: LocalVariableTable 98 * local_variable_table_length: 1 99 * start_pc: <0> 100 * length: <4> 101 * name_index: args 102 * descriptor_index: [Ljava/lang/String; 103 * index: 0 104 * attributes_count: 1 105 * attribute_name_index: SourceFile 106 * sourcefile_index: HelloWorld.java 107 * </pre> 108 * 109 * <p>This class is used to compare classes in unit tests. Its source code is as close as possible 110 * to the Java Virtual Machine specification for ease of reference. The constant pool and bytecode 111 * offsets are abstracted away so that two classes which differ only by their constant pool or low 112 * level byte code instruction representation (e.g. a ldc vs. a ldc_w) are still considered equal. 113 * Likewise, attributes (resp. type annotations) are re-ordered into alphabetical order, so that two 114 * classes which differ only via the ordering of their attributes (resp. type annotations) are still 115 * considered equal. 116 * 117 * @author Eric Bruneton 118 */ 119 public class ClassFile { 120 121 /** The name of JDK9 module classes. */ 122 static final String MODULE_INFO = "module-info"; 123 124 /** The binary content of a Java class file. */ 125 private final byte[] classBytes; 126 127 /** The name of the class contained in this class file, lazily computed. */ 128 private String className; 129 130 /** The dump of the constant pool of {@link #classBytes}, lazily computed. */ 131 private String constantPoolDump; 132 133 /** The dump of {@link #classBytes}, lazily computed. */ 134 private String dump; 135 136 /** 137 * Constructs a new ClassFile instance. 138 * 139 * @param classBytes the binary content of a Java class file. 140 */ ClassFile(final byte[] classBytes)141 public ClassFile(final byte[] classBytes) { 142 this.classBytes = classBytes; 143 } 144 145 /** 146 * Returns a string representation of the constant pool of the class contained in this class file. 147 * 148 * @return a string representation of the constant pool of the class contained in this class file. 149 */ getConstantPoolDump()150 public String getConstantPoolDump() { 151 if (constantPoolDump == null) { 152 computeNameAndDumps(); 153 } 154 return constantPoolDump; 155 } 156 157 /** 158 * Returns a new instance of the class contained in this class file. The class is loaded in a new 159 * class loader. 160 * 161 * @return a new instance of the class, or {@literal null} if the class is abstract, is an enum, 162 * or a module info. 163 * @throws ReflectiveOperationException if the class is invalid or if an error occurs in its 164 * constructor. 165 */ newInstance()166 public Object newInstance() throws ReflectiveOperationException { 167 if (className == null) { 168 computeNameAndDumps(); 169 } 170 return newInstance(className, classBytes); 171 } 172 173 /** 174 * Returns a new instance of the given class. The class is loaded in a new class loader. 175 * 176 * @param className the name of the class to load. 177 * @param classContent the content of the class to load. 178 * @return a new instance of the class, or {@literal null} if the class is abstract, is an enum, 179 * or a module info. 180 * @throws ReflectiveOperationException if the class is invalid or if an error occurs in its 181 * constructor. 182 */ newInstance(final String className, final byte[] classContent)183 static Object newInstance(final String className, final byte[] classContent) 184 throws ReflectiveOperationException { 185 if (className.endsWith(MODULE_INFO)) { 186 if (Util.getMajorJavaVersion() < 9) { 187 throw new UnsupportedClassVersionError("Module info is not supported before JDK 9"); 188 } else { 189 return null; 190 } 191 } 192 ByteClassLoader byteClassLoader = new ByteClassLoader(className, classContent); 193 Class<?> clazz = byteClassLoader.loadClass(className); 194 // Make sure the class is loaded from the given byte array, excluding any other source. 195 if (!byteClassLoader.classLoaded()) { 196 // This should never happen, given the implementation of ByteClassLoader. 197 throw new AssertionError("Class " + className + " loaded from wrong source"); 198 } 199 if (!clazz.isEnum() && (clazz.getModifiers() & Modifier.ABSTRACT) == 0) { 200 Constructor<?> constructor = clazz.getDeclaredConstructors()[0]; 201 ArrayList<Object> arguments = new ArrayList<>(); 202 for (Class<?> parameterType : constructor.getParameterTypes()) { 203 arguments.add(Array.get(Array.newInstance(parameterType, 1), 0)); 204 } 205 constructor.setAccessible(true); // NOPMD(AvoidAccessibilityAlteration): ok for tests. 206 return constructor.newInstance(arguments.toArray(new Object[0])); 207 } 208 return null; 209 } 210 211 /** 212 * Returns whether the given class file is the same as this one. 213 * 214 * @return true if 'other' is a {@link ClassFile} with the same string representation. 215 * @throws ClassFormatException if the class content can't be parsed. 216 */ 217 @Override equals(final Object other)218 public boolean equals(final Object other) { 219 if (other instanceof ClassFile) { 220 return toString().equals(other.toString()); 221 } 222 return false; 223 } 224 225 /** 226 * Returns the hashcode of this class file. 227 * 228 * @return the hashcode of the string representation of this class file. 229 * @throws ClassFormatException if the class content can't be parsed. 230 */ 231 @Override hashCode()232 public int hashCode() { 233 return toString().hashCode(); 234 } 235 236 /** 237 * Returns a string representation of this class file. 238 * 239 * @return a string representation of this class file (see the class comments for more details). 240 * @throws ClassFormatException if the class content can't be parsed. 241 */ 242 @Override toString()243 public String toString() { 244 if (dump == null) { 245 computeNameAndDumps(); 246 } 247 return dump; 248 } 249 250 /** 251 * Computes the name and the string representation of the class (and of its constant pool) 252 * contained in this class file. 253 * 254 * @throws ClassFormatException if the class content can't be parsed. 255 */ computeNameAndDumps()256 private void computeNameAndDumps() { 257 try { 258 Builder builder = new Builder("ClassFile", /* parent = */ null); 259 Builder constantPoolBuilder = new Builder("ConstantPool", /* parent = */ null); 260 ConstantClassInfo classInfo = 261 dumpClassFile(new Parser(classBytes), builder, constantPoolBuilder); 262 className = classInfo.dump().replace('/', '.'); 263 StringBuilder stringBuilder = new StringBuilder(); 264 builder.build(stringBuilder); 265 dump = stringBuilder.toString(); 266 StringBuilder constantPoolStringBuilder = new StringBuilder(); 267 constantPoolBuilder.build(constantPoolStringBuilder); 268 constantPoolDump = constantPoolStringBuilder.toString(); 269 } catch (IOException e) { 270 throw new ClassFormatException(e.getMessage(), e); 271 } 272 } 273 274 /** 275 * Parses and dumps the high level structure of the class. 276 * 277 * @param parser a class parser. 278 * @param builder a dump builder. 279 * @param constantPoolBuilder a dump builder for the constant pool. 280 * @return the ConstantClassInfo corresponding to the parsed class. 281 * @throws IOException if the class can't be parsed. 282 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.1">JVMS 283 * 4.1</a> 284 */ dumpClassFile( final Parser parser, final Builder builder, final Builder constantPoolBuilder)285 private static ConstantClassInfo dumpClassFile( 286 final Parser parser, final Builder builder, final Builder constantPoolBuilder) 287 throws IOException { 288 builder.add("magic: ", parser.u4()); 289 builder.add("minor_version: ", parser.u2()); 290 int majorVersion = parser.u2(); 291 if (majorVersion > /* V15 = */ 59) { 292 throw new ClassFormatException("Unsupported class version"); 293 } 294 builder.add("major_version: ", majorVersion); 295 int constantPoolCount = parser.u2(); 296 int cpIndex = 1; 297 while (cpIndex < constantPoolCount) { 298 CpInfo cpInfo = parseCpInfo(parser, builder); 299 builder.putCpInfo(cpIndex, cpInfo); 300 constantPoolBuilder.putCpInfo(cpIndex, cpInfo); 301 constantPoolBuilder.addCpInfo("constant_pool: ", cpIndex); 302 cpIndex += cpInfo.size(); 303 } 304 builder.add("access_flags: ", parser.u2()); 305 int thisClass = parser.u2(); 306 builder.addCpInfo("this_class: ", thisClass); 307 builder.addCpInfo("super_class: ", parser.u2()); 308 int interfaceCount = builder.add("interfaces_count: ", parser.u2()); 309 for (int i = 0; i < interfaceCount; ++i) { 310 builder.addCpInfo("interface: ", parser.u2()); 311 } 312 int fieldCount = builder.add("fields_count: ", parser.u2()); 313 for (int i = 0; i < fieldCount; ++i) { 314 dumpFieldInfo(parser, builder); 315 } 316 int methodCount = builder.add("methods_count: ", parser.u2()); 317 for (int i = 0; i < methodCount; ++i) { 318 dumpMethodInfo(parser, builder); 319 } 320 dumpAttributeList(parser, builder); 321 return builder.getCpInfo(thisClass, ConstantClassInfo.class); 322 } 323 324 /** 325 * Parses and dumps a list of attributes. 326 * 327 * @param parser a class parser. 328 * @param builder a dump builder. 329 * @throws IOException if the class can't be parsed. 330 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.1">JVMS 331 * 4.1</a> 332 */ dumpAttributeList(final Parser parser, final Builder builder)333 private static void dumpAttributeList(final Parser parser, final Builder builder) 334 throws IOException { 335 int attributeCount = builder.add("attributes_count: ", parser.u2()); 336 SortedBuilder sortedBuilder = builder.addSortedBuilder(); 337 for (int i = 0; i < attributeCount; ++i) { 338 dumpAttributeInfo(parser, sortedBuilder); 339 } 340 } 341 342 /** 343 * Parses a cp_info structure. 344 * 345 * @param parser a class parser. 346 * @param classContext a context to lookup constant pool items from their index. 347 * @return the parsed constant pool item. 348 * @throws IOException if the class can't be parsed. 349 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.4">JVMS 350 * 4.4</a> 351 */ parseCpInfo(final Parser parser, final ClassContext classContext)352 private static CpInfo parseCpInfo(final Parser parser, final ClassContext classContext) 353 throws IOException { 354 int tag = parser.u1(); 355 switch (tag) { 356 case 7: 357 return new ConstantClassInfo(parser, classContext); 358 case 9: 359 return new ConstantFieldRefInfo(parser, classContext); 360 case 10: 361 return new ConstantMethodRefInfo(parser, classContext); 362 case 11: 363 return new ConstantInterfaceMethodRefInfo(parser, classContext); 364 case 8: 365 return new ConstantStringInfo(parser, classContext); 366 case 3: 367 return new ConstantIntegerInfo(parser); 368 case 4: 369 return new ConstantFloatInfo(parser); 370 case 5: 371 return new ConstantLongInfo(parser); 372 case 6: 373 return new ConstantDoubleInfo(parser); 374 case 12: 375 return new ConstantNameAndTypeInfo(parser, classContext); 376 case 1: 377 return new ConstantUtf8Info(parser); 378 case 15: 379 return new ConstantMethodHandleInfo(parser, classContext); 380 case 16: 381 return new ConstantMethodTypeInfo(parser, classContext); 382 case 17: 383 return new ConstantDynamicInfo(parser, classContext); 384 case 18: 385 return new ConstantInvokeDynamicInfo(parser, classContext); 386 case 19: 387 return new ConstantModuleInfo(parser, classContext); 388 case 20: 389 return new ConstantPackageInfo(parser, classContext); 390 default: 391 throw new ClassFormatException("Invalid constant pool item tag " + tag); 392 } 393 } 394 395 /** 396 * Parses and dumps a field_info structure. 397 * 398 * @param parser a class parser. 399 * @param builder a dump builder. 400 * @throws IOException if the class can't be parsed. 401 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.5">JVMS 402 * 4.5</a> 403 */ dumpFieldInfo(final Parser parser, final Builder builder)404 private static void dumpFieldInfo(final Parser parser, final Builder builder) throws IOException { 405 builder.add("access_flags: ", parser.u2()); 406 builder.addCpInfo("name_index: ", parser.u2()); 407 builder.addCpInfo("descriptor_index: ", parser.u2()); 408 dumpAttributeList(parser, builder); 409 } 410 411 /** 412 * Parses and dumps a method_info structure. 413 * 414 * @param parser a class parser. 415 * @param builder a dump builder. 416 * @throws IOException if the class can't be parsed. 417 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.6">JVMS 418 * 4.6</a> 419 */ dumpMethodInfo(final Parser parser, final Builder builder)420 private static void dumpMethodInfo(final Parser parser, final Builder builder) 421 throws IOException { 422 // method_info has the same top level structure as field_info. 423 dumpFieldInfo(parser, builder); 424 } 425 426 /** 427 * Parses and dumps an attribute_info structure. 428 * 429 * @param parser a class parser. 430 * @param sortedBuilder a dump builder. 431 * @throws IOException if the class can't be parsed. 432 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7">JVMS 433 * 4.7</a> 434 */ dumpAttributeInfo(final Parser parser, final SortedBuilder sortedBuilder)435 private static void dumpAttributeInfo(final Parser parser, final SortedBuilder sortedBuilder) 436 throws IOException { 437 String attributeName = sortedBuilder.getCpInfo(parser.u2()).toString(); 438 int attributeLength = parser.u4(); 439 Builder builder = sortedBuilder.addBuilder(attributeName); 440 builder.add("attribute_name_index: ", attributeName); 441 if (attributeName.equals("ConstantValue")) { 442 dumpConstantValueAttribute(parser, builder); 443 } else if (attributeName.equals("Code")) { 444 dumpCodeAttribute(parser, builder); 445 } else if (attributeName.equals("StackMapTable")) { 446 dumpStackMapTableAttribute(parser, builder); 447 } else if (attributeName.equals("Exceptions")) { 448 dumpExceptionsAttribute(parser, builder); 449 } else if (attributeName.equals("InnerClasses")) { 450 dumpInnerClassesAttribute(parser, builder); 451 } else if (attributeName.equals("EnclosingMethod")) { 452 dumpEnclosingMethodAttribute(parser, builder); 453 } else if (attributeName.equals("Synthetic")) { 454 dumpSyntheticAttribute(); 455 } else if (attributeName.equals("Signature")) { 456 dumpSignatureAttribute(parser, builder); 457 } else if (attributeName.equals("SourceFile")) { 458 dumpSourceFileAttribute(parser, builder); 459 } else if (attributeName.equals("SourceDebugExtension")) { 460 dumpSourceDebugAttribute(attributeLength, parser, builder); 461 } else if (attributeName.equals("LineNumberTable")) { 462 dumpLineNumberTableAttribute(parser, builder); 463 } else if (attributeName.equals("LocalVariableTable")) { 464 dumpLocalVariableTableAttribute(parser, builder); 465 } else if (attributeName.equals("LocalVariableTypeTable")) { 466 dumpLocalVariableTypeTableAttribute(parser, builder); 467 } else if (attributeName.equals("Deprecated")) { 468 dumpDeprecatedAttribute(); 469 } else if (attributeName.equals("RuntimeVisibleAnnotations")) { 470 dumpRuntimeVisibleAnnotationsAttribute(parser, builder); 471 } else if (attributeName.equals("RuntimeInvisibleAnnotations")) { 472 dumpRuntimeInvisibleAnnotationsAttribute(parser, builder); 473 } else if (attributeName.equals("RuntimeVisibleParameterAnnotations")) { 474 dumpRuntimeVisibleParameterAnnotationsAttribute(parser, builder); 475 } else if (attributeName.equals("RuntimeInvisibleParameterAnnotations")) { 476 dumpRuntimeInvisibleParameterAnnotationsAttribute(parser, builder); 477 } else if (attributeName.equals("RuntimeVisibleTypeAnnotations")) { 478 dumpRuntimeVisibleTypeAnnotationsAttribute(parser, builder); 479 } else if (attributeName.equals("RuntimeInvisibleTypeAnnotations")) { 480 dumpRuntimeInvisibleTypeAnnotationsAttribute(parser, builder); 481 } else if (attributeName.equals("AnnotationDefault")) { 482 dumpAnnotationDefaultAttribute(parser, builder); 483 } else if (attributeName.equals("BootstrapMethods")) { 484 dumpBootstrapMethodsAttribute(parser, builder); 485 } else if (attributeName.equals("MethodParameters")) { 486 dumpMethodParametersAttribute(parser, builder); 487 } else if (attributeName.equals("Module")) { 488 dumpModuleAttribute(parser, builder); 489 } else if (attributeName.equals("ModulePackages")) { 490 dumpModulePackagesAttribute(parser, builder); 491 } else if (attributeName.equals("ModuleMainClass")) { 492 dumpModuleMainClassAttribute(parser, builder); 493 } else if (attributeName.equals("NestHost")) { 494 dumpNestHostAttribute(parser, builder); 495 } else if (attributeName.equals("NestMembers")) { 496 dumpNestMembersAttribute(parser, builder); 497 } else if (attributeName.equals("PermittedSubclasses")) { 498 dumpPermittedSubclassesAttribute(parser, builder); 499 } else if (attributeName.equals("Record")) { 500 dumpRecordAttribute(parser, builder); 501 } else if (attributeName.equals("StackMap")) { 502 dumpStackMapAttribute(parser, builder); 503 } else if (!attributeName.equals("CodeComment") && !attributeName.equals("Comment")) { 504 // Not a standard attribute nor one the of empty non-standard attributes used for tests. 505 throw new ClassFormatException("Unknown attribute " + attributeName); 506 } 507 } 508 509 /** 510 * Parses and dumps a ConstantValue attribute. 511 * 512 * @param parser a class parser. 513 * @param builder a dump builder. 514 * @throws IOException if the class can't be parsed. 515 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.2">JVMS 516 * 4.7.2</a> 517 */ dumpConstantValueAttribute(final Parser parser, final Builder builder)518 private static void dumpConstantValueAttribute(final Parser parser, final Builder builder) 519 throws IOException { 520 builder.addCpInfo("constantvalue_index: ", parser.u2()); 521 } 522 523 /** 524 * Parses and dumps a Code attribute. 525 * 526 * @param parser a class parser. 527 * @param builder a dump builder. 528 * @throws IOException if the class can't be parsed. 529 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.3">JVMS 530 * 4.7.3</a> 531 */ dumpCodeAttribute(final Parser parser, final Builder builder)532 private static void dumpCodeAttribute(final Parser parser, final Builder builder) 533 throws IOException { 534 builder.add("max_stack: ", parser.u2()); 535 builder.add("max_locals: ", parser.u2()); 536 int codeLength = parser.u4(); 537 dumpInstructions(codeLength, parser, builder); 538 int exceptionCount = builder.add("exception_table_length: ", parser.u2()); 539 for (int i = 0; i < exceptionCount; ++i) { 540 builder.addInsnIndex("start_pc: ", parser.u2()); 541 builder.addInsnIndex("end_pc: ", parser.u2()); 542 builder.addInsnIndex("handler_pc: ", parser.u2()); 543 builder.addCpInfo("catch_type: ", parser.u2()); 544 } 545 dumpAttributeList(parser, builder); 546 } 547 548 /** 549 * Parses and dumps the bytecode instructions of a method. 550 * 551 * @param codeLength the number of bytes to parse. 552 * @param parser a class parser. 553 * @param builder a dump builder. 554 * @throws IOException if the class can't be parsed. 555 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-6.html#jvms-6.5">JVMS 556 * 6.5</a> 557 */ dumpInstructions( final int codeLength, final Parser parser, final Builder builder)558 private static void dumpInstructions( 559 final int codeLength, final Parser parser, final Builder builder) throws IOException { 560 int bytecodeOffset = 0; // Number of bytes parsed so far. 561 int insnIndex = 0; // Number of instructions parsed so far. 562 while (bytecodeOffset < codeLength) { 563 builder.putInsnIndex(bytecodeOffset, insnIndex); 564 int opcode = parser.u1(); 565 int startOffset = bytecodeOffset++; 566 // Instructions are in alphabetical order of their opcode name, as 567 // in the specification. This leads to some duplicated code, but is 568 // done on purpose for ease of reference. 569 switch (opcode) { 570 case 0x32: // aaload 571 case 0x53: // aastore 572 case 0x01: // aconst_null 573 builder.addInsn(insnIndex, opcode); 574 break; 575 case 0x19: // aload 576 builder.addInsn(insnIndex, opcode, parser.u1()); 577 bytecodeOffset += 1; 578 break; 579 case 0x2A: // aload_0 580 case 0x2B: // aload_1 581 case 0x2C: // aload_2 582 case 0x2D: // aload_3 583 builder.addInsn(insnIndex, 0x19, opcode - 0x2A); 584 break; 585 case 0xBD: // anewarray 586 builder.addInsn(insnIndex, opcode, builder.getCpInfo(parser.u2())); 587 bytecodeOffset += 2; 588 break; 589 case 0xB0: // areturn 590 case 0xBE: // arraylength 591 builder.addInsn(insnIndex, opcode); 592 break; 593 case 0x3A: // astore 594 builder.addInsn(insnIndex, opcode, parser.u1()); 595 bytecodeOffset += 1; 596 break; 597 case 0x4B: // astore_0 598 case 0x4C: // astore_1 599 case 0x4D: // astore_2 600 case 0x4E: // astore_3 601 builder.addInsn(insnIndex, 0x3A, opcode - 0x4B); 602 break; 603 case 0xBF: // athrow 604 case 0x33: // baload 605 case 0x54: // bastore 606 builder.addInsn(insnIndex, opcode); 607 break; 608 case 0x10: // bipush 609 builder.addInsn(insnIndex, opcode, parser.u1()); 610 bytecodeOffset += 1; 611 break; 612 case 0x34: // caload 613 case 0x55: // castore 614 builder.addInsn(insnIndex, opcode); 615 break; 616 case 0xC0: // checkcast 617 builder.addInsn(insnIndex, opcode, builder.getCpInfo(parser.u2())); 618 bytecodeOffset += 2; 619 break; 620 case 0x90: // d2f 621 case 0x8E: // d2i 622 case 0x8F: // d2l 623 case 0x63: // dadd 624 case 0x31: // daload 625 case 0x52: // dastore 626 case 0x98: // dcmpg 627 case 0x97: // dcmpl 628 case 0x0E: // dconst_0 629 case 0x0F: // dconst_1 630 case 0x6F: // ddiv 631 builder.addInsn(insnIndex, opcode); 632 break; 633 case 0x18: // dload 634 builder.addInsn(insnIndex, opcode, parser.u1()); 635 bytecodeOffset += 1; 636 break; 637 case 0x26: // dload_0 638 case 0x27: // dload_1 639 case 0x28: // dload_2 640 case 0x29: // dload_3 641 builder.addInsn(insnIndex, 0x18, opcode - 0x26); 642 break; 643 case 0x6B: // dmul 644 case 0x77: // dneg 645 case 0x73: // drem 646 case 0xAF: // dreturn 647 builder.addInsn(insnIndex, opcode); 648 break; 649 case 0x39: // dstore 650 builder.addInsn(insnIndex, opcode, parser.u1()); 651 bytecodeOffset += 1; 652 break; 653 case 0x47: // dstore_0 654 case 0x48: // dstore_1 655 case 0x49: // dstore_2 656 case 0x4A: // dstore_3 657 builder.addInsn(insnIndex, 0x39, opcode - 0x47); 658 break; 659 case 0x67: // dsub 660 case 0x59: // dup 661 case 0x5A: // dup_x1 662 case 0x5B: // dup_x2 663 case 0x5C: // dup2 664 case 0x5D: // dup2_x1 665 case 0x5E: // dup2_x2 666 case 0x8D: // f2d 667 case 0x8B: // f2i 668 case 0x8C: // f2l 669 case 0x62: // fadd 670 case 0x30: // faload 671 case 0x51: // fastore 672 case 0x96: // fcmpg 673 case 0x95: // fcmpl 674 case 0x0B: // fconst_0 675 case 0x0C: // fconst_1 676 case 0x0D: // fconst_2 677 case 0x6E: // fdiv 678 builder.addInsn(insnIndex, opcode); 679 break; 680 case 0x17: // fload 681 builder.addInsn(insnIndex, opcode, parser.u1()); 682 bytecodeOffset += 1; 683 break; 684 case 0x22: // fload_0 685 case 0x23: // fload_1 686 case 0x24: // fload_2 687 case 0x25: // fload_3 688 builder.addInsn(insnIndex, 0x17, opcode - 0x22); 689 break; 690 case 0x6A: // fmul 691 case 0x76: // fneg 692 case 0x72: // frem 693 case 0xAE: // freturn 694 builder.addInsn(insnIndex, opcode); 695 break; 696 case 0x38: // fstore 697 builder.addInsn(insnIndex, opcode, parser.u1()); 698 bytecodeOffset += 1; 699 break; 700 case 0x43: // fstore_0 701 case 0x44: // fstore_1 702 case 0x45: // fstore_2 703 case 0x46: // fstore_3 704 builder.addInsn(insnIndex, 0x38, opcode - 0x43); 705 break; 706 case 0x66: // fsub 707 builder.addInsn(insnIndex, opcode); 708 break; 709 case 0xB4: // getfield 710 case 0xB2: // getstatic 711 builder.addInsn(insnIndex, opcode, builder.getCpInfo(parser.u2())); 712 bytecodeOffset += 2; 713 break; 714 case 0xA7: // goto 715 builder.addInsn( 716 insnIndex, opcode, new InstructionIndex(startOffset + parser.s2(), builder)); 717 bytecodeOffset += 2; 718 break; 719 case 0xC8: // goto_w 720 builder.addInsn( 721 insnIndex, 0xA7, new InstructionIndex(startOffset + parser.u4(), builder)); 722 bytecodeOffset += 4; 723 break; 724 case 0x91: // i2b 725 case 0x92: // i2c 726 case 0x87: // i2d 727 case 0x86: // i2f 728 case 0x85: // i2l 729 case 0x93: // i2s 730 case 0x60: // iadd 731 case 0x2E: // iaload 732 case 0x7E: // iand 733 case 0x4F: // iastore 734 case 0x02: // iconst_m1 735 case 0x03: // iconst_0 736 case 0x04: // iconst_1 737 case 0x05: // iconst_2 738 case 0x06: // iconst_3 739 case 0x07: // iconst_4 740 case 0x08: // iconst_5 741 case 0x6C: // idiv 742 builder.addInsn(insnIndex, opcode); 743 break; 744 case 0xA5: // if_acmpeq 745 case 0xA6: // if_acmpne 746 case 0x9F: // if_icmpeq 747 case 0xA0: // if_icmpne 748 case 0xA1: // if_icmplt 749 case 0xA2: // if_icmpge 750 case 0xA3: // if_icmpgt 751 case 0xA4: // if_icmple 752 case 0x99: // ifeq 753 case 0x9A: // ifne 754 case 0x9B: // iflt 755 case 0x9C: // ifge 756 case 0x9D: // ifgt 757 case 0x9E: // ifle 758 case 0xC7: // ifnonnull 759 case 0xC6: // ifnull 760 builder.addInsn( 761 insnIndex, opcode, new InstructionIndex(startOffset + parser.s2(), builder)); 762 bytecodeOffset += 2; 763 break; 764 case 0x84: // iinc 765 builder.addInsn(insnIndex, opcode, parser.u1(), parser.s1()); 766 bytecodeOffset += 2; 767 break; 768 case 0x15: // iload 769 builder.addInsn(insnIndex, opcode, parser.u1()); 770 bytecodeOffset += 1; 771 break; 772 case 0x1A: // iload_0 773 case 0x1B: // iload_1 774 case 0x1C: // iload_2 775 case 0x1D: // iload_3 776 builder.addInsn(insnIndex, 0x15, opcode - 0x1A); 777 break; 778 case 0x68: // imul 779 case 0x74: // ineg 780 builder.addInsn(insnIndex, opcode); 781 break; 782 case 0xC1: // instanceof 783 builder.addInsn(insnIndex, opcode, builder.getCpInfo(parser.u2())); 784 bytecodeOffset += 2; 785 break; 786 case 0xBA: // invokedynamic 787 builder.addInsn(insnIndex, opcode, builder.getCpInfo(parser.u2())); 788 parser.u2(); 789 bytecodeOffset += 4; 790 break; 791 case 0xB9: // invokeinterface 792 builder.addInsn(insnIndex, opcode, builder.getCpInfo(parser.u2()), parser.u1()); 793 parser.u1(); 794 bytecodeOffset += 4; 795 break; 796 case 0xB7: // invokespecial 797 case 0xB8: // invokestatic 798 case 0xB6: // invokevirtual 799 builder.addInsn(insnIndex, opcode, builder.getCpInfo(parser.u2())); 800 bytecodeOffset += 2; 801 break; 802 case 0x80: // ior 803 case 0x70: // irem 804 case 0xAC: // ireturn 805 case 0x78: // ishl 806 case 0x7A: // ishr 807 builder.addInsn(insnIndex, opcode); 808 break; 809 case 0x36: // istore 810 builder.addInsn(insnIndex, opcode, parser.u1()); 811 bytecodeOffset += 1; 812 break; 813 case 0x3B: // istore_0 814 case 0x3C: // istore_1 815 case 0x3D: // istore_2 816 case 0x3E: // istore_3 817 builder.addInsn(insnIndex, 0x36, opcode - 0x3B); 818 break; 819 case 0x64: // isub 820 case 0x7C: // iushr 821 case 0x82: // ixor 822 builder.addInsn(insnIndex, opcode); 823 break; 824 case 0xA8: // jsr 825 builder.addInsn( 826 insnIndex, opcode, new InstructionIndex(startOffset + parser.s2(), builder)); 827 bytecodeOffset += 2; 828 break; 829 case 0xC9: // jsr_w 830 builder.addInsn( 831 insnIndex, 0xA8, new InstructionIndex(startOffset + parser.u4(), builder)); 832 bytecodeOffset += 4; 833 break; 834 case 0x8A: // l2d 835 case 0x89: // l2f 836 case 0x88: // l2i 837 case 0x61: // ladd 838 case 0x2F: // laload 839 case 0x7F: // land 840 case 0x50: // lastore 841 case 0x94: // lcmp 842 case 0x09: // lconst_0 843 case 0x0A: // lconst_1 844 builder.addInsn(insnIndex, opcode); 845 break; 846 case 0x12: // ldc 847 builder.addInsn(insnIndex, 0x12, builder.getCpInfo(parser.u1())); 848 bytecodeOffset += 1; 849 break; 850 case 0x13: // ldc_w 851 case 0x14: // ldc2_w 852 builder.addInsn(insnIndex, opcode == 0x13 ? 0x12 : 0x14, builder.getCpInfo(parser.u2())); 853 bytecodeOffset += 2; 854 break; 855 case 0x6D: // ldiv 856 builder.addInsn(insnIndex, opcode); 857 break; 858 case 0x16: // lload 859 builder.addInsn(insnIndex, opcode, parser.u1()); 860 bytecodeOffset += 1; 861 break; 862 case 0x1E: // lload_0 863 case 0x1F: // lload_1 864 case 0x20: // lload_2 865 case 0x21: // lload_3 866 builder.addInsn(insnIndex, 0x16, opcode - 0x1E); 867 break; 868 case 0x69: // lmul 869 case 0x75: // lneg 870 builder.addInsn(insnIndex, opcode); 871 break; 872 case 0xAB: // lookupswitch 873 builder.addInsn(insnIndex, opcode); 874 while (bytecodeOffset % 4 != 0) { 875 parser.u1(); 876 bytecodeOffset++; 877 } 878 builder.addInsnIndex("default: ", startOffset + parser.u4()); 879 int pairCount = builder.add("npairs: ", parser.u4()); 880 bytecodeOffset += 8; 881 for (int i = 0; i < pairCount; ++i) { 882 builder.addInsnIndex(parser.u4() + ": ", startOffset + parser.u4()); 883 bytecodeOffset += 8; 884 } 885 break; 886 case 0x81: // lor 887 case 0x71: // lrem 888 case 0xAD: // lreturn 889 case 0x79: // lshl 890 case 0x7B: // lshr 891 builder.addInsn(insnIndex, opcode); 892 break; 893 case 0x37: // lstore 894 builder.addInsn(insnIndex, opcode, parser.u1()); 895 bytecodeOffset += 1; 896 break; 897 case 0x3F: // lstore_0 898 case 0x40: // lstore_1 899 case 0x41: // lstore_2 900 case 0x42: // lstore_3 901 builder.addInsn(insnIndex, 0x37, opcode - 0x3F); 902 break; 903 case 0x65: // lsub 904 case 0x7D: // lushr 905 case 0x83: // lxor 906 case 0xC2: // monitorenter 907 case 0xC3: // monitorexit 908 builder.addInsn(insnIndex, opcode); 909 break; 910 case 0xC5: // multianewarray 911 builder.addInsn(insnIndex, opcode, builder.getCpInfo(parser.u2()), parser.u1()); 912 bytecodeOffset += 3; 913 break; 914 case 0xBB: // new 915 builder.addInsn(insnIndex, opcode, builder.getCpInfo(parser.u2())); 916 bytecodeOffset += 2; 917 break; 918 case 0xBC: // newarray 919 builder.addInsn(insnIndex, opcode, parser.u1()); 920 bytecodeOffset += 1; 921 break; 922 case 0x00: // nop 923 case 0x57: // pop 924 case 0x58: // pop2 925 builder.addInsn(insnIndex, opcode); 926 break; 927 case 0xB5: // putfield 928 case 0xB3: // putstatic 929 builder.addInsn(insnIndex, opcode, builder.getCpInfo(parser.u2())); 930 bytecodeOffset += 2; 931 break; 932 case 0xA9: // ret 933 builder.addInsn(insnIndex, opcode, parser.u1()); 934 bytecodeOffset += 1; 935 break; 936 case 0xB1: // return 937 case 0x35: // saload 938 case 0x56: // sastore 939 builder.addInsn(insnIndex, opcode); 940 break; 941 case 0x11: // sipush 942 builder.addInsn(insnIndex, opcode, parser.s2()); 943 bytecodeOffset += 2; 944 break; 945 case 0x5F: // swap 946 builder.addInsn(insnIndex, opcode); 947 break; 948 case 0xAA: // tableswitch 949 builder.addInsn(insnIndex, opcode); 950 while (bytecodeOffset % 4 != 0) { 951 parser.u1(); 952 bytecodeOffset++; 953 } 954 builder.addInsnIndex("default: ", startOffset + parser.u4()); 955 int low = builder.add("low: ", parser.u4()); 956 int high = builder.add("high: ", parser.u4()); 957 bytecodeOffset += 12; 958 for (int i = low; i <= high; ++i) { 959 builder.addInsnIndex(i + ": ", startOffset + parser.u4()); 960 bytecodeOffset += 4; 961 } 962 break; 963 case 0xC4: // wide 964 opcode = parser.u1(); 965 bytecodeOffset += 1; 966 switch (opcode) { 967 case 0x15: // iload 968 case 0x17: // fload 969 case 0x19: // aload 970 case 0x16: // lload 971 case 0x18: // dload 972 case 0x36: // istore 973 case 0x38: // fstore 974 case 0x3A: // astore 975 case 0x37: // lstore 976 case 0x39: // dstore 977 case 0xA9: // ret 978 builder.addInsn(insnIndex, opcode, parser.u2()); 979 bytecodeOffset += 2; 980 break; 981 case 0x84: // iinc 982 builder.addInsn(insnIndex, opcode, parser.u2(), parser.s2()); 983 bytecodeOffset += 4; 984 break; 985 default: 986 throw new ClassFormatException("Unknown wide opcode: " + opcode); 987 } 988 break; 989 default: 990 throw new ClassFormatException("Unknown opcode: " + opcode); 991 } 992 insnIndex++; 993 } 994 builder.putInsnIndex(bytecodeOffset, insnIndex); 995 } 996 997 /** 998 * Parses and dumps a StackMapTable attribute. 999 * 1000 * @param parser a class parser. 1001 * @param builder a dump builder. 1002 * @throws IOException if the class can't be parsed. 1003 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.4">JVMS 1004 * 4.7.4</a> 1005 */ dumpStackMapTableAttribute(final Parser parser, final Builder builder)1006 private static void dumpStackMapTableAttribute(final Parser parser, final Builder builder) 1007 throws IOException { 1008 int entryCount = builder.add("number_of_entries: ", parser.u2()); 1009 int bytecodeOffset = -1; 1010 for (int i = 0; i < entryCount; ++i) { 1011 int frameType = parser.u1(); 1012 if (frameType < 64) { 1013 int offsetDelta = frameType; 1014 bytecodeOffset += offsetDelta + 1; 1015 builder.addInsnIndex("SAME ", bytecodeOffset); 1016 } else if (frameType < 128) { 1017 int offsetDelta = frameType - 64; 1018 bytecodeOffset += offsetDelta + 1; 1019 builder.addInsnIndex("SAME_LOCALS_1_STACK_ITEM ", bytecodeOffset); 1020 dumpVerificationTypeInfo(parser, builder); 1021 } else if (frameType < 247) { 1022 throw new ClassFormatException("Unknown frame type " + frameType); 1023 } else if (frameType == 247) { 1024 int offsetDelta = parser.u2(); 1025 bytecodeOffset += offsetDelta + 1; 1026 builder.addInsnIndex("SAME_LOCALS_1_STACK_ITEM ", bytecodeOffset); 1027 dumpVerificationTypeInfo(parser, builder); 1028 } else if (frameType < 251) { 1029 int offsetDelta = parser.u2(); 1030 bytecodeOffset += offsetDelta + 1; 1031 builder.addInsnIndex("CHOP_" + (251 - frameType) + " ", bytecodeOffset); 1032 } else if (frameType == 251) { 1033 int offsetDelta = parser.u2(); 1034 bytecodeOffset += offsetDelta + 1; 1035 builder.addInsnIndex("SAME ", bytecodeOffset); 1036 } else if (frameType < 255) { 1037 int offsetDelta = parser.u2(); 1038 bytecodeOffset += offsetDelta + 1; 1039 builder.addInsnIndex("APPEND_" + (frameType - 251) + " ", bytecodeOffset); 1040 for (int j = 0; j < frameType - 251; ++j) { 1041 dumpVerificationTypeInfo(parser, builder); 1042 } 1043 } else { 1044 int offsetDelta = parser.u2(); 1045 bytecodeOffset += offsetDelta + 1; 1046 builder.addInsnIndex("FULL ", bytecodeOffset); 1047 int numberOfLocals = builder.add("number_of_locals: ", parser.u2()); 1048 for (int j = 0; j < numberOfLocals; ++j) { 1049 dumpVerificationTypeInfo(parser, builder); 1050 } 1051 int numberOfStackItems = builder.add("number_of_stack_items: ", parser.u2()); 1052 for (int j = 0; j < numberOfStackItems; ++j) { 1053 dumpVerificationTypeInfo(parser, builder); 1054 } 1055 } 1056 } 1057 } 1058 1059 /** 1060 * Parses and dumps a verification_type_info structure. 1061 * 1062 * @param parser a class parser. 1063 * @param builder a dump builder. 1064 * @throws IOException if the class can't be parsed. 1065 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.2">JVMS 1066 * 4.7.2</a> 1067 */ dumpVerificationTypeInfo(final Parser parser, final Builder builder)1068 private static void dumpVerificationTypeInfo(final Parser parser, final Builder builder) 1069 throws IOException { 1070 int tag = builder.add("tag: ", parser.u1()); 1071 if (tag > 8) { 1072 throw new ClassFormatException("Unknown verification_type_info tag: " + tag); 1073 } 1074 if (tag == 7) { 1075 builder.addCpInfo("cpool_index: ", parser.u2()); 1076 } else if (tag == 8) { 1077 builder.addInsnIndex("offset: ", parser.u2()); 1078 } 1079 } 1080 1081 /** 1082 * Parses and dumps an Exception attribute. 1083 * 1084 * @param parser a class parser. 1085 * @param builder a dump builder. 1086 * @throws IOException if the class can't be parsed. 1087 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.5">JVMS 1088 * 4.7.5</a> 1089 */ dumpExceptionsAttribute(final Parser parser, final Builder builder)1090 private static void dumpExceptionsAttribute(final Parser parser, final Builder builder) 1091 throws IOException { 1092 int exceptionCount = builder.add("number_of_exceptions: ", parser.u2()); 1093 for (int i = 0; i < exceptionCount; ++i) { 1094 builder.addCpInfo("exception_index: ", parser.u2()); 1095 } 1096 } 1097 1098 /** 1099 * Parses and dumps an InnerClasses attribute. 1100 * 1101 * @param parser a class parser. 1102 * @param builder a dump builder. 1103 * @throws IOException if the class can't be parsed. 1104 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.6">JVMS 1105 * 4.7.6</a> 1106 */ dumpInnerClassesAttribute(final Parser parser, final Builder builder)1107 private static void dumpInnerClassesAttribute(final Parser parser, final Builder builder) 1108 throws IOException { 1109 int classCount = builder.add("number_of_classes: ", parser.u2()); 1110 for (int i = 0; i < classCount; ++i) { 1111 builder.addCpInfo("inner_class_info_index: ", parser.u2()); 1112 builder.addCpInfo("outer_class_info_index: ", parser.u2()); 1113 builder.addCpInfo("inner_name_index: ", parser.u2()); 1114 builder.add("inner_class_access_flags: ", parser.u2()); 1115 } 1116 } 1117 1118 /** 1119 * Parses and dumps an EnclosingMethod attribute. 1120 * 1121 * @param parser a class parser. 1122 * @param builder a dump builder. 1123 * @throws IOException if the class can't be parsed. 1124 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.7">JVMS 1125 * 4.7.7</a> 1126 */ dumpEnclosingMethodAttribute(final Parser parser, final Builder builder)1127 private static void dumpEnclosingMethodAttribute(final Parser parser, final Builder builder) 1128 throws IOException { 1129 builder.addCpInfo("class_index: ", parser.u2()); 1130 builder.addCpInfo("method_index: ", parser.u2()); 1131 } 1132 1133 /** 1134 * Parses and dumps a Synthetic attribute. 1135 * 1136 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.8">JVMS 1137 * 4.7.8</a> 1138 */ dumpSyntheticAttribute()1139 private static void dumpSyntheticAttribute() { 1140 // Nothing to parse. 1141 } 1142 1143 /** 1144 * Parses and dumps a Signature attribute. 1145 * 1146 * @param parser a class parser. 1147 * @param builder a dump builder. 1148 * @throws IOException if the class can't be parsed. 1149 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.9">JVMS 1150 * 4.7.9</a> 1151 */ dumpSignatureAttribute(final Parser parser, final Builder builder)1152 private static void dumpSignatureAttribute(final Parser parser, final Builder builder) 1153 throws IOException { 1154 builder.addCpInfo("signature_index: ", parser.u2()); 1155 } 1156 1157 /** 1158 * Parses and dumps a SourceFile attribute. 1159 * 1160 * @param parser a class parser. 1161 * @param builder a dump builder. 1162 * @throws IOException if the class can't be parsed. 1163 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.10">JVMS 1164 * 4.7.10</a> 1165 */ dumpSourceFileAttribute(final Parser parser, final Builder builder)1166 private static void dumpSourceFileAttribute(final Parser parser, final Builder builder) 1167 throws IOException { 1168 builder.addCpInfo("sourcefile_index: ", parser.u2()); 1169 } 1170 1171 /** 1172 * Parses and dumps a SourceDebug attribute. 1173 * 1174 * @param attributeLength the length of the SourceDebug attribute (excluding its 6 header bytes). 1175 * @param parser a class parser. 1176 * @param builder a dump builder. 1177 * @throws IOException if the class can't be parsed. 1178 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.11">JVMS 1179 * 4.7.11</a> 1180 */ dumpSourceDebugAttribute( final int attributeLength, final Parser parser, final Builder builder)1181 private static void dumpSourceDebugAttribute( 1182 final int attributeLength, final Parser parser, final Builder builder) throws IOException { 1183 byte[] attributeData = parser.bytes(attributeLength); 1184 StringBuilder stringBuilder = new StringBuilder(); 1185 for (byte data : attributeData) { 1186 stringBuilder.append(data).append(','); 1187 } 1188 builder.add("debug_extension: ", stringBuilder.toString()); 1189 } 1190 1191 /** 1192 * Parses and dumps a LineNumberTable attribute. 1193 * 1194 * @param parser a class parser. 1195 * @param builder a dump builder. 1196 * @throws IOException if the class can't be parsed. 1197 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.12">JVMS 1198 * 4.7.12</a> 1199 */ dumpLineNumberTableAttribute(final Parser parser, final Builder builder)1200 private static void dumpLineNumberTableAttribute(final Parser parser, final Builder builder) 1201 throws IOException { 1202 int lineNumberCount = builder.add("line_number_table_length: ", parser.u2()); 1203 for (int i = 0; i < lineNumberCount; ++i) { 1204 builder.addInsnIndex("start_pc: ", parser.u2()); 1205 builder.add("line_number: ", parser.u2()); 1206 } 1207 } 1208 1209 /** 1210 * Parses and dumps a LocalVariableTable attribute. 1211 * 1212 * @param parser a class parser. 1213 * @param builder a dump builder. 1214 * @throws IOException if the class can't be parsed. 1215 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.13">JVMS 1216 * 4.7.13</a> 1217 */ dumpLocalVariableTableAttribute(final Parser parser, final Builder builder)1218 private static void dumpLocalVariableTableAttribute(final Parser parser, final Builder builder) 1219 throws IOException { 1220 int localVariableCount = builder.add("local_variable_table_length: ", parser.u2()); 1221 for (int i = 0; i < localVariableCount; ++i) { 1222 int startPc = builder.addInsnIndex("start_pc: ", parser.u2()); 1223 builder.addInsnIndex("length: ", startPc + parser.u2()); 1224 builder.addCpInfo("name_index: ", parser.u2()); 1225 builder.addCpInfo("descriptor_index: ", parser.u2()); 1226 builder.add("index: ", parser.u2()); 1227 } 1228 } 1229 1230 /** 1231 * Parses and dumps a LocalVariableTypeTable attribute. 1232 * 1233 * @param parser a class parser. 1234 * @param builder a dump builder. 1235 * @throws IOException if the class can't be parsed. 1236 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.14">JVMS 1237 * 4.7.14</a> 1238 */ dumpLocalVariableTypeTableAttribute( final Parser parser, final Builder builder)1239 private static void dumpLocalVariableTypeTableAttribute( 1240 final Parser parser, final Builder builder) throws IOException { 1241 int localVariableCount = builder.add("local_variable_type_table_length: ", parser.u2()); 1242 for (int i = 0; i < localVariableCount; ++i) { 1243 int startPc = builder.addInsnIndex("start_pc: ", parser.u2()); 1244 builder.addInsnIndex("length: ", startPc + parser.u2()); 1245 builder.addCpInfo("name_index: ", parser.u2()); 1246 builder.addCpInfo("signature_index: ", parser.u2()); 1247 builder.add("index: ", parser.u2()); 1248 } 1249 } 1250 1251 /** 1252 * Parses and dumps a Deprecated attribute. 1253 * 1254 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.15">JVMS 1255 * 4.7.15</a> 1256 */ dumpDeprecatedAttribute()1257 private static void dumpDeprecatedAttribute() { 1258 // Nothing to parse. 1259 } 1260 1261 /** 1262 * Parses and dumps a RuntimeVisibleAnnotations attribute. 1263 * 1264 * @param parser a class parser. 1265 * @param builder a dump builder. 1266 * @throws IOException if the class can't be parsed. 1267 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16">JVMS 1268 * 4.7.16</a> 1269 */ dumpRuntimeVisibleAnnotationsAttribute( final Parser parser, final Builder builder)1270 private static void dumpRuntimeVisibleAnnotationsAttribute( 1271 final Parser parser, final Builder builder) throws IOException { 1272 int annotationCount = builder.add("num_annotations: ", parser.u2()); 1273 for (int i = 0; i < annotationCount; ++i) { 1274 dumpAnnotation(parser, builder); 1275 } 1276 } 1277 1278 /** 1279 * Parses and dumps an annotations structure. 1280 * 1281 * @param parser a class parser. 1282 * @param builder a dump builder. 1283 * @throws IOException if the class can't be parsed. 1284 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16">JVMS 1285 * 4.7.16</a> 1286 */ dumpAnnotation(final Parser parser, final Builder builder)1287 private static void dumpAnnotation(final Parser parser, final Builder builder) 1288 throws IOException { 1289 builder.addCpInfo("type_index: ", parser.u2()); 1290 int elementValuePairCount = builder.add("num_element_value_pairs: ", parser.u2()); 1291 for (int i = 0; i < elementValuePairCount; ++i) { 1292 builder.addCpInfo("element_name_index: ", parser.u2()); 1293 dumpElementValue(parser, builder); 1294 } 1295 } 1296 1297 /** 1298 * Parses and dumps an element_value structure. 1299 * 1300 * @param parser a class parser. 1301 * @param builder a dump builder. 1302 * @throws IOException if the class can't be parsed. 1303 * @see <a 1304 * href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16.1">JVMS 1305 * 4.7.16.1</a> 1306 */ dumpElementValue(final Parser parser, final Builder builder)1307 private static void dumpElementValue(final Parser parser, final Builder builder) 1308 throws IOException { 1309 int tag = parser.u1(); 1310 switch (tag) { 1311 case 'B': 1312 case 'C': 1313 case 'D': 1314 case 'F': 1315 case 'I': 1316 case 'J': 1317 case 'S': 1318 case 'Z': 1319 case 's': 1320 builder.addCpInfo(((char) tag) + ": ", parser.u2()); 1321 return; 1322 case 'e': 1323 builder.addCpInfo("e: ", parser.u2()); 1324 builder.addCpInfo("const_name_index: ", parser.u2()); 1325 return; 1326 case 'c': 1327 builder.addCpInfo(((char) tag) + ": ", parser.u2()); 1328 return; 1329 case '@': 1330 builder.add("@: ", ""); 1331 dumpAnnotation(parser, builder); 1332 return; 1333 case '[': 1334 int valueCount = builder.add("[: ", parser.u2()); 1335 for (int i = 0; i < valueCount; ++i) { 1336 dumpElementValue(parser, builder); 1337 } 1338 return; 1339 default: 1340 throw new ClassFormatException("Unknown element_type tag: " + tag); 1341 } 1342 } 1343 1344 /** 1345 * Parses and dumps a RuntimeInvisibleAnnotations attribute. 1346 * 1347 * @param parser a class parser. 1348 * @param builder a dump builder. 1349 * @throws IOException if the class can't be parsed. 1350 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.17">JVMS 1351 * 4.7.17</a> 1352 */ dumpRuntimeInvisibleAnnotationsAttribute( final Parser parser, final Builder builder)1353 private static void dumpRuntimeInvisibleAnnotationsAttribute( 1354 final Parser parser, final Builder builder) throws IOException { 1355 dumpRuntimeVisibleAnnotationsAttribute(parser, builder); 1356 } 1357 1358 /** 1359 * Parses and dumps a RuntimeVisibleParameterAnnotations attribute. 1360 * 1361 * @param parser a class parser. 1362 * @param builder a dump builder. 1363 * @throws IOException if the class can't be parsed. 1364 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.18">JVMS 1365 * 4.7.18</a> 1366 */ dumpRuntimeVisibleParameterAnnotationsAttribute( final Parser parser, final Builder builder)1367 private static void dumpRuntimeVisibleParameterAnnotationsAttribute( 1368 final Parser parser, final Builder builder) throws IOException { 1369 int parameterCount = builder.add("num_parameters: ", parser.u1()); 1370 for (int i = 0; i < parameterCount; ++i) { 1371 int annotationCount = builder.add("num_annotations: ", parser.u2()); 1372 for (int j = 0; j < annotationCount; ++j) { 1373 dumpAnnotation(parser, builder); 1374 } 1375 } 1376 } 1377 1378 /** 1379 * Parses and dumps a RuntimeInvisibleParameterAnnotations attribute. 1380 * 1381 * @param parser a class parser. 1382 * @param builder a dump builder. 1383 * @throws IOException if the class can't be parsed. 1384 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.19">JVMS 1385 * 4.7.19</a> 1386 */ dumpRuntimeInvisibleParameterAnnotationsAttribute( final Parser parser, final Builder builder)1387 private static void dumpRuntimeInvisibleParameterAnnotationsAttribute( 1388 final Parser parser, final Builder builder) throws IOException { 1389 dumpRuntimeVisibleParameterAnnotationsAttribute(parser, builder); 1390 } 1391 1392 /** 1393 * Parses and dumps a RuntimeVisibleTypeAnnotations attribute. 1394 * 1395 * @param parser a class parser. 1396 * @param builder a dump builder. 1397 * @throws IOException if the class can't be parsed. 1398 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.20">JVMS 1399 * 4.7.20</a> 1400 */ dumpRuntimeVisibleTypeAnnotationsAttribute( final Parser parser, final Builder builder)1401 private static void dumpRuntimeVisibleTypeAnnotationsAttribute( 1402 final Parser parser, final Builder builder) throws IOException { 1403 int annotationCount = builder.add("num_annotations: ", parser.u2()); 1404 SortedBuilder sortedBuilder = builder.addSortedBuilder(); 1405 for (int i = 0; i < annotationCount; ++i) { 1406 dumpTypeAnnotation(parser, sortedBuilder); 1407 } 1408 } 1409 1410 /** 1411 * Parses and dumps a type_annotation structure. 1412 * 1413 * @param parser a class parser. 1414 * @param sortedBuilder a dump builder. 1415 * @throws IOException if the class can't be parsed. 1416 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.20">JVMS 1417 * 4.7.20</a> 1418 */ dumpTypeAnnotation(final Parser parser, final SortedBuilder sortedBuilder)1419 private static void dumpTypeAnnotation(final Parser parser, final SortedBuilder sortedBuilder) 1420 throws IOException { 1421 int targetType = parser.u1(); 1422 Builder builder = sortedBuilder.addBuilder(String.valueOf(targetType)); 1423 builder.add("target_type: ", targetType); 1424 switch (targetType) { 1425 case 0x00: 1426 case 0x01: 1427 // type_parameter_target 1428 builder.add("type_parameter_index: ", parser.u1()); 1429 break; 1430 case 0x10: 1431 // supertype_target 1432 builder.add("supertype_index: ", parser.u2()); 1433 break; 1434 case 0x11: 1435 case 0x12: 1436 // type_parameter_bound_target 1437 builder.add("type_parameter_index: ", parser.u1()); 1438 builder.add("bound_index: ", parser.u1()); 1439 break; 1440 case 0x13: 1441 case 0x14: 1442 case 0x15: 1443 // empty_target 1444 // Nothing to parse. 1445 break; 1446 case 0x16: 1447 // formal_parameter_target 1448 builder.add("formal_parameter_index: ", parser.u1()); 1449 break; 1450 case 0x17: 1451 // throws_target 1452 builder.add("throws_type_index: ", parser.u2()); 1453 break; 1454 case 0x40: 1455 case 0x41: 1456 // localvar_target 1457 int tableLength = builder.add("table_length: ", parser.u2()); 1458 for (int i = 0; i < tableLength; ++i) { 1459 int startPc = builder.addInsnIndex("start_pc: ", parser.u2()); 1460 builder.addInsnIndex("length: ", startPc + parser.u2()); 1461 builder.add("index: ", parser.u2()); 1462 } 1463 break; 1464 case 0x42: 1465 // catch_target 1466 builder.add("exception_table_index: ", parser.u2()); 1467 break; 1468 case 0x43: 1469 case 0x44: 1470 case 0x45: 1471 case 0x46: 1472 // offset_target 1473 builder.addInsnIndex("offset: ", parser.u2()); 1474 break; 1475 case 0x47: 1476 case 0x48: 1477 case 0x49: 1478 case 0x4A: 1479 case 0x4B: 1480 // type_argument_target 1481 builder.addInsnIndex("offset: ", parser.u2()); 1482 builder.add("type_argument_index: ", parser.u1()); 1483 break; 1484 default: 1485 throw new ClassFormatException("Unknown target_type: " + targetType); 1486 } 1487 dumpTypePath(parser, builder); 1488 // Sort type annotations based on the full target_info and type_path (excluding the annotation 1489 // content), instead of only on their target_type. 1490 builder.sortByContent(); 1491 dumpAnnotation(parser, builder); 1492 } 1493 1494 /** 1495 * Parses and dumps a type_path structure. 1496 * 1497 * @param parser a class parser. 1498 * @param builder a dump builder. 1499 * @throws IOException if the class can't be parsed. 1500 * @see <a 1501 * href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.20.2">JVMS 1502 * 4.7.20.2</a> 1503 */ dumpTypePath(final Parser parser, final Builder builder)1504 private static void dumpTypePath(final Parser parser, final Builder builder) throws IOException { 1505 int pathLength = builder.add("path_length: ", parser.u1()); 1506 for (int i = 0; i < pathLength; ++i) { 1507 builder.add("type_path_kind: ", parser.u1()); 1508 builder.add("type_argument_index: ", parser.u1()); 1509 } 1510 } 1511 1512 /** 1513 * Parses and dumps a RuntimeInvisibleTypeAnnotations attribute. 1514 * 1515 * @param parser a class parser. 1516 * @param builder a dump builder. 1517 * @throws IOException if the class can't be parsed. 1518 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.21">JVMS 1519 * 4.7.21</a> 1520 */ dumpRuntimeInvisibleTypeAnnotationsAttribute( final Parser parser, final Builder builder)1521 private static void dumpRuntimeInvisibleTypeAnnotationsAttribute( 1522 final Parser parser, final Builder builder) throws IOException { 1523 dumpRuntimeVisibleTypeAnnotationsAttribute(parser, builder); 1524 } 1525 1526 /** 1527 * Parses and dumps an AnnotationDefault attribute. 1528 * 1529 * @param parser a class parser. 1530 * @param builder a dump builder. 1531 * @throws IOException if the class can't be parsed. 1532 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.22">JVMS 1533 * 4.7.22</a> 1534 */ dumpAnnotationDefaultAttribute(final Parser parser, final Builder builder)1535 private static void dumpAnnotationDefaultAttribute(final Parser parser, final Builder builder) 1536 throws IOException { 1537 dumpElementValue(parser, builder); 1538 } 1539 1540 /** 1541 * Parses and dumps a BootstrapMethods attribute. 1542 * 1543 * @param parser a class parser. 1544 * @param builder a dump builder. 1545 * @throws IOException if the class can't be parsed. 1546 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.23">JVMS 1547 * 4.7.23</a> 1548 */ dumpBootstrapMethodsAttribute(final Parser parser, final Builder builder)1549 private static void dumpBootstrapMethodsAttribute(final Parser parser, final Builder builder) 1550 throws IOException { 1551 int bootstrapMethodCount = builder.add("num_bootstrap_methods: ", parser.u2()); 1552 for (int i = 0; i < bootstrapMethodCount; ++i) { 1553 builder.addCpInfo("bootstrap_method_ref: ", parser.u2()); 1554 int bootstrapArgumentCount = builder.add("num_bootstrap_arguments: ", parser.u2()); 1555 for (int j = 0; j < bootstrapArgumentCount; ++j) { 1556 builder.addCpInfo("bootstrap_argument: ", parser.u2()); 1557 } 1558 } 1559 } 1560 1561 /** 1562 * Parses and dumps a MethodParameters attribute. 1563 * 1564 * @param parser a class parser. 1565 * @param builder a dump builder. 1566 * @throws IOException if the class can't be parsed. 1567 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.24">JVMS 1568 * 4.7.24</a> 1569 */ dumpMethodParametersAttribute(final Parser parser, final Builder builder)1570 private static void dumpMethodParametersAttribute(final Parser parser, final Builder builder) 1571 throws IOException { 1572 int parameterCount = builder.add("parameters_count: ", parser.u1()); 1573 for (int i = 0; i < parameterCount; ++i) { 1574 builder.addCpInfo("name_index: ", parser.u2()); 1575 builder.add("access_flags: ", parser.u2()); 1576 } 1577 } 1578 1579 /** 1580 * Parses and dumps a Module attribute. 1581 * 1582 * @param parser a class parser. 1583 * @param builder a dump builder. 1584 * @throws IOException if the class can't be parsed. 1585 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.25">JVMS 1586 * 4.7.25</a> 1587 */ dumpModuleAttribute(final Parser parser, final Builder builder)1588 private static void dumpModuleAttribute(final Parser parser, final Builder builder) 1589 throws IOException { 1590 builder.addCpInfo("name: ", parser.u2()); 1591 builder.add("access: ", parser.u2()); 1592 builder.addCpInfo("version: ", parser.u2()); 1593 int requireCount = builder.add("require_count: ", parser.u2()); 1594 for (int i = 0; i < requireCount; ++i) { 1595 builder.addCpInfo("name: ", parser.u2()); 1596 builder.add("access: ", parser.u2()); 1597 builder.addCpInfo("version: ", parser.u2()); 1598 } 1599 int exportCount = builder.add("export_count: ", parser.u2()); 1600 for (int i = 0; i < exportCount; ++i) { 1601 builder.addCpInfo("name: ", parser.u2()); 1602 builder.add("access: ", parser.u2()); 1603 int exportToCount = builder.add("export_to_count: ", parser.u2()); 1604 for (int j = 0; j < exportToCount; ++j) { 1605 builder.addCpInfo("to: ", parser.u2()); 1606 } 1607 } 1608 int openCount = builder.add("open_count: ", parser.u2()); 1609 for (int i = 0; i < openCount; ++i) { 1610 builder.addCpInfo("name: ", parser.u2()); 1611 builder.add("access: ", parser.u2()); 1612 int openToCount = builder.add("open_to_count: ", parser.u2()); 1613 for (int j = 0; j < openToCount; ++j) { 1614 builder.addCpInfo("to: ", parser.u2()); 1615 } 1616 } 1617 int useCount = builder.add("use_count: ", parser.u2()); 1618 for (int i = 0; i < useCount; ++i) { 1619 builder.addCpInfo("use: ", parser.u2()); 1620 } 1621 int provideCount = builder.add("provide_count: ", parser.u2()); 1622 for (int i = 0; i < provideCount; ++i) { 1623 builder.addCpInfo("provide: ", parser.u2()); 1624 int provideWithCount = builder.add("provide_with_count: ", parser.u2()); 1625 for (int j = 0; j < provideWithCount; ++j) { 1626 builder.addCpInfo("with: ", parser.u2()); 1627 } 1628 } 1629 } 1630 1631 /** 1632 * Parses and dumps a ModulePackages attribute. 1633 * 1634 * @param parser a class parser. 1635 * @param builder a dump builder. 1636 * @throws IOException if the class can't be parsed. 1637 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.26">JVMS 1638 * 4.7.26</a> 1639 */ dumpModulePackagesAttribute(final Parser parser, final Builder builder)1640 private static void dumpModulePackagesAttribute(final Parser parser, final Builder builder) 1641 throws IOException { 1642 int packageCount = builder.add("package_count: ", parser.u2()); 1643 for (int i = 0; i < packageCount; ++i) { 1644 builder.addCpInfo("package: ", parser.u2()); 1645 } 1646 } 1647 1648 /** 1649 * Parses and dumps a ModuleMainClass attribute. 1650 * 1651 * @param parser a class parser. 1652 * @param builder a dump builder. 1653 * @throws IOException if the class can't be parsed. 1654 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.27">JVMS 1655 * 4.7.27</a> 1656 */ dumpModuleMainClassAttribute(final Parser parser, final Builder builder)1657 private static void dumpModuleMainClassAttribute(final Parser parser, final Builder builder) 1658 throws IOException { 1659 builder.addCpInfo("main_class: ", parser.u2()); 1660 } 1661 1662 /** 1663 * Parses and dumps a NestHost attribute. 1664 * 1665 * @param parser a class parser. 1666 * @param builder a dump builder. 1667 * @throws IOException if the class can't be parsed. 1668 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-4.html#jvms-4.7.28">JVMS 1669 * 4.7.28</a> 1670 */ dumpNestHostAttribute(final Parser parser, final Builder builder)1671 private static void dumpNestHostAttribute(final Parser parser, final Builder builder) 1672 throws IOException { 1673 builder.addCpInfo("host_class: ", parser.u2()); 1674 } 1675 1676 /** 1677 * Parses and dumps a NestMembers attribute. 1678 * 1679 * @param parser a class parser. 1680 * @param builder a dump builder. 1681 * @throws IOException if the class can't be parsed. 1682 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-4.html#jvms-4.7.29">JVMS 1683 * 4.7.29</a> 1684 */ dumpNestMembersAttribute(final Parser parser, final Builder builder)1685 private static void dumpNestMembersAttribute(final Parser parser, final Builder builder) 1686 throws IOException { 1687 int numberOfClasses = builder.add("number_of_classes: ", parser.u2()); 1688 for (int i = 0; i < numberOfClasses; ++i) { 1689 builder.addCpInfo("class: ", parser.u2()); 1690 } 1691 } 1692 1693 /** 1694 * Parses and dumps a PermittedSubclasses attribute. 1695 * 1696 * @param parser a class parser. 1697 * @param builder a dump builder. 1698 * @throws IOException if the class can't be parsed. 1699 * @see <a href="https://openjdk.java.net/jeps/360">JEP 360</a> 1700 */ dumpPermittedSubclassesAttribute(final Parser parser, final Builder builder)1701 private static void dumpPermittedSubclassesAttribute(final Parser parser, final Builder builder) 1702 throws IOException { 1703 int permittedSubclassesCount = builder.add("permitted_subclasses_count: ", parser.u2()); 1704 for (int i = 0; i < permittedSubclassesCount; ++i) { 1705 builder.addCpInfo("class: ", parser.u2()); 1706 } 1707 } 1708 1709 /** 1710 * Parses and dumps a Record attribute. 1711 * 1712 * @param parser a class parser. 1713 * @param builder a dump builder. 1714 * @throws IOException if the class can't be parsed. 1715 * @see <a href="https://openjdk.java.net/jeps/360">JEP 360</a> 1716 */ dumpRecordAttribute(final Parser parser, final Builder builder)1717 private static void dumpRecordAttribute(final Parser parser, final Builder builder) 1718 throws IOException { 1719 int numberOfComponentRecords = builder.add("number_of_component_records: ", parser.u2()); 1720 for (int i = 0; i < numberOfComponentRecords; ++i) { 1721 builder.addCpInfo("record_component_name: ", parser.u2()); 1722 builder.addCpInfo("record_component_descriptor: ", parser.u2()); 1723 dumpAttributeList(parser, builder); 1724 } 1725 } 1726 1727 /** 1728 * Parses and dumps a StackMap attribute. 1729 * 1730 * @param parser a class parser. 1731 * @param builder a dump builder. 1732 * @throws IOException if the class can't be parsed. 1733 * @see <a 1734 * href="http://docs.oracle.com/javame/config/cldc/opt-pkgs/api/cldc/api/Appendix1-verifier.pdf">CLDC</a> 1735 */ dumpStackMapAttribute(final Parser parser, final Builder builder)1736 private static void dumpStackMapAttribute(final Parser parser, final Builder builder) 1737 throws IOException { 1738 int entryCount = builder.add("number_of_entries: ", parser.u2()); 1739 for (int i = 0; i < entryCount; ++i) { 1740 builder.addInsnIndex("offset: ", parser.u2()); 1741 int numberOfLocals = builder.add("number_of_locals: ", parser.u2()); 1742 for (int j = 0; j < numberOfLocals; ++j) { 1743 dumpVerificationTypeInfo(parser, builder); 1744 } 1745 int numberOfStackItems = builder.add("number_of_stack_items: ", parser.u2()); 1746 for (int j = 0; j < numberOfStackItems; ++j) { 1747 dumpVerificationTypeInfo(parser, builder); 1748 } 1749 } 1750 } 1751 1752 /** An abstract constant pool item. */ 1753 private abstract static class CpInfo { 1754 /** The dump of this item. */ 1755 private String dump; 1756 /** The context to use to get the referenced constant pool items. */ 1757 private final ClassContext classContext; 1758 1759 /** 1760 * Constructs a CpInfo for an item without references to other items. 1761 * 1762 * @param dump the dump of this item. 1763 */ CpInfo(final String dump)1764 CpInfo(final String dump) { 1765 this.dump = dump; 1766 this.classContext = null; 1767 } 1768 1769 /** 1770 * Constructs a CpInfo for an item with references to other items. 1771 * 1772 * @param classContext a context to lookup constant pool items from their index. 1773 */ CpInfo(final ClassContext classContext)1774 CpInfo(final ClassContext classContext) { 1775 this.classContext = classContext; 1776 } 1777 1778 /** 1779 * Returns the number of entries used by this item in constant_pool. 1780 * 1781 * @return the number of entries used by this item in constant_pool. 1782 */ size()1783 int size() { 1784 return 1; 1785 } 1786 1787 /** 1788 * Returns the constant pool item with the given index. 1789 * 1790 * @param <C> a CpInfo subclass. 1791 * @param cpIndex a constant pool entry index. 1792 * @param cpInfoType the expected type of the constant pool entry. 1793 * @return the constant pool item with the given index. 1794 */ getCpInfo(final int cpIndex, final Class<C> cpInfoType)1795 <C extends CpInfo> C getCpInfo(final int cpIndex, final Class<C> cpInfoType) { 1796 return classContext.getCpInfo(cpIndex, cpInfoType); 1797 } 1798 1799 /** 1800 * Returns the dump of this item. 1801 * 1802 * @return the dump of this item. 1803 */ dump()1804 String dump() { 1805 return dump; 1806 } 1807 1808 @Override toString()1809 public String toString() { 1810 if (dump == null) { 1811 dump = getClass().getSimpleName() + " " + dump(); 1812 } 1813 return dump; 1814 } 1815 } 1816 1817 /** 1818 * A CONSTANT_Class_info item. 1819 * 1820 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.4.1">JVMS 1821 * 4.4.1</a> 1822 */ 1823 private static class ConstantClassInfo extends CpInfo { 1824 private final int nameIndex; 1825 1826 /** 1827 * Parses a CONSTANT_Class_info item. 1828 * 1829 * @param parser a class parser. 1830 * @param classContext a context to lookup constant pool items from their index. 1831 * @throws IOException if the class can't be parsed. 1832 */ ConstantClassInfo(final Parser parser, final ClassContext classContext)1833 ConstantClassInfo(final Parser parser, final ClassContext classContext) throws IOException { 1834 super(classContext); 1835 this.nameIndex = parser.u2(); 1836 } 1837 1838 @Override dump()1839 String dump() { 1840 return getCpInfo(nameIndex, ConstantUtf8Info.class).dump(); 1841 } 1842 } 1843 1844 /** 1845 * A CONSTANT_Fieldref_info item. 1846 * 1847 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.4.2">JVMS 1848 * 4.4.2</a> 1849 */ 1850 private static class ConstantFieldRefInfo extends CpInfo { 1851 private final int classIndex; 1852 private final int nameAndTypeIndex; 1853 1854 /** 1855 * Parses a CONSTANT_Fieldref_info item. 1856 * 1857 * @param parser a class parser. 1858 * @param classContext a context to lookup constant pool items from their index. 1859 * @throws IOException if the class can't be parsed. 1860 */ ConstantFieldRefInfo(final Parser parser, final ClassContext classContext)1861 ConstantFieldRefInfo(final Parser parser, final ClassContext classContext) throws IOException { 1862 super(classContext); 1863 this.classIndex = parser.u2(); 1864 this.nameAndTypeIndex = parser.u2(); 1865 } 1866 1867 @Override dump()1868 String dump() { 1869 return getCpInfo(classIndex, ConstantClassInfo.class).dump() 1870 + "." 1871 + getCpInfo(nameAndTypeIndex, ConstantNameAndTypeInfo.class).dump(); 1872 } 1873 } 1874 1875 /** 1876 * A CONSTANT_Methodref_info item. 1877 * 1878 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.4.2">JVMS 1879 * 4.4.2</a> 1880 */ 1881 private static class ConstantMethodRefInfo extends CpInfo { 1882 private final int classIndex; 1883 private final int nameAndTypeIndex; 1884 1885 /** 1886 * Parses a CONSTANT_Methodref_info item. 1887 * 1888 * @param parser a class parser. 1889 * @param classContext a context to lookup constant pool items from their index. 1890 * @throws IOException if the class can't be parsed. 1891 */ ConstantMethodRefInfo(final Parser parser, final ClassContext classContext)1892 ConstantMethodRefInfo(final Parser parser, final ClassContext classContext) throws IOException { 1893 super(classContext); 1894 this.classIndex = parser.u2(); 1895 this.nameAndTypeIndex = parser.u2(); 1896 } 1897 1898 @Override dump()1899 String dump() { 1900 return getCpInfo(classIndex, ConstantClassInfo.class).dump() 1901 + "." 1902 + getCpInfo(nameAndTypeIndex, ConstantNameAndTypeInfo.class).dump(); 1903 } 1904 } 1905 1906 /** 1907 * A CONSTANT_InterfaceMethodref_info item. 1908 * 1909 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.4.2">JVMS 1910 * 4.4.2</a> 1911 */ 1912 private static class ConstantInterfaceMethodRefInfo extends CpInfo { 1913 private final int classIndex; 1914 private final int nameAndTypeIndex; 1915 1916 /** 1917 * Parses a CONSTANT_InterfaceMethodref_info item. 1918 * 1919 * @param parser a class parser. 1920 * @param classContext a context to lookup constant pool items from their index. 1921 * @throws IOException if the class can't be parsed. 1922 */ ConstantInterfaceMethodRefInfo(final Parser parser, final ClassContext classContext)1923 ConstantInterfaceMethodRefInfo(final Parser parser, final ClassContext classContext) 1924 throws IOException { 1925 super(classContext); 1926 this.classIndex = parser.u2(); 1927 this.nameAndTypeIndex = parser.u2(); 1928 } 1929 1930 @Override dump()1931 String dump() { 1932 return getCpInfo(classIndex, ConstantClassInfo.class).dump() 1933 + "." 1934 + getCpInfo(nameAndTypeIndex, ConstantNameAndTypeInfo.class).dump(); 1935 } 1936 } 1937 1938 /** 1939 * A CONSTANT_String_info item. 1940 * 1941 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.4.3">JVMS 1942 * 4.4.3</a> 1943 */ 1944 private static class ConstantStringInfo extends CpInfo { 1945 final int stringIndex; 1946 1947 /** 1948 * Parses a CONSTANT_String_info item. 1949 * 1950 * @param parser a class parser. 1951 * @param classContext a context to lookup constant pool items from their index. 1952 * @throws IOException if the class can't be parsed. 1953 */ ConstantStringInfo(final Parser parser, final ClassContext classContext)1954 ConstantStringInfo(final Parser parser, final ClassContext classContext) throws IOException { 1955 super(classContext); 1956 this.stringIndex = parser.u2(); 1957 } 1958 1959 @Override dump()1960 String dump() { 1961 return getCpInfo(stringIndex, ConstantUtf8Info.class).dump(); 1962 } 1963 } 1964 1965 /** 1966 * A CONSTANT_Integer_info item. 1967 * 1968 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.4.4">JVMS 1969 * 4.4.4</a> 1970 */ 1971 private static class ConstantIntegerInfo extends CpInfo { 1972 1973 /** 1974 * Parses a CONSTANT_Integer_info item. 1975 * 1976 * @param parser a class parser. 1977 * @throws IOException if the class can't be parsed. 1978 */ ConstantIntegerInfo(final Parser parser)1979 ConstantIntegerInfo(final Parser parser) throws IOException { 1980 super(Integer.toString(parser.u4())); 1981 } 1982 } 1983 1984 /** 1985 * A CONSTANT_Float_info item. 1986 * 1987 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.4.4">JVMS 1988 * 4.4.4</a> 1989 */ 1990 private static class ConstantFloatInfo extends CpInfo { 1991 1992 /** 1993 * Parses a CONSTANT_Float_info item. 1994 * 1995 * @param parser a class parser. 1996 * @throws IOException if the class can't be parsed. 1997 */ ConstantFloatInfo(final Parser parser)1998 ConstantFloatInfo(final Parser parser) throws IOException { 1999 super(Float.toString(Float.intBitsToFloat(parser.u4()))); 2000 } 2001 } 2002 2003 /** 2004 * A CONSTANT_Long_info item. 2005 * 2006 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.4.5">JVMS 2007 * 4.4.5</a> 2008 */ 2009 private static class ConstantLongInfo extends CpInfo { 2010 2011 /** 2012 * Parses a CONSTANT_Long_info item. 2013 * 2014 * @param parser a class parser. 2015 * @throws IOException if the class can't be parsed. 2016 */ ConstantLongInfo(final Parser parser)2017 ConstantLongInfo(final Parser parser) throws IOException { 2018 super(Long.toString(parser.s8())); 2019 } 2020 2021 @Override size()2022 int size() { 2023 return 2; 2024 } 2025 } 2026 2027 /** 2028 * A CONSTANT_Double_info item. 2029 * 2030 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.4.5">JVMS 2031 * 4.4.5</a> 2032 */ 2033 private static class ConstantDoubleInfo extends CpInfo { 2034 2035 /** 2036 * Parses a CONSTANT_Double_info item. 2037 * 2038 * @param parser a class parser. 2039 * @throws IOException if the class can't be parsed. 2040 */ ConstantDoubleInfo(final Parser parser)2041 ConstantDoubleInfo(final Parser parser) throws IOException { 2042 super(Double.toString(Double.longBitsToDouble(parser.s8()))); 2043 } 2044 2045 @Override size()2046 int size() { 2047 return 2; 2048 } 2049 } 2050 2051 /** 2052 * A CONSTANT_NameAndType_info item. 2053 * 2054 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.4.6">JVMS 2055 * 4.4.6</a> 2056 */ 2057 private static class ConstantNameAndTypeInfo extends CpInfo { 2058 private final int nameIndex; 2059 private final int descriptorIndex; 2060 2061 /** 2062 * Parses a CONSTANT_NameAndType_info item. 2063 * 2064 * @param parser a class parser. 2065 * @param classContext a context to lookup constant pool items from their index. 2066 * @throws IOException if the class can't be parsed. 2067 */ ConstantNameAndTypeInfo(final Parser parser, final ClassContext classContext)2068 ConstantNameAndTypeInfo(final Parser parser, final ClassContext classContext) 2069 throws IOException { 2070 super(classContext); 2071 this.nameIndex = parser.u2(); 2072 this.descriptorIndex = parser.u2(); 2073 } 2074 2075 @Override dump()2076 String dump() { 2077 return getCpInfo(nameIndex, ConstantUtf8Info.class).dump() 2078 + getCpInfo(descriptorIndex, ConstantUtf8Info.class).dump(); 2079 } 2080 } 2081 2082 /** 2083 * A CONSTANT_Utf8_info item. 2084 * 2085 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.4.7">JVMS 2086 * 4.4.7</a> 2087 */ 2088 private static class ConstantUtf8Info extends CpInfo { 2089 2090 /** 2091 * Parses a CONSTANT_Utf8_info item. 2092 * 2093 * @param parser a class parser. 2094 * @throws IOException if the class can't be parsed. 2095 */ ConstantUtf8Info(final Parser parser)2096 ConstantUtf8Info(final Parser parser) throws IOException { 2097 super(parser.utf8()); 2098 } 2099 } 2100 2101 /** 2102 * A CONSTANT_MethodHandle_info item. 2103 * 2104 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.4.8">JVMS 2105 * 4.4.8</a> 2106 */ 2107 private static class ConstantMethodHandleInfo extends CpInfo { 2108 private final int referenceKind; 2109 private final int referenceIndex; 2110 2111 /** 2112 * Parses a CONSTANT_MethodHandle_info item. 2113 * 2114 * @param parser a class parser. 2115 * @param classContext a context to lookup constant pool items from their index. 2116 * @throws IOException if the class can't be parsed. 2117 */ ConstantMethodHandleInfo(final Parser parser, final ClassContext classContext)2118 ConstantMethodHandleInfo(final Parser parser, final ClassContext classContext) 2119 throws IOException { 2120 super(classContext); 2121 this.referenceKind = parser.u1(); 2122 this.referenceIndex = parser.u2(); 2123 } 2124 2125 @Override dump()2126 String dump() { 2127 return referenceKind + "." + getCpInfo(referenceIndex, CpInfo.class); 2128 } 2129 } 2130 2131 /** 2132 * A CONSTANT_MethodType_info item. 2133 * 2134 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.4.9">JVMS 2135 * 4.4.9</a> 2136 */ 2137 private static class ConstantMethodTypeInfo extends CpInfo { 2138 private final int descriptorIndex; 2139 2140 /** 2141 * Parses a CONSTANT_MethodType_info item. 2142 * 2143 * @param parser a class parser. 2144 * @param classContext a context to lookup constant pool items from their index. 2145 * @throws IOException if the class can't be parsed. 2146 */ ConstantMethodTypeInfo(final Parser parser, final ClassContext classContext)2147 ConstantMethodTypeInfo(final Parser parser, final ClassContext classContext) 2148 throws IOException { 2149 super(classContext); 2150 this.descriptorIndex = parser.u2(); 2151 } 2152 2153 @Override dump()2154 String dump() { 2155 return getCpInfo(descriptorIndex, ConstantUtf8Info.class).dump(); 2156 } 2157 } 2158 2159 /** 2160 * A CONSTANT_InvokeDynamic_info item. 2161 * 2162 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.4.10">JVMS 2163 * 4.4.10</a> 2164 */ 2165 private static class ConstantInvokeDynamicInfo extends CpInfo { 2166 private final int bootstrapMethodAttrIndex; 2167 private final int nameAndTypeIndex; 2168 2169 /** 2170 * Parses a CONSTANT_InvokeDynamic_info item. 2171 * 2172 * @param parser a class parser. 2173 * @param classContext a context to lookup constant pool items from their index. 2174 * @throws IOException if the class can't be parsed. 2175 */ ConstantInvokeDynamicInfo(final Parser parser, final ClassContext classContext)2176 ConstantInvokeDynamicInfo(final Parser parser, final ClassContext classContext) 2177 throws IOException { 2178 super(classContext); 2179 this.bootstrapMethodAttrIndex = parser.u2(); 2180 this.nameAndTypeIndex = parser.u2(); 2181 } 2182 2183 @Override dump()2184 String dump() { 2185 return bootstrapMethodAttrIndex 2186 + "." 2187 + getCpInfo(nameAndTypeIndex, ConstantNameAndTypeInfo.class).dump(); 2188 } 2189 } 2190 2191 /** 2192 * A CONSTANT_Module_info item. 2193 * 2194 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.4.11">JVMS 2195 * 4.4.11</a> 2196 */ 2197 private static class ConstantModuleInfo extends CpInfo { 2198 private final int descriptorIndex; 2199 2200 /** 2201 * Parses a CONSTANT_Module_info item. 2202 * 2203 * @param parser a class parser. 2204 * @param classContext a context to lookup constant pool items from their index. 2205 * @throws IOException if the class can't be parsed. 2206 */ ConstantModuleInfo(final Parser parser, final ClassContext classContext)2207 ConstantModuleInfo(final Parser parser, final ClassContext classContext) throws IOException { 2208 super(classContext); 2209 this.descriptorIndex = parser.u2(); 2210 } 2211 2212 @Override dump()2213 String dump() { 2214 return getCpInfo(descriptorIndex, ConstantUtf8Info.class).dump(); 2215 } 2216 } 2217 2218 /** 2219 * A CONSTANT_Package_info item. 2220 * 2221 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.4.12">JVMS 2222 * 4.4.12</a> 2223 */ 2224 private static class ConstantPackageInfo extends CpInfo { 2225 private final int descriptorIndex; 2226 2227 /** 2228 * Parses a CONSTANT_Package_info item. 2229 * 2230 * @param parser a class parser. 2231 * @param classContext a context to lookup constant pool items from their index. 2232 * @throws IOException if the class can't be parsed. 2233 */ ConstantPackageInfo(final Parser parser, final ClassContext classContext)2234 ConstantPackageInfo(final Parser parser, final ClassContext classContext) throws IOException { 2235 super(classContext); 2236 this.descriptorIndex = parser.u2(); 2237 } 2238 2239 @Override dump()2240 String dump() { 2241 return getCpInfo(descriptorIndex, ConstantUtf8Info.class).dump(); 2242 } 2243 } 2244 2245 /** 2246 * A CONSTANT_Dynamic_info item. 2247 * 2248 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.4.13">JVMS 2249 * 4.4.13</a> 2250 */ 2251 private static class ConstantDynamicInfo extends CpInfo { 2252 private final int bootstrapMethodAttrIndex; 2253 private final int nameAndTypeIndex; 2254 2255 /** 2256 * Parses a CONSTANT_Dynamic_info item. 2257 * 2258 * @param parser a class parser. 2259 * @param classContext a context to lookup constant pool items from their index. 2260 * @throws IOException if the class can't be parsed. 2261 */ ConstantDynamicInfo(final Parser parser, final ClassContext classContext)2262 ConstantDynamicInfo(final Parser parser, final ClassContext classContext) throws IOException { 2263 super(classContext); 2264 this.bootstrapMethodAttrIndex = parser.u2(); 2265 this.nameAndTypeIndex = parser.u2(); 2266 } 2267 2268 @Override dump()2269 String dump() { 2270 return bootstrapMethodAttrIndex 2271 + "." 2272 + getCpInfo(nameAndTypeIndex, ConstantNameAndTypeInfo.class).dump(); 2273 } 2274 } 2275 2276 /** 2277 * The index of a bytecode instruction. This index is computed in {@link #toString}, from the 2278 * bytecode offset of the instruction, after the whole class has been parsed. Indeed, due to 2279 * forward references, the index of an instruction might not be known when its offset is used. 2280 * 2281 * <p>Dumps use instruction indices instead of bytecode offsets in order to abstract away the low 2282 * level byte code instruction representation details (e.g. an ldc vs. an ldc_w). 2283 */ 2284 private static class InstructionIndex { 2285 /** An offset in bytes from the start of the bytecode of a method. */ 2286 private final int bytecodeOffset; 2287 /** The context to use to find the index from the bytecode offset. */ 2288 private final MethodContext methodContext; 2289 InstructionIndex(final int bytecodeOffset, final MethodContext methodContext)2290 InstructionIndex(final int bytecodeOffset, final MethodContext methodContext) { 2291 this.bytecodeOffset = bytecodeOffset; 2292 this.methodContext = methodContext; 2293 } 2294 getBytecodeOffset()2295 int getBytecodeOffset() { 2296 return bytecodeOffset; 2297 } 2298 2299 @Override toString()2300 public String toString() { 2301 return "<" + methodContext.getInsnIndex(bytecodeOffset) + ">"; 2302 } 2303 } 2304 2305 /** 2306 * A simple byte array parser. The method names reflect the type names used in the Java Virtual 2307 * Machine Specification for ease of reference. 2308 */ 2309 private static class Parser { 2310 private final DataInputStream dataInputStream; 2311 Parser(final byte[] data)2312 Parser(final byte[] data) { 2313 this.dataInputStream = new DataInputStream(new ByteArrayInputStream(data)); 2314 } 2315 u1()2316 int u1() throws IOException { 2317 return dataInputStream.readUnsignedByte(); 2318 } 2319 s1()2320 int s1() throws IOException { 2321 return dataInputStream.readByte(); 2322 } 2323 u2()2324 int u2() throws IOException { 2325 return dataInputStream.readUnsignedShort(); 2326 } 2327 s2()2328 int s2() throws IOException { 2329 return dataInputStream.readShort(); 2330 } 2331 u4()2332 int u4() throws IOException { 2333 return dataInputStream.readInt(); 2334 } 2335 s8()2336 long s8() throws IOException { 2337 long highBytes = dataInputStream.readInt(); 2338 long lowBytes = dataInputStream.readInt() & 0xFFFFFFFFL; 2339 return (highBytes << 32) | lowBytes; 2340 } 2341 utf8()2342 String utf8() throws IOException { 2343 return dataInputStream.readUTF(); 2344 } 2345 bytes(final int length)2346 byte[] bytes(final int length) throws IOException { 2347 if (length > dataInputStream.available()) { 2348 throw new ClassFormatException("Invalid length: " + length); 2349 } 2350 byte[] bytes = new byte[length]; 2351 dataInputStream.readFully(bytes); 2352 return bytes; 2353 } 2354 } 2355 2356 /** A context to lookup constant pool items from their index. */ 2357 private interface ClassContext { getCpInfo(int cpIndex, Class<C> cpInfoType)2358 <C extends CpInfo> C getCpInfo(int cpIndex, Class<C> cpInfoType); 2359 } 2360 2361 /** A context to lookup instruction indices from their bytecode offset. */ 2362 private interface MethodContext { getInsnIndex(int bytecodeOffset)2363 int getInsnIndex(int bytecodeOffset); 2364 } 2365 2366 /** 2367 * A helper class to build the dump of a class file. The dump can't be output fully sequentially, 2368 * as the input class is parsed, in particular due to the re-ordering of attributes and 2369 * annotations. Instead, a tree is constructed first, then its nodes are sorted and finally the 2370 * tree is parsed in Depth First Search order to build the dump. This class is the super class of 2371 * the internal nodes of the tree. 2372 * 2373 * <p>Each internal node is a context that can store a mapping between constant pool indices and 2374 * constant pool items and between bytecode offsets and instructions indices. This can be used to 2375 * resolve references to such objects. Contexts inherit from their parent, i.e. if a lookup fails 2376 * in some builder, the lookup continues in the parent, and so on until the root is reached. 2377 */ 2378 private abstract static class AbstractBuilder<T> implements ClassContext, MethodContext { 2379 /** Flag used to distinguish CpInfo keys in {@link #context}. */ 2380 private static final int CP_INFO_KEY = 0xF0000000; 2381 /** The parent node of this node. May be {@literal null}. */ 2382 private final AbstractBuilder<?> parent; 2383 /** The children of this builder. */ 2384 final ArrayList<T> children; 2385 /** The map used to implement the Context interfaces. */ 2386 private final HashMap<Integer, Object> context; 2387 AbstractBuilder(final AbstractBuilder<?> parent)2388 AbstractBuilder(final AbstractBuilder<?> parent) { 2389 this.parent = parent; 2390 this.children = new ArrayList<>(); 2391 this.context = new HashMap<>(); 2392 } 2393 2394 /** 2395 * Lookup constant pool items from their index. 2396 * 2397 * @param cpIndex a constant pool item index. 2398 * @return the constant pool item at the given index. 2399 */ getCpInfo(final int cpIndex)2400 CpInfo getCpInfo(final int cpIndex) { 2401 return getCpInfo(cpIndex, CpInfo.class); 2402 } 2403 2404 @Override getCpInfo(final int cpIndex, final Class<C> cpInfoType)2405 public <C extends CpInfo> C getCpInfo(final int cpIndex, final Class<C> cpInfoType) { 2406 Object cpInfo = get(CP_INFO_KEY | cpIndex); 2407 if (cpInfo == null) { 2408 throw new ClassFormatException("Invalid constant pool index: " + cpIndex); 2409 } else if (!cpInfoType.isInstance(cpInfo)) { 2410 throw new ClassFormatException( 2411 "Invalid constant pool type: " 2412 + cpInfo.getClass().getName() 2413 + " should be " 2414 + cpInfoType.getName()); 2415 } 2416 return cpInfoType.cast(cpInfo); 2417 } 2418 2419 @Override getInsnIndex(final int bytecodeOffset)2420 public int getInsnIndex(final int bytecodeOffset) { 2421 Integer insnIndex = (Integer) get(bytecodeOffset); 2422 if (insnIndex == null) { 2423 throw new ClassFormatException("Invalid bytecode offset: " + bytecodeOffset); 2424 } 2425 return insnIndex; 2426 } 2427 2428 /** 2429 * Registers the CpInfo for the given constant pool index. 2430 * 2431 * @param cpIndex a constant pool item index. 2432 * @param cpInfo a constant pool item. 2433 */ putCpInfo(final int cpIndex, final CpInfo cpInfo)2434 void putCpInfo(final int cpIndex, final CpInfo cpInfo) { 2435 context.put(CP_INFO_KEY | cpIndex, cpInfo); 2436 } 2437 2438 /** 2439 * Registers the instruction index for the given bytecode offset. 2440 * 2441 * @param bytecodeOffset a bytecode offset. 2442 * @param instructionIndex the index of a bytecode instruction. 2443 */ putInsnIndex(final int bytecodeOffset, final int instructionIndex)2444 void putInsnIndex(final int bytecodeOffset, final int instructionIndex) { 2445 context.put(bytecodeOffset, instructionIndex); 2446 } 2447 2448 /** 2449 * Recursively appends the builder's children to the given string. 2450 * 2451 * @param stringBuilder a string builder. 2452 */ build(final StringBuilder stringBuilder)2453 void build(final StringBuilder stringBuilder) { 2454 for (Object child : children) { 2455 if (child instanceof AbstractBuilder<?>) { 2456 ((AbstractBuilder<?>) child).build(stringBuilder); 2457 } else { 2458 stringBuilder.append(child); 2459 } 2460 } 2461 } 2462 2463 /** 2464 * Returns the value associated with the given key. 2465 * 2466 * @param key a context key. 2467 * @return the value associated with the given key in this context or, if not found, in the 2468 * parent context (recursively). 2469 */ get(final int key)2470 private Object get(final int key) { 2471 Object value = context.get(key); 2472 if (value != null) { 2473 return value; 2474 } 2475 return parent == null ? null : parent.get(key); 2476 } 2477 } 2478 2479 /** An {@link AbstractBuilder} with concrete methods to add children. */ 2480 private static class Builder extends AbstractBuilder<Object> implements Comparable<Builder> { 2481 /** The name of this builder, for sorting in {@link SortedBuilder}. */ 2482 private String name; 2483 Builder(final String name, final AbstractBuilder<?> parent)2484 Builder(final String name, final AbstractBuilder<?> parent) { 2485 super(parent); 2486 this.name = name; 2487 } 2488 2489 /** 2490 * Appends name and value to children and returns value. 2491 * 2492 * @param <T> a value type. 2493 * @param name a name. 2494 * @param value a value. 2495 * @return value 2496 */ add(final String name, final T value)2497 <T> T add(final String name, final T value) { 2498 children.add(name); 2499 children.add(value); 2500 children.add("\n"); 2501 return value; 2502 } 2503 2504 /** 2505 * Appends name and the instruction index corresponding to bytecodeOffset to children, and 2506 * returns bytecodeOffset. 2507 * 2508 * @param name a name. 2509 * @param bytecodeOffset the offset of a bytecode instruction. 2510 * @return bytecodeOffset. 2511 */ addInsnIndex(final String name, final int bytecodeOffset)2512 int addInsnIndex(final String name, final int bytecodeOffset) { 2513 add(name, new InstructionIndex(bytecodeOffset, this)); 2514 return bytecodeOffset; 2515 } 2516 2517 /** 2518 * Appends the given arguments to children. 2519 * 2520 * @param insnIndex the index of a bytecode instruction. 2521 * @param opcode a bytecode instruction opcode. 2522 * @param arguments the bytecode instruction arguments. 2523 */ addInsn(final int insnIndex, final int opcode, final Object... arguments)2524 void addInsn(final int insnIndex, final int opcode, final Object... arguments) { 2525 children.add(insnIndex); 2526 children.add(": "); 2527 children.add(opcode); 2528 for (Object argument : arguments) { 2529 children.add(" "); 2530 children.add(argument); 2531 } 2532 children.add("\n"); 2533 } 2534 2535 /** 2536 * Appends name and the CpInfo corresponding to cpIndex to children. 2537 * 2538 * @param name a name. 2539 * @param cpIndex a constant pool item index. 2540 */ addCpInfo(final String name, final int cpIndex)2541 void addCpInfo(final String name, final int cpIndex) { 2542 add(name, cpIndex == 0 ? 0 : getCpInfo(cpIndex)); 2543 } 2544 2545 /** 2546 * Appends a new {@link SortedBuilder} to children and returns it. 2547 * 2548 * @return a new {@link SortedBuilder}. 2549 */ addSortedBuilder()2550 SortedBuilder addSortedBuilder() { 2551 SortedBuilder sortedBuilder = new SortedBuilder(this); 2552 children.add(sortedBuilder); 2553 return sortedBuilder; 2554 } 2555 2556 /** Use the content of this builder, instead of its name, to sort it in a SortedBuilder. */ sortByContent()2557 void sortByContent() { 2558 StringBuilder stringBuilder = new StringBuilder(); 2559 for (Object child : children) { 2560 if (child instanceof InstructionIndex) { 2561 // Instruction index might not be known at this point, use bytecodeOffset instead. 2562 stringBuilder.append(((InstructionIndex) child).getBytecodeOffset()); 2563 } else { 2564 stringBuilder.append(child.toString()); 2565 } 2566 } 2567 name = stringBuilder.toString(); 2568 } 2569 2570 @Override compareTo(final Builder builder)2571 public int compareTo(final Builder builder) { 2572 return name.compareTo(builder.name); 2573 } 2574 2575 @Override equals(final Object other)2576 public boolean equals(final Object other) { 2577 return (other instanceof Builder) && name.equals(((Builder) other).name); 2578 } 2579 2580 @Override hashCode()2581 public int hashCode() { 2582 return name.hashCode(); 2583 } 2584 } 2585 2586 /** An {@link AbstractBuilder} which sorts its children by name before building. */ 2587 private static class SortedBuilder extends AbstractBuilder<Builder> { SortedBuilder(final Builder parent)2588 SortedBuilder(final Builder parent) { 2589 super(parent); 2590 } 2591 2592 /** 2593 * Appends a new {@link Builder} to children and returns it. 2594 * 2595 * @param name the name of the new builder. 2596 * @return the new builder. 2597 */ addBuilder(final String name)2598 Builder addBuilder(final String name) { 2599 Builder builder = new Builder(name, this); 2600 children.add(builder); 2601 return builder; 2602 } 2603 2604 @Override build(final StringBuilder stringBuilder)2605 void build(final StringBuilder stringBuilder) { 2606 Collections.sort(children); 2607 super.build(stringBuilder); 2608 } 2609 } 2610 2611 /** A simple ClassLoader to test that a class can be loaded in the JVM. */ 2612 private static class ByteClassLoader extends ClassLoader { 2613 private final String className; 2614 private final byte[] classContent; 2615 private boolean classLoaded; 2616 ByteClassLoader(final String className, final byte[] classContent)2617 ByteClassLoader(final String className, final byte[] classContent) { 2618 this.className = className; 2619 this.classContent = classContent; 2620 } 2621 classLoaded()2622 boolean classLoaded() { 2623 return classLoaded; 2624 } 2625 2626 @Override loadClass(final String name, final boolean resolve)2627 protected Class<?> loadClass(final String name, final boolean resolve) 2628 throws ClassNotFoundException { 2629 if (name.equals(className)) { 2630 classLoaded = true; 2631 return defineClass(className, classContent, 0, classContent.length); 2632 } else { 2633 return super.loadClass(name, resolve); 2634 } 2635 } 2636 } 2637 } 2638