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