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