src/share/classes/jdk/internal/org/objectweb/asm/util/Textifier.java

Print this page




  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.signature.SignatureReader;
  73 
  74 /**
  75  * A {@link Printer} that prints a disassembled view of the classes it visits.
  76  *
  77  * @author Eric Bruneton
  78  */
  79 public class Textifier extends Printer {
  80 
  81     /**
  82      * Constant used in {@link #appendDescriptor appendDescriptor} for internal
  83      * type names in bytecode notation.
  84      */
  85     public static final int INTERNAL_NAME = 0;
  86 
  87     /**
  88      * Constant used in {@link #appendDescriptor appendDescriptor} for field
  89      * descriptors, formatted in bytecode notation
  90      */
  91     public static final int FIELD_DESCRIPTOR = 1;


 155     protected String tab3 = "      ";
 156 
 157     /**
 158      * Tab for labels.
 159      */
 160     protected String ltab = "   ";
 161 
 162     /**
 163      * The label names. This map associate String values to Label keys.
 164      */
 165     protected Map<Label, String> labelNames;
 166 
 167     private int valueNumber = 0;
 168 
 169     /**
 170      * Constructs a new {@link Textifier}. <i>Subclasses must not use this
 171      * constructor</i>. Instead, they must use the {@link #Textifier(int)}
 172      * version.
 173      */
 174     public Textifier() {
 175         this(Opcodes.ASM4);
 176     }
 177 
 178     /**
 179      * Constructs a new {@link Textifier}.
 180      *
 181      * @param api the ASM API version implemented by this visitor. Must be one
 182      *        of {@link Opcodes#ASM4}.

 183      */
 184     protected Textifier(final int api) {
 185         super(api);
 186     }
 187 
 188     /**
 189      * Prints a disassembled view of the given class to the standard output. <p>
 190      * Usage: Textifier [-debug] &lt;binary class name or class
 191      * file name &gt;
 192      *
 193      * @param args the command line arguments.

 194      *
 195      * @throws Exception if the class cannot be found, or if an IO exception
 196      *         occurs.
 197      */
 198     public static void main(final String[] args) throws Exception {
 199         int i = 0;
 200         int flags = ClassReader.SKIP_DEBUG;
 201 
 202         boolean ok = true;
 203         if (args.length < 1 || args.length > 2) {
 204             ok = false;
 205         }
 206         if (ok && "-debug".equals(args[0])) {
 207             i = 1;
 208             flags = 0;
 209             if (args.length != 2) {
 210                 ok = false;
 211             }
 212         }
 213         if (!ok) {
 214             System.err.println("Prints a disassembled view of the given class.");

 215             System.err.println("Usage: Textifier [-debug] "
 216                     + "<fully qualified class name or class file name>");
 217             return;
 218         }
 219         ClassReader cr;
 220         if (args[i].endsWith(".class") || args[i].indexOf('\\') > -1
 221                 || args[i].indexOf('/') > -1)
 222         {
 223             cr = new ClassReader(new FileInputStream(args[i]));
 224         } else {
 225             cr = new ClassReader(args[i]);
 226         }
 227         cr.accept(new TraceClassVisitor(new PrintWriter(System.out)),
 228                 flags);
 229     }
 230 
 231     // ------------------------------------------------------------------------
 232     // Classes
 233     // ------------------------------------------------------------------------
 234 
 235     @Override
 236     public void visit(
 237         final int version,
 238         final int access,
 239         final String name,
 240         final String signature,
 241         final String superName,
 242         final String[] interfaces)
 243     {
 244         int major = version & 0xFFFF;
 245         int minor = version >>> 16;
 246         buf.setLength(0);
 247         buf.append("// class version ")
 248                 .append(major)
 249                 .append('.')
 250                 .append(minor)
 251                 .append(" (")
 252                 .append(version)
 253                 .append(")\n");
 254         if ((access & Opcodes.ACC_DEPRECATED) != 0) {
 255             buf.append("// DEPRECATED\n");
 256         }
 257         buf.append("// access flags 0x").append(Integer.toHexString(access).toUpperCase()).append('\n');

 258 
 259         appendDescriptor(CLASS_SIGNATURE, signature);
 260         if (signature != null) {
 261             TraceSignatureVisitor sv = new TraceSignatureVisitor(access);
 262             SignatureReader r = new SignatureReader(signature);
 263             r.accept(sv);
 264             buf.append("// declaration: ")
 265                     .append(name)
 266                     .append(sv.getDeclaration())
 267                     .append('\n');
 268         }
 269 
 270         appendAccess(access & ~Opcodes.ACC_SUPER);
 271         if ((access & Opcodes.ACC_ANNOTATION) != 0) {
 272             buf.append("@interface ");
 273         } else if ((access & Opcodes.ACC_INTERFACE) != 0) {
 274             buf.append("interface ");
 275         } else if ((access & Opcodes.ACC_ENUM) == 0) {
 276             buf.append("class ");
 277         }
 278         appendDescriptor(INTERNAL_NAME, name);
 279 
 280         if (superName != null && !"java/lang/Object".equals(superName)) {
 281             buf.append(" extends ");
 282             appendDescriptor(INTERNAL_NAME, superName);
 283             buf.append(' ');
 284         }
 285         if (interfaces != null && interfaces.length > 0) {
 286             buf.append(" implements ");
 287             for (int i = 0; i < interfaces.length; ++i) {
 288                 appendDescriptor(INTERNAL_NAME, interfaces[i]);
 289                 buf.append(' ');
 290             }
 291         }
 292         buf.append(" {\n\n");
 293 
 294         text.add(buf.toString());
 295     }
 296 
 297     @Override
 298     public void visitSource(final String file, final String debug) {
 299         buf.setLength(0);
 300         if (file != null) {
 301             buf.append(tab)
 302                     .append("// compiled from: ")
 303                     .append(file)
 304                     .append('\n');
 305         }
 306         if (debug != null) {
 307             buf.append(tab)
 308                     .append("// debug info: ")
 309                     .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(
 319         final String owner,
 320         final String name,
 321         final String desc)
 322     {
 323         buf.setLength(0);
 324         buf.append(tab).append("OUTERCLASS ");
 325         appendDescriptor(INTERNAL_NAME, owner);
 326         buf.append(' ');
 327         if (name != null) {
 328             buf.append(name).append(' ');
 329         }
 330         appendDescriptor(METHOD_DESCRIPTOR, desc);
 331         buf.append('\n');
 332         text.add(buf.toString());
 333     }
 334 
 335     @Override
 336     public Textifier visitClassAnnotation(
 337         final String desc,
 338         final boolean visible)
 339     {
 340         text.add("\n");
 341         return visitAnnotation(desc, visible);
 342     }
 343 
 344     @Override







 345     public void visitClassAttribute(final Attribute attr) {
 346         text.add("\n");
 347         visitAttribute(attr);
 348     }
 349 
 350     @Override
 351     public void visitInnerClass(
 352         final String name,
 353         final String outerName,
 354         final String innerName,
 355         final int access)
 356     {
 357         buf.setLength(0);
 358         buf.append(tab).append("// access flags 0x");
 359         buf.append(Integer.toHexString(access & ~Opcodes.ACC_SUPER).toUpperCase()).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(
 374         final int access,
 375         final String name,
 376         final String desc,
 377         final String signature,
 378         final Object value)
 379     {
 380         buf.setLength(0);
 381         buf.append('\n');
 382         if ((access & Opcodes.ACC_DEPRECATED) != 0) {
 383             buf.append(tab).append("// DEPRECATED\n");
 384         }
 385         buf.append(tab).append("// access flags 0x").append(Integer.toHexString(access).toUpperCase()).append('\n');

 386         if (signature != null) {
 387             buf.append(tab);
 388             appendDescriptor(FIELD_SIGNATURE, signature);
 389 
 390             TraceSignatureVisitor sv = new TraceSignatureVisitor(0);
 391             SignatureReader r = new SignatureReader(signature);
 392             r.acceptType(sv);
 393             buf.append(tab)
 394                     .append("// declaration: ")
 395                     .append(sv.getDeclaration())
 396                     .append('\n');
 397         }
 398 
 399         buf.append(tab);
 400         appendAccess(access);
 401 
 402         appendDescriptor(FIELD_DESCRIPTOR, desc);
 403         buf.append(' ').append(name);
 404         if (value != null) {
 405             buf.append(" = ");
 406             if (value instanceof String) {
 407                 buf.append('\"').append(value).append('\"');
 408             } else {
 409                 buf.append(value);
 410             }
 411         }
 412 
 413         buf.append('\n');
 414         text.add(buf.toString());
 415 
 416         Textifier t = createTextifier();
 417         text.add(t.getText());
 418         return t;
 419     }
 420 
 421     @Override
 422     public Textifier visitMethod(
 423         final int access,
 424         final String name,
 425         final String desc,
 426         final String signature,
 427         final String[] exceptions)
 428     {
 429         buf.setLength(0);
 430         buf.append('\n');
 431         if ((access & Opcodes.ACC_DEPRECATED) != 0) {
 432             buf.append(tab).append("// DEPRECATED\n");
 433         }
 434         buf.append(tab).append("// access flags 0x").append(Integer.toHexString(access).toUpperCase()).append('\n');

 435 
 436         if (signature != null) {
 437             buf.append(tab);
 438             appendDescriptor(METHOD_SIGNATURE, signature);
 439 
 440             TraceSignatureVisitor v = new TraceSignatureVisitor(0);
 441             SignatureReader r = new SignatureReader(signature);
 442             r.accept(v);
 443             String genericDecl = v.getDeclaration();
 444             String genericReturn = v.getReturnType();
 445             String genericExceptions = v.getExceptions();
 446 
 447             buf.append(tab)
 448                     .append("// declaration: ")
 449                     .append(genericReturn)
 450                     .append(' ')
 451                     .append(name)
 452                     .append(genericDecl);
 453             if (genericExceptions != null) {
 454                 buf.append(" throws ").append(genericExceptions);
 455             }
 456             buf.append('\n');
 457         }
 458 
 459         buf.append(tab);
 460         appendAccess(access);
 461         if ((access & Opcodes.ACC_NATIVE) != 0) {
 462             buf.append("native ");
 463         }
 464         if ((access & Opcodes.ACC_VARARGS) != 0) {
 465             buf.append("varargs ");
 466         }
 467         if ((access & Opcodes.ACC_BRIDGE) != 0) {
 468             buf.append("bridge ");
 469         }
 470 
 471         buf.append(name);
 472         appendDescriptor(METHOD_DESCRIPTOR, desc);


 605         buf.append("(short)").append(value);
 606     }
 607 
 608     private void visitByte(final byte value) {
 609         buf.append("(byte)").append(value);
 610     }
 611 
 612     private void visitBoolean(final boolean value) {
 613         buf.append(value);
 614     }
 615 
 616     private void visitString(final String value) {
 617         appendString(buf, value);
 618     }
 619 
 620     private void visitType(final Type value) {
 621         buf.append(value.getClassName()).append(".class");
 622     }
 623 
 624     @Override
 625     public void visitEnum(
 626         final String name,
 627         final String desc,
 628         final String value)
 629     {
 630         buf.setLength(0);
 631         appendComa(valueNumber++);
 632         if (name != null) {
 633             buf.append(name).append('=');
 634         }
 635         appendDescriptor(FIELD_DESCRIPTOR, desc);
 636         buf.append('.').append(value);
 637         text.add(buf.toString());
 638     }
 639 
 640     @Override
 641     public Textifier visitAnnotation(
 642         final String name,
 643         final String desc)
 644     {
 645         buf.setLength(0);
 646         appendComa(valueNumber++);
 647         if (name != null) {
 648             buf.append(name).append('=');
 649         }
 650         buf.append('@');
 651         appendDescriptor(FIELD_DESCRIPTOR, desc);
 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 Textifier visitArray(
 662         final String name)
 663     {
 664         buf.setLength(0);
 665         appendComa(valueNumber++);
 666         if (name != null) {
 667             buf.append(name).append('=');
 668         }
 669         buf.append('{');
 670         text.add(buf.toString());
 671         Textifier t = createTextifier();
 672         text.add(t.getText());
 673         text.add("}");
 674         return t;
 675     }
 676 
 677     @Override
 678     public void visitAnnotationEnd() {
 679     }
 680 
 681     // ------------------------------------------------------------------------
 682     // Fields
 683     // ------------------------------------------------------------------------
 684 
 685     @Override
 686     public Textifier visitFieldAnnotation(
 687         final String desc,
 688         final boolean visible)
 689     {
 690         return visitAnnotation(desc, visible);
 691     }
 692 
 693     @Override






 694     public void visitFieldAttribute(final Attribute attr) {
 695         visitAttribute(attr);
 696     }
 697 
 698     @Override
 699     public void visitFieldEnd() {
 700     }
 701 
 702     // ------------------------------------------------------------------------
 703     // Methods
 704     // ------------------------------------------------------------------------
 705 
 706     @Override










 707     public Textifier visitAnnotationDefault() {
 708         text.add(tab2 + "default=");
 709         Textifier t = createTextifier();
 710         text.add(t.getText());
 711         text.add("\n");
 712         return t;
 713     }
 714 
 715     @Override
 716     public Textifier visitMethodAnnotation(
 717         final String desc,
 718         final boolean visible)
 719     {
 720         return visitAnnotation(desc, visible);
 721     }
 722 
 723     @Override
 724     public Textifier visitParameterAnnotation(
 725         final int parameter,
 726         final String desc,
 727         final boolean visible)
 728     {



 729         buf.setLength(0);
 730         buf.append(tab2).append('@');
 731         appendDescriptor(FIELD_DESCRIPTOR, desc);
 732         buf.append('(');
 733         text.add(buf.toString());
 734         Textifier t = createTextifier();
 735         text.add(t.getText());
 736         text.add(visible ? ") // parameter " : ") // invisible, parameter ");
 737         text.add(new Integer(parameter));
 738         text.add("\n");
 739         return t;
 740     }
 741 
 742     @Override
 743     public void visitMethodAttribute(final Attribute attr) {
 744         buf.setLength(0);
 745         buf.append(tab).append("ATTRIBUTE ");
 746         appendDescriptor(-1, attr.type);
 747 
 748         if (attr instanceof Textifiable) {
 749             ((Textifiable) attr).textify(buf, labelNames);
 750         } else {
 751             buf.append(" : unknown\n");
 752         }
 753 
 754         text.add(buf.toString());
 755     }
 756 
 757     @Override
 758     public void visitCode() {
 759     }
 760 
 761     @Override
 762     public void visitFrame(
 763         final int type,
 764         final int nLocal,
 765         final Object[] local,
 766         final int nStack,
 767         final Object[] stack)
 768     {
 769         buf.setLength(0);
 770         buf.append(ltab);
 771         buf.append("FRAME ");
 772         switch (type) {
 773             case Opcodes.F_NEW:
 774             case Opcodes.F_FULL:
 775                 buf.append("FULL [");
 776                 appendFrameTypes(nLocal, local);
 777                 buf.append("] [");
 778                 appendFrameTypes(nStack, stack);
 779                 buf.append(']');
 780                 break;
 781             case Opcodes.F_APPEND:
 782                 buf.append("APPEND [");
 783                 appendFrameTypes(nLocal, local);
 784                 buf.append(']');
 785                 break;
 786             case Opcodes.F_CHOP:
 787                 buf.append("CHOP ").append(nLocal);
 788                 break;


 794                 appendFrameTypes(1, stack);
 795                 break;
 796         }
 797         buf.append('\n');
 798         text.add(buf.toString());
 799     }
 800 
 801     @Override
 802     public void visitInsn(final int opcode) {
 803         buf.setLength(0);
 804         buf.append(tab2).append(OPCODES[opcode]).append('\n');
 805         text.add(buf.toString());
 806     }
 807 
 808     @Override
 809     public void visitIntInsn(final int opcode, final int operand) {
 810         buf.setLength(0);
 811         buf.append(tab2)
 812                 .append(OPCODES[opcode])
 813                 .append(' ')
 814                 .append(opcode == Opcodes.NEWARRAY
 815                         ? TYPES[operand]
 816                         : Integer.toString(operand))
 817                 .append('\n');
 818         text.add(buf.toString());
 819     }
 820 
 821     @Override
 822     public void visitVarInsn(final int opcode, final int var) {
 823         buf.setLength(0);
 824         buf.append(tab2)
 825                 .append(OPCODES[opcode])
 826                 .append(' ')
 827                 .append(var)
 828                 .append('\n');
 829         text.add(buf.toString());
 830     }
 831 
 832     @Override
 833     public void visitTypeInsn(final int opcode, final String type) {
 834         buf.setLength(0);
 835         buf.append(tab2).append(OPCODES[opcode]).append(' ');
 836         appendDescriptor(INTERNAL_NAME, type);
 837         buf.append('\n');
 838         text.add(buf.toString());
 839     }
 840 
 841     @Override
 842     public void visitFieldInsn(
 843         final int opcode,
 844         final String owner,
 845         final String name,
 846         final String desc)
 847     {
 848         buf.setLength(0);
 849         buf.append(tab2).append(OPCODES[opcode]).append(' ');
 850         appendDescriptor(INTERNAL_NAME, owner);
 851         buf.append('.').append(name).append(" : ");
 852         appendDescriptor(FIELD_DESCRIPTOR, desc);
 853         buf.append('\n');
 854         text.add(buf.toString());
 855     }
 856 
 857     @Override
 858     public void visitMethodInsn(
 859         final int opcode,
 860         final String owner,
 861         final String name,
 862         final String desc)
 863     {
 864         buf.setLength(0);
 865         buf.append(tab2).append(OPCODES[opcode]).append(' ');
 866         appendDescriptor(INTERNAL_NAME, owner);
 867         buf.append('.').append(name).append(' ');
 868         appendDescriptor(METHOD_DESCRIPTOR, desc);
 869         buf.append('\n');
 870         text.add(buf.toString());
 871     }
 872 
 873     @Override
 874     public void visitInvokeDynamicInsn(
 875         String name,
 876         String desc,
 877         Handle bsm,
 878         Object... bsmArgs)
 879     {
 880         buf.setLength(0);
 881         buf.append(tab2).append("INVOKEDYNAMIC").append(' ');
 882         buf.append(name);
 883         appendDescriptor(METHOD_DESCRIPTOR, desc);
 884         buf.append(" [");
 885         appendHandle(bsm);
 886         buf.append(tab3).append("// arguments:");
 887         if(bsmArgs.length == 0) {
 888             buf.append(" none");
 889         } else {
 890             buf.append('\n').append(tab3);
 891             for(int i = 0; i < bsmArgs.length; i++) {
 892                 Object cst = bsmArgs[i];
 893                 if (cst instanceof String) {
 894                     Printer.appendString(buf, (String) cst);
 895                 } else if (cst instanceof Type) {
 896                     buf.append(((Type) cst).getDescriptor()).append(".class");
 897                 } else if (cst instanceof Handle) {
 898                     appendHandle((Handle) cst);
 899                 } else {
 900                     buf.append(cst);
 901                 }
 902                 buf.append(", ");
 903             }
 904             buf.setLength(buf.length() - 2);
 905         }
 906         buf.append('\n');
 907         buf.append(tab2).append("]\n");
 908         text.add(buf.toString());
 909     }
 910 
 911     @Override


 927     }
 928 
 929     @Override
 930     public void visitLdcInsn(final Object cst) {
 931         buf.setLength(0);
 932         buf.append(tab2).append("LDC ");
 933         if (cst instanceof String) {
 934             Printer.appendString(buf, (String) cst);
 935         } else if (cst instanceof Type) {
 936             buf.append(((Type) cst).getDescriptor()).append(".class");
 937         } else {
 938             buf.append(cst);
 939         }
 940         buf.append('\n');
 941         text.add(buf.toString());
 942     }
 943 
 944     @Override
 945     public void visitIincInsn(final int var, final int increment) {
 946         buf.setLength(0);
 947         buf.append(tab2)
 948                 .append("IINC ")
 949                 .append(var)
 950                 .append(' ')
 951                 .append(increment)
 952                 .append('\n');
 953         text.add(buf.toString());
 954     }
 955 
 956     @Override
 957     public void visitTableSwitchInsn(
 958         final int min,
 959         final int max,
 960         final Label dflt,
 961         final Label... labels)
 962     {
 963         buf.setLength(0);
 964         buf.append(tab2).append("TABLESWITCH\n");
 965         for (int i = 0; i < labels.length; ++i) {
 966             buf.append(tab3).append(min + i).append(": ");
 967             appendLabel(labels[i]);
 968             buf.append('\n');
 969         }
 970         buf.append(tab3).append("default: ");
 971         appendLabel(dflt);
 972         buf.append('\n');
 973         text.add(buf.toString());
 974     }
 975 
 976     @Override
 977     public void visitLookupSwitchInsn(
 978         final Label dflt,
 979         final int[] keys,
 980         final Label[] labels)
 981     {
 982         buf.setLength(0);
 983         buf.append(tab2).append("LOOKUPSWITCH\n");
 984         for (int i = 0; i < labels.length; ++i) {
 985             buf.append(tab3).append(keys[i]).append(": ");
 986             appendLabel(labels[i]);
 987             buf.append('\n');
 988         }
 989         buf.append(tab3).append("default: ");
 990         appendLabel(dflt);
 991         buf.append('\n');
 992         text.add(buf.toString());
 993     }
 994 
 995     @Override
 996     public void visitMultiANewArrayInsn(final String desc, final int dims) {
 997         buf.setLength(0);
 998         buf.append(tab2).append("MULTIANEWARRAY ");
 999         appendDescriptor(FIELD_DESCRIPTOR, desc);
1000         buf.append(' ').append(dims).append('\n');
1001         text.add(buf.toString());
1002     }
1003 
1004     @Override
1005     public void visitTryCatchBlock(
1006         final Label start,
1007         final Label end,
1008         final Label handler,
1009         final String type)
1010     {


1011         buf.setLength(0);
1012         buf.append(tab2).append("TRYCATCHBLOCK ");
1013         appendLabel(start);
1014         buf.append(' ');
1015         appendLabel(end);
1016         buf.append(' ');
1017         appendLabel(handler);
1018         buf.append(' ');
1019         appendDescriptor(INTERNAL_NAME, type);
1020         buf.append('\n');
1021         text.add(buf.toString());
1022     }
1023 
1024     @Override
1025     public void visitLocalVariable(
1026         final String name,
1027         final String desc,
1028         final String signature,
1029         final Label start,
1030         final Label end,
1031         final int index)
1032     {
1033         buf.setLength(0);




















1034         buf.append(tab2).append("LOCALVARIABLE ").append(name).append(' ');
1035         appendDescriptor(FIELD_DESCRIPTOR, desc);
1036         buf.append(' ');
1037         appendLabel(start);
1038         buf.append(' ');
1039         appendLabel(end);
1040         buf.append(' ').append(index).append('\n');
1041 
1042         if (signature != null) {
1043             buf.append(tab2);
1044             appendDescriptor(FIELD_SIGNATURE, signature);
1045 
1046             TraceSignatureVisitor sv = new TraceSignatureVisitor(0);
1047             SignatureReader r = new SignatureReader(signature);
1048             r.acceptType(sv);
1049             buf.append(tab2)
1050                     .append("// declaration: ")
1051                     .append(sv.getDeclaration())
1052                     .append('\n');
1053         }
1054         text.add(buf.toString());
1055     }
1056 
1057     @Override



























1058     public void visitLineNumber(final int line, final Label start) {
1059         buf.setLength(0);
1060         buf.append(tab2).append("LINENUMBER ").append(line).append(' ');
1061         appendLabel(start);
1062         buf.append('\n');
1063         text.add(buf.toString());
1064     }
1065 
1066     @Override
1067     public void visitMaxs(final int maxStack, final int maxLocals) {
1068         buf.setLength(0);
1069         buf.append(tab2).append("MAXSTACK = ").append(maxStack).append('\n');
1070         text.add(buf.toString());
1071 
1072         buf.setLength(0);
1073         buf.append(tab2).append("MAXLOCALS = ").append(maxLocals).append('\n');
1074         text.add(buf.toString());
1075     }
1076 
1077     @Override
1078     public void visitMethodEnd() {
1079     }
1080 
1081     // ------------------------------------------------------------------------
1082     // Common methods
1083     // ------------------------------------------------------------------------
1084 
1085     /**
1086      * Prints a disassembled view of the given annotation.
1087      *
1088      * @param desc the class descriptor of the annotation class.
1089      * @param visible <tt>true</tt> if the annotation is visible at runtime.


1090      * @return a visitor to visit the annotation values.
1091      */
1092     public Textifier visitAnnotation(
1093         final String desc,
1094         final boolean visible)
1095     {
1096         buf.setLength(0);
1097         buf.append(tab).append('@');
1098         appendDescriptor(FIELD_DESCRIPTOR, desc);
1099         buf.append('(');
1100         text.add(buf.toString());
1101         Textifier t = createTextifier();
1102         text.add(t.getText());
1103         text.add(visible ? ")\n" : ") // invisible\n");
1104         return t;
1105     }
1106 
1107     /**

































1108      * Prints a disassembled view of the given attribute.
1109      *
1110      * @param attr an attribute.

1111      */
1112     public void visitAttribute(final Attribute attr) {
1113         buf.setLength(0);
1114         buf.append(tab).append("ATTRIBUTE ");
1115         appendDescriptor(-1, attr.type);
1116 
1117         if (attr instanceof Textifiable) {
1118             ((Textifiable) attr).textify(buf, null);
1119         } else {
1120             buf.append(" : unknown\n");
1121         }
1122 
1123         text.add(buf.toString());
1124     }
1125 
1126     // ------------------------------------------------------------------------
1127     // Utility methods
1128     // ------------------------------------------------------------------------
1129 
1130     /**
1131      * Creates a new TraceVisitor instance.
1132      *
1133      * @return a new TraceVisitor.
1134      */
1135     protected Textifier createTextifier() {
1136         return new Textifier();
1137     }
1138 
1139     /**
1140      * Appends an internal name, a type descriptor or a type signature to
1141      * {@link #buf buf}.
1142      *
1143      * @param type indicates if desc is an internal name, a field descriptor, a

1144      *        method descriptor, a class signature, ...
1145      * @param desc an internal name, type descriptor, or type signature. May be

1146      *        <tt>null</tt>.
1147      */
1148     protected void appendDescriptor(final int type, final String desc) {
1149         if (type == CLASS_SIGNATURE || type == FIELD_SIGNATURE
1150                 || type == METHOD_SIGNATURE)
1151         {
1152             if (desc != null) {
1153                 buf.append("// signature ").append(desc).append('\n');
1154             }
1155         } else {
1156             buf.append(desc);
1157         }
1158     }
1159 
1160     /**
1161      * Appends the name of the given label to {@link #buf buf}. Creates a new
1162      * label name if the given label does not yet have one.
1163      *
1164      * @param l a label.

1165      */
1166     protected void appendLabel(final Label l) {
1167         if (labelNames == null) {
1168             labelNames = new HashMap<Label, String>();
1169         }
1170         String name = labelNames.get(l);
1171         if (name == null) {
1172             name = "L" + labelNames.size();
1173             labelNames.put(l, name);
1174         }
1175         buf.append(name);
1176     }
1177 
1178     /**
1179      * Appends the information about the given handle to {@link #buf buf}.
1180      *
1181      * @param h a handle, non null.

1182      */
1183     protected void appendHandle(final Handle h) {
1184         buf.append('\n').append(tab3);
1185         int tag = h.getTag();
1186         buf.append("// handle kind 0x").append(Integer.toHexString(tag)).append(" : ");

1187         switch (tag) {
1188             case Opcodes.H_GETFIELD:
1189                 buf.append("GETFIELD");
1190                 break;
1191             case Opcodes.H_GETSTATIC:
1192                 buf.append("GETSTATIC");
1193                 break;
1194             case Opcodes.H_PUTFIELD:
1195                 buf.append("PUTFIELD");
1196                 break;
1197             case Opcodes.H_PUTSTATIC:
1198                 buf.append("PUTSTATIC");
1199                 break;
1200             case Opcodes.H_INVOKEINTERFACE:
1201                 buf.append("INVOKEINTERFACE");
1202                 break;
1203             case Opcodes.H_INVOKESPECIAL:
1204                 buf.append("INVOKESPECIAL");
1205                 break;
1206             case Opcodes.H_INVOKESTATIC:
1207                 buf.append("INVOKESTATIC");
1208                 break;
1209             case Opcodes.H_INVOKEVIRTUAL:
1210                 buf.append("INVOKEVIRTUAL");
1211                 break;
1212             case Opcodes.H_NEWINVOKESPECIAL:
1213                 buf.append("NEWINVOKESPECIAL");
1214                 break;
1215         }
1216         buf.append('\n');
1217         buf.append(tab3);
1218         appendDescriptor(INTERNAL_NAME, h.getOwner());
1219         buf.append('.');
1220         buf.append(h.getName());
1221         buf.append('(');
1222         appendDescriptor(HANDLE_DESCRIPTOR, h.getDesc());
1223         buf.append(')').append('\n');
1224     }
1225 
1226     /**
1227      * Appends a string representation of the given access modifiers to {@link
1228      * #buf buf}.
1229      *
1230      * @param access some access modifiers.

1231      */
1232     private void appendAccess(final int access) {
1233         if ((access & Opcodes.ACC_PUBLIC) != 0) {
1234             buf.append("public ");
1235         }
1236         if ((access & Opcodes.ACC_PRIVATE) != 0) {
1237             buf.append("private ");
1238         }
1239         if ((access & Opcodes.ACC_PROTECTED) != 0) {
1240             buf.append("protected ");
1241         }
1242         if ((access & Opcodes.ACC_FINAL) != 0) {
1243             buf.append("final ");
1244         }
1245         if ((access & Opcodes.ACC_STATIC) != 0) {
1246             buf.append("static ");
1247         }
1248         if ((access & Opcodes.ACC_SYNCHRONIZED) != 0) {
1249             buf.append("synchronized ");
1250         }
1251         if ((access & Opcodes.ACC_VOLATILE) != 0) {
1252             buf.append("volatile ");
1253         }
1254         if ((access & Opcodes.ACC_TRANSIENT) != 0) {
1255             buf.append("transient ");
1256         }
1257         if ((access & Opcodes.ACC_ABSTRACT) != 0) {
1258             buf.append("abstract ");
1259         }
1260         if ((access & Opcodes.ACC_STRICT) != 0) {
1261             buf.append("strictfp ");
1262         }






1263         if ((access & Opcodes.ACC_ENUM) != 0) {
1264             buf.append("enum ");
1265         }
1266     }
1267 
1268     private void appendComa(final int i) {
1269         if (i != 0) {
1270             buf.append(", ");
1271         }
1272     }




















































































1273 
1274     private void appendFrameTypes(final int n, final Object[] o) {
1275         for (int i = 0; i < n; ++i) {
1276             if (i > 0) {
1277                 buf.append(' ');
1278             }
1279             if (o[i] instanceof String) {
1280                 String desc = (String) o[i];
1281                 if (desc.startsWith("[")) {
1282                     appendDescriptor(FIELD_DESCRIPTOR, desc);
1283                 } else {
1284                     appendDescriptor(INTERNAL_NAME, desc);
1285                 }
1286             } else if (o[i] instanceof Integer) {
1287                 switch (((Integer) o[i]).intValue()) {
1288                     case 0:
1289                         appendDescriptor(FIELD_DESCRIPTOR, "T");
1290                         break;
1291                     case 1:
1292                         appendDescriptor(FIELD_DESCRIPTOR, "I");




  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;


 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     private int valueNumber = 0;
 170 
 171     /**
 172      * Constructs a new {@link Textifier}. <i>Subclasses must not use this
 173      * constructor</i>. Instead, they must use the {@link #Textifier(int)}
 174      * version.
 175      */
 176     public Textifier() {
 177         this(Opcodes.ASM5);
 178     }
 179 
 180     /**
 181      * Constructs a new {@link Textifier}.
 182      *
 183      * @param api
 184      *            the ASM API version implemented by this visitor. Must be one
 185      *            of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}.
 186      */
 187     protected Textifier(final int api) {
 188         super(api);
 189     }
 190 
 191     /**
 192      * Prints a disassembled view of the given class to the standard output.
 193      * <p>
 194      * Usage: Textifier [-debug] &lt;binary class name or class file name &gt;
 195      *
 196      * @param args
 197      *            the command line arguments.
 198      *
 199      * @throws Exception
 200      *             if the class cannot be found, or if an IO exception occurs.
 201      */
 202     public static void main(final String[] args) throws Exception {
 203         int i = 0;
 204         int flags = ClassReader.SKIP_DEBUG;
 205 
 206         boolean ok = true;
 207         if (args.length < 1 || args.length > 2) {
 208             ok = false;
 209         }
 210         if (ok && "-debug".equals(args[0])) {
 211             i = 1;
 212             flags = 0;
 213             if (args.length != 2) {
 214                 ok = false;
 215             }
 216         }
 217         if (!ok) {
 218             System.err
 219                     .println("Prints a disassembled view of the given class.");
 220             System.err.println("Usage: Textifier [-debug] "
 221                     + "<fully qualified class name or class file name>");
 222             return;
 223         }
 224         ClassReader cr;
 225         if (args[i].endsWith(".class") || args[i].indexOf('\\') > -1
 226                 || args[i].indexOf('/') > -1) {

 227             cr = new ClassReader(new FileInputStream(args[i]));
 228         } else {
 229             cr = new ClassReader(args[i]);
 230         }
 231         cr.accept(new TraceClassVisitor(new PrintWriter(System.out)), flags);

 232     }
 233 
 234     // ------------------------------------------------------------------------
 235     // Classes
 236     // ------------------------------------------------------------------------
 237 
 238     @Override
 239     public void visit(final int version, final int access, final String name,
 240             final String signature, final String superName,
 241             final String[] interfaces) {





 242         int major = version & 0xFFFF;
 243         int minor = version >>> 16;
 244         buf.setLength(0);
 245         buf.append("// class version ").append(major).append('.').append(minor)
 246                 .append(" (").append(version).append(")\n");





 247         if ((access & Opcodes.ACC_DEPRECATED) != 0) {
 248             buf.append("// DEPRECATED\n");
 249         }
 250         buf.append("// access flags 0x")
 251                 .append(Integer.toHexString(access).toUpperCase()).append('\n');
 252 
 253         appendDescriptor(CLASS_SIGNATURE, signature);
 254         if (signature != null) {
 255             TraceSignatureVisitor sv = new TraceSignatureVisitor(access);
 256             SignatureReader r = new SignatureReader(signature);
 257             r.accept(sv);
 258             buf.append("// declaration: ").append(name)
 259                     .append(sv.getDeclaration()).append('\n');


 260         }
 261 
 262         appendAccess(access & ~Opcodes.ACC_SUPER);
 263         if ((access & Opcodes.ACC_ANNOTATION) != 0) {
 264             buf.append("@interface ");
 265         } else if ((access & Opcodes.ACC_INTERFACE) != 0) {
 266             buf.append("interface ");
 267         } else if ((access & Opcodes.ACC_ENUM) == 0) {
 268             buf.append("class ");
 269         }
 270         appendDescriptor(INTERNAL_NAME, name);
 271 
 272         if (superName != null && !"java/lang/Object".equals(superName)) {
 273             buf.append(" extends ");
 274             appendDescriptor(INTERNAL_NAME, superName);
 275             buf.append(' ');
 276         }
 277         if (interfaces != null && interfaces.length > 0) {
 278             buf.append(" implements ");
 279             for (int i = 0; i < interfaces.length; ++i) {
 280                 appendDescriptor(INTERNAL_NAME, interfaces[i]);
 281                 buf.append(' ');
 282             }
 283         }
 284         buf.append(" {\n\n");
 285 
 286         text.add(buf.toString());
 287     }
 288 
 289     @Override
 290     public void visitSource(final String file, final String debug) {
 291         buf.setLength(0);
 292         if (file != null) {
 293             buf.append(tab).append("// compiled from: ").append(file)


 294                     .append('\n');
 295         }
 296         if (debug != null) {
 297             buf.append(tab).append("// debug info: ").append(debug)


 298                     .append('\n');
 299         }
 300         if (buf.length() > 0) {
 301             text.add(buf.toString());
 302         }
 303     }
 304 
 305     @Override
 306     public void visitOuterClass(final String owner, final String name,
 307             final String desc) {



 308         buf.setLength(0);
 309         buf.append(tab).append("OUTERCLASS ");
 310         appendDescriptor(INTERNAL_NAME, owner);
 311         buf.append(' ');
 312         if (name != null) {
 313             buf.append(name).append(' ');
 314         }
 315         appendDescriptor(METHOD_DESCRIPTOR, desc);
 316         buf.append('\n');
 317         text.add(buf.toString());
 318     }
 319 
 320     @Override
 321     public Textifier visitClassAnnotation(final String desc,
 322             final boolean visible) {


 323         text.add("\n");
 324         return visitAnnotation(desc, visible);
 325     }
 326 
 327     @Override
 328     public Printer visitClassTypeAnnotation(int typeRef, TypePath typePath,
 329             String desc, boolean visible) {
 330         text.add("\n");
 331         return visitTypeAnnotation(typeRef, typePath, desc, visible);
 332     }
 333 
 334     @Override
 335     public void visitClassAttribute(final Attribute attr) {
 336         text.add("\n");
 337         visitAttribute(attr);
 338     }
 339 
 340     @Override
 341     public void visitInnerClass(final String name, final String outerName,
 342             final String innerName, final int access) {




 343         buf.setLength(0);
 344         buf.append(tab).append("// access flags 0x");
 345         buf.append(
 346                 Integer.toHexString(access & ~Opcodes.ACC_SUPER).toUpperCase())
 347                 .append('\n');
 348         buf.append(tab);
 349         appendAccess(access);
 350         buf.append("INNERCLASS ");
 351         appendDescriptor(INTERNAL_NAME, name);
 352         buf.append(' ');
 353         appendDescriptor(INTERNAL_NAME, outerName);
 354         buf.append(' ');
 355         appendDescriptor(INTERNAL_NAME, innerName);
 356         buf.append('\n');
 357         text.add(buf.toString());
 358     }
 359 
 360     @Override
 361     public Textifier visitField(final int access, final String name,
 362             final String desc, final String signature, final Object value) {





 363         buf.setLength(0);
 364         buf.append('\n');
 365         if ((access & Opcodes.ACC_DEPRECATED) != 0) {
 366             buf.append(tab).append("// DEPRECATED\n");
 367         }
 368         buf.append(tab).append("// access flags 0x")
 369                 .append(Integer.toHexString(access).toUpperCase()).append('\n');
 370         if (signature != null) {
 371             buf.append(tab);
 372             appendDescriptor(FIELD_SIGNATURE, signature);
 373 
 374             TraceSignatureVisitor sv = new TraceSignatureVisitor(0);
 375             SignatureReader r = new SignatureReader(signature);
 376             r.acceptType(sv);
 377             buf.append(tab).append("// declaration: ")
 378                     .append(sv.getDeclaration()).append('\n');


 379         }
 380 
 381         buf.append(tab);
 382         appendAccess(access);
 383 
 384         appendDescriptor(FIELD_DESCRIPTOR, desc);
 385         buf.append(' ').append(name);
 386         if (value != null) {
 387             buf.append(" = ");
 388             if (value instanceof String) {
 389                 buf.append('\"').append(value).append('\"');
 390             } else {
 391                 buf.append(value);
 392             }
 393         }
 394 
 395         buf.append('\n');
 396         text.add(buf.toString());
 397 
 398         Textifier t = createTextifier();
 399         text.add(t.getText());
 400         return t;
 401     }
 402 
 403     @Override
 404     public Textifier visitMethod(final int access, final String name,
 405             final String desc, final String signature, final String[] exceptions) {





 406         buf.setLength(0);
 407         buf.append('\n');
 408         if ((access & Opcodes.ACC_DEPRECATED) != 0) {
 409             buf.append(tab).append("// DEPRECATED\n");
 410         }
 411         buf.append(tab).append("// access flags 0x")
 412                 .append(Integer.toHexString(access).toUpperCase()).append('\n');
 413 
 414         if (signature != null) {
 415             buf.append(tab);
 416             appendDescriptor(METHOD_SIGNATURE, signature);
 417 
 418             TraceSignatureVisitor v = new TraceSignatureVisitor(0);
 419             SignatureReader r = new SignatureReader(signature);
 420             r.accept(v);
 421             String genericDecl = v.getDeclaration();
 422             String genericReturn = v.getReturnType();
 423             String genericExceptions = v.getExceptions();
 424 
 425             buf.append(tab).append("// declaration: ").append(genericReturn)
 426                     .append(' ').append(name).append(genericDecl);




 427             if (genericExceptions != null) {
 428                 buf.append(" throws ").append(genericExceptions);
 429             }
 430             buf.append('\n');
 431         }
 432 
 433         buf.append(tab);
 434         appendAccess(access);
 435         if ((access & Opcodes.ACC_NATIVE) != 0) {
 436             buf.append("native ");
 437         }
 438         if ((access & Opcodes.ACC_VARARGS) != 0) {
 439             buf.append("varargs ");
 440         }
 441         if ((access & Opcodes.ACC_BRIDGE) != 0) {
 442             buf.append("bridge ");
 443         }
 444 
 445         buf.append(name);
 446         appendDescriptor(METHOD_DESCRIPTOR, desc);


 579         buf.append("(short)").append(value);
 580     }
 581 
 582     private void visitByte(final byte value) {
 583         buf.append("(byte)").append(value);
 584     }
 585 
 586     private void visitBoolean(final boolean value) {
 587         buf.append(value);
 588     }
 589 
 590     private void visitString(final String value) {
 591         appendString(buf, value);
 592     }
 593 
 594     private void visitType(final Type value) {
 595         buf.append(value.getClassName()).append(".class");
 596     }
 597 
 598     @Override
 599     public void visitEnum(final String name, final String desc,
 600             final String value) {



 601         buf.setLength(0);
 602         appendComa(valueNumber++);
 603         if (name != null) {
 604             buf.append(name).append('=');
 605         }
 606         appendDescriptor(FIELD_DESCRIPTOR, desc);
 607         buf.append('.').append(value);
 608         text.add(buf.toString());
 609     }
 610 
 611     @Override
 612     public Textifier visitAnnotation(final String name, final String desc) {



 613         buf.setLength(0);
 614         appendComa(valueNumber++);
 615         if (name != null) {
 616             buf.append(name).append('=');
 617         }
 618         buf.append('@');
 619         appendDescriptor(FIELD_DESCRIPTOR, desc);
 620         buf.append('(');
 621         text.add(buf.toString());
 622         Textifier t = createTextifier();
 623         text.add(t.getText());
 624         text.add(")");
 625         return t;
 626     }
 627 
 628     @Override
 629     public Textifier visitArray(final String name) {


 630         buf.setLength(0);
 631         appendComa(valueNumber++);
 632         if (name != null) {
 633             buf.append(name).append('=');
 634         }
 635         buf.append('{');
 636         text.add(buf.toString());
 637         Textifier t = createTextifier();
 638         text.add(t.getText());
 639         text.add("}");
 640         return t;
 641     }
 642 
 643     @Override
 644     public void visitAnnotationEnd() {
 645     }
 646 
 647     // ------------------------------------------------------------------------
 648     // Fields
 649     // ------------------------------------------------------------------------
 650 
 651     @Override
 652     public Textifier visitFieldAnnotation(final String desc,
 653             final boolean visible) {


 654         return visitAnnotation(desc, visible);
 655     }
 656 
 657     @Override
 658     public Printer visitFieldTypeAnnotation(int typeRef, TypePath typePath,
 659             String desc, boolean visible) {
 660         return visitTypeAnnotation(typeRef, typePath, desc, visible);
 661     }
 662 
 663     @Override
 664     public void visitFieldAttribute(final Attribute attr) {
 665         visitAttribute(attr);
 666     }
 667 
 668     @Override
 669     public void visitFieldEnd() {
 670     }
 671 
 672     // ------------------------------------------------------------------------
 673     // Methods
 674     // ------------------------------------------------------------------------
 675 
 676     @Override
 677     public void visitParameter(final String name, final int access) {
 678         buf.setLength(0);
 679         buf.append(tab2).append("// parameter ");
 680         appendAccess(access);
 681         buf.append(' ').append((name == null) ? "<no name>" : name)
 682                 .append('\n');
 683         text.add(buf.toString());
 684     }
 685 
 686     @Override
 687     public Textifier visitAnnotationDefault() {
 688         text.add(tab2 + "default=");
 689         Textifier t = createTextifier();
 690         text.add(t.getText());
 691         text.add("\n");
 692         return t;
 693     }
 694 
 695     @Override
 696     public Textifier visitMethodAnnotation(final String desc,
 697             final boolean visible) {


 698         return visitAnnotation(desc, visible);
 699     }
 700 
 701     @Override
 702     public Printer visitMethodTypeAnnotation(int typeRef, TypePath typePath,
 703             String desc, boolean visible) {
 704         return visitTypeAnnotation(typeRef, typePath, desc, visible);
 705     }
 706 
 707     @Override
 708     public Textifier visitParameterAnnotation(final int parameter,
 709             final String desc, final boolean visible) {
 710         buf.setLength(0);
 711         buf.append(tab2).append('@');
 712         appendDescriptor(FIELD_DESCRIPTOR, desc);
 713         buf.append('(');
 714         text.add(buf.toString());
 715         Textifier t = createTextifier();
 716         text.add(t.getText());
 717         text.add(visible ? ") // parameter " : ") // invisible, parameter ");
 718         text.add(new Integer(parameter));
 719         text.add("\n");
 720         return t;
 721     }
 722 
 723     @Override
 724     public void visitMethodAttribute(final Attribute attr) {
 725         buf.setLength(0);
 726         buf.append(tab).append("ATTRIBUTE ");
 727         appendDescriptor(-1, attr.type);
 728 
 729         if (attr instanceof Textifiable) {
 730             ((Textifiable) attr).textify(buf, labelNames);
 731         } else {
 732             buf.append(" : unknown\n");
 733         }
 734 
 735         text.add(buf.toString());
 736     }
 737 
 738     @Override
 739     public void visitCode() {
 740     }
 741 
 742     @Override
 743     public void visitFrame(final int type, final int nLocal,
 744             final Object[] local, final int nStack, final Object[] stack) {





 745         buf.setLength(0);
 746         buf.append(ltab);
 747         buf.append("FRAME ");
 748         switch (type) {
 749         case Opcodes.F_NEW:
 750         case Opcodes.F_FULL:
 751             buf.append("FULL [");
 752             appendFrameTypes(nLocal, local);
 753             buf.append("] [");
 754             appendFrameTypes(nStack, stack);
 755             buf.append(']');
 756             break;
 757         case Opcodes.F_APPEND:
 758             buf.append("APPEND [");
 759             appendFrameTypes(nLocal, local);
 760             buf.append(']');
 761             break;
 762         case Opcodes.F_CHOP:
 763             buf.append("CHOP ").append(nLocal);
 764             break;


 770             appendFrameTypes(1, stack);
 771             break;
 772         }
 773         buf.append('\n');
 774         text.add(buf.toString());
 775     }
 776 
 777     @Override
 778     public void visitInsn(final int opcode) {
 779         buf.setLength(0);
 780         buf.append(tab2).append(OPCODES[opcode]).append('\n');
 781         text.add(buf.toString());
 782     }
 783 
 784     @Override
 785     public void visitIntInsn(final int opcode, final int operand) {
 786         buf.setLength(0);
 787         buf.append(tab2)
 788                 .append(OPCODES[opcode])
 789                 .append(' ')
 790                 .append(opcode == Opcodes.NEWARRAY ? TYPES[operand] : Integer
 791                         .toString(operand)).append('\n');


 792         text.add(buf.toString());
 793     }
 794 
 795     @Override
 796     public void visitVarInsn(final int opcode, final int var) {
 797         buf.setLength(0);
 798         buf.append(tab2).append(OPCODES[opcode]).append(' ').append(var)



 799                 .append('\n');
 800         text.add(buf.toString());
 801     }
 802 
 803     @Override
 804     public void visitTypeInsn(final int opcode, final String type) {
 805         buf.setLength(0);
 806         buf.append(tab2).append(OPCODES[opcode]).append(' ');
 807         appendDescriptor(INTERNAL_NAME, type);
 808         buf.append('\n');
 809         text.add(buf.toString());
 810     }
 811 
 812     @Override
 813     public void visitFieldInsn(final int opcode, final String owner,
 814             final String name, final String desc) {




 815         buf.setLength(0);
 816         buf.append(tab2).append(OPCODES[opcode]).append(' ');
 817         appendDescriptor(INTERNAL_NAME, owner);
 818         buf.append('.').append(name).append(" : ");
 819         appendDescriptor(FIELD_DESCRIPTOR, desc);
 820         buf.append('\n');
 821         text.add(buf.toString());
 822     }
 823 
 824     @Override
 825     public void visitMethodInsn(final int opcode, final String owner,
 826             final String name, final String desc) {




 827         buf.setLength(0);
 828         buf.append(tab2).append(OPCODES[opcode]).append(' ');
 829         appendDescriptor(INTERNAL_NAME, owner);
 830         buf.append('.').append(name).append(' ');
 831         appendDescriptor(METHOD_DESCRIPTOR, desc);
 832         buf.append('\n');
 833         text.add(buf.toString());
 834     }
 835 
 836     @Override
 837     public void visitInvokeDynamicInsn(String name, String desc, Handle bsm,
 838             Object... bsmArgs) {




 839         buf.setLength(0);
 840         buf.append(tab2).append("INVOKEDYNAMIC").append(' ');
 841         buf.append(name);
 842         appendDescriptor(METHOD_DESCRIPTOR, desc);
 843         buf.append(" [");
 844         appendHandle(bsm);
 845         buf.append(tab3).append("// arguments:");
 846         if (bsmArgs.length == 0) {
 847             buf.append(" none");
 848         } else {
 849             buf.append('\n').append(tab3);
 850             for (int i = 0; i < bsmArgs.length; i++) {
 851                 Object cst = bsmArgs[i];
 852                 if (cst instanceof String) {
 853                     Printer.appendString(buf, (String) cst);
 854                 } else if (cst instanceof Type) {
 855                     buf.append(((Type) cst).getDescriptor()).append(".class");
 856                 } else if (cst instanceof Handle) {
 857                     appendHandle((Handle) cst);
 858                 } else {
 859                     buf.append(cst);
 860                 }
 861                 buf.append(", ");
 862             }
 863             buf.setLength(buf.length() - 2);
 864         }
 865         buf.append('\n');
 866         buf.append(tab2).append("]\n");
 867         text.add(buf.toString());
 868     }
 869 
 870     @Override


 886     }
 887 
 888     @Override
 889     public void visitLdcInsn(final Object cst) {
 890         buf.setLength(0);
 891         buf.append(tab2).append("LDC ");
 892         if (cst instanceof String) {
 893             Printer.appendString(buf, (String) cst);
 894         } else if (cst instanceof Type) {
 895             buf.append(((Type) cst).getDescriptor()).append(".class");
 896         } else {
 897             buf.append(cst);
 898         }
 899         buf.append('\n');
 900         text.add(buf.toString());
 901     }
 902 
 903     @Override
 904     public void visitIincInsn(final int var, final int increment) {
 905         buf.setLength(0);
 906         buf.append(tab2).append("IINC ").append(var).append(' ')
 907                 .append(increment).append('\n');




 908         text.add(buf.toString());
 909     }
 910 
 911     @Override
 912     public void visitTableSwitchInsn(final int min, final int max,
 913             final Label dflt, final Label... labels) {




 914         buf.setLength(0);
 915         buf.append(tab2).append("TABLESWITCH\n");
 916         for (int i = 0; i < labels.length; ++i) {
 917             buf.append(tab3).append(min + i).append(": ");
 918             appendLabel(labels[i]);
 919             buf.append('\n');
 920         }
 921         buf.append(tab3).append("default: ");
 922         appendLabel(dflt);
 923         buf.append('\n');
 924         text.add(buf.toString());
 925     }
 926 
 927     @Override
 928     public void visitLookupSwitchInsn(final Label dflt, final int[] keys,
 929             final Label[] labels) {



 930         buf.setLength(0);
 931         buf.append(tab2).append("LOOKUPSWITCH\n");
 932         for (int i = 0; i < labels.length; ++i) {
 933             buf.append(tab3).append(keys[i]).append(": ");
 934             appendLabel(labels[i]);
 935             buf.append('\n');
 936         }
 937         buf.append(tab3).append("default: ");
 938         appendLabel(dflt);
 939         buf.append('\n');
 940         text.add(buf.toString());
 941     }
 942 
 943     @Override
 944     public void visitMultiANewArrayInsn(final String desc, final int dims) {
 945         buf.setLength(0);
 946         buf.append(tab2).append("MULTIANEWARRAY ");
 947         appendDescriptor(FIELD_DESCRIPTOR, desc);
 948         buf.append(' ').append(dims).append('\n');
 949         text.add(buf.toString());
 950     }
 951 
 952     @Override
 953     public Printer visitInsnAnnotation(int typeRef, TypePath typePath,
 954             String desc, boolean visible) {
 955         return visitTypeAnnotation(typeRef, typePath, desc, visible);
 956     }
 957 
 958     @Override
 959     public void visitTryCatchBlock(final Label start, final Label end,
 960             final Label handler, final String type) {
 961         buf.setLength(0);
 962         buf.append(tab2).append("TRYCATCHBLOCK ");
 963         appendLabel(start);
 964         buf.append(' ');
 965         appendLabel(end);
 966         buf.append(' ');
 967         appendLabel(handler);
 968         buf.append(' ');
 969         appendDescriptor(INTERNAL_NAME, type);
 970         buf.append('\n');
 971         text.add(buf.toString());
 972     }
 973 
 974     @Override
 975     public Printer visitTryCatchAnnotation(int typeRef, TypePath typePath,
 976             String desc, boolean visible) {






 977         buf.setLength(0);
 978         buf.append(tab2).append("TRYCATCHBLOCK @");
 979         appendDescriptor(FIELD_DESCRIPTOR, desc);
 980         buf.append('(');
 981         text.add(buf.toString());
 982         Textifier t = createTextifier();
 983         text.add(t.getText());
 984         buf.setLength(0);
 985         buf.append(") : ");
 986         appendTypeReference(typeRef);
 987         buf.append(", ").append(typePath);
 988         buf.append(visible ? "\n" : " // invisible\n");
 989         text.add(buf.toString());
 990         return t;
 991     }
 992 
 993     @Override
 994     public void visitLocalVariable(final String name, final String desc,
 995             final String signature, final Label start, final Label end,
 996             final int index) {
 997         buf.setLength(0);
 998         buf.append(tab2).append("LOCALVARIABLE ").append(name).append(' ');
 999         appendDescriptor(FIELD_DESCRIPTOR, desc);
1000         buf.append(' ');
1001         appendLabel(start);
1002         buf.append(' ');
1003         appendLabel(end);
1004         buf.append(' ').append(index).append('\n');
1005 
1006         if (signature != null) {
1007             buf.append(tab2);
1008             appendDescriptor(FIELD_SIGNATURE, signature);
1009 
1010             TraceSignatureVisitor sv = new TraceSignatureVisitor(0);
1011             SignatureReader r = new SignatureReader(signature);
1012             r.acceptType(sv);
1013             buf.append(tab2).append("// declaration: ")
1014                     .append(sv.getDeclaration()).append('\n');


1015         }
1016         text.add(buf.toString());
1017     }
1018 
1019     @Override
1020     public Printer visitLocalVariableAnnotation(int typeRef, TypePath typePath,
1021             Label[] start, Label[] end, int[] index, String desc,
1022             boolean visible) {
1023         buf.setLength(0);
1024         buf.append(tab2).append("LOCALVARIABLE @");
1025         appendDescriptor(FIELD_DESCRIPTOR, desc);
1026         buf.append('(');
1027         text.add(buf.toString());
1028         Textifier t = createTextifier();
1029         text.add(t.getText());
1030         buf.setLength(0);
1031         buf.append(") : ");
1032         appendTypeReference(typeRef);
1033         buf.append(", ").append(typePath);
1034         for (int i = 0; i < start.length; ++i) {
1035             buf.append(" [ ");
1036             appendLabel(start[i]);
1037             buf.append(" - ");
1038             appendLabel(end[i]);
1039             buf.append(" - ").append(index[i]).append(" ]");
1040         }
1041         buf.append(visible ? "\n" : " // invisible\n");
1042         text.add(buf.toString());
1043         return t;
1044     }
1045 
1046     @Override
1047     public void visitLineNumber(final int line, final Label start) {
1048         buf.setLength(0);
1049         buf.append(tab2).append("LINENUMBER ").append(line).append(' ');
1050         appendLabel(start);
1051         buf.append('\n');
1052         text.add(buf.toString());
1053     }
1054 
1055     @Override
1056     public void visitMaxs(final int maxStack, final int maxLocals) {
1057         buf.setLength(0);
1058         buf.append(tab2).append("MAXSTACK = ").append(maxStack).append('\n');
1059         text.add(buf.toString());
1060 
1061         buf.setLength(0);
1062         buf.append(tab2).append("MAXLOCALS = ").append(maxLocals).append('\n');
1063         text.add(buf.toString());
1064     }
1065 
1066     @Override
1067     public void visitMethodEnd() {
1068     }
1069 
1070     // ------------------------------------------------------------------------
1071     // Common methods
1072     // ------------------------------------------------------------------------
1073 
1074     /**
1075      * Prints a disassembled view of the given annotation.
1076      *
1077      * @param desc
1078      *            the class descriptor of the annotation class.
1079      * @param visible
1080      *            <tt>true</tt> if the annotation is visible at runtime.
1081      * @return a visitor to visit the annotation values.
1082      */
1083     public Textifier visitAnnotation(final String desc, final boolean visible) {



1084         buf.setLength(0);
1085         buf.append(tab).append('@');
1086         appendDescriptor(FIELD_DESCRIPTOR, desc);
1087         buf.append('(');
1088         text.add(buf.toString());
1089         Textifier t = createTextifier();
1090         text.add(t.getText());
1091         text.add(visible ? ")\n" : ") // invisible\n");
1092         return t;
1093     }
1094 
1095     /**
1096      * Prints a disassembled view of the given type annotation.
1097      *
1098      * @param typeRef
1099      *            a reference to the annotated type. See {@link TypeReference}.
1100      * @param typePath
1101      *            the path to the annotated type argument, wildcard bound, array
1102      *            element type, or static inner type within 'typeRef'. May be
1103      *            <tt>null</tt> if the annotation targets 'typeRef' as a whole.
1104      * @param desc
1105      *            the class descriptor of the annotation class.
1106      * @param visible
1107      *            <tt>true</tt> if the annotation is visible at runtime.
1108      * @return a visitor to visit the annotation values.
1109      */
1110     public Textifier visitTypeAnnotation(final int typeRef,
1111             final TypePath typePath, final String desc, final boolean visible) {
1112         buf.setLength(0);
1113         buf.append(tab).append('@');
1114         appendDescriptor(FIELD_DESCRIPTOR, desc);
1115         buf.append('(');
1116         text.add(buf.toString());
1117         Textifier t = createTextifier();
1118         text.add(t.getText());
1119         buf.setLength(0);
1120         buf.append(") : ");
1121         appendTypeReference(typeRef);
1122         buf.append(", ").append(typePath);
1123         buf.append(visible ? "\n" : " // invisible\n");
1124         text.add(buf.toString());
1125         return t;
1126     }
1127 
1128     /**
1129      * Prints a disassembled view of the given attribute.
1130      *
1131      * @param attr
1132      *            an attribute.
1133      */
1134     public void visitAttribute(final Attribute attr) {
1135         buf.setLength(0);
1136         buf.append(tab).append("ATTRIBUTE ");
1137         appendDescriptor(-1, attr.type);
1138 
1139         if (attr instanceof Textifiable) {
1140             ((Textifiable) attr).textify(buf, null);
1141         } else {
1142             buf.append(" : unknown\n");
1143         }
1144 
1145         text.add(buf.toString());
1146     }
1147 
1148     // ------------------------------------------------------------------------
1149     // Utility methods
1150     // ------------------------------------------------------------------------
1151 
1152     /**
1153      * Creates a new TraceVisitor instance.
1154      *
1155      * @return a new TraceVisitor.
1156      */
1157     protected Textifier createTextifier() {
1158         return new Textifier();
1159     }
1160 
1161     /**
1162      * Appends an internal name, a type descriptor or a type signature to
1163      * {@link #buf buf}.
1164      *
1165      * @param type
1166      *            indicates if desc is an internal name, a field descriptor, a
1167      *            method descriptor, a class signature, ...
1168      * @param desc
1169      *            an internal name, type descriptor, or type signature. May be
1170      *            <tt>null</tt>.
1171      */
1172     protected void appendDescriptor(final int type, final String desc) {
1173         if (type == CLASS_SIGNATURE || type == FIELD_SIGNATURE
1174                 || type == METHOD_SIGNATURE) {

1175             if (desc != null) {
1176                 buf.append("// signature ").append(desc).append('\n');
1177             }
1178         } else {
1179             buf.append(desc);
1180         }
1181     }
1182 
1183     /**
1184      * Appends the name of the given label to {@link #buf buf}. Creates a new
1185      * label name if the given label does not yet have one.
1186      *
1187      * @param l
1188      *            a label.
1189      */
1190     protected void appendLabel(final Label l) {
1191         if (labelNames == null) {
1192             labelNames = new HashMap<Label, String>();
1193         }
1194         String name = labelNames.get(l);
1195         if (name == null) {
1196             name = "L" + labelNames.size();
1197             labelNames.put(l, name);
1198         }
1199         buf.append(name);
1200     }
1201 
1202     /**
1203      * Appends the information about the given handle to {@link #buf buf}.
1204      *
1205      * @param h
1206      *            a handle, non null.
1207      */
1208     protected void appendHandle(final Handle h) {
1209         buf.append('\n').append(tab3);
1210         int tag = h.getTag();
1211         buf.append("// handle kind 0x").append(Integer.toHexString(tag))
1212                 .append(" : ");
1213         switch (tag) {
1214         case Opcodes.H_GETFIELD:
1215             buf.append("GETFIELD");
1216             break;
1217         case Opcodes.H_GETSTATIC:
1218             buf.append("GETSTATIC");
1219             break;
1220         case Opcodes.H_PUTFIELD:
1221             buf.append("PUTFIELD");
1222             break;
1223         case Opcodes.H_PUTSTATIC:
1224             buf.append("PUTSTATIC");
1225             break;
1226         case Opcodes.H_INVOKEINTERFACE:
1227             buf.append("INVOKEINTERFACE");
1228             break;
1229         case Opcodes.H_INVOKESPECIAL:
1230             buf.append("INVOKESPECIAL");
1231             break;
1232         case Opcodes.H_INVOKESTATIC:
1233             buf.append("INVOKESTATIC");
1234             break;
1235         case Opcodes.H_INVOKEVIRTUAL:
1236             buf.append("INVOKEVIRTUAL");
1237             break;
1238         case Opcodes.H_NEWINVOKESPECIAL:
1239             buf.append("NEWINVOKESPECIAL");
1240             break;
1241         }
1242         buf.append('\n');
1243         buf.append(tab3);
1244         appendDescriptor(INTERNAL_NAME, h.getOwner());
1245         buf.append('.');
1246         buf.append(h.getName());
1247         buf.append('(');
1248         appendDescriptor(HANDLE_DESCRIPTOR, h.getDesc());
1249         buf.append(')').append('\n');
1250     }
1251 
1252     /**
1253      * Appends a string representation of the given access modifiers to
1254      * {@link #buf buf}.
1255      *
1256      * @param access
1257      *            some access modifiers.
1258      */
1259     private void appendAccess(final int access) {
1260         if ((access & Opcodes.ACC_PUBLIC) != 0) {
1261             buf.append("public ");
1262         }
1263         if ((access & Opcodes.ACC_PRIVATE) != 0) {
1264             buf.append("private ");
1265         }
1266         if ((access & Opcodes.ACC_PROTECTED) != 0) {
1267             buf.append("protected ");
1268         }
1269         if ((access & Opcodes.ACC_FINAL) != 0) {
1270             buf.append("final ");
1271         }
1272         if ((access & Opcodes.ACC_STATIC) != 0) {
1273             buf.append("static ");
1274         }
1275         if ((access & Opcodes.ACC_SYNCHRONIZED) != 0) {
1276             buf.append("synchronized ");
1277         }
1278         if ((access & Opcodes.ACC_VOLATILE) != 0) {
1279             buf.append("volatile ");
1280         }
1281         if ((access & Opcodes.ACC_TRANSIENT) != 0) {
1282             buf.append("transient ");
1283         }
1284         if ((access & Opcodes.ACC_ABSTRACT) != 0) {
1285             buf.append("abstract ");
1286         }
1287         if ((access & Opcodes.ACC_STRICT) != 0) {
1288             buf.append("strictfp ");
1289         }
1290         if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
1291             buf.append("synthetic ");
1292         }
1293         if ((access & Opcodes.ACC_MANDATED) != 0) {
1294             buf.append("mandated ");
1295         }
1296         if ((access & Opcodes.ACC_ENUM) != 0) {
1297             buf.append("enum ");
1298         }
1299     }
1300 
1301     private void appendComa(final int i) {
1302         if (i != 0) {
1303             buf.append(", ");
1304         }
1305     }
1306 
1307     private void appendTypeReference(final int typeRef) {
1308         TypeReference ref = new TypeReference(typeRef);
1309         switch (ref.getSort()) {
1310         case TypeReference.CLASS_TYPE_PARAMETER:
1311             buf.append("CLASS_TYPE_PARAMETER ").append(
1312                     ref.getTypeParameterIndex());
1313             break;
1314         case TypeReference.METHOD_TYPE_PARAMETER:
1315             buf.append("METHOD_TYPE_PARAMETER ").append(
1316                     ref.getTypeParameterIndex());
1317             break;
1318         case TypeReference.CLASS_EXTENDS:
1319             buf.append("CLASS_EXTENDS ").append(ref.getSuperTypeIndex());
1320             break;
1321         case TypeReference.CLASS_TYPE_PARAMETER_BOUND:
1322             buf.append("CLASS_TYPE_PARAMETER_BOUND ")
1323                     .append(ref.getTypeParameterIndex()).append(", ")
1324                     .append(ref.getTypeParameterBoundIndex());
1325             break;
1326         case TypeReference.METHOD_TYPE_PARAMETER_BOUND:
1327             buf.append("METHOD_TYPE_PARAMETER_BOUND ")
1328                     .append(ref.getTypeParameterIndex()).append(", ")
1329                     .append(ref.getTypeParameterBoundIndex());
1330             break;
1331         case TypeReference.FIELD:
1332             buf.append("FIELD");
1333             break;
1334         case TypeReference.METHOD_RETURN:
1335             buf.append("METHOD_RETURN");
1336             break;
1337         case TypeReference.METHOD_RECEIVER:
1338             buf.append("METHOD_RECEIVER");
1339             break;
1340         case TypeReference.METHOD_FORMAL_PARAMETER:
1341             buf.append("METHOD_FORMAL_PARAMETER ").append(
1342                     ref.getFormalParameterIndex());
1343             break;
1344         case TypeReference.THROWS:
1345             buf.append("THROWS ").append(ref.getExceptionIndex());
1346             break;
1347         case TypeReference.LOCAL_VARIABLE:
1348             buf.append("LOCAL_VARIABLE");
1349             break;
1350         case TypeReference.RESOURCE_VARIABLE:
1351             buf.append("RESOURCE_VARIABLE");
1352             break;
1353         case TypeReference.EXCEPTION_PARAMETER:
1354             buf.append("EXCEPTION_PARAMETER ").append(
1355                     ref.getTryCatchBlockIndex());
1356             break;
1357         case TypeReference.INSTANCEOF:
1358             buf.append("INSTANCEOF");
1359             break;
1360         case TypeReference.NEW:
1361             buf.append("NEW");
1362             break;
1363         case TypeReference.CONSTRUCTOR_REFERENCE:
1364             buf.append("CONSTRUCTOR_REFERENCE");
1365             break;
1366         case TypeReference.METHOD_REFERENCE:
1367             buf.append("METHOD_REFERENCE");
1368             break;
1369         case TypeReference.CAST:
1370             buf.append("CAST ").append(ref.getTypeArgumentIndex());
1371             break;
1372         case TypeReference.CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT:
1373             buf.append("CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT ").append(
1374                     ref.getTypeArgumentIndex());
1375             break;
1376         case TypeReference.METHOD_INVOCATION_TYPE_ARGUMENT:
1377             buf.append("METHOD_INVOCATION_TYPE_ARGUMENT ").append(
1378                     ref.getTypeArgumentIndex());
1379             break;
1380         case TypeReference.CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT:
1381             buf.append("CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT ").append(
1382                     ref.getTypeArgumentIndex());
1383             break;
1384         case TypeReference.METHOD_REFERENCE_TYPE_ARGUMENT:
1385             buf.append("METHOD_REFERENCE_TYPE_ARGUMENT ").append(
1386                     ref.getTypeArgumentIndex());
1387             break;
1388         }
1389     }
1390 
1391     private void appendFrameTypes(final int n, final Object[] o) {
1392         for (int i = 0; i < n; ++i) {
1393             if (i > 0) {
1394                 buf.append(' ');
1395             }
1396             if (o[i] instanceof String) {
1397                 String desc = (String) o[i];
1398                 if (desc.startsWith("[")) {
1399                     appendDescriptor(FIELD_DESCRIPTOR, desc);
1400                 } else {
1401                     appendDescriptor(INTERNAL_NAME, desc);
1402                 }
1403             } else if (o[i] instanceof Integer) {
1404                 switch (((Integer) o[i]).intValue()) {
1405                 case 0:
1406                     appendDescriptor(FIELD_DESCRIPTOR, "T");
1407                     break;
1408                 case 1:
1409                     appendDescriptor(FIELD_DESCRIPTOR, "I");