1 /*
   2  * Copyright (c) 2007, 2014, 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.Formatter;
  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.Code_attribute;
  37 import com.sun.tools.classfile.CompilationID_attribute;
  38 import com.sun.tools.classfile.ConstantPool;
  39 import com.sun.tools.classfile.ConstantPoolException;
  40 import com.sun.tools.classfile.ConstantValue_attribute;
  41 import com.sun.tools.classfile.DefaultAttribute;
  42 import com.sun.tools.classfile.Deprecated_attribute;
  43 import com.sun.tools.classfile.EnclosingMethod_attribute;
  44 import com.sun.tools.classfile.Exceptions_attribute;
  45 import com.sun.tools.classfile.InnerClasses_attribute;
  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.RuntimeInvisibleAnnotations_attribute;
  51 import com.sun.tools.classfile.RuntimeInvisibleParameterAnnotations_attribute;
  52 import com.sun.tools.classfile.RuntimeInvisibleTypeAnnotations_attribute;
  53 import com.sun.tools.classfile.RuntimeVisibleAnnotations_attribute;
  54 import com.sun.tools.classfile.RuntimeVisibleParameterAnnotations_attribute;
  55 import com.sun.tools.classfile.RuntimeVisibleTypeAnnotations_attribute;
  56 import com.sun.tools.classfile.Signature_attribute;
  57 import com.sun.tools.classfile.SourceDebugExtension_attribute;
  58 import com.sun.tools.classfile.SourceFile_attribute;
  59 import com.sun.tools.classfile.SourceID_attribute;
  60 import com.sun.tools.classfile.StackMapTable_attribute;
  61 import com.sun.tools.classfile.StackMap_attribute;
  62 import com.sun.tools.classfile.Synthetic_attribute;
  63 
  64 import static com.sun.tools.classfile.AccessFlags.*;
  65 
  66 import com.sun.tools.javac.util.Assert;
  67 import com.sun.tools.javac.util.StringUtils;
  68 
  69 /*
  70  *  A writer for writing Attributes as text.
  71  *
  72  *  <p><b>This is NOT part of any supported API.
  73  *  If you write code that depends on this, you do so at your own risk.
  74  *  This code and its internal interfaces are subject to change or
  75  *  deletion without notice.</b>
  76  */
  77 public class AttributeWriter extends BasicWriter
  78         implements Attribute.Visitor<Void,Void>
  79 {
  80     public static AttributeWriter instance(Context context) {
  81         AttributeWriter instance = context.get(AttributeWriter.class);
  82         if (instance == null)
  83             instance = new AttributeWriter(context);
  84         return instance;
  85     }
  86 
  87     protected AttributeWriter(Context context) {
  88         super(context);
  89         context.put(AttributeWriter.class, this);
  90         annotationWriter = AnnotationWriter.instance(context);
  91         codeWriter = CodeWriter.instance(context);
  92         constantWriter = ConstantWriter.instance(context);
  93         options = Options.instance(context);
  94     }
  95 
  96     public void write(Object owner, Attribute attr, ConstantPool constant_pool) {
  97         if (attr != null) {
  98             Assert.checkNonNull(constant_pool);
  99             Assert.checkNonNull(owner);
 100             this.constant_pool = constant_pool;
 101             this.owner = owner;
 102             attr.accept(this, null);
 103         }
 104     }
 105 
 106     public void write(Object owner, Attributes attrs, ConstantPool constant_pool) {
 107         if (attrs != null) {
 108             Assert.checkNonNull(constant_pool);
 109             Assert.checkNonNull(owner);
 110             this.constant_pool = constant_pool;
 111             this.owner = owner;
 112             for (Attribute attr: attrs)
 113                 attr.accept(this, null);
 114         }
 115     }
 116 
 117     public Void visitDefault(DefaultAttribute attr, Void ignore) {
 118         if (attr.reason != null) {
 119             report(attr.reason);
 120         }
 121         byte[] data = attr.info;
 122         int i = 0;
 123         int j = 0;
 124         print("  ");
 125         try {
 126             print(attr.getName(constant_pool));
 127         } catch (ConstantPoolException e) {
 128             report(e);
 129             print("attribute name = #" + attr.attribute_name_index);
 130         }
 131         print(": ");
 132         println("length = 0x" + toHex(attr.info.length));
 133 
 134         print("   ");
 135 
 136         while (i < data.length) {
 137             print(toHex(data[i], 2));
 138 
 139             j++;
 140             if (j == 16) {
 141                 println();
 142                 print("   ");
 143                 j = 0;
 144             } else {
 145                 print(" ");
 146             }
 147             i++;
 148         }
 149         println();
 150         return null;
 151     }
 152 
 153     public Void visitAnnotationDefault(AnnotationDefault_attribute attr, Void ignore) {
 154         println("AnnotationDefault:");
 155         indent(+1);
 156         print("default_value: ");
 157         annotationWriter.write(attr.default_value);
 158         indent(-1);
 159         return null;
 160     }
 161 
 162     public Void visitBootstrapMethods(BootstrapMethods_attribute attr, Void p) {
 163         println(Attribute.BootstrapMethods + ":");
 164         for (int i = 0; i < attr.bootstrap_method_specifiers.length ; i++) {
 165             BootstrapMethods_attribute.BootstrapMethodSpecifier bsm = attr.bootstrap_method_specifiers[i];
 166             indent(+1);
 167             print(i + ": #" + bsm.bootstrap_method_ref + " ");
 168             println(constantWriter.stringValue(bsm.bootstrap_method_ref));
 169             indent(+1);
 170             println("Method arguments:");
 171             indent(+1);
 172             for (int j = 0; j < bsm.bootstrap_arguments.length; j++) {
 173                 print("#" + bsm.bootstrap_arguments[j] + " ");
 174                 println(constantWriter.stringValue(bsm.bootstrap_arguments[j]));
 175             }
 176             indent(-3);
 177         }
 178         return null;
 179     }
 180 
 181     public Void visitCharacterRangeTable(CharacterRangeTable_attribute attr, Void ignore) {
 182         println("CharacterRangeTable:");
 183         indent(+1);
 184         for (int i = 0; i < attr.character_range_table.length; i++) {
 185             CharacterRangeTable_attribute.Entry e = attr.character_range_table[i];
 186             print(String.format("    %2d, %2d, %6x, %6x, %4x",
 187                     e.start_pc, e.end_pc,
 188                     e.character_range_start, e.character_range_end,
 189                     e.flags));
 190             tab();
 191             print(String.format("// %2d, %2d, %4d:%02d, %4d:%02d",
 192                     e.start_pc, e.end_pc,
 193                     (e.character_range_start >> 10), (e.character_range_start & 0x3ff),
 194                     (e.character_range_end >> 10), (e.character_range_end & 0x3ff)));
 195             if ((e.flags & CharacterRangeTable_attribute.CRT_STATEMENT) != 0)
 196                 print(", statement");
 197             if ((e.flags & CharacterRangeTable_attribute.CRT_BLOCK) != 0)
 198                 print(", block");
 199             if ((e.flags & CharacterRangeTable_attribute.CRT_ASSIGNMENT) != 0)
 200                 print(", assignment");
 201             if ((e.flags & CharacterRangeTable_attribute.CRT_FLOW_CONTROLLER) != 0)
 202                 print(", flow-controller");
 203             if ((e.flags & CharacterRangeTable_attribute.CRT_FLOW_TARGET) != 0)
 204                 print(", flow-target");
 205             if ((e.flags & CharacterRangeTable_attribute.CRT_INVOKE) != 0)
 206                 print(", invoke");
 207             if ((e.flags & CharacterRangeTable_attribute.CRT_CREATE) != 0)
 208                 print(", create");
 209             if ((e.flags & CharacterRangeTable_attribute.CRT_BRANCH_TRUE) != 0)
 210                 print(", branch-true");
 211             if ((e.flags & CharacterRangeTable_attribute.CRT_BRANCH_FALSE) != 0)
 212                 print(", branch-false");
 213             println();
 214         }
 215         indent(-1);
 216         return null;
 217     }
 218 
 219     public Void visitCode(Code_attribute attr, Void ignore) {
 220         codeWriter.write(attr, constant_pool);
 221         return null;
 222     }
 223 
 224     public Void visitCompilationID(CompilationID_attribute attr, Void ignore) {
 225         constantWriter.write(attr.compilationID_index);
 226         return null;
 227     }
 228 
 229     public Void visitConstantValue(ConstantValue_attribute attr, Void ignore) {
 230         print("ConstantValue: ");
 231         constantWriter.write(attr.constantvalue_index);
 232         println();
 233         return null;
 234     }
 235 
 236     public Void visitDeprecated(Deprecated_attribute attr, Void ignore) {
 237         println("Deprecated: true");
 238         return null;
 239     }
 240 
 241     public Void visitEnclosingMethod(EnclosingMethod_attribute attr, Void ignore) {
 242         print("EnclosingMethod: #" + attr.class_index + ".#" + attr.method_index);
 243         tab();
 244         print("// " + getJavaClassName(attr));
 245         if (attr.method_index != 0)
 246             print("." + getMethodName(attr));
 247         println();
 248         return null;
 249     }
 250 
 251     private String getJavaClassName(EnclosingMethod_attribute a) {
 252         try {
 253             return getJavaName(a.getClassName(constant_pool));
 254         } catch (ConstantPoolException e) {
 255             return report(e);
 256         }
 257     }
 258 
 259     private String getMethodName(EnclosingMethod_attribute a) {
 260         try {
 261             return a.getMethodName(constant_pool);
 262         } catch (ConstantPoolException e) {
 263             return report(e);
 264         }
 265     }
 266 
 267     public Void visitExceptions(Exceptions_attribute attr, Void ignore) {
 268         println("Exceptions:");
 269         indent(+1);
 270         print("throws ");
 271         for (int i = 0; i < attr.number_of_exceptions; i++) {
 272             if (i > 0)
 273                 print(", ");
 274             print(getJavaException(attr, i));
 275         }
 276         println();
 277         indent(-1);
 278         return null;
 279     }
 280 
 281     private String getJavaException(Exceptions_attribute attr, int index) {
 282         try {
 283             return getJavaName(attr.getException(index, constant_pool));
 284         } catch (ConstantPoolException e) {
 285             return report(e);
 286         }
 287     }
 288 
 289     public Void visitInnerClasses(InnerClasses_attribute attr, Void ignore) {
 290         boolean first = true;
 291         for (int i = 0 ; i < attr.classes.length; i++) {
 292             InnerClasses_attribute.Info info = attr.classes[i];
 293             //access
 294             AccessFlags access_flags = info.inner_class_access_flags;
 295             if (options.checkAccess(access_flags)) {
 296                 if (first) {
 297                     writeInnerClassHeader();
 298                     first = false;
 299                 }
 300                 for (String name: access_flags.getInnerClassModifiers())
 301                     print(name + " ");
 302                 if (info.inner_name_index != 0) {
 303                     print("#" + info.inner_name_index + "= ");
 304                 }
 305                 print("#" + info.inner_class_info_index);
 306                 if (info.outer_class_info_index != 0) {
 307                     print(" of #" + info.outer_class_info_index);
 308                 }
 309                 print(";");
 310                 tab();
 311                 print("// ");
 312                 if (info.inner_name_index != 0) {
 313                     print(getInnerName(constant_pool, info) + "=");
 314                 }
 315                 constantWriter.write(info.inner_class_info_index);
 316                 if (info.outer_class_info_index != 0) {
 317                     print(" of ");
 318                     constantWriter.write(info.outer_class_info_index);
 319                 }
 320                 println();
 321             }
 322         }
 323         if (!first)
 324             indent(-1);
 325         return null;
 326     }
 327 
 328     String getInnerName(ConstantPool constant_pool, InnerClasses_attribute.Info info) {
 329         try {
 330             return info.getInnerName(constant_pool);
 331         } catch (ConstantPoolException e) {
 332             return report(e);
 333         }
 334     }
 335 
 336     private void writeInnerClassHeader() {
 337         println("InnerClasses:");
 338         indent(+1);
 339     }
 340 
 341     public Void visitLineNumberTable(LineNumberTable_attribute attr, Void ignore) {
 342         println("LineNumberTable:");
 343         indent(+1);
 344         for (LineNumberTable_attribute.Entry entry: attr.line_number_table) {
 345             println("line " + entry.line_number + ": " + entry.start_pc);
 346         }
 347         indent(-1);
 348         return null;
 349     }
 350 
 351     public Void visitLocalVariableTable(LocalVariableTable_attribute attr, Void ignore) {
 352         println("LocalVariableTable:");
 353         indent(+1);
 354         println("Start  Length  Slot  Name   Signature");
 355         for (LocalVariableTable_attribute.Entry entry : attr.local_variable_table) {
 356             println(String.format("%5d %7d %5d %5s   %s",
 357                     entry.start_pc, entry.length, entry.index,
 358                     constantWriter.stringValue(entry.name_index),
 359                     constantWriter.stringValue(entry.descriptor_index)));
 360         }
 361         indent(-1);
 362         return null;
 363     }
 364 
 365     public Void visitLocalVariableTypeTable(LocalVariableTypeTable_attribute attr, Void ignore) {
 366         println("LocalVariableTypeTable:");
 367         indent(+1);
 368         println("Start  Length  Slot  Name   Signature");
 369         for (LocalVariableTypeTable_attribute.Entry entry : attr.local_variable_table) {
 370             println(String.format("%5d %7d %5d %5s   %s",
 371                     entry.start_pc, entry.length, entry.index,
 372                     constantWriter.stringValue(entry.name_index),
 373                     constantWriter.stringValue(entry.signature_index)));
 374         }
 375         indent(-1);
 376         return null;
 377     }
 378 
 379     private static final String format = "%-31s%s";
 380 
 381     public Void visitMethodParameters(MethodParameters_attribute attr,
 382                                       Void ignore) {
 383 
 384         final String header = String.format(format, "Name", "Flags");
 385         println("MethodParameters:");
 386         indent(+1);
 387         println(header);
 388         for (MethodParameters_attribute.Entry entry :
 389                  attr.method_parameter_table) {
 390             String namestr =
 391                 entry.name_index != 0 ?
 392                 constantWriter.stringValue(entry.name_index) : "<no name>";
 393             String flagstr =
 394                 (0 != (entry.flags & ACC_FINAL) ? "final " : "") +
 395                 (0 != (entry.flags & ACC_MANDATED) ? "mandated " : "") +
 396                 (0 != (entry.flags & ACC_SYNTHETIC) ? "synthetic" : "");
 397             println(String.format(format, namestr, flagstr));
 398         }
 399         indent(-1);
 400         return null;
 401     }
 402 
 403     public Void visitRuntimeVisibleAnnotations(RuntimeVisibleAnnotations_attribute attr, Void ignore) {
 404         println("RuntimeVisibleAnnotations:");
 405         indent(+1);
 406         for (int i = 0; i < attr.annotations.length; i++) {
 407             print(i + ": ");
 408             annotationWriter.write(attr.annotations[i]);
 409             println();
 410         }
 411         indent(-1);
 412         return null;
 413     }
 414 
 415     public Void visitRuntimeInvisibleAnnotations(RuntimeInvisibleAnnotations_attribute attr, Void ignore) {
 416         println("RuntimeInvisibleAnnotations:");
 417         indent(+1);
 418         for (int i = 0; i < attr.annotations.length; i++) {
 419             print(i + ": ");
 420             annotationWriter.write(attr.annotations[i]);
 421             println();
 422         }
 423         indent(-1);
 424         return null;
 425     }
 426 
 427     public Void visitRuntimeVisibleTypeAnnotations(RuntimeVisibleTypeAnnotations_attribute attr, Void ignore) {
 428         println("RuntimeVisibleTypeAnnotations:");
 429         indent(+1);
 430         for (int i = 0; i < attr.annotations.length; i++) {
 431             print(i + ": ");
 432             annotationWriter.write(attr.annotations[i]);
 433             println();
 434         }
 435         indent(-1);
 436         return null;
 437     }
 438 
 439     public Void visitRuntimeInvisibleTypeAnnotations(RuntimeInvisibleTypeAnnotations_attribute attr, Void ignore) {
 440         println("RuntimeInvisibleTypeAnnotations:");
 441         indent(+1);
 442         for (int i = 0; i < attr.annotations.length; i++) {
 443             print(i + ": ");
 444             annotationWriter.write(attr.annotations[i]);
 445             println();
 446         }
 447         indent(-1);
 448         return null;
 449     }
 450 
 451     public Void visitRuntimeVisibleParameterAnnotations(RuntimeVisibleParameterAnnotations_attribute attr, Void ignore) {
 452         println("RuntimeVisibleParameterAnnotations:");
 453         indent(+1);
 454         for (int param = 0; param < attr.parameter_annotations.length; param++) {
 455             println("parameter " + param + ": ");
 456             indent(+1);
 457             for (int i = 0; i < attr.parameter_annotations[param].length; i++) {
 458                 print(i + ": ");
 459                 annotationWriter.write(attr.parameter_annotations[param][i]);
 460                 println();
 461             }
 462             indent(-1);
 463         }
 464         indent(-1);
 465         return null;
 466     }
 467 
 468     public Void visitRuntimeInvisibleParameterAnnotations(RuntimeInvisibleParameterAnnotations_attribute attr, Void ignore) {
 469         println("RuntimeInvisibleParameterAnnotations:");
 470         indent(+1);
 471         for (int param = 0; param < attr.parameter_annotations.length; param++) {
 472             println(param + ": ");
 473             indent(+1);
 474             for (int i = 0; i < attr.parameter_annotations[param].length; i++) {
 475                 print(i + ": ");
 476                 annotationWriter.write(attr.parameter_annotations[param][i]);
 477                 println();
 478             }
 479             indent(-1);
 480         }
 481         indent(-1);
 482         return null;
 483     }
 484 
 485     public Void visitSignature(Signature_attribute attr, Void ignore) {
 486         print("Signature: #" + attr.signature_index);
 487         tab();
 488         println("// " + getSignature(attr));
 489         return null;
 490     }
 491 
 492     String getSignature(Signature_attribute info) {
 493         try {
 494             return info.getSignature(constant_pool);
 495         } catch (ConstantPoolException e) {
 496             return report(e);
 497         }
 498     }
 499 
 500     public Void visitSourceDebugExtension(SourceDebugExtension_attribute attr, Void ignore) {
 501         println("SourceDebugExtension:");
 502         indent(+1);
 503         for (String s: attr.getValue().split("[\r\n]+")) {
 504             println(s);
 505         }
 506         indent(-1);
 507         return null;
 508     }
 509 
 510     public Void visitSourceFile(SourceFile_attribute attr, Void ignore) {
 511         println("SourceFile: \"" + getSourceFile(attr) + "\"");
 512         return null;
 513     }
 514 
 515     private String getSourceFile(SourceFile_attribute attr) {
 516         try {
 517             return attr.getSourceFile(constant_pool);
 518         } catch (ConstantPoolException e) {
 519             return report(e);
 520         }
 521     }
 522 
 523     public Void visitSourceID(SourceID_attribute attr, Void ignore) {
 524         constantWriter.write(attr.sourceID_index);
 525         return null;
 526     }
 527 
 528     public Void visitStackMap(StackMap_attribute attr, Void ignore) {
 529         println("StackMap: number_of_entries = " + attr.number_of_entries);
 530         indent(+1);
 531         StackMapTableWriter w = new StackMapTableWriter();
 532         for (StackMapTable_attribute.stack_map_frame entry : attr.entries) {
 533             w.write(entry);
 534         }
 535         indent(-1);
 536         return null;
 537     }
 538 
 539     public Void visitStackMapTable(StackMapTable_attribute attr, Void ignore) {
 540         println("StackMapTable: number_of_entries = " + attr.number_of_entries);
 541         indent(+1);
 542         StackMapTableWriter w = new StackMapTableWriter();
 543         for (StackMapTable_attribute.stack_map_frame entry : attr.entries) {
 544             w.write(entry);
 545         }
 546         indent(-1);
 547         return null;
 548     }
 549 
 550     class StackMapTableWriter // also handles CLDC StackMap attributes
 551             implements StackMapTable_attribute.stack_map_frame.Visitor<Void,Void> {
 552         public void write(StackMapTable_attribute.stack_map_frame frame) {
 553             frame.accept(this, null);
 554         }
 555 
 556         public Void visit_same_frame(StackMapTable_attribute.same_frame frame, Void p) {
 557             printHeader(frame, "/* same */");
 558             return null;
 559         }
 560 
 561         public Void visit_same_locals_1_stack_item_frame(StackMapTable_attribute.same_locals_1_stack_item_frame frame, Void p) {
 562             printHeader(frame, "/* same_locals_1_stack_item */");
 563             indent(+1);
 564             printMap("stack", frame.stack);
 565             indent(-1);
 566             return null;
 567         }
 568 
 569         public Void visit_same_locals_1_stack_item_frame_extended(StackMapTable_attribute.same_locals_1_stack_item_frame_extended frame, Void p) {
 570             printHeader(frame, "/* same_locals_1_stack_item_frame_extended */");
 571             indent(+1);
 572             println("offset_delta = " + frame.offset_delta);
 573             printMap("stack", frame.stack);
 574             indent(-1);
 575             return null;
 576         }
 577 
 578         public Void visit_chop_frame(StackMapTable_attribute.chop_frame frame, Void p) {
 579             printHeader(frame, "/* chop */");
 580             indent(+1);
 581             println("offset_delta = " + frame.offset_delta);
 582             indent(-1);
 583             return null;
 584         }
 585 
 586         public Void visit_same_frame_extended(StackMapTable_attribute.same_frame_extended frame, Void p) {
 587             printHeader(frame, "/* same_frame_extended */");
 588             indent(+1);
 589             println("offset_delta = " + frame.offset_delta);
 590             indent(-1);
 591             return null;
 592         }
 593 
 594         public Void visit_append_frame(StackMapTable_attribute.append_frame frame, Void p) {
 595             printHeader(frame, "/* append */");
 596             indent(+1);
 597             println("offset_delta = " + frame.offset_delta);
 598             printMap("locals", frame.locals);
 599             indent(-1);
 600             return null;
 601         }
 602 
 603         public Void visit_full_frame(StackMapTable_attribute.full_frame frame, Void p) {
 604             if (frame instanceof StackMap_attribute.stack_map_frame) {
 605                 printHeader(frame, "offset = " + frame.offset_delta);
 606                 indent(+1);
 607             } else {
 608                 printHeader(frame, "/* full_frame */");
 609                 indent(+1);
 610                 println("offset_delta = " + frame.offset_delta);
 611             }
 612             printMap("locals", frame.locals);
 613             printMap("stack", frame.stack);
 614             indent(-1);
 615             return null;
 616         }
 617 
 618         void printHeader(StackMapTable_attribute.stack_map_frame frame, String extra) {
 619             print("frame_type = " + frame.frame_type + " ");
 620             println(extra);
 621         }
 622 
 623         void printMap(String name, StackMapTable_attribute.verification_type_info[] map) {
 624             print(name + " = [");
 625             for (int i = 0; i < map.length; i++) {
 626                 StackMapTable_attribute.verification_type_info info = map[i];
 627                 int tag = info.tag;
 628                 switch (tag) {
 629                     case StackMapTable_attribute.verification_type_info.ITEM_Object:
 630                         print(" ");
 631                         constantWriter.write(((StackMapTable_attribute.Object_variable_info) info).cpool_index);
 632                         break;
 633                     case StackMapTable_attribute.verification_type_info.ITEM_Uninitialized:
 634                         print(" " + mapTypeName(tag));
 635                         print(" " + ((StackMapTable_attribute.Uninitialized_variable_info) info).offset);
 636                         break;
 637                     default:
 638                         print(" " + mapTypeName(tag));
 639                 }
 640                 print(i == (map.length - 1) ? " " : ",");
 641             }
 642             println("]");
 643         }
 644 
 645         String mapTypeName(int tag) {
 646             switch (tag) {
 647             case StackMapTable_attribute.verification_type_info.ITEM_Top:
 648                 return "top";
 649 
 650             case StackMapTable_attribute.verification_type_info.ITEM_Integer:
 651                 return "int";
 652 
 653             case StackMapTable_attribute.verification_type_info.ITEM_Float:
 654                 return "float";
 655 
 656             case StackMapTable_attribute.verification_type_info.ITEM_Long:
 657                 return "long";
 658 
 659             case StackMapTable_attribute.verification_type_info.ITEM_Double:
 660                 return "double";
 661 
 662             case StackMapTable_attribute.verification_type_info.ITEM_Null:
 663                 return "null";
 664 
 665             case StackMapTable_attribute.verification_type_info.ITEM_UninitializedThis:
 666                 return "this";
 667 
 668             case StackMapTable_attribute.verification_type_info.ITEM_Object:
 669                 return "CP";
 670 
 671             case StackMapTable_attribute.verification_type_info.ITEM_Uninitialized:
 672                 return "uninitialized";
 673 
 674             default:
 675                 report("unrecognized verification_type_info tag: " + tag);
 676                 return "[tag:" + tag + "]";
 677             }
 678         }
 679     }
 680 
 681     public Void visitSynthetic(Synthetic_attribute attr, Void ignore) {
 682         println("Synthetic: true");
 683         return null;
 684     }
 685 
 686     static String getJavaName(String name) {
 687         return name.replace('/', '.');
 688     }
 689 
 690     String toHex(byte b, int w) {
 691         return toHex(b & 0xff, w);
 692     }
 693 
 694     static String toHex(int i) {
 695         return StringUtils.toUpperCase(Integer.toString(i, 16));
 696     }
 697 
 698     static String toHex(int i, int w) {
 699         String s = StringUtils.toUpperCase(Integer.toHexString(i));
 700         while (s.length() < w)
 701             s = "0" + s;
 702         return StringUtils.toUpperCase(s);
 703     }
 704 
 705     private AnnotationWriter annotationWriter;
 706     private CodeWriter codeWriter;
 707     private ConstantWriter constantWriter;
 708     private Options options;
 709 
 710     private ConstantPool constant_pool;
 711     private Object owner;
 712 }