1 /*
   2  * Copyright (c) 2007, 2018, 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.util.Collection;
  29 
  30 import com.sun.tools.classfile.AccessFlags;
  31 import com.sun.tools.classfile.AnnotationDefault_attribute;
  32 import com.sun.tools.classfile.Attribute;
  33 import com.sun.tools.classfile.Attributes;
  34 import com.sun.tools.classfile.BootstrapMethods_attribute;
  35 import com.sun.tools.classfile.CharacterRangeTable_attribute;
  36 import com.sun.tools.classfile.CharacterRangeTable_attribute.Entry;
  37 import com.sun.tools.classfile.Code_attribute;
  38 import com.sun.tools.classfile.CompilationID_attribute;
  39 import com.sun.tools.classfile.ConstantPool;
  40 import com.sun.tools.classfile.ConstantPool.CONSTANT_Class_info;
  41 import com.sun.tools.classfile.ConstantPoolException;
  42 import com.sun.tools.classfile.ConstantValue_attribute;
  43 import com.sun.tools.classfile.DefaultAttribute;
  44 import com.sun.tools.classfile.Deprecated_attribute;
  45 import com.sun.tools.classfile.Descriptor;
  46 import com.sun.tools.classfile.Descriptor.InvalidDescriptor;
  47 import com.sun.tools.classfile.EnclosingMethod_attribute;
  48 import com.sun.tools.classfile.Exceptions_attribute;
  49 import com.sun.tools.classfile.InnerClasses_attribute;
  50 import com.sun.tools.classfile.InnerClasses_attribute.Info;
  51 import com.sun.tools.classfile.LineNumberTable_attribute;
  52 import com.sun.tools.classfile.LocalVariableTable_attribute;
  53 import com.sun.tools.classfile.LocalVariableTypeTable_attribute;
  54 import com.sun.tools.classfile.MethodParameters_attribute;
  55 import com.sun.tools.classfile.Module_attribute;
  56 import com.sun.tools.classfile.ModuleHashes_attribute;
  57 import com.sun.tools.classfile.ModuleMainClass_attribute;
  58 import com.sun.tools.classfile.ModulePackages_attribute;
  59 import com.sun.tools.classfile.ModuleResolution_attribute;
  60 import com.sun.tools.classfile.ModuleTarget_attribute;
  61 import com.sun.tools.classfile.NestHost_attribute;
  62 import com.sun.tools.classfile.NestMembers_attribute;
  63 import com.sun.tools.classfile.Record_attribute;
  64 import com.sun.tools.classfile.RuntimeInvisibleAnnotations_attribute;
  65 import com.sun.tools.classfile.RuntimeInvisibleParameterAnnotations_attribute;
  66 import com.sun.tools.classfile.RuntimeInvisibleTypeAnnotations_attribute;
  67 import com.sun.tools.classfile.RuntimeParameterAnnotations_attribute;
  68 import com.sun.tools.classfile.RuntimeVisibleAnnotations_attribute;
  69 import com.sun.tools.classfile.RuntimeVisibleParameterAnnotations_attribute;
  70 import com.sun.tools.classfile.RuntimeVisibleTypeAnnotations_attribute;
  71 import com.sun.tools.classfile.PermittedSubtypes_attribute;
  72 import com.sun.tools.classfile.Signature;
  73 import com.sun.tools.classfile.Signature_attribute;
  74 import com.sun.tools.classfile.SourceDebugExtension_attribute;
  75 import com.sun.tools.classfile.SourceFile_attribute;
  76 import com.sun.tools.classfile.SourceID_attribute;
  77 import com.sun.tools.classfile.StackMapTable_attribute;
  78 import com.sun.tools.classfile.StackMap_attribute;
  79 import com.sun.tools.classfile.Synthetic_attribute;
  80 import com.sun.tools.classfile.Type;
  81 
  82 import static com.sun.tools.classfile.AccessFlags.*;
  83 
  84 import com.sun.tools.javac.util.Assert;
  85 import com.sun.tools.javac.util.StringUtils;
  86 
  87 import java.util.stream.Collectors;
  88 import java.util.stream.IntStream;
  89 import java.util.stream.Stream;
  90 
  91 /*
  92  *  A writer for writing Attributes as text.
  93  *
  94  *  <p><b>This is NOT part of any supported API.
  95  *  If you write code that depends on this, you do so at your own risk.
  96  *  This code and its internal interfaces are subject to change or
  97  *  deletion without notice.</b>
  98  */
  99 public class AttributeWriter extends BasicWriter
 100         implements Attribute.Visitor<Void,Void>
 101 {
 102     public static AttributeWriter instance(Context context) {
 103         AttributeWriter instance = context.get(AttributeWriter.class);
 104         if (instance == null)
 105             instance = new AttributeWriter(context);
 106         return instance;
 107     }
 108 
 109     protected AttributeWriter(Context context) {
 110         super(context);
 111         context.put(AttributeWriter.class, this);
 112         annotationWriter = AnnotationWriter.instance(context);
 113         codeWriter = CodeWriter.instance(context);
 114         constantWriter = ConstantWriter.instance(context);
 115         options = Options.instance(context);
 116     }
 117 
 118     public void write(Object owner, Attribute attr, ConstantPool constant_pool) {
 119         if (attr != null) {
 120             Assert.checkNonNull(constant_pool);
 121             Assert.checkNonNull(owner);
 122             this.constant_pool = constant_pool;
 123             this.owner = owner;
 124             attr.accept(this, null);
 125         }
 126     }
 127 
 128     public void write(Object owner, Attributes attrs, ConstantPool constant_pool) {
 129         if (attrs != null) {
 130             Assert.checkNonNull(constant_pool);
 131             Assert.checkNonNull(owner);
 132             this.constant_pool = constant_pool;
 133             this.owner = owner;
 134             for (Attribute attr: attrs)
 135                 attr.accept(this, null);
 136         }
 137     }
 138 
 139     @Override
 140     public Void visitDefault(DefaultAttribute attr, Void ignore) {
 141         if (attr.reason != null) {
 142             report(attr.reason);
 143         }
 144         byte[] data = attr.info;
 145         int i = 0;
 146         int j = 0;
 147         print("  ");
 148         try {
 149             print(attr.getName(constant_pool));
 150         } catch (ConstantPoolException e) {
 151             report(e);
 152             print("attribute name = #" + attr.attribute_name_index);
 153         }
 154         print(": ");
 155         println("length = 0x" + toHex(attr.info.length));
 156 
 157         print("   ");
 158 
 159         while (i < data.length) {
 160             print(toHex(data[i], 2));
 161 
 162             j++;
 163             if (j == 16) {
 164                 println();
 165                 print("   ");
 166                 j = 0;
 167             } else {
 168                 print(" ");
 169             }
 170             i++;
 171         }
 172         println();
 173         return null;
 174     }
 175 
 176     @Override
 177     public Void visitAnnotationDefault(AnnotationDefault_attribute attr, Void ignore) {
 178         println("AnnotationDefault:");
 179         indent(+1);
 180         print("default_value: ");
 181         annotationWriter.write(attr.default_value);
 182         indent(-1);
 183         println();
 184         return null;
 185     }
 186 
 187     @Override
 188     public Void visitBootstrapMethods(BootstrapMethods_attribute attr, Void p) {
 189         println(Attribute.BootstrapMethods + ":");
 190         for (int i = 0; i < attr.bootstrap_method_specifiers.length ; i++) {
 191             BootstrapMethods_attribute.BootstrapMethodSpecifier bsm = attr.bootstrap_method_specifiers[i];
 192             indent(+1);
 193             print(i + ": #" + bsm.bootstrap_method_ref + " ");
 194             println(constantWriter.stringValue(bsm.bootstrap_method_ref));
 195             indent(+1);
 196             println("Method arguments:");
 197             indent(+1);
 198             for (int j = 0; j < bsm.bootstrap_arguments.length; j++) {
 199                 print("#" + bsm.bootstrap_arguments[j] + " ");
 200                 println(constantWriter.stringValue(bsm.bootstrap_arguments[j]));
 201             }
 202             indent(-3);
 203         }
 204         return null;
 205     }
 206 
 207     @Override
 208     public Void visitCharacterRangeTable(CharacterRangeTable_attribute attr, Void ignore) {
 209         println("CharacterRangeTable:");
 210         indent(+1);
 211         for (Entry e : attr.character_range_table) {
 212             print(String.format("    %2d, %2d, %6x, %6x, %4x",
 213                     e.start_pc, e.end_pc,
 214                     e.character_range_start, e.character_range_end,
 215                     e.flags));
 216             tab();
 217             print(String.format("// %2d, %2d, %4d:%02d, %4d:%02d",
 218                     e.start_pc, e.end_pc,
 219                     (e.character_range_start >> 10), (e.character_range_start & 0x3ff),
 220                     (e.character_range_end >> 10), (e.character_range_end & 0x3ff)));
 221             if ((e.flags & CharacterRangeTable_attribute.CRT_STATEMENT) != 0)
 222                 print(", statement");
 223             if ((e.flags & CharacterRangeTable_attribute.CRT_BLOCK) != 0)
 224                 print(", block");
 225             if ((e.flags & CharacterRangeTable_attribute.CRT_ASSIGNMENT) != 0)
 226                 print(", assignment");
 227             if ((e.flags & CharacterRangeTable_attribute.CRT_FLOW_CONTROLLER) != 0)
 228                 print(", flow-controller");
 229             if ((e.flags & CharacterRangeTable_attribute.CRT_FLOW_TARGET) != 0)
 230                 print(", flow-target");
 231             if ((e.flags & CharacterRangeTable_attribute.CRT_INVOKE) != 0)
 232                 print(", invoke");
 233             if ((e.flags & CharacterRangeTable_attribute.CRT_CREATE) != 0)
 234                 print(", create");
 235             if ((e.flags & CharacterRangeTable_attribute.CRT_BRANCH_TRUE) != 0)
 236                 print(", branch-true");
 237             if ((e.flags & CharacterRangeTable_attribute.CRT_BRANCH_FALSE) != 0)
 238                 print(", branch-false");
 239             println();
 240         }
 241         indent(-1);
 242         return null;
 243     }
 244 
 245     @Override
 246     public Void visitCode(Code_attribute attr, Void ignore) {
 247         codeWriter.write(attr, constant_pool);
 248         return null;
 249     }
 250 
 251     @Override
 252     public Void visitCompilationID(CompilationID_attribute attr, Void ignore) {
 253         constantWriter.write(attr.compilationID_index);
 254         return null;
 255     }
 256 
 257     @Override
 258     public Void visitConstantValue(ConstantValue_attribute attr, Void ignore) {
 259         print("ConstantValue: ");
 260         constantWriter.write(attr.constantvalue_index);
 261         println();
 262         return null;
 263     }
 264 
 265     @Override
 266     public Void visitDeprecated(Deprecated_attribute attr, Void ignore) {
 267         println("Deprecated: true");
 268         return null;
 269     }
 270 
 271     @Override
 272     public Void visitEnclosingMethod(EnclosingMethod_attribute attr, Void ignore) {
 273         print("EnclosingMethod: #" + attr.class_index + ".#" + attr.method_index);
 274         tab();
 275         print("// " + getJavaClassName(attr));
 276         if (attr.method_index != 0)
 277             print("." + getMethodName(attr));
 278         println();
 279         return null;
 280     }
 281 
 282     private String getJavaClassName(EnclosingMethod_attribute a) {
 283         try {
 284             return getJavaName(a.getClassName(constant_pool));
 285         } catch (ConstantPoolException e) {
 286             return report(e);
 287         }
 288     }
 289 
 290     private String getMethodName(EnclosingMethod_attribute a) {
 291         try {
 292             return a.getMethodName(constant_pool);
 293         } catch (ConstantPoolException e) {
 294             return report(e);
 295         }
 296     }
 297 
 298     @Override
 299     public Void visitExceptions(Exceptions_attribute attr, Void ignore) {
 300         println("Exceptions:");
 301         indent(+1);
 302         print("throws ");
 303         for (int i = 0; i < attr.number_of_exceptions; i++) {
 304             if (i > 0)
 305                 print(", ");
 306             print(getJavaException(attr, i));
 307         }
 308         println();
 309         indent(-1);
 310         return null;
 311     }
 312 
 313     private String getJavaException(Exceptions_attribute attr, int index) {
 314         try {
 315             return getJavaName(attr.getException(index, constant_pool));
 316         } catch (ConstantPoolException e) {
 317             return report(e);
 318         }
 319     }
 320 
 321 
 322     @Override
 323     public Void visitInnerClasses(InnerClasses_attribute attr, Void ignore) {
 324         boolean first = true;
 325         for (Info info : attr.classes) {
 326             //access
 327             AccessFlags access_flags = info.inner_class_access_flags;
 328             if (options.checkAccess(access_flags)) {
 329                 if (first) {
 330                     writeInnerClassHeader();
 331                     first = false;
 332                 }
 333                 for (String name: access_flags.getInnerClassModifiers())
 334                     print(name + " ");
 335                 if (info.inner_name_index != 0) {
 336                     print("#" + info.inner_name_index + "= ");
 337                 }
 338                 print("#" + info.inner_class_info_index);
 339                 if (info.outer_class_info_index != 0) {
 340                     print(" of #" + info.outer_class_info_index);
 341                 }
 342                 print(";");
 343                 tab();
 344                 print("// ");
 345                 if (info.inner_name_index != 0) {
 346                     print(getInnerName(constant_pool, info) + "=");
 347                 }
 348                 constantWriter.write(info.inner_class_info_index);
 349                 if (info.outer_class_info_index != 0) {
 350                     print(" of ");
 351                     constantWriter.write(info.outer_class_info_index);
 352                 }
 353                 println();
 354             }
 355         }
 356         if (!first)
 357             indent(-1);
 358         return null;
 359     }
 360 
 361     String getInnerName(ConstantPool constant_pool, InnerClasses_attribute.Info info) {
 362         try {
 363             return info.getInnerName(constant_pool);
 364         } catch (ConstantPoolException e) {
 365             return report(e);
 366         }
 367     }
 368 
 369     private void writeInnerClassHeader() {
 370         println("InnerClasses:");
 371         indent(+1);
 372     }
 373 
 374     @Override
 375     public Void visitLineNumberTable(LineNumberTable_attribute attr, Void ignore) {
 376         println("LineNumberTable:");
 377         indent(+1);
 378         for (LineNumberTable_attribute.Entry entry: attr.line_number_table) {
 379             println("line " + entry.line_number + ": " + entry.start_pc);
 380         }
 381         indent(-1);
 382         return null;
 383     }
 384 
 385     @Override
 386     public Void visitLocalVariableTable(LocalVariableTable_attribute attr, Void ignore) {
 387         println("LocalVariableTable:");
 388         indent(+1);
 389         println("Start  Length  Slot  Name   Signature");
 390         for (LocalVariableTable_attribute.Entry entry : attr.local_variable_table) {
 391             println(String.format("%5d %7d %5d %5s   %s",
 392                     entry.start_pc, entry.length, entry.index,
 393                     constantWriter.stringValue(entry.name_index),
 394                     constantWriter.stringValue(entry.descriptor_index)));
 395         }
 396         indent(-1);
 397         return null;
 398     }
 399 
 400     @Override
 401     public Void visitLocalVariableTypeTable(LocalVariableTypeTable_attribute attr, Void ignore) {
 402         println("LocalVariableTypeTable:");
 403         indent(+1);
 404         println("Start  Length  Slot  Name   Signature");
 405         for (LocalVariableTypeTable_attribute.Entry entry : attr.local_variable_table) {
 406             println(String.format("%5d %7d %5d %5s   %s",
 407                     entry.start_pc, entry.length, entry.index,
 408                     constantWriter.stringValue(entry.name_index),
 409                     constantWriter.stringValue(entry.signature_index)));
 410         }
 411         indent(-1);
 412         return null;
 413     }
 414 
 415     @Override
 416     public Void visitNestHost(NestHost_attribute attr, Void aVoid) {
 417         print("NestHost: ");
 418         constantWriter.write(attr.top_index);
 419         println();
 420         return null;
 421     }
 422 
 423     private String getJavaClassName(ModuleMainClass_attribute a) {
 424         try {
 425             return getJavaName(a.getMainClassName(constant_pool));
 426         } catch (ConstantPoolException e) {
 427             return report(e);
 428         }
 429     }
 430 
 431     private static final String format = "%-31s%s";
 432 
 433     @Override
 434     public Void visitMethodParameters(MethodParameters_attribute attr,
 435                                       Void ignore) {
 436         final String header = String.format(format, "Name", "Flags");
 437         println("MethodParameters:");
 438         indent(+1);
 439         println(header);
 440         for (MethodParameters_attribute.Entry entry :
 441                  attr.method_parameter_table) {
 442             String namestr =
 443                 entry.name_index != 0 ?
 444                 constantWriter.stringValue(entry.name_index) : "<no name>";
 445             String flagstr =
 446                 (0 != (entry.flags & ACC_FINAL) ? "final " : "") +
 447                 (0 != (entry.flags & ACC_MANDATED) ? "mandated " : "") +
 448                 (0 != (entry.flags & ACC_SYNTHETIC) ? "synthetic" : "");
 449             println(String.format(format, namestr, flagstr));
 450         }
 451         indent(-1);
 452         return null;
 453     }
 454 
 455     @Override
 456     public Void visitModule(Module_attribute attr, Void ignore) {
 457         println("Module:");
 458         indent(+1);
 459 
 460         print("#" + attr.module_name);
 461         print(",");
 462         print(String.format("%x", attr.module_flags));
 463         tab();
 464         print("// " + constantWriter.stringValue(attr.module_name));
 465         if ((attr.module_flags & Module_attribute.ACC_OPEN) != 0)
 466             print(" ACC_OPEN");
 467         if ((attr.module_flags & Module_attribute.ACC_MANDATED) != 0)
 468             print(" ACC_MANDATED");
 469         if ((attr.module_flags & Module_attribute.ACC_SYNTHETIC) != 0)
 470             print(" ACC_SYNTHETIC");
 471         println();
 472         print("#" + attr.module_version_index);
 473         if (attr.module_version_index != 0) {
 474             tab();
 475             print("// " + constantWriter.stringValue(attr.module_version_index));
 476         }
 477         println();
 478 
 479         printRequiresTable(attr);
 480         printExportsTable(attr);
 481         printOpensTable(attr);
 482         printUsesTable(attr);
 483         printProvidesTable(attr);
 484         indent(-1);
 485         return null;
 486     }
 487 
 488     protected void printRequiresTable(Module_attribute attr) {
 489         Module_attribute.RequiresEntry[] entries = attr.requires;
 490         print(entries.length);
 491         tab();
 492         println("// " + "requires");
 493         indent(+1);
 494         for (Module_attribute.RequiresEntry e: entries) {
 495             print("#" + e.requires_index + "," + String.format("%x", e.requires_flags));
 496             tab();
 497             print("// " + constantWriter.stringValue(e.requires_index));
 498             if ((e.requires_flags & Module_attribute.ACC_TRANSITIVE) != 0)
 499                 print(" ACC_TRANSITIVE");
 500             if ((e.requires_flags & Module_attribute.ACC_STATIC_PHASE) != 0)
 501                 print(" ACC_STATIC_PHASE");
 502             if ((e.requires_flags & Module_attribute.ACC_SYNTHETIC) != 0)
 503                 print(" ACC_SYNTHETIC");
 504             if ((e.requires_flags & Module_attribute.ACC_MANDATED) != 0)
 505                 print(" ACC_MANDATED");
 506             println();
 507             print("#" + e.requires_version_index);
 508             if (e.requires_version_index != 0) {
 509                 tab();
 510                 print("// " + constantWriter.stringValue(e.requires_version_index));
 511             }
 512             println();
 513         }
 514         indent(-1);
 515     }
 516 
 517     protected void printExportsTable(Module_attribute attr) {
 518         Module_attribute.ExportsEntry[] entries = attr.exports;
 519         print(entries.length);
 520         tab();
 521         println("// exports");
 522         indent(+1);
 523         for (Module_attribute.ExportsEntry e: entries) {
 524             printExportOpenEntry(e.exports_index, e.exports_flags, e.exports_to_index);
 525         }
 526         indent(-1);
 527     }
 528 
 529     protected void printOpensTable(Module_attribute attr) {
 530         Module_attribute.OpensEntry[] entries = attr.opens;
 531         print(entries.length);
 532         tab();
 533         println("// opens");
 534         indent(+1);
 535         for (Module_attribute.OpensEntry e: entries) {
 536             printExportOpenEntry(e.opens_index, e.opens_flags, e.opens_to_index);
 537         }
 538         indent(-1);
 539     }
 540 
 541     protected void printExportOpenEntry(int index, int flags, int[] to_index) {
 542         print("#" + index + "," + String.format("%x", flags));
 543         tab();
 544         print("// ");
 545         print(constantWriter.stringValue(index));
 546         if ((flags & Module_attribute.ACC_MANDATED) != 0)
 547             print(" ACC_MANDATED");
 548         if ((flags & Module_attribute.ACC_SYNTHETIC) != 0)
 549             print(" ACC_SYNTHETIC");
 550         if (to_index.length == 0) {
 551             println();
 552         } else {
 553             println(" to ... " + to_index.length);
 554             indent(+1);
 555             for (int to: to_index) {
 556                 print("#" + to);
 557                 tab();
 558                 println("// ... to " + constantWriter.stringValue(to));
 559             }
 560             indent(-1);
 561         }
 562     }
 563 
 564     protected void printUsesTable(Module_attribute attr) {
 565         int[] entries = attr.uses_index;
 566         print(entries.length);
 567         tab();
 568         println("// " + "uses");
 569         indent(+1);
 570         for (int e: entries) {
 571             print("#" + e);
 572             tab();
 573             println("// " + constantWriter.stringValue(e));
 574         }
 575         indent(-1);
 576     }
 577 
 578     protected void printProvidesTable(Module_attribute attr) {
 579         Module_attribute.ProvidesEntry[] entries = attr.provides;
 580         print(entries.length);
 581         tab();
 582         println("// " + "provides");
 583         indent(+1);
 584         for (Module_attribute.ProvidesEntry e: entries) {
 585             print("#" + e.provides_index);
 586             tab();
 587             print("// ");
 588             print(constantWriter.stringValue(e.provides_index));
 589             println(" with ... " + e.with_count);
 590             indent(+1);
 591             for (int with : e.with_index) {
 592                 print("#" + with);
 593                 tab();
 594                 println("// ... with " + constantWriter.stringValue(with));
 595             }
 596             indent(-1);
 597         }
 598         indent(-1);
 599     }
 600 
 601     @Override
 602     public Void visitModuleHashes(ModuleHashes_attribute attr, Void ignore) {
 603         println("ModuleHashes:");
 604         indent(+1);
 605         print("algorithm: #" + attr.algorithm_index);
 606         tab();
 607         println("// " + getAlgorithm(attr));
 608         print(attr.hashes_table_length);
 609         tab();
 610         println("// hashes");
 611         for (ModuleHashes_attribute.Entry e : attr.hashes_table) {
 612             print("#" + e.module_name_index);
 613             tab();
 614             println("// " + getModuleName(e));
 615             println("hash_length: " + e.hash.length);
 616             println("hash: [" + toHex(e.hash) + "]");
 617         }
 618         indent(-1);
 619         return null;
 620     }
 621 
 622     private String getAlgorithm(ModuleHashes_attribute attr) {
 623         try {
 624             return constant_pool.getUTF8Value(attr.algorithm_index);
 625         } catch (ConstantPoolException e) {
 626             return report(e);
 627         }
 628     }
 629 
 630     private String getModuleName(ModuleHashes_attribute.Entry entry) {
 631         try {
 632             int utf8Index = constant_pool.getModuleInfo(entry.module_name_index).name_index;
 633             return constant_pool.getUTF8Value(utf8Index);
 634         } catch (ConstantPoolException e) {
 635             return report(e);
 636         }
 637     }
 638 
 639     @Override
 640     public Void visitModuleMainClass(ModuleMainClass_attribute attr, Void ignore) {
 641         print("ModuleMainClass: #" + attr.main_class_index);
 642         tab();
 643         print("// " + getJavaClassName(attr));
 644         println();
 645         return null;
 646     }
 647 
 648     @Override
 649     public Void visitModulePackages(ModulePackages_attribute attr, Void ignore) {
 650         println("ModulePackages: ");
 651         indent(+1);
 652         for (int i = 0; i < attr.packages_count; i++) {
 653             print("#" + attr.packages_index[i]);
 654             tab();
 655             println("// " + getJavaPackage(attr, i));
 656         }
 657         indent(-1);
 658         return null;
 659     }
 660 
 661     private String getJavaPackage(ModulePackages_attribute attr, int index) {
 662         try {
 663             return getJavaName(attr.getPackage(index, constant_pool));
 664         } catch (ConstantPoolException e) {
 665             return report(e);
 666         }
 667     }
 668 
 669     @Override
 670     public Void visitModuleResolution(ModuleResolution_attribute attr, Void ignore) {
 671         println("ModuleResolution:");
 672         indent(+1);
 673         print(String.format("%x", attr.resolution_flags));
 674         tab();
 675         print("// ");
 676         int flags = attr.resolution_flags;
 677         if ((flags & ModuleResolution_attribute.DO_NOT_RESOLVE_BY_DEFAULT) != 0)
 678             print(" DO_NOT_RESOLVE_BY_DEFAULT");
 679         if ((flags & ModuleResolution_attribute.WARN_DEPRECATED) != 0)
 680             print(" WARN_DEPRECATED");
 681         if ((flags & ModuleResolution_attribute.WARN_DEPRECATED_FOR_REMOVAL) != 0)
 682             print(" WARN_DEPRECATED_FOR_REMOVAL");
 683         if ((flags & ModuleResolution_attribute.WARN_INCUBATING) != 0)
 684             print(" WARN_INCUBATING");
 685         println();
 686         indent(-1);
 687         return null;
 688     }
 689 
 690     @Override
 691     public Void visitModuleTarget(ModuleTarget_attribute attr, Void ignore) {
 692         println("ModuleTarget:");
 693         indent(+1);
 694         print("target_platform: #" + attr.target_platform_index);
 695         if (attr.target_platform_index != 0) {
 696             tab();
 697             print("// " + getTargetPlatform(attr));
 698         }
 699         println();
 700         indent(-1);
 701         return null;
 702     }
 703 
 704     private String getTargetPlatform(ModuleTarget_attribute attr) {
 705         try {
 706             return constant_pool.getUTF8Value(attr.target_platform_index);
 707         } catch (ConstantPoolException e) {
 708             return report(e);
 709         }
 710     }
 711 
 712     @Override
 713     public Void visitNestMembers(NestMembers_attribute attr, Void aVoid) {
 714         println("NestMembers:");
 715         indent(+1);
 716         try {
 717             CONSTANT_Class_info[] children = attr.getChildren(constant_pool);
 718             for (int i = 0; i < attr.members_indexes.length; i++) {
 719                 println(constantWriter.stringValue(children[i]));
 720             }
 721             indent(-1);
 722         } catch (ConstantPoolException ex) {
 723             throw new AssertionError(ex);
 724         }
 725         return null;
 726     }
 727 
 728     @Override
 729     public Void visitRecord(Record_attribute attr, Void p) {
 730         println("Record:");
 731         indent(+1);
 732         for (int i = 0; i < attr.num_params; i++) {
 733             println("#" + attr.accessors[i]);
 734         }
 735         indent(-1);
 736         return null;
 737     }
 738 
 739     void writeList(String prefix, Collection<?> items, String suffix) {
 740         print(prefix);
 741         String sep = "";
 742         for (Object item: items) {
 743             print(sep);
 744             print(item);
 745             sep = ", ";
 746         }
 747         print(suffix);
 748     }
 749 
 750     String getJavaFieldType(Descriptor d) {
 751         try {
 752             return getJavaName(d.getFieldType(constant_pool));
 753         } catch (ConstantPoolException e) {
 754             return report(e);
 755         } catch (InvalidDescriptor e) {
 756             return report(e);
 757         }
 758     }
 759 
 760     void writeModifiers(Collection<String> items) {
 761         for (Object item: items) {
 762             print(item);
 763             print(" ");
 764         }
 765     }
 766 
 767     @Override
 768     public Void visitRuntimeVisibleAnnotations(RuntimeVisibleAnnotations_attribute attr, Void ignore) {
 769         println("RuntimeVisibleAnnotations:");
 770         indent(+1);
 771         for (int i = 0; i < attr.annotations.length; i++) {
 772             print(i + ": ");
 773             annotationWriter.write(attr.annotations[i]);
 774             println();
 775         }
 776         indent(-1);
 777         return null;
 778     }
 779 
 780     @Override
 781     public Void visitRuntimeInvisibleAnnotations(RuntimeInvisibleAnnotations_attribute attr, Void ignore) {
 782         println("RuntimeInvisibleAnnotations:");
 783         indent(+1);
 784         for (int i = 0; i < attr.annotations.length; i++) {
 785             print(i + ": ");
 786             annotationWriter.write(attr.annotations[i]);
 787             println();
 788         }
 789         indent(-1);
 790         return null;
 791     }
 792 
 793     @Override
 794     public Void visitRuntimeVisibleTypeAnnotations(RuntimeVisibleTypeAnnotations_attribute attr, Void ignore) {
 795         println("RuntimeVisibleTypeAnnotations:");
 796         indent(+1);
 797         for (int i = 0; i < attr.annotations.length; i++) {
 798             print(i + ": ");
 799             annotationWriter.write(attr.annotations[i]);
 800             println();
 801         }
 802         indent(-1);
 803         return null;
 804     }
 805 
 806     @Override
 807     public Void visitRuntimeInvisibleTypeAnnotations(RuntimeInvisibleTypeAnnotations_attribute attr, Void ignore) {
 808         println("RuntimeInvisibleTypeAnnotations:");
 809         indent(+1);
 810         for (int i = 0; i < attr.annotations.length; i++) {
 811             print(i + ": ");
 812             annotationWriter.write(attr.annotations[i]);
 813             println();
 814         }
 815         indent(-1);
 816         return null;
 817     }
 818 
 819     private void visitParameterAnnotations(String message, RuntimeParameterAnnotations_attribute attr) {
 820         println(message);
 821         indent(+1);
 822         for (int param = 0; param < attr.parameter_annotations.length; param++) {
 823             println("parameter " + param + ": ");
 824             indent(+1);
 825             for (int i = 0; i < attr.parameter_annotations[param].length; i++) {
 826                 print(i + ": ");
 827                 annotationWriter.write(attr.parameter_annotations[param][i]);
 828                 println();
 829             }
 830             indent(-1);
 831         }
 832         indent(-1);
 833     }
 834 
 835     @Override
 836     public Void visitRuntimeVisibleParameterAnnotations(RuntimeVisibleParameterAnnotations_attribute attr, Void ignore) {
 837         visitParameterAnnotations("RuntimeVisibleParameterAnnotations:", (RuntimeParameterAnnotations_attribute) attr);
 838         return null;
 839     }
 840 
 841     @Override
 842     public Void visitRuntimeInvisibleParameterAnnotations(RuntimeInvisibleParameterAnnotations_attribute attr, Void ignore) {
 843         visitParameterAnnotations("RuntimeInvisibleParameterAnnotations:", (RuntimeParameterAnnotations_attribute) attr);
 844         return null;
 845     }
 846 
 847     @Override
 848     public Void visitPermittedSubtypes(PermittedSubtypes_attribute attr, Void ignore) {
 849         println("PermittedSubtypes:");
 850         indent(+1);
 851         try {
 852             CONSTANT_Class_info[] subtypes = attr.getSubtypes(constant_pool);
 853             for (int i = 0; i < subtypes.length; i++) {
 854                 println(constantWriter.stringValue(subtypes[i]));
 855             }
 856             indent(-1);
 857         } catch (ConstantPoolException ex) {
 858             throw new AssertionError(ex);
 859         }
 860         return null;
 861     }
 862 
 863     @Override
 864     public Void visitSignature(Signature_attribute attr, Void ignore) {
 865         print("Signature: #" + attr.signature_index);
 866         tab();
 867         println("// " + getSignature(attr));
 868         return null;
 869     }
 870 
 871     String getSignature(Signature_attribute info) {
 872         try {
 873             return info.getSignature(constant_pool);
 874         } catch (ConstantPoolException e) {
 875             return report(e);
 876         }
 877     }
 878 
 879     @Override
 880     public Void visitSourceDebugExtension(SourceDebugExtension_attribute attr, Void ignore) {
 881         println("SourceDebugExtension:");
 882         indent(+1);
 883         for (String s: attr.getValue().split("[\r\n]+")) {
 884             println(s);
 885         }
 886         indent(-1);
 887         return null;
 888     }
 889 
 890     @Override
 891     public Void visitSourceFile(SourceFile_attribute attr, Void ignore) {
 892         println("SourceFile: \"" + getSourceFile(attr) + "\"");
 893         return null;
 894     }
 895 
 896     private String getSourceFile(SourceFile_attribute attr) {
 897         try {
 898             return attr.getSourceFile(constant_pool);
 899         } catch (ConstantPoolException e) {
 900             return report(e);
 901         }
 902     }
 903 
 904     @Override
 905     public Void visitSourceID(SourceID_attribute attr, Void ignore) {
 906         constantWriter.write(attr.sourceID_index);
 907         return null;
 908     }
 909 
 910     @Override
 911     public Void visitStackMap(StackMap_attribute attr, Void ignore) {
 912         println("StackMap: number_of_entries = " + attr.number_of_entries);
 913         indent(+1);
 914         StackMapTableWriter w = new StackMapTableWriter();
 915         for (StackMapTable_attribute.stack_map_frame entry : attr.entries) {
 916             w.write(entry);
 917         }
 918         indent(-1);
 919         return null;
 920     }
 921 
 922     @Override
 923     public Void visitStackMapTable(StackMapTable_attribute attr, Void ignore) {
 924         println("StackMapTable: number_of_entries = " + attr.number_of_entries);
 925         indent(+1);
 926         StackMapTableWriter w = new StackMapTableWriter();
 927         for (StackMapTable_attribute.stack_map_frame entry : attr.entries) {
 928             w.write(entry);
 929         }
 930         indent(-1);
 931         return null;
 932     }
 933 
 934     class StackMapTableWriter // also handles CLDC StackMap attributes
 935             implements StackMapTable_attribute.stack_map_frame.Visitor<Void,Void> {
 936         public void write(StackMapTable_attribute.stack_map_frame frame) {
 937             frame.accept(this, null);
 938         }
 939 
 940         @Override
 941         public Void visit_same_frame(StackMapTable_attribute.same_frame frame, Void p) {
 942             printHeader(frame, "/* same */");
 943             return null;
 944         }
 945 
 946         @Override
 947         public Void visit_same_locals_1_stack_item_frame(StackMapTable_attribute.same_locals_1_stack_item_frame frame, Void p) {
 948             printHeader(frame, "/* same_locals_1_stack_item */");
 949             indent(+1);
 950             printMap("stack", frame.stack);
 951             indent(-1);
 952             return null;
 953         }
 954 
 955         @Override
 956         public Void visit_same_locals_1_stack_item_frame_extended(StackMapTable_attribute.same_locals_1_stack_item_frame_extended frame, Void p) {
 957             printHeader(frame, "/* same_locals_1_stack_item_frame_extended */");
 958             indent(+1);
 959             println("offset_delta = " + frame.offset_delta);
 960             printMap("stack", frame.stack);
 961             indent(-1);
 962             return null;
 963         }
 964 
 965         @Override
 966         public Void visit_chop_frame(StackMapTable_attribute.chop_frame frame, Void p) {
 967             printHeader(frame, "/* chop */");
 968             indent(+1);
 969             println("offset_delta = " + frame.offset_delta);
 970             indent(-1);
 971             return null;
 972         }
 973 
 974         @Override
 975         public Void visit_same_frame_extended(StackMapTable_attribute.same_frame_extended frame, Void p) {
 976             printHeader(frame, "/* same_frame_extended */");
 977             indent(+1);
 978             println("offset_delta = " + frame.offset_delta);
 979             indent(-1);
 980             return null;
 981         }
 982 
 983         @Override
 984         public Void visit_append_frame(StackMapTable_attribute.append_frame frame, Void p) {
 985             printHeader(frame, "/* append */");
 986             indent(+1);
 987             println("offset_delta = " + frame.offset_delta);
 988             printMap("locals", frame.locals);
 989             indent(-1);
 990             return null;
 991         }
 992 
 993         @Override
 994         public Void visit_full_frame(StackMapTable_attribute.full_frame frame, Void p) {
 995             if (frame instanceof StackMap_attribute.stack_map_frame) {
 996                 printHeader(frame, "offset = " + frame.offset_delta);
 997                 indent(+1);
 998             } else {
 999                 printHeader(frame, "/* full_frame */");
1000                 indent(+1);
1001                 println("offset_delta = " + frame.offset_delta);
1002             }
1003             printMap("locals", frame.locals);
1004             printMap("stack", frame.stack);
1005             indent(-1);
1006             return null;
1007         }
1008 
1009         void printHeader(StackMapTable_attribute.stack_map_frame frame, String extra) {
1010             print("frame_type = " + frame.frame_type + " ");
1011             println(extra);
1012         }
1013 
1014         void printMap(String name, StackMapTable_attribute.verification_type_info[] map) {
1015             print(name + " = [");
1016             for (int i = 0; i < map.length; i++) {
1017                 StackMapTable_attribute.verification_type_info info = map[i];
1018                 int tag = info.tag;
1019                 switch (tag) {
1020                     case StackMapTable_attribute.verification_type_info.ITEM_Object:
1021                         print(" ");
1022                         constantWriter.write(((StackMapTable_attribute.Object_variable_info) info).cpool_index);
1023                         break;
1024                     case StackMapTable_attribute.verification_type_info.ITEM_Uninitialized:
1025                         print(" " + mapTypeName(tag));
1026                         print(" " + ((StackMapTable_attribute.Uninitialized_variable_info) info).offset);
1027                         break;
1028                     default:
1029                         print(" " + mapTypeName(tag));
1030                 }
1031                 print(i == (map.length - 1) ? " " : ",");
1032             }
1033             println("]");
1034         }
1035 
1036         String mapTypeName(int tag) {
1037             switch (tag) {
1038             case StackMapTable_attribute.verification_type_info.ITEM_Top:
1039                 return "top";
1040 
1041             case StackMapTable_attribute.verification_type_info.ITEM_Integer:
1042                 return "int";
1043 
1044             case StackMapTable_attribute.verification_type_info.ITEM_Float:
1045                 return "float";
1046 
1047             case StackMapTable_attribute.verification_type_info.ITEM_Long:
1048                 return "long";
1049 
1050             case StackMapTable_attribute.verification_type_info.ITEM_Double:
1051                 return "double";
1052 
1053             case StackMapTable_attribute.verification_type_info.ITEM_Null:
1054                 return "null";
1055 
1056             case StackMapTable_attribute.verification_type_info.ITEM_UninitializedThis:
1057                 return "this";
1058 
1059             case StackMapTable_attribute.verification_type_info.ITEM_Object:
1060                 return "CP";
1061 
1062             case StackMapTable_attribute.verification_type_info.ITEM_Uninitialized:
1063                 return "uninitialized";
1064 
1065             default:
1066                 report("unrecognized verification_type_info tag: " + tag);
1067                 return "[tag:" + tag + "]";
1068             }
1069         }
1070     }
1071 
1072     @Override
1073     public Void visitSynthetic(Synthetic_attribute attr, Void ignore) {
1074         println("Synthetic: true");
1075         return null;
1076     }
1077 
1078     static String getJavaName(String name) {
1079         return name.replace('/', '.');
1080     }
1081 
1082     String toHex(byte b, int w) {
1083         return toHex(b & 0xff, w);
1084     }
1085 
1086     static String toHex(int i) {
1087         return StringUtils.toUpperCase(Integer.toString(i, 16));
1088     }
1089 
1090     static String toHex(int i, int w) {
1091         String s = StringUtils.toUpperCase(Integer.toHexString(i));
1092         while (s.length() < w)
1093             s = "0" + s;
1094         return StringUtils.toUpperCase(s);
1095     }
1096 
1097     static String toHex(byte[] ba) {
1098         StringBuilder sb = new StringBuilder(ba.length);
1099         for (byte b: ba) {
1100             sb.append(String.format("%02x", b & 0xff));
1101         }
1102         return sb.toString();
1103     }
1104 
1105     private final AnnotationWriter annotationWriter;
1106     private final CodeWriter codeWriter;
1107     private final ConstantWriter constantWriter;
1108     private final Options options;
1109 
1110     private ConstantPool constant_pool;
1111     private Object owner;
1112 }