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