1 /*
  2  * Copyright (c) 2007, 2020, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.  Oracle designates this
  8  * particular file as subject to the "Classpath" exception as provided
  9  * by Oracle in the LICENSE file that accompanied this code.
 10  *
 11  * This code is distributed in the hope that it will be useful, but WITHOUT
 12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 14  * version 2 for more details (a copy is included in the LICENSE file that
 15  * accompanied this code).
 16  *
 17  * You should have received a copy of the GNU General Public License version
 18  * 2 along with this work; if not, write to the Free Software Foundation,
 19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 20  *
 21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 22  * or visit www.oracle.com if you need additional information or have any
 23  * questions.
 24  */
 25 
 26 package com.sun.tools.javap;
 27 
 28 import java.lang.reflect.Modifier;
 29 import java.nio.charset.StandardCharsets;
 30 import java.util.List;
 31 import java.util.Locale;
 32 import java.lang.classfile.*;
 33 import java.lang.reflect.AccessFlag;
 34 import java.lang.classfile.constantpool.*;
 35 import java.lang.classfile.attribute.*;
 36 import static java.lang.classfile.ClassFile.*;
 37 import static java.lang.classfile.attribute.StackMapFrameInfo.*;
 38 
 39 /*
 40  *  A writer for writing Attributes as text.
 41  *
 42  *  <p><b>This is NOT part of any supported API.
 43  *  If you write code that depends on this, you do so at your own risk.
 44  *  This code and its internal interfaces are subject to change or
 45  *  deletion without notice.</b>
 46  */
 47 public class AttributeWriter extends BasicWriter {
 48 
 49     public static AttributeWriter instance(Context context) {
 50         AttributeWriter instance = context.get(AttributeWriter.class);
 51         if (instance == null)
 52             instance = new AttributeWriter(context);
 53         return instance;
 54     }
 55 
 56     protected AttributeWriter(Context context) {
 57         super(context);
 58         context.put(AttributeWriter.class, this);
 59         annotationWriter = AnnotationWriter.instance(context);
 60         codeWriter = CodeWriter.instance(context);
 61         constantWriter = ConstantWriter.instance(context);
 62         options = Options.instance(context);
 63     }
 64 
 65     public void write(List<Attribute<?>> attrs) {
 66         write(attrs, null);
 67     }
 68 
 69     public void write(List<Attribute<?>> attrs, CodeAttribute lr) {
 70         if (attrs != null) {
 71             for (var attr : attrs) try {
 72                 write(attr, lr);
 73             } catch (IllegalArgumentException e) {
 74                 report(e);
 75             }
 76         }
 77     }
 78 
 79     public void write(Attribute<?> a, CodeAttribute lr) {
 80         switch (a) {
 81             case UnknownAttribute attr -> {
 82                 byte[] data = attr.contents();
 83                 int i = 0;
 84                 int j = 0;
 85                 print("  ");
 86                 print(attr.attributeName());
 87                 print(": ");
 88                 print("length = 0x" + toHex(data.length));
 89                 print(" (unknown attribute)");
 90                 println();
 91                 print("   ");
 92                 while (i < data.length) {
 93                     print(toHex(data[i], 2));
 94 
 95                     j++;
 96                     if (j == 16) {
 97                         println();
 98                         print("   ");
 99                         j = 0;
100                     } else {
101                         print(" ");
102                     }
103                     i++;
104                 }
105                 println();
106             }
107             case AnnotationDefaultAttribute attr -> {
108                 println("AnnotationDefault:");
109                 indent(+1);
110                 print("default_value: ");
111                 annotationWriter.write(attr.defaultValue());
112                 indent(-1);
113                 println();
114             }
115             case BootstrapMethodsAttribute attr -> {
116                 println("BootstrapMethods:");
117                 for (int i = 0; i < attr.bootstrapMethodsSize() ; i++) {
118                     var bsm = attr.bootstrapMethods().get(i);
119                     indent(+1);
120                     print(i + ": #" + bsm.bootstrapMethod().index() + " ");
121                     println(constantWriter.stringValue(bsm.bootstrapMethod()));
122                     indent(+1);
123                     println("Method arguments:");
124                     indent(+1);
125                     for (var arg : bsm.arguments()) {
126                         print("#" + arg.index() + " ");
127                         println(constantWriter.stringValue(arg));
128                     }
129                     indent(-3);
130                 }
131             }
132             case CharacterRangeTableAttribute attr -> {
133                 println("CharacterRangeTable:");
134                 indent(+1);
135                 for (var e : attr.characterRangeTable()) {
136                     print(String.format("    %2d, %2d, %6x, %6x, %4x",
137                             e.startPc(), e.endPc(),
138                             e.characterRangeStart(), e.characterRangeEnd(),
139                             e.flags()));
140                     tab();
141                     print(String.format("// %2d, %2d, %4d:%02d, %4d:%02d",
142                             e.startPc(), e.endPc(),
143                             (e.characterRangeStart() >> 10),
144                             (e.characterRangeStart() & 0x3ff),
145                             (e.characterRangeEnd() >> 10),
146                             (e.characterRangeEnd() & 0x3ff)));
147                     if ((e.flags() & CRT_STATEMENT) != 0)
148                         print(", statement");
149                     if ((e.flags() & CRT_BLOCK) != 0)
150                         print(", block");
151                     if ((e.flags() & CRT_ASSIGNMENT) != 0)
152                         print(", assignment");
153                     if ((e.flags() & CRT_FLOW_CONTROLLER) != 0)
154                         print(", flow-controller");
155                     if ((e.flags() & CRT_FLOW_TARGET) != 0)
156                         print(", flow-target");
157                     if ((e.flags() & CRT_INVOKE) != 0)
158                         print(", invoke");
159                     if ((e.flags() & CRT_CREATE) != 0)
160                         print(", create");
161                     if ((e.flags() & CRT_BRANCH_TRUE) != 0)
162                         print(", branch-true");
163                     if ((e.flags() & CRT_BRANCH_FALSE) != 0)
164                         print(", branch-false");
165                     println();
166                 }
167                 indent(-1);
168             }
169             case CodeAttribute attr -> codeWriter.write(attr);
170             case CompilationIDAttribute attr ->
171                 constantWriter.write(attr.compilationId().index());
172             case ConstantValueAttribute attr -> {
173                 print("ConstantValue: ");
174                 constantWriter.write(attr.constant().index());
175                 println();
176             }
177             case DeprecatedAttribute attr -> println("Deprecated: true");
178             case EnclosingMethodAttribute attr -> {
179                 print("EnclosingMethod: #" + attr.enclosingClass().index() + ".#"
180                         +  attr.enclosingMethod().map(PoolEntry::index).orElse(0));
181                 tab();
182                 print("// " + getJavaName(attr.enclosingClass().asInternalName()));
183                 if (attr.enclosingMethod().isPresent())
184                     print("." + attr.enclosingMethod().get().name().stringValue());
185                 println();
186             }
187             case ExceptionsAttribute attr -> {
188                 println("Exceptions:");
189                 indent(+1);
190                 print("throws ");
191                 var exc = attr.exceptions();
192                 for (int i = 0; i < exc.size(); i++) {
193                     if (i > 0)
194                         print(", ");
195                     print(getJavaName(exc.get(i).asInternalName()));
196                 }
197                 println();
198                 indent(-1);
199             }
200             case InnerClassesAttribute attr -> {
201                 boolean first = true;
202                 for (var info : attr.classes()) {
203                     //access
204                     int access_flags = info.flagsMask();
205                     if (options.checkAccess(access_flags)) {
206                         if (first) {
207                             println("InnerClasses:");
208                             indent(+1);
209                             first = false;
210                         }
211                         for (var flag : info.flags()) {
212                             if (flag.sourceModifier() && (flag != AccessFlag.ABSTRACT
213                                     || !info.has(AccessFlag.INTERFACE))) {
214                                 print(Modifier.toString(flag.mask()) + " ");
215                             }
216                         }
217                         if (info.innerName().isPresent()) {
218                             print("#" + info.innerName().get().index() + "= ");
219                         }
220                         print("#" + info.innerClass().index());
221                         if (info.outerClass().isPresent()) {
222                             print(" of #" + info.outerClass().get().index());
223                         }
224                         print(";");
225                         tab();
226                         print("// ");
227                         if (info.innerName().isPresent()) {
228                             print(info.innerName().get().stringValue() + "=");
229                         }
230                         constantWriter.write(info.innerClass().index());
231                         if (info.outerClass().isPresent()) {
232                             print(" of ");
233                             constantWriter.write(info.outerClass().get().index());
234                         }
235                         println();
236                     }
237                 }
238                 if (!first)
239                     indent(-1);
240             }
241             case LineNumberTableAttribute attr -> {
242                 println("LineNumberTable:");
243                 indent(+1);
244                 for (var entry: attr.lineNumbers()) {
245                     println("line " + entry.lineNumber() + ": " + entry.startPc());
246                 }
247                 indent(-1);
248             }
249             case LocalVariableTableAttribute attr -> {
250                 println("LocalVariableTable:");
251                 indent(+1);
252                 println("Start  Length  Slot  Name   Signature");
253                 for (var entry : attr.localVariables()) {
254                     println(String.format("%5d %7d %5d %5s   %s",
255                             entry.startPc(), entry.length(), entry.slot(),
256                             constantWriter.stringValue(entry.name()),
257                             constantWriter.stringValue(entry.type())));
258                 }
259                 indent(-1);
260             }
261             case LocalVariableTypeTableAttribute attr -> {
262                 println("LocalVariableTypeTable:");
263                 indent(+1);
264                 println("Start  Length  Slot  Name   Signature");
265                 for (var entry : attr.localVariableTypes()) {
266                     println(String.format("%5d %7d %5d %5s   %s",
267                             entry.startPc(), entry.length(), entry.slot(),
268                             constantWriter.stringValue(entry.name()),
269                             constantWriter.stringValue(entry.signature())));
270                 }
271                 indent(-1);
272             }
273             case NestHostAttribute attr -> {
274                 print("NestHost: ");
275                 constantWriter.write(attr.nestHost().index());
276                 println();
277             }
278             case MethodParametersAttribute attr -> {
279                 final String header = String.format(format, "Name", "Flags");
280                 println("MethodParameters:");
281                 indent(+1);
282                 println(header);
283                 for (var entry : attr.parameters()) {
284                     String namestr =
285                         entry.name().isPresent() ?
286                         constantWriter.stringValue(entry.name().get()) : "<no name>";
287                     String flagstr =
288                         (entry.has(AccessFlag.FINAL) ? "final " : "") +
289                         (entry.has(AccessFlag.MANDATED) ? "mandated " : "") +
290                         (entry.has(AccessFlag.SYNTHETIC) ? "synthetic" : "");
291                     println(String.format(format, namestr, flagstr));
292                 }
293                 indent(-1);
294             }
295             case ModuleAttribute attr -> {
296                 println("Module:");
297                 indent(+1);
298 
299                 print("#" + attr.moduleName().index());
300                 print(",");
301                 print(String.format("%x", attr.moduleFlagsMask()));
302                 tab();
303                 print("// " + constantWriter.stringValue(attr.moduleName()));
304                 if (attr.has(AccessFlag.OPEN))
305                     print(" ACC_OPEN");
306                 if (attr.has(AccessFlag.MANDATED))
307                     print(" ACC_MANDATED");
308                 if (attr.has(AccessFlag.SYNTHETIC))
309                     print(" ACC_SYNTHETIC");
310                 println();
311                 var ver = attr.moduleVersion();
312                 print("#" + ver.map(Utf8Entry::index).orElse(0));
313                 if (ver.isPresent()) {
314                     tab();
315                     print("// " + constantWriter.stringValue(ver.get()));
316                 }
317                 println();
318                 {
319                     var entries = attr.requires();
320                     print(entries.size());
321                     tab();
322                     println("// " + "requires");
323                     indent(+1);
324                     for (var e: entries) {
325                         print("#" + e.requires().index() + ","
326                                 + String.format("%x", e.requiresFlagsMask()));
327                         tab();
328                         print("// " + constantWriter.stringValue(e.requires()));
329                         if (e.has(AccessFlag.TRANSITIVE))
330                             print(" ACC_TRANSITIVE");
331                         if (e.has(AccessFlag.STATIC_PHASE))
332                             print(" ACC_STATIC_PHASE");
333                         if (e.has(AccessFlag.SYNTHETIC))
334                             print(" ACC_SYNTHETIC");
335                         if (e.has(AccessFlag.MANDATED))
336                             print(" ACC_MANDATED");
337                         println();
338                         var reqVer = e.requiresVersion();
339                         print("#" + reqVer.map(Utf8Entry::index).orElse(0));
340                         if (reqVer.isPresent()) {
341                             tab();
342                             print("// " + constantWriter.stringValue(reqVer.get()));
343                         }
344                         println();
345                     }
346                     indent(-1);
347                 }
348                 {
349                     var entries = attr.exports();
350                     print(entries.size());
351                     tab();
352                     println("// exports");
353                     indent(+1);
354                     for (var e: entries) {
355                         printExportOpenEntry(e.exportedPackage().index(),
356                                 e.exportsFlagsMask(), e.exportsTo());
357                     }
358                     indent(-1);
359                 }
360                 {
361                     var entries = attr.opens();
362                     print(entries.size());
363                     tab();
364                     println("// opens");
365                     indent(+1);
366                     for (var e: entries) {
367                         printExportOpenEntry(e.openedPackage().index(),
368                                 e.opensFlagsMask(), e.opensTo());
369                     }
370                     indent(-1);
371                 }
372                 {
373                     var entries = attr.uses();
374                     print(entries.size());
375                     tab();
376                     println("// " + "uses");
377                     indent(+1);
378                     for (var e: entries) {
379                         print("#" + e.index());
380                         tab();
381                         println("// " + constantWriter.stringValue(e));
382                     }
383                     indent(-1);
384                 }
385                 {
386                     var entries = attr.provides();
387                     print(entries.size());
388                     tab();
389                     println("// " + "provides");
390                     indent(+1);
391                     for (var e: entries) {
392                         print("#" + e.provides().index());
393                         tab();
394                         print("// ");
395                         print(constantWriter.stringValue(e.provides()));
396                         println(" with ... " + e.providesWith().size());
397                         indent(+1);
398                         for (var with : e.providesWith()) {
399                             print("#" + with.index());
400                             tab();
401                             println("// ... with " + constantWriter.stringValue(with));
402                         }
403                         indent(-1);
404                     }
405                     indent(-1);
406                 }
407                 indent(-1);
408             }
409             case ModuleHashesAttribute attr -> {
410                 println("ModuleHashes:");
411                 indent(+1);
412                 print("algorithm: #" + attr.algorithm().index());
413                 tab();
414                 println("// " + attr.algorithm().stringValue());
415                 print(attr.hashes().size());
416                 tab();
417                 println("// hashes");
418                 for (var e : attr.hashes()) {
419                     print("#" + e.moduleName().index());
420                     tab();
421                     println("// " + e.moduleName().name().stringValue());
422                     println("hash_length: " + e.hash().length);
423                     println("hash: [" + toHex(e.hash()) + "]");
424                 }
425                 indent(-1);
426             }
427             case ModuleMainClassAttribute attr -> {
428                 print("ModuleMainClass: #" + attr.mainClass().index());
429                 tab();
430                 print("// " + getJavaName(attr.mainClass().asInternalName()));
431                 println();
432             }
433             case ModulePackagesAttribute attr -> {
434                 println("ModulePackages: ");
435                 indent(+1);
436                 for (var p : attr.packages()) {
437                     print("#" + p.index());
438                     tab();
439                     println("// " + getJavaName(p.name().stringValue()));
440                 }
441                 indent(-1);
442             }
443             case ModuleResolutionAttribute attr -> {
444                 println("ModuleResolution:");
445                 indent(+1);
446                 print(String.format("%x", attr.resolutionFlags()));
447                 tab();
448                 print("// ");
449                 int flags = attr.resolutionFlags();
450                 if ((flags & DO_NOT_RESOLVE_BY_DEFAULT) != 0)
451                     print(" DO_NOT_RESOLVE_BY_DEFAULT");
452                 if ((flags & WARN_DEPRECATED) != 0)
453                     print(" WARN_DEPRECATED");
454                 if ((flags & WARN_DEPRECATED_FOR_REMOVAL) != 0)
455                     print(" WARN_DEPRECATED_FOR_REMOVAL");
456                 if ((flags & WARN_INCUBATING) != 0)
457                     print(" WARN_INCUBATING");
458                 println();
459                 indent(-1);
460             }
461             case ModuleTargetAttribute attr -> {
462                 println("ModuleTarget:");
463                 indent(+1);
464                 print("target_platform: #" + attr.targetPlatform().index());
465                 tab();
466                 println("// " + attr.targetPlatform().stringValue());
467                 indent(-1);
468             }
469             case NestMembersAttribute attr -> {
470                 println("NestMembers:");
471                 indent(+1);
472                 for (var m : attr.nestMembers()) {
473                     println(constantWriter.stringValue(m));
474                 }
475                 indent(-1);
476             }
477             case RecordAttribute attr -> {
478                 println("Record:");
479                 indent(+1);
480                 for (var componentInfo : attr.components()) {
481                     var sigAttr = componentInfo.findAttribute(Attributes.SIGNATURE);
482                     print(getJavaName(
483                             new ClassWriter.SignaturePrinter(options.verbose).print(
484                                     sigAttr.map(SignatureAttribute::asTypeSignature)
485                                             .orElse(Signature.of(
486                                                     componentInfo.descriptorSymbol())))));
487                     print(" ");
488                     print(componentInfo.name().stringValue());
489                     print(";");
490                     println();
491                     indent(+1);
492                     if (options.showDescriptors) {
493                         println("descriptor: " + componentInfo.descriptor().stringValue());
494                     }
495                     if (options.showAllAttrs) {
496                         write(componentInfo.attributes());
497                         println();
498                     }
499                     indent(-1);
500                 }
501                 indent(-1);
502             }
503             case RuntimeVisibleAnnotationsAttribute attr ->
504                 printAnnotations("RuntimeVisibleAnnotations:", attr.annotations());
505             case RuntimeInvisibleAnnotationsAttribute attr ->
506                 printAnnotations("RuntimeInvisibleAnnotations:", attr.annotations());
507             case RuntimeVisibleTypeAnnotationsAttribute attr ->
508                 printTypeAnnotations("RuntimeVisibleTypeAnnotations:",
509                         attr.annotations(), lr);
510             case RuntimeInvisibleTypeAnnotationsAttribute attr ->
511                 printTypeAnnotations("RuntimeInvisibleTypeAnnotations:",
512                         attr.annotations(), lr);
513             case RuntimeVisibleParameterAnnotationsAttribute attr ->
514                 printParameterAnnotations("RuntimeVisibleParameterAnnotations:",
515                         attr.parameterAnnotations());
516             case RuntimeInvisibleParameterAnnotationsAttribute attr ->
517                 printParameterAnnotations("RuntimeInvisibleParameterAnnotations:",
518                         attr.parameterAnnotations());
519             case PermittedSubclassesAttribute attr -> {
520                 println("PermittedSubclasses:");
521                 indent(+1);
522                 for (var sc : attr.permittedSubclasses()) {
523                     println(constantWriter.stringValue(sc));
524                 }
525                 indent(-1);
526             }
527             case SignatureAttribute attr -> {
528                 print("Signature: #" + attr.signature().index());
529                 tab();
530                 println("// " + attr.signature().stringValue());
531             }
532             case SourceDebugExtensionAttribute attr -> {
533                 println("SourceDebugExtension:");
534                 indent(+1);
535                 for (String s: new String(attr.contents(), StandardCharsets.UTF_8)
536                         .split("[\r\n]+")) {
537                     println(s);
538                 }
539                 indent(-1);
540             }
541             case SourceFileAttribute attr ->
542                 println("SourceFile: \"" + attr.sourceFile().stringValue() + "\"");
543             case SourceIDAttribute attr ->
544                 constantWriter.write(attr.sourceId().index());
545             case StackMapTableAttribute attr -> {
546                 var entries = attr.entries();
547                 println("StackMapTable: number_of_entries = " + entries.size());
548                 indent(+1);
549                 int lastOffset = -1;
550                 for (var frame : entries) {
551                     int frameType = frame.frameType();
552                     if (frameType < 64) {
553                         printHeader(frameType, "/* same */");
554                     } else if (frameType < 128) {
555                         printHeader(frameType, "/* same_locals_1_stack_item */");
556                         indent(+1);
557                         printMap("stack", frame.stack(), lr);
558                         indent(-1);
559                     } else {
560                         int offsetDelta = lr.labelToBci(frame.target()) - lastOffset - 1;
561                         switch (frameType) {
562                             case 247 -> {
563                                 printHeader(frameType, "/* same_locals_1_stack_item_frame_extended */");
564                                 indent(+1);
565                                 println("offset_delta = " + offsetDelta);
566                                 printMap("stack", frame.stack(), lr);
567                                 indent(-1);
568                             }
569                             case 248, 249, 250 -> {
570                                 printHeader(frameType, "/* chop */");
571                                 indent(+1);
572                                 println("offset_delta = " + offsetDelta);
573                                 indent(-1);
574                             }
575                             case 251 -> {
576                                 printHeader(frameType, "/* same_frame_extended */");
577                                 indent(+1);
578                                 println("offset_delta = " + offsetDelta);
579                                 indent(-1);
580                             }
581                             case 252, 253, 254 -> {
582                                 printHeader(frameType, "/* append */");
583                                 indent(+1);
584                                 println("offset_delta = " + offsetDelta);
585                                 var locals = frame.locals();
586                                 printMap("locals", locals.subList(locals.size()
587                                         - frameType + 251, locals.size()), lr);
588                                 indent(-1);
589                             }
590                             case 255 -> {
591                                 printHeader(frameType, "/* full_frame */");
592                                 indent(+1);
593                                 println("offset_delta = " + offsetDelta);
594                                 printMap("locals", frame.locals(), lr);
595                                 printMap("stack", frame.stack(), lr);
596                                 indent(-1);
597                             }
598                         }
599                     }
600                     lastOffset = lr.labelToBci(frame.target());
601                 }
602                 indent(-1);
603             }
604             case SyntheticAttribute attr ->
605                 println("Synthetic: true");
606             default -> {}
607         }
608     }
609 
610     //ToDo move somewhere to Bytecode API
611     public static final int DO_NOT_RESOLVE_BY_DEFAULT   = 0x0001;
612     public static final int WARN_DEPRECATED             = 0x0002;
613     public static final int WARN_DEPRECATED_FOR_REMOVAL = 0x0004;
614     public static final int WARN_INCUBATING             = 0x0008;
615 
616     private static final String format = "%-31s%s";
617 
618     protected void printExportOpenEntry(int index, int flags, List<ModuleEntry> to_index) {
619         print("#" + index + "," + String.format("%x", flags));
620         tab();
621         print("// ");
622         print(constantWriter.stringValue(index));
623         if ((flags & ACC_MANDATED) != 0)
624             print(" ACC_MANDATED");
625         if ((flags & ACC_SYNTHETIC) != 0)
626             print(" ACC_SYNTHETIC");
627         if (to_index.size() == 0) {
628             println();
629         } else {
630             println(" to ... " + to_index.size());
631             indent(+1);
632             for (var to: to_index) {
633                 print("#" + to.index());
634                 tab();
635                 println("// ... to " + constantWriter.stringValue(to));
636             }
637             indent(-1);
638         }
639     }
640 
641     private void printAnnotations(String message, List<? extends Annotation> anno) {
642         println(message);
643         indent(+1);
644         for (int i = 0; i < anno.size(); i++) {
645             print(i + ": ");
646             annotationWriter.write(anno.get(i));
647             println();
648         }
649         indent(-1);
650     }
651 
652     private void printTypeAnnotations(String message,
653             List<? extends TypeAnnotation> anno, CodeAttribute lr) {
654         println(message);
655         indent(+1);
656         for (int i = 0; i < anno.size(); i++) {
657             print(i + ": ");
658             annotationWriter.write(anno.get(i), lr);
659             println();
660         }
661         indent(-1);
662     }
663 
664     private void printParameterAnnotations(String message, List<List<Annotation>> paramsAnno) {
665         println(message);
666         indent(+1);
667         for (int param = 0; param < paramsAnno.size(); param++) {
668             println("parameter " + param + ": ");
669             indent(+1);
670             var annos = paramsAnno.get(param);
671             for (int i = 0; i < annos.size(); i++) {
672                 print(i + ": ");
673                 annotationWriter.write(annos.get(i));
674                 println();
675             }
676             indent(-1);
677         }
678         indent(-1);
679     }
680 
681     void printHeader(int frameType, String extra) {
682         print("frame_type = " + frameType + " ");
683         println(extra);
684     }
685 
686     void printMap(String name, List<VerificationTypeInfo> map, CodeAttribute lr) {
687         print(name + " = [");
688         for (int i = 0; i < map.size(); i++) {
689             var info = map.get(i);
690             switch (info) {
691                 case ObjectVerificationTypeInfo obj -> {
692                     print(" ");
693                     constantWriter.write(obj.className().index());
694                 }
695                 case UninitializedVerificationTypeInfo u -> {
696                     print(" uninitialized " + lr.labelToBci(u.newTarget()));
697                 }
698                 case SimpleVerificationTypeInfo s ->
699                     print(" " + mapTypeName(s));
700             }
701             print(i == (map.size() - 1) ? " " : ",");
702         }
703         println("]");
704     }
705 
706     String mapTypeName(SimpleVerificationTypeInfo type) {
707         return switch (type) {
708             case ITEM_TOP -> "top";
709             case ITEM_INTEGER -> "int";
710             case ITEM_FLOAT -> "float";
711             case ITEM_LONG -> "long";
712             case ITEM_DOUBLE -> "double";
713             case ITEM_NULL -> "null";
714             case ITEM_UNINITIALIZED_THIS -> "this";
715         };
716     }
717 
718     static String getJavaName(String name) {
719         return name.replace('/', '.');
720     }
721 
722     String toHex(byte b, int w) {
723         return toHex(b & 0xff, w);
724     }
725 
726     static String toHex(int i) {
727         return Integer.toString(i, 16).toUpperCase(Locale.US);
728     }
729 
730     static String toHex(int i, int w) {
731         String s = Integer.toHexString(i).toUpperCase(Locale.US);
732         while (s.length() < w)
733             s = "0" + s;
734         return s;
735     }
736 
737     static String toHex(byte[] ba) {
738         StringBuilder sb = new StringBuilder(ba.length);
739         for (byte b: ba) {
740             sb.append(String.format("%02x", b & 0xff));
741         }
742         return sb.toString();
743     }
744 
745     private final AnnotationWriter annotationWriter;
746     private final CodeWriter codeWriter;
747     private final ConstantWriter constantWriter;
748     private final Options options;
749 }