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