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