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 */
59package jdk.internal.org.objectweb.asm.util;
60
61import java.io.FileInputStream;
62import java.io.PrintWriter;
63import java.util.HashMap;
64import java.util.Map;
65
66import jdk.internal.org.objectweb.asm.Attribute;
67import jdk.internal.org.objectweb.asm.ClassReader;
68import jdk.internal.org.objectweb.asm.Handle;
69import jdk.internal.org.objectweb.asm.Label;
70import jdk.internal.org.objectweb.asm.Opcodes;
71import jdk.internal.org.objectweb.asm.Type;
72import jdk.internal.org.objectweb.asm.TypePath;
73import jdk.internal.org.objectweb.asm.TypeReference;
74import 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 */
81public 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(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}
1489