1 /*
   2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   3  *
   4  * This code is free software; you can redistribute it and/or modify it
   5  * under the terms of the GNU General Public License version 2 only, as
   6  * published by the Free Software Foundation.  Oracle designates this
   7  * particular file as subject to the "Classpath" exception as provided
   8  * by Oracle in the LICENSE file that accompanied this code.
   9  *
  10  * This code is distributed in the hope that it will be useful, but WITHOUT
  11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  13  * version 2 for more details (a copy is included in the LICENSE file that
  14  * accompanied this code).
  15  *
  16  * You should have received a copy of the GNU General Public License version
  17  * 2 along with this work; if not, write to the Free Software Foundation,
  18  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  19  *
  20  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  21  * or visit www.oracle.com if you need additional information or have any
  22  * questions.
  23  */
  24 
  25 /*
  26  * This file is available under and governed by the GNU General Public
  27  * License version 2 only, as published by the Free Software Foundation.
  28  * However, the following notice accompanied the original version of this
  29  * file:
  30  *
  31  * ASM: a very small and fast Java bytecode manipulation framework
  32  * Copyright (c) 2000-2011 INRIA, France Telecom
  33  * All rights reserved.
  34  *
  35  * Redistribution and use in source and binary forms, with or without
  36  * modification, are permitted provided that the following conditions
  37  * are met:
  38  * 1. Redistributions of source code must retain the above copyright
  39  *    notice, this list of conditions and the following disclaimer.
  40  * 2. Redistributions in binary form must reproduce the above copyright
  41  *    notice, this list of conditions and the following disclaimer in the
  42  *    documentation and/or other materials provided with the distribution.
  43  * 3. Neither the name of the copyright holders nor the names of its
  44  *    contributors may be used to endorse or promote products derived from
  45  *    this software without specific prior written permission.
  46  *
  47  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  48  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  49  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  50  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  51  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  52  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  53  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  54  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  55  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  56  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  57  * THE POSSIBILITY OF SUCH DAMAGE.
  58  */
  59 package jdk.internal.org.objectweb.asm.util;
  60 
  61 import java.io.FileInputStream;
  62 import java.io.PrintWriter;
  63 import java.util.HashMap;
  64 import java.util.Map;
  65 
  66 import jdk.internal.org.objectweb.asm.Attribute;
  67 import jdk.internal.org.objectweb.asm.ClassReader;
  68 import jdk.internal.org.objectweb.asm.Handle;
  69 import jdk.internal.org.objectweb.asm.Label;
  70 import jdk.internal.org.objectweb.asm.Opcodes;
  71 import jdk.internal.org.objectweb.asm.Type;
  72 import jdk.internal.org.objectweb.asm.TypePath;
  73 import jdk.internal.org.objectweb.asm.TypeReference;
  74 import jdk.internal.org.objectweb.asm.signature.SignatureReader;
  75 
  76 /**
  77  * A {@link Printer} that prints a disassembled view of the classes it visits.
  78  *
  79  * @author Eric Bruneton
  80  */
  81 public class Textifier extends Printer {
  82 
  83     /**
  84      * Constant used in {@link #appendDescriptor appendDescriptor} for internal
  85      * type names in bytecode notation.
  86      */
  87     public static final int INTERNAL_NAME = 0;
  88 
  89     /**
  90      * Constant used in {@link #appendDescriptor appendDescriptor} for field
  91      * descriptors, formatted in bytecode notation
  92      */
  93     public static final int FIELD_DESCRIPTOR = 1;
  94 
  95     /**
  96      * Constant used in {@link #appendDescriptor appendDescriptor} for field
  97      * signatures, formatted in bytecode notation
  98      */
  99     public static final int FIELD_SIGNATURE = 2;
 100 
 101     /**
 102      * Constant used in {@link #appendDescriptor appendDescriptor} for method
 103      * descriptors, formatted in bytecode notation
 104      */
 105     public static final int METHOD_DESCRIPTOR = 3;
 106 
 107     /**
 108      * Constant used in {@link #appendDescriptor appendDescriptor} for method
 109      * signatures, formatted in bytecode notation
 110      */
 111     public static final int METHOD_SIGNATURE = 4;
 112 
 113     /**
 114      * Constant used in {@link #appendDescriptor appendDescriptor} for class
 115      * signatures, formatted in bytecode notation
 116      */
 117     public static final int CLASS_SIGNATURE = 5;
 118 
 119     /**
 120      * Constant used in {@link #appendDescriptor appendDescriptor} for field or
 121      * method return value signatures, formatted in default Java notation
 122      * (non-bytecode)
 123      */
 124     public static final int TYPE_DECLARATION = 6;
 125 
 126     /**
 127      * Constant used in {@link #appendDescriptor appendDescriptor} for class
 128      * signatures, formatted in default Java notation (non-bytecode)
 129      */
 130     public static final int CLASS_DECLARATION = 7;
 131 
 132     /**
 133      * Constant used in {@link #appendDescriptor appendDescriptor} for method
 134      * parameter signatures, formatted in default Java notation (non-bytecode)
 135      */
 136     public static final int PARAMETERS_DECLARATION = 8;
 137 
 138     /**
 139      * Constant used in {@link #appendDescriptor appendDescriptor} for handle
 140      * descriptors, formatted in bytecode notation
 141      */
 142     public static final int HANDLE_DESCRIPTOR = 9;
 143 
 144     /**
 145      * Tab for class members.
 146      */
 147     protected String tab = "  ";
 148 
 149     /**
 150      * Tab for bytecode instructions.
 151      */
 152     protected String tab2 = "    ";
 153 
 154     /**
 155      * Tab for table and lookup switch instructions.
 156      */
 157     protected String tab3 = "      ";
 158 
 159     /**
 160      * Tab for labels.
 161      */
 162     protected String ltab = "   ";
 163 
 164     /**
 165      * The label names. This map associate String values to Label keys.
 166      */
 167     protected Map<Label, String> labelNames;
 168 
 169     /**
 170      * Class access flags
 171      */
 172     private int access;
 173 
 174     private int valueNumber = 0;
 175 
 176     /**
 177      * Constructs a new {@link Textifier}. <i>Subclasses must not use this
 178      * constructor</i>. Instead, they must use the {@link #Textifier(int)}
 179      * version.
 180      *
 181      * @throws IllegalStateException
 182      *             If a subclass calls this constructor.
 183      */
 184     public Textifier() {
 185         this(Opcodes.ASM5);
 186         if (getClass() != Textifier.class) {
 187             throw new IllegalStateException();
 188         }
 189     }
 190 
 191     /**
 192      * Constructs a new {@link Textifier}.
 193      *
 194      * @param api
 195      *            the ASM API version implemented by this visitor. Must be one
 196      *            of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}.
 197      */
 198     protected Textifier(final int api) {
 199         super(api);
 200     }
 201 
 202     /**
 203      * Prints a disassembled view of the given class to the standard output.
 204      * <p>
 205      * Usage: Textifier [-debug] &lt;binary class name or class file name &gt;
 206      *
 207      * @param args
 208      *            the command line arguments.
 209      *
 210      * @throws Exception
 211      *             if the class cannot be found, or if an IO exception occurs.
 212      */
 213     public static void main(final String[] args) throws Exception {
 214         int i = 0;
 215         int flags = ClassReader.SKIP_DEBUG;
 216 
 217         boolean ok = true;
 218         if (args.length < 1 || args.length > 2) {
 219             ok = false;
 220         }
 221         if (ok && "-debug".equals(args[0])) {
 222             i = 1;
 223             flags = 0;
 224             if (args.length != 2) {
 225                 ok = false;
 226             }
 227         }
 228         if (!ok) {
 229             System.err
 230                     .println("Prints a disassembled view of the given class.");
 231             System.err.println("Usage: Textifier [-debug] "
 232                     + "<fully qualified class name or class file name>");
 233             return;
 234         }
 235         ClassReader cr;
 236         if (args[i].endsWith(".class") || args[i].indexOf('\\') > -1
 237                 || args[i].indexOf('/') > -1) {
 238             cr = new ClassReader(new FileInputStream(args[i]));
 239         } else {
 240             cr = new ClassReader(args[i]);
 241         }
 242         cr.accept(new TraceClassVisitor(new PrintWriter(System.out)), flags);
 243     }
 244 
 245     // ------------------------------------------------------------------------
 246     // Classes
 247     // ------------------------------------------------------------------------
 248 
 249     @Override
 250     public void visit(final int version, final int access, final String name,
 251             final String signature, final String superName,
 252             final String[] interfaces) {
 253         this.access = access;
 254         int major = version & 0xFFFF;
 255         int minor = version >>> 16;
 256         buf.setLength(0);
 257         buf.append("// class version ").append(major).append('.').append(minor)
 258                 .append(" (").append(version).append(")\n");
 259         if ((access & Opcodes.ACC_DEPRECATED) != 0) {
 260             buf.append("// DEPRECATED\n");
 261         }
 262         buf.append("// access flags 0x")
 263                 .append(Integer.toHexString(access).toUpperCase()).append('\n');
 264 
 265         appendDescriptor(CLASS_SIGNATURE, signature);
 266         if (signature != null) {
 267             TraceSignatureVisitor sv = new TraceSignatureVisitor(access);
 268             SignatureReader r = new SignatureReader(signature);
 269             r.accept(sv);
 270             buf.append("// declaration: ").append(name)
 271                     .append(sv.getDeclaration()).append('\n');
 272         }
 273 
 274         appendAccess(access & ~Opcodes.ACC_SUPER);
 275         if ((access & Opcodes.ACC_ANNOTATION) != 0) {
 276             buf.append("@interface ");
 277         } else if ((access & Opcodes.ACC_INTERFACE) != 0) {
 278             buf.append("interface ");
 279         } else if ((access & Opcodes.ACC_ENUM) == 0) {
 280             buf.append("class ");
 281         }
 282         appendDescriptor(INTERNAL_NAME, name);
 283 
 284         if (superName != null && !"java/lang/Object".equals(superName)) {
 285             buf.append(" extends ");
 286             appendDescriptor(INTERNAL_NAME, superName);
 287             buf.append(' ');
 288         }
 289         if (interfaces != null && interfaces.length > 0) {
 290             buf.append(" implements ");
 291             for (int i = 0; i < interfaces.length; ++i) {
 292                 appendDescriptor(INTERNAL_NAME, interfaces[i]);
 293                 buf.append(' ');
 294             }
 295         }
 296         buf.append(" {\n\n");
 297 
 298         text.add(buf.toString());
 299     }
 300 
 301     @Override
 302     public void visitSource(final String file, final String debug) {
 303         buf.setLength(0);
 304         if (file != null) {
 305             buf.append(tab).append("// compiled from: ").append(file)
 306                     .append('\n');
 307         }
 308         if (debug != null) {
 309             buf.append(tab).append("// debug info: ").append(debug)
 310                     .append('\n');
 311         }
 312         if (buf.length() > 0) {
 313             text.add(buf.toString());
 314         }
 315     }
 316 
 317     @Override
 318     public void visitOuterClass(final String owner, final String name,
 319             final String desc) {
 320         buf.setLength(0);
 321         buf.append(tab).append("OUTERCLASS ");
 322         appendDescriptor(INTERNAL_NAME, owner);
 323         buf.append(' ');
 324         if (name != null) {
 325             buf.append(name).append(' ');
 326         }
 327         appendDescriptor(METHOD_DESCRIPTOR, desc);
 328         buf.append('\n');
 329         text.add(buf.toString());
 330     }
 331 
 332     @Override
 333     public Textifier visitClassAnnotation(final String desc,
 334             final boolean visible) {
 335         text.add("\n");
 336         return visitAnnotation(desc, visible);
 337     }
 338 
 339     @Override
 340     public Printer visitClassTypeAnnotation(int typeRef, TypePath typePath,
 341             String desc, boolean visible) {
 342         text.add("\n");
 343         return visitTypeAnnotation(typeRef, typePath, desc, visible);
 344     }
 345 
 346     @Override
 347     public void visitClassAttribute(final Attribute attr) {
 348         text.add("\n");
 349         visitAttribute(attr);
 350     }
 351 
 352     @Override
 353     public void visitInnerClass(final String name, final String outerName,
 354             final String innerName, final int access) {
 355         buf.setLength(0);
 356         buf.append(tab).append("// access flags 0x");
 357         buf.append(
 358                 Integer.toHexString(access & ~Opcodes.ACC_SUPER).toUpperCase())
 359                 .append('\n');
 360         buf.append(tab);
 361         appendAccess(access);
 362         buf.append("INNERCLASS ");
 363         appendDescriptor(INTERNAL_NAME, name);
 364         buf.append(' ');
 365         appendDescriptor(INTERNAL_NAME, outerName);
 366         buf.append(' ');
 367         appendDescriptor(INTERNAL_NAME, innerName);
 368         buf.append('\n');
 369         text.add(buf.toString());
 370     }
 371 
 372     @Override
 373     public Textifier visitField(final int access, final String name,
 374             final String desc, final String signature, final Object value) {
 375         buf.setLength(0);
 376         buf.append('\n');
 377         if ((access & Opcodes.ACC_DEPRECATED) != 0) {
 378             buf.append(tab).append("// DEPRECATED\n");
 379         }
 380         buf.append(tab).append("// access flags 0x")
 381                 .append(Integer.toHexString(access).toUpperCase()).append('\n');
 382         if (signature != null) {
 383             buf.append(tab);
 384             appendDescriptor(FIELD_SIGNATURE, signature);
 385 
 386             TraceSignatureVisitor sv = new TraceSignatureVisitor(0);
 387             SignatureReader r = new SignatureReader(signature);
 388             r.acceptType(sv);
 389             buf.append(tab).append("// declaration: ")
 390                     .append(sv.getDeclaration()).append('\n');
 391         }
 392 
 393         buf.append(tab);
 394         appendAccess(access);
 395 
 396         appendDescriptor(FIELD_DESCRIPTOR, desc);
 397         buf.append(' ').append(name);
 398         if (value != null) {
 399             buf.append(" = ");
 400             if (value instanceof String) {
 401                 buf.append('\"').append(value).append('\"');
 402             } else {
 403                 buf.append(value);
 404             }
 405         }
 406 
 407         buf.append('\n');
 408         text.add(buf.toString());
 409 
 410         Textifier t = createTextifier();
 411         text.add(t.getText());
 412         return t;
 413     }
 414 
 415     @Override
 416     public Textifier visitMethod(final int access, final String name,
 417             final String desc, final String signature, final String[] exceptions) {
 418         buf.setLength(0);
 419         buf.append('\n');
 420         if ((access & Opcodes.ACC_DEPRECATED) != 0) {
 421             buf.append(tab).append("// DEPRECATED\n");
 422         }
 423         buf.append(tab).append("// access flags 0x")
 424                 .append(Integer.toHexString(access).toUpperCase()).append('\n');
 425 
 426         if (signature != null) {
 427             buf.append(tab);
 428             appendDescriptor(METHOD_SIGNATURE, signature);
 429 
 430             TraceSignatureVisitor v = new TraceSignatureVisitor(0);
 431             SignatureReader r = new SignatureReader(signature);
 432             r.accept(v);
 433             String genericDecl = v.getDeclaration();
 434             String genericReturn = v.getReturnType();
 435             String genericExceptions = v.getExceptions();
 436 
 437             buf.append(tab).append("// declaration: ").append(genericReturn)
 438                     .append(' ').append(name).append(genericDecl);
 439             if (genericExceptions != null) {
 440                 buf.append(" throws ").append(genericExceptions);
 441             }
 442             buf.append('\n');
 443         }
 444 
 445         buf.append(tab);
 446         appendAccess(access & ~Opcodes.ACC_VOLATILE);
 447         if ((access & Opcodes.ACC_NATIVE) != 0) {
 448             buf.append("native ");
 449         }
 450         if ((access & Opcodes.ACC_VARARGS) != 0) {
 451             buf.append("varargs ");
 452         }
 453         if ((access & Opcodes.ACC_BRIDGE) != 0) {
 454             buf.append("bridge ");
 455         }
 456         if ((this.access & Opcodes.ACC_INTERFACE) != 0
 457                 && (access & Opcodes.ACC_ABSTRACT) == 0
 458                 && (access & Opcodes.ACC_STATIC) == 0) {
 459             buf.append("default ");
 460         }
 461 
 462         buf.append(name);
 463         appendDescriptor(METHOD_DESCRIPTOR, desc);
 464         if (exceptions != null && exceptions.length > 0) {
 465             buf.append(" throws ");
 466             for (int i = 0; i < exceptions.length; ++i) {
 467                 appendDescriptor(INTERNAL_NAME, exceptions[i]);
 468                 buf.append(' ');
 469             }
 470         }
 471 
 472         buf.append('\n');
 473         text.add(buf.toString());
 474 
 475         Textifier t = createTextifier();
 476         text.add(t.getText());
 477         return t;
 478     }
 479 
 480     @Override
 481     public void visitClassEnd() {
 482         text.add("}\n");
 483     }
 484 
 485     // ------------------------------------------------------------------------
 486     // Annotations
 487     // ------------------------------------------------------------------------
 488 
 489     @Override
 490     public void visit(final String name, final Object value) {
 491         buf.setLength(0);
 492         appendComa(valueNumber++);
 493 
 494         if (name != null) {
 495             buf.append(name).append('=');
 496         }
 497 
 498         if (value instanceof String) {
 499             visitString((String) value);
 500         } else if (value instanceof Type) {
 501             visitType((Type) value);
 502         } else if (value instanceof Byte) {
 503             visitByte(((Byte) value).byteValue());
 504         } else if (value instanceof Boolean) {
 505             visitBoolean(((Boolean) value).booleanValue());
 506         } else if (value instanceof Short) {
 507             visitShort(((Short) value).shortValue());
 508         } else if (value instanceof Character) {
 509             visitChar(((Character) value).charValue());
 510         } else if (value instanceof Integer) {
 511             visitInt(((Integer) value).intValue());
 512         } else if (value instanceof Float) {
 513             visitFloat(((Float) value).floatValue());
 514         } else if (value instanceof Long) {
 515             visitLong(((Long) value).longValue());
 516         } else if (value instanceof Double) {
 517             visitDouble(((Double) value).doubleValue());
 518         } else if (value.getClass().isArray()) {
 519             buf.append('{');
 520             if (value instanceof byte[]) {
 521                 byte[] v = (byte[]) value;
 522                 for (int i = 0; i < v.length; i++) {
 523                     appendComa(i);
 524                     visitByte(v[i]);
 525                 }
 526             } else if (value instanceof boolean[]) {
 527                 boolean[] v = (boolean[]) value;
 528                 for (int i = 0; i < v.length; i++) {
 529                     appendComa(i);
 530                     visitBoolean(v[i]);
 531                 }
 532             } else if (value instanceof short[]) {
 533                 short[] v = (short[]) value;
 534                 for (int i = 0; i < v.length; i++) {
 535                     appendComa(i);
 536                     visitShort(v[i]);
 537                 }
 538             } else if (value instanceof char[]) {
 539                 char[] v = (char[]) value;
 540                 for (int i = 0; i < v.length; i++) {
 541                     appendComa(i);
 542                     visitChar(v[i]);
 543                 }
 544             } else if (value instanceof int[]) {
 545                 int[] v = (int[]) value;
 546                 for (int i = 0; i < v.length; i++) {
 547                     appendComa(i);
 548                     visitInt(v[i]);
 549                 }
 550             } else if (value instanceof long[]) {
 551                 long[] v = (long[]) value;
 552                 for (int i = 0; i < v.length; i++) {
 553                     appendComa(i);
 554                     visitLong(v[i]);
 555                 }
 556             } else if (value instanceof float[]) {
 557                 float[] v = (float[]) value;
 558                 for (int i = 0; i < v.length; i++) {
 559                     appendComa(i);
 560                     visitFloat(v[i]);
 561                 }
 562             } else if (value instanceof double[]) {
 563                 double[] v = (double[]) value;
 564                 for (int i = 0; i < v.length; i++) {
 565                     appendComa(i);
 566                     visitDouble(v[i]);
 567                 }
 568             }
 569             buf.append('}');
 570         }
 571 
 572         text.add(buf.toString());
 573     }
 574 
 575     private void visitInt(final int value) {
 576         buf.append(value);
 577     }
 578 
 579     private void visitLong(final long value) {
 580         buf.append(value).append('L');
 581     }
 582 
 583     private void visitFloat(final float value) {
 584         buf.append(value).append('F');
 585     }
 586 
 587     private void visitDouble(final double value) {
 588         buf.append(value).append('D');
 589     }
 590 
 591     private void visitChar(final char value) {
 592         buf.append("(char)").append((int) value);
 593     }
 594 
 595     private void visitShort(final short value) {
 596         buf.append("(short)").append(value);
 597     }
 598 
 599     private void visitByte(final byte value) {
 600         buf.append("(byte)").append(value);
 601     }
 602 
 603     private void visitBoolean(final boolean value) {
 604         buf.append(value);
 605     }
 606 
 607     private void visitString(final String value) {
 608         appendString(buf, value);
 609     }
 610 
 611     private void visitType(final Type value) {
 612         buf.append(value.getClassName()).append(".class");
 613     }
 614 
 615     @Override
 616     public void visitEnum(final String name, final String desc,
 617             final String value) {
 618         buf.setLength(0);
 619         appendComa(valueNumber++);
 620         if (name != null) {
 621             buf.append(name).append('=');
 622         }
 623         appendDescriptor(FIELD_DESCRIPTOR, desc);
 624         buf.append('.').append(value);
 625         text.add(buf.toString());
 626     }
 627 
 628     @Override
 629     public Textifier visitAnnotation(final String name, final String desc) {
 630         buf.setLength(0);
 631         appendComa(valueNumber++);
 632         if (name != null) {
 633             buf.append(name).append('=');
 634         }
 635         buf.append('@');
 636         appendDescriptor(FIELD_DESCRIPTOR, desc);
 637         buf.append('(');
 638         text.add(buf.toString());
 639         Textifier t = createTextifier();
 640         text.add(t.getText());
 641         text.add(")");
 642         return t;
 643     }
 644 
 645     @Override
 646     public Textifier visitArray(final String name) {
 647         buf.setLength(0);
 648         appendComa(valueNumber++);
 649         if (name != null) {
 650             buf.append(name).append('=');
 651         }
 652         buf.append('{');
 653         text.add(buf.toString());
 654         Textifier t = createTextifier();
 655         text.add(t.getText());
 656         text.add("}");
 657         return t;
 658     }
 659 
 660     @Override
 661     public void visitAnnotationEnd() {
 662     }
 663 
 664     // ------------------------------------------------------------------------
 665     // Fields
 666     // ------------------------------------------------------------------------
 667 
 668     @Override
 669     public Textifier visitFieldAnnotation(final String desc,
 670             final boolean visible) {
 671         return visitAnnotation(desc, visible);
 672     }
 673 
 674     @Override
 675     public Printer visitFieldTypeAnnotation(int typeRef, TypePath typePath,
 676             String desc, boolean visible) {
 677         return visitTypeAnnotation(typeRef, typePath, desc, visible);
 678     }
 679 
 680     @Override
 681     public void visitFieldAttribute(final Attribute attr) {
 682         visitAttribute(attr);
 683     }
 684 
 685     @Override
 686     public void visitFieldEnd() {
 687     }
 688 
 689     // ------------------------------------------------------------------------
 690     // Methods
 691     // ------------------------------------------------------------------------
 692 
 693     @Override
 694     public void visitParameter(final String name, final int access) {
 695         buf.setLength(0);
 696         buf.append(tab2).append("// parameter ");
 697         appendAccess(access);
 698         buf.append(' ').append((name == null) ? "<no name>" : name)
 699                 .append('\n');
 700         text.add(buf.toString());
 701     }
 702 
 703     @Override
 704     public Textifier visitAnnotationDefault() {
 705         text.add(tab2 + "default=");
 706         Textifier t = createTextifier();
 707         text.add(t.getText());
 708         text.add("\n");
 709         return t;
 710     }
 711 
 712     @Override
 713     public Textifier visitMethodAnnotation(final String desc,
 714             final boolean visible) {
 715         return visitAnnotation(desc, visible);
 716     }
 717 
 718     @Override
 719     public Printer visitMethodTypeAnnotation(int typeRef, TypePath typePath,
 720             String desc, boolean visible) {
 721         return visitTypeAnnotation(typeRef, typePath, desc, visible);
 722     }
 723 
 724     @Override
 725     public Textifier visitParameterAnnotation(final int parameter,
 726             final String desc, final boolean visible) {
 727         buf.setLength(0);
 728         buf.append(tab2).append('@');
 729         appendDescriptor(FIELD_DESCRIPTOR, desc);
 730         buf.append('(');
 731         text.add(buf.toString());
 732         Textifier t = createTextifier();
 733         text.add(t.getText());
 734         text.add(visible ? ") // parameter " : ") // invisible, parameter ");
 735         text.add(new Integer(parameter));
 736         text.add("\n");
 737         return t;
 738     }
 739 
 740     @Override
 741     public void visitMethodAttribute(final Attribute attr) {
 742         buf.setLength(0);
 743         buf.append(tab).append("ATTRIBUTE ");
 744         appendDescriptor(-1, attr.type);
 745 
 746         if (attr instanceof Textifiable) {
 747             ((Textifiable) attr).textify(buf, labelNames);
 748         } else {
 749             buf.append(" : unknown\n");
 750         }
 751 
 752         text.add(buf.toString());
 753     }
 754 
 755     @Override
 756     public void visitCode() {
 757     }
 758 
 759     @Override
 760     public void visitFrame(final int type, final int nLocal,
 761             final Object[] local, final int nStack, final Object[] stack) {
 762         buf.setLength(0);
 763         buf.append(ltab);
 764         buf.append("FRAME ");
 765         switch (type) {
 766         case Opcodes.F_NEW:
 767         case Opcodes.F_FULL:
 768             buf.append("FULL [");
 769             appendFrameTypes(nLocal, local);
 770             buf.append("] [");
 771             appendFrameTypes(nStack, stack);
 772             buf.append(']');
 773             break;
 774         case Opcodes.F_APPEND:
 775             buf.append("APPEND [");
 776             appendFrameTypes(nLocal, local);
 777             buf.append(']');
 778             break;
 779         case Opcodes.F_CHOP:
 780             buf.append("CHOP ").append(nLocal);
 781             break;
 782         case Opcodes.F_SAME:
 783             buf.append("SAME");
 784             break;
 785         case Opcodes.F_SAME1:
 786             buf.append("SAME1 ");
 787             appendFrameTypes(1, stack);
 788             break;
 789         }
 790         buf.append('\n');
 791         text.add(buf.toString());
 792     }
 793 
 794     @Override
 795     public void visitInsn(final int opcode) {
 796         buf.setLength(0);
 797         buf.append(tab2).append(OPCODES[opcode]).append('\n');
 798         text.add(buf.toString());
 799     }
 800 
 801     @Override
 802     public void visitIntInsn(final int opcode, final int operand) {
 803         buf.setLength(0);
 804         buf.append(tab2)
 805                 .append(OPCODES[opcode])
 806                 .append(' ')
 807                 .append(opcode == Opcodes.NEWARRAY ? TYPES[operand] : Integer
 808                         .toString(operand)).append('\n');
 809         text.add(buf.toString());
 810     }
 811 
 812     @Override
 813     public void visitVarInsn(final int opcode, final int var) {
 814         buf.setLength(0);
 815         buf.append(tab2).append(OPCODES[opcode]).append(' ').append(var)
 816                 .append('\n');
 817         text.add(buf.toString());
 818     }
 819 
 820     @Override
 821     public void visitTypeInsn(final int opcode, final String type) {
 822         buf.setLength(0);
 823         buf.append(tab2).append(OPCODES[opcode]).append(' ');
 824         appendDescriptor(INTERNAL_NAME, type);
 825         buf.append('\n');
 826         text.add(buf.toString());
 827     }
 828 
 829     @Override
 830     public void visitFieldInsn(final int opcode, final String owner,
 831             final String name, final String desc) {
 832         buf.setLength(0);
 833         buf.append(tab2).append(OPCODES[opcode]).append(' ');
 834         appendDescriptor(INTERNAL_NAME, owner);
 835         buf.append('.').append(name).append(" : ");
 836         appendDescriptor(FIELD_DESCRIPTOR, desc);
 837         buf.append('\n');
 838         text.add(buf.toString());
 839     }
 840 
 841     @Deprecated
 842     @Override
 843     public void visitMethodInsn(final int opcode, final String owner,
 844             final String name, final String desc) {
 845         if (api >= Opcodes.ASM5) {
 846             super.visitMethodInsn(opcode, owner, name, desc);
 847             return;
 848         }
 849         doVisitMethodInsn(opcode, owner, name, desc,
 850                 opcode == Opcodes.INVOKEINTERFACE);
 851     }
 852 
 853     @Override
 854     public void visitMethodInsn(final int opcode, final String owner,
 855             final String name, final String desc, final boolean itf) {
 856         if (api < Opcodes.ASM5) {
 857             super.visitMethodInsn(opcode, owner, name, desc, itf);
 858             return;
 859         }
 860         doVisitMethodInsn(opcode, owner, name, desc, itf);
 861     }
 862 
 863     private void doVisitMethodInsn(final int opcode, final String owner,
 864             final String name, final String desc, final boolean itf) {
 865         buf.setLength(0);
 866         buf.append(tab2).append(OPCODES[opcode]).append(' ');
 867         appendDescriptor(INTERNAL_NAME, owner);
 868         buf.append('.').append(name).append(' ');
 869         appendDescriptor(METHOD_DESCRIPTOR, desc);
 870         buf.append('\n');
 871         text.add(buf.toString());
 872     }
 873 
 874     @Override
 875     public void visitInvokeDynamicInsn(String name, String desc, Handle bsm,
 876             Object... bsmArgs) {
 877         buf.setLength(0);
 878         buf.append(tab2).append("INVOKEDYNAMIC").append(' ');
 879         buf.append(name);
 880         appendDescriptor(METHOD_DESCRIPTOR, desc);
 881         buf.append(" [");
 882         buf.append('\n');
 883         buf.append(tab3);
 884         appendHandle(bsm);
 885         buf.append('\n');
 886         buf.append(tab3).append("// arguments:");
 887         if (bsmArgs.length == 0) {
 888             buf.append(" none");
 889         } else {
 890             buf.append('\n');
 891             for (int i = 0; i < bsmArgs.length; i++) {
 892                 buf.append(tab3);
 893                 Object cst = bsmArgs[i];
 894                 if (cst instanceof String) {
 895                     Printer.appendString(buf, (String) cst);
 896                 } else if (cst instanceof Type) {
 897                     Type type = (Type) cst;
 898                     if(type.getSort() == Type.METHOD){
 899                         appendDescriptor(METHOD_DESCRIPTOR, type.getDescriptor());
 900                     } else {
 901                         buf.append(type.getDescriptor()).append(".class");
 902                     }
 903                 } else if (cst instanceof Handle) {
 904                     appendHandle((Handle) cst);
 905                 } else {
 906                     buf.append(cst);
 907                 }
 908                 buf.append(", \n");
 909             }
 910             buf.setLength(buf.length() - 3);
 911         }
 912         buf.append('\n');
 913         buf.append(tab2).append("]\n");
 914         text.add(buf.toString());
 915     }
 916 
 917     @Override
 918     public void visitJumpInsn(final int opcode, final Label label) {
 919         buf.setLength(0);
 920         buf.append(tab2).append(OPCODES[opcode]).append(' ');
 921         appendLabel(label);
 922         buf.append('\n');
 923         text.add(buf.toString());
 924     }
 925 
 926     @Override
 927     public void visitLabel(final Label label) {
 928         buf.setLength(0);
 929         buf.append(ltab);
 930         appendLabel(label);
 931         buf.append('\n');
 932         text.add(buf.toString());
 933     }
 934 
 935     @Override
 936     public void visitLdcInsn(final Object cst) {
 937         buf.setLength(0);
 938         buf.append(tab2).append("LDC ");
 939         if (cst instanceof String) {
 940             Printer.appendString(buf, (String) cst);
 941         } else if (cst instanceof Type) {
 942             buf.append(((Type) cst).getDescriptor()).append(".class");
 943         } else {
 944             buf.append(cst);
 945         }
 946         buf.append('\n');
 947         text.add(buf.toString());
 948     }
 949 
 950     @Override
 951     public void visitIincInsn(final int var, final int increment) {
 952         buf.setLength(0);
 953         buf.append(tab2).append("IINC ").append(var).append(' ')
 954                 .append(increment).append('\n');
 955         text.add(buf.toString());
 956     }
 957 
 958     @Override
 959     public void visitTableSwitchInsn(final int min, final int max,
 960             final Label dflt, final Label... labels) {
 961         buf.setLength(0);
 962         buf.append(tab2).append("TABLESWITCH\n");
 963         for (int i = 0; i < labels.length; ++i) {
 964             buf.append(tab3).append(min + i).append(": ");
 965             appendLabel(labels[i]);
 966             buf.append('\n');
 967         }
 968         buf.append(tab3).append("default: ");
 969         appendLabel(dflt);
 970         buf.append('\n');
 971         text.add(buf.toString());
 972     }
 973 
 974     @Override
 975     public void visitLookupSwitchInsn(final Label dflt, final int[] keys,
 976             final Label[] labels) {
 977         buf.setLength(0);
 978         buf.append(tab2).append("LOOKUPSWITCH\n");
 979         for (int i = 0; i < labels.length; ++i) {
 980             buf.append(tab3).append(keys[i]).append(": ");
 981             appendLabel(labels[i]);
 982             buf.append('\n');
 983         }
 984         buf.append(tab3).append("default: ");
 985         appendLabel(dflt);
 986         buf.append('\n');
 987         text.add(buf.toString());
 988     }
 989 
 990     @Override
 991     public void visitMultiANewArrayInsn(final String desc, final int dims) {
 992         buf.setLength(0);
 993         buf.append(tab2).append("MULTIANEWARRAY ");
 994         appendDescriptor(FIELD_DESCRIPTOR, desc);
 995         buf.append(' ').append(dims).append('\n');
 996         text.add(buf.toString());
 997     }
 998 
 999     @Override
1000     public Printer visitInsnAnnotation(int typeRef, TypePath typePath,
1001             String desc, boolean visible) {
1002         return visitTypeAnnotation(typeRef, typePath, desc, visible);
1003     }
1004 
1005     @Override
1006     public void visitTryCatchBlock(final Label start, final Label end,
1007             final Label handler, final String type) {
1008         buf.setLength(0);
1009         buf.append(tab2).append("TRYCATCHBLOCK ");
1010         appendLabel(start);
1011         buf.append(' ');
1012         appendLabel(end);
1013         buf.append(' ');
1014         appendLabel(handler);
1015         buf.append(' ');
1016         appendDescriptor(INTERNAL_NAME, type);
1017         buf.append('\n');
1018         text.add(buf.toString());
1019     }
1020 
1021     @Override
1022     public Printer visitTryCatchAnnotation(int typeRef, TypePath typePath,
1023             String desc, boolean visible) {
1024         buf.setLength(0);
1025         buf.append(tab2).append("TRYCATCHBLOCK @");
1026         appendDescriptor(FIELD_DESCRIPTOR, desc);
1027         buf.append('(');
1028         text.add(buf.toString());
1029         Textifier t = createTextifier();
1030         text.add(t.getText());
1031         buf.setLength(0);
1032         buf.append(") : ");
1033         appendTypeReference(typeRef);
1034         buf.append(", ").append(typePath);
1035         buf.append(visible ? "\n" : " // invisible\n");
1036         text.add(buf.toString());
1037         return t;
1038     }
1039 
1040     @Override
1041     public void visitLocalVariable(final String name, final String desc,
1042             final String signature, final Label start, final Label end,
1043             final int index) {
1044         buf.setLength(0);
1045         buf.append(tab2).append("LOCALVARIABLE ").append(name).append(' ');
1046         appendDescriptor(FIELD_DESCRIPTOR, desc);
1047         buf.append(' ');
1048         appendLabel(start);
1049         buf.append(' ');
1050         appendLabel(end);
1051         buf.append(' ').append(index).append('\n');
1052 
1053         if (signature != null) {
1054             buf.append(tab2);
1055             appendDescriptor(FIELD_SIGNATURE, signature);
1056 
1057             TraceSignatureVisitor sv = new TraceSignatureVisitor(0);
1058             SignatureReader r = new SignatureReader(signature);
1059             r.acceptType(sv);
1060             buf.append(tab2).append("// declaration: ")
1061                     .append(sv.getDeclaration()).append('\n');
1062         }
1063         text.add(buf.toString());
1064     }
1065 
1066     @Override
1067     public Printer visitLocalVariableAnnotation(int typeRef, TypePath typePath,
1068             Label[] start, Label[] end, int[] index, String desc,
1069             boolean visible) {
1070         buf.setLength(0);
1071         buf.append(tab2).append("LOCALVARIABLE @");
1072         appendDescriptor(FIELD_DESCRIPTOR, desc);
1073         buf.append('(');
1074         text.add(buf.toString());
1075         Textifier t = createTextifier();
1076         text.add(t.getText());
1077         buf.setLength(0);
1078         buf.append(") : ");
1079         appendTypeReference(typeRef);
1080         buf.append(", ").append(typePath);
1081         for (int i = 0; i < start.length; ++i) {
1082             buf.append(" [ ");
1083             appendLabel(start[i]);
1084             buf.append(" - ");
1085             appendLabel(end[i]);
1086             buf.append(" - ").append(index[i]).append(" ]");
1087         }
1088         buf.append(visible ? "\n" : " // invisible\n");
1089         text.add(buf.toString());
1090         return t;
1091     }
1092 
1093     @Override
1094     public void visitLineNumber(final int line, final Label start) {
1095         buf.setLength(0);
1096         buf.append(tab2).append("LINENUMBER ").append(line).append(' ');
1097         appendLabel(start);
1098         buf.append('\n');
1099         text.add(buf.toString());
1100     }
1101 
1102     @Override
1103     public void visitMaxs(final int maxStack, final int maxLocals) {
1104         buf.setLength(0);
1105         buf.append(tab2).append("MAXSTACK = ").append(maxStack).append('\n');
1106         text.add(buf.toString());
1107 
1108         buf.setLength(0);
1109         buf.append(tab2).append("MAXLOCALS = ").append(maxLocals).append('\n');
1110         text.add(buf.toString());
1111     }
1112 
1113     @Override
1114     public void visitMethodEnd() {
1115     }
1116 
1117     // ------------------------------------------------------------------------
1118     // Common methods
1119     // ------------------------------------------------------------------------
1120 
1121     /**
1122      * Prints a disassembled view of the given annotation.
1123      *
1124      * @param desc
1125      *            the class descriptor of the annotation class.
1126      * @param visible
1127      *            <tt>true</tt> if the annotation is visible at runtime.
1128      * @return a visitor to visit the annotation values.
1129      */
1130     public Textifier visitAnnotation(final String desc, final boolean visible) {
1131         buf.setLength(0);
1132         buf.append(tab).append('@');
1133         appendDescriptor(FIELD_DESCRIPTOR, desc);
1134         buf.append('(');
1135         text.add(buf.toString());
1136         Textifier t = createTextifier();
1137         text.add(t.getText());
1138         text.add(visible ? ")\n" : ") // invisible\n");
1139         return t;
1140     }
1141 
1142     /**
1143      * Prints a disassembled view of the given type annotation.
1144      *
1145      * @param typeRef
1146      *            a reference to the annotated type. See {@link TypeReference}.
1147      * @param typePath
1148      *            the path to the annotated type argument, wildcard bound, array
1149      *            element type, or static inner type within 'typeRef'. May be
1150      *            <tt>null</tt> if the annotation targets 'typeRef' as a whole.
1151      * @param desc
1152      *            the class descriptor of the annotation class.
1153      * @param visible
1154      *            <tt>true</tt> if the annotation is visible at runtime.
1155      * @return a visitor to visit the annotation values.
1156      */
1157     public Textifier visitTypeAnnotation(final int typeRef,
1158             final TypePath typePath, final String desc, final boolean visible) {
1159         buf.setLength(0);
1160         buf.append(tab).append('@');
1161         appendDescriptor(FIELD_DESCRIPTOR, desc);
1162         buf.append('(');
1163         text.add(buf.toString());
1164         Textifier t = createTextifier();
1165         text.add(t.getText());
1166         buf.setLength(0);
1167         buf.append(") : ");
1168         appendTypeReference(typeRef);
1169         buf.append(", ").append(typePath);
1170         buf.append(visible ? "\n" : " // invisible\n");
1171         text.add(buf.toString());
1172         return t;
1173     }
1174 
1175     /**
1176      * Prints a disassembled view of the given attribute.
1177      *
1178      * @param attr
1179      *            an attribute.
1180      */
1181     public void visitAttribute(final Attribute attr) {
1182         buf.setLength(0);
1183         buf.append(tab).append("ATTRIBUTE ");
1184         appendDescriptor(-1, attr.type);
1185 
1186         if (attr instanceof Textifiable) {
1187             ((Textifiable) attr).textify(buf, null);
1188         } else {
1189             buf.append(" : unknown\n");
1190         }
1191 
1192         text.add(buf.toString());
1193     }
1194 
1195     // ------------------------------------------------------------------------
1196     // Utility methods
1197     // ------------------------------------------------------------------------
1198 
1199     /**
1200      * Creates a new TraceVisitor instance.
1201      *
1202      * @return a new TraceVisitor.
1203      */
1204     protected Textifier createTextifier() {
1205         return new Textifier();
1206     }
1207 
1208     /**
1209      * Appends an internal name, a type descriptor or a type signature to
1210      * {@link #buf buf}.
1211      *
1212      * @param type
1213      *            indicates if desc is an internal name, a field descriptor, a
1214      *            method descriptor, a class signature, ...
1215      * @param desc
1216      *            an internal name, type descriptor, or type signature. May be
1217      *            <tt>null</tt>.
1218      */
1219     protected void appendDescriptor(final int type, final String desc) {
1220         if (type == CLASS_SIGNATURE || type == FIELD_SIGNATURE
1221                 || type == METHOD_SIGNATURE) {
1222             if (desc != null) {
1223                 buf.append("// signature ").append(desc).append('\n');
1224             }
1225         } else {
1226             buf.append(desc);
1227         }
1228     }
1229 
1230     /**
1231      * Appends the name of the given label to {@link #buf buf}. Creates a new
1232      * label name if the given label does not yet have one.
1233      *
1234      * @param l
1235      *            a label.
1236      */
1237     protected void appendLabel(final Label l) {
1238         if (labelNames == null) {
1239             labelNames = new HashMap<Label, String>();
1240         }
1241         String name = labelNames.get(l);
1242         if (name == null) {
1243             name = "L" + labelNames.size();
1244             labelNames.put(l, name);
1245         }
1246         buf.append(name);
1247     }
1248 
1249     /**
1250      * Appends the information about the given handle to {@link #buf buf}.
1251      *
1252      * @param h
1253      *            a handle, non null.
1254      */
1255     protected void appendHandle(final Handle h) {
1256         int tag = h.getTag();
1257         buf.append("// handle kind 0x").append(Integer.toHexString(tag))
1258                 .append(" : ");
1259         boolean isMethodHandle = false;
1260         switch (tag) {
1261         case Opcodes.H_GETFIELD:
1262             buf.append("GETFIELD");
1263             break;
1264         case Opcodes.H_GETSTATIC:
1265             buf.append("GETSTATIC");
1266             break;
1267         case Opcodes.H_PUTFIELD:
1268             buf.append("PUTFIELD");
1269             break;
1270         case Opcodes.H_PUTSTATIC:
1271             buf.append("PUTSTATIC");
1272             break;
1273         case Opcodes.H_INVOKEINTERFACE:
1274             buf.append("INVOKEINTERFACE");
1275             isMethodHandle = true;
1276             break;
1277         case Opcodes.H_INVOKESPECIAL:
1278             buf.append("INVOKESPECIAL");
1279             isMethodHandle = true;
1280             break;
1281         case Opcodes.H_INVOKESTATIC:
1282             buf.append("INVOKESTATIC");
1283             isMethodHandle = true;
1284             break;
1285         case Opcodes.H_INVOKEVIRTUAL:
1286             buf.append("INVOKEVIRTUAL");
1287             isMethodHandle = true;
1288             break;
1289         case Opcodes.H_NEWINVOKESPECIAL:
1290             buf.append("NEWINVOKESPECIAL");
1291             isMethodHandle = true;
1292             break;
1293         }
1294         buf.append('\n');
1295         buf.append(tab3);
1296         appendDescriptor(INTERNAL_NAME, h.getOwner());
1297         buf.append('.');
1298         buf.append(h.getName());
1299         if(!isMethodHandle){
1300             buf.append('(');
1301         }
1302         appendDescriptor(HANDLE_DESCRIPTOR, h.getDesc());
1303         if(!isMethodHandle){
1304             buf.append(')');
1305         }
1306     }
1307 
1308     /**
1309      * Appends a string representation of the given access modifiers to
1310      * {@link #buf buf}.
1311      *
1312      * @param access
1313      *            some access modifiers.
1314      */
1315     private void appendAccess(final int access) {
1316         if ((access & Opcodes.ACC_PUBLIC) != 0) {
1317             buf.append("public ");
1318         }
1319         if ((access & Opcodes.ACC_PRIVATE) != 0) {
1320             buf.append("private ");
1321         }
1322         if ((access & Opcodes.ACC_PROTECTED) != 0) {
1323             buf.append("protected ");
1324         }
1325         if ((access & Opcodes.ACC_FINAL) != 0) {
1326             buf.append("final ");
1327         }
1328         if ((access & Opcodes.ACC_STATIC) != 0) {
1329             buf.append("static ");
1330         }
1331         if ((access & Opcodes.ACC_SYNCHRONIZED) != 0) {
1332             buf.append("synchronized ");
1333         }
1334         if ((access & Opcodes.ACC_VOLATILE) != 0) {
1335             buf.append("volatile ");
1336         }
1337         if ((access & Opcodes.ACC_TRANSIENT) != 0) {
1338             buf.append("transient ");
1339         }
1340         if ((access & Opcodes.ACC_ABSTRACT) != 0) {
1341             buf.append("abstract ");
1342         }
1343         if ((access & Opcodes.ACC_STRICT) != 0) {
1344             buf.append("strictfp ");
1345         }
1346         if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
1347             buf.append("synthetic ");
1348         }
1349         if ((access & Opcodes.ACC_MANDATED) != 0) {
1350             buf.append("mandated ");
1351         }
1352         if ((access & Opcodes.ACC_ENUM) != 0) {
1353             buf.append("enum ");
1354         }
1355     }
1356 
1357     private void appendComa(final int i) {
1358         if (i != 0) {
1359             buf.append(", ");
1360         }
1361     }
1362 
1363     private void appendTypeReference(final int typeRef) {
1364         TypeReference ref = new TypeReference(typeRef);
1365         switch (ref.getSort()) {
1366         case TypeReference.CLASS_TYPE_PARAMETER:
1367             buf.append("CLASS_TYPE_PARAMETER ").append(
1368                     ref.getTypeParameterIndex());
1369             break;
1370         case TypeReference.METHOD_TYPE_PARAMETER:
1371             buf.append("METHOD_TYPE_PARAMETER ").append(
1372                     ref.getTypeParameterIndex());
1373             break;
1374         case TypeReference.CLASS_EXTENDS:
1375             buf.append("CLASS_EXTENDS ").append(ref.getSuperTypeIndex());
1376             break;
1377         case TypeReference.CLASS_TYPE_PARAMETER_BOUND:
1378             buf.append("CLASS_TYPE_PARAMETER_BOUND ")
1379                     .append(ref.getTypeParameterIndex()).append(", ")
1380                     .append(ref.getTypeParameterBoundIndex());
1381             break;
1382         case TypeReference.METHOD_TYPE_PARAMETER_BOUND:
1383             buf.append("METHOD_TYPE_PARAMETER_BOUND ")
1384                     .append(ref.getTypeParameterIndex()).append(", ")
1385                     .append(ref.getTypeParameterBoundIndex());
1386             break;
1387         case TypeReference.FIELD:
1388             buf.append("FIELD");
1389             break;
1390         case TypeReference.METHOD_RETURN:
1391             buf.append("METHOD_RETURN");
1392             break;
1393         case TypeReference.METHOD_RECEIVER:
1394             buf.append("METHOD_RECEIVER");
1395             break;
1396         case TypeReference.METHOD_FORMAL_PARAMETER:
1397             buf.append("METHOD_FORMAL_PARAMETER ").append(
1398                     ref.getFormalParameterIndex());
1399             break;
1400         case TypeReference.THROWS:
1401             buf.append("THROWS ").append(ref.getExceptionIndex());
1402             break;
1403         case TypeReference.LOCAL_VARIABLE:
1404             buf.append("LOCAL_VARIABLE");
1405             break;
1406         case TypeReference.RESOURCE_VARIABLE:
1407             buf.append("RESOURCE_VARIABLE");
1408             break;
1409         case TypeReference.EXCEPTION_PARAMETER:
1410             buf.append("EXCEPTION_PARAMETER ").append(
1411                     ref.getTryCatchBlockIndex());
1412             break;
1413         case TypeReference.INSTANCEOF:
1414             buf.append("INSTANCEOF");
1415             break;
1416         case TypeReference.NEW:
1417             buf.append("NEW");
1418             break;
1419         case TypeReference.CONSTRUCTOR_REFERENCE:
1420             buf.append("CONSTRUCTOR_REFERENCE");
1421             break;
1422         case TypeReference.METHOD_REFERENCE:
1423             buf.append("METHOD_REFERENCE");
1424             break;
1425         case TypeReference.CAST:
1426             buf.append("CAST ").append(ref.getTypeArgumentIndex());
1427             break;
1428         case TypeReference.CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT:
1429             buf.append("CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT ").append(
1430                     ref.getTypeArgumentIndex());
1431             break;
1432         case TypeReference.METHOD_INVOCATION_TYPE_ARGUMENT:
1433             buf.append("METHOD_INVOCATION_TYPE_ARGUMENT ").append(
1434                     ref.getTypeArgumentIndex());
1435             break;
1436         case TypeReference.CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT:
1437             buf.append("CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT ").append(
1438                     ref.getTypeArgumentIndex());
1439             break;
1440         case TypeReference.METHOD_REFERENCE_TYPE_ARGUMENT:
1441             buf.append("METHOD_REFERENCE_TYPE_ARGUMENT ").append(
1442                     ref.getTypeArgumentIndex());
1443             break;
1444         }
1445     }
1446 
1447     private void appendFrameTypes(final int n, final Object[] o) {
1448         for (int i = 0; i < n; ++i) {
1449             if (i > 0) {
1450                 buf.append(' ');
1451             }
1452             if (o[i] instanceof String) {
1453                 String desc = (String) o[i];
1454                 if (desc.startsWith("[")) {
1455                     appendDescriptor(FIELD_DESCRIPTOR, desc);
1456                 } else {
1457                     appendDescriptor(INTERNAL_NAME, desc);
1458                 }
1459             } else if (o[i] instanceof Integer) {
1460                 switch (((Integer) o[i]).intValue()) {
1461                 case 0:
1462                     appendDescriptor(FIELD_DESCRIPTOR, "T");
1463                     break;
1464                 case 1:
1465                     appendDescriptor(FIELD_DESCRIPTOR, "I");
1466                     break;
1467                 case 2:
1468                     appendDescriptor(FIELD_DESCRIPTOR, "F");
1469                     break;
1470                 case 3:
1471                     appendDescriptor(FIELD_DESCRIPTOR, "D");
1472                     break;
1473                 case 4:
1474                     appendDescriptor(FIELD_DESCRIPTOR, "J");
1475                     break;
1476                 case 5:
1477                     appendDescriptor(FIELD_DESCRIPTOR, "N");
1478                     break;
1479                 case 6:
1480                     appendDescriptor(FIELD_DESCRIPTOR, "U");
1481                     break;
1482                 }
1483             } else {
1484                 appendLabel((Label) o[i]);
1485             }
1486         }
1487     }
1488 }