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