• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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: &lt;init&gt;
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.&lt;init&gt;()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: &lt;0&gt;
70  * line_number: 31
71  * attribute_name_index: LocalVariableTable
72  * local_variable_table_length: 1
73  * start_pc: &lt;0&gt;
74  * length: &lt;3&gt;
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: &lt;0&gt;
94  * line_number: 33
95  * start_pc: &lt;3&gt;
96  * line_number: 34
97  * attribute_name_index: LocalVariableTable
98  * local_variable_table_length: 1
99  * start_pc: &lt;0&gt;
100  * length: &lt;4&gt;
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