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