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