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;
73
74/**
75 * A {@link Printer} that prints the ASM code to generate the classes if visits.
76 *
77 * @author Eric Bruneton
78 */
79public class ASMifier extends Printer {
80
81    /**
82     * The name of the visitor variable in the produced code.
83     */
84    protected final String name;
85
86    /**
87     * Identifier of the annotation visitor variable in the produced code.
88     */
89    protected final int id;
90
91    /**
92     * The label names. This map associates String values to Label keys. It is
93     * used only in ASMifierMethodVisitor.
94     */
95    protected Map<Label, String> labelNames;
96
97    /**
98     * Pseudo access flag used to distinguish class access flags.
99     */
100    private static final int ACCESS_CLASS = 262144;
101
102    /**
103     * Pseudo access flag used to distinguish field access flags.
104     */
105    private static final int ACCESS_FIELD = 524288;
106
107    /**
108     * Pseudo access flag used to distinguish inner class flags.
109     */
110    private static final int ACCESS_INNER = 1048576;
111
112    /**
113     * Constructs a new {@link ASMifier}. <i>Subclasses must not use this
114     * constructor</i>. Instead, they must use the
115     * {@link #ASMifier(int, String, int)} version.
116     *
117     * @throws IllegalStateException
118     *             If a subclass calls this constructor.
119     */
120    public ASMifier() {
121        this(Opcodes.ASM5, "cw", 0);
122        if (getClass() != ASMifier.class) {
123            throw new IllegalStateException();
124        }
125    }
126
127    /**
128     * Constructs a new {@link ASMifier}.
129     *
130     * @param api
131     *            the ASM API version implemented by this class. Must be one of
132     *            {@link Opcodes#ASM4} or {@link Opcodes#ASM5}.
133     * @param name
134     *            the name of the visitor variable in the produced code.
135     * @param id
136     *            identifier of the annotation visitor variable in the produced
137     *            code.
138     */
139    protected ASMifier(final int api, final String name, final int id) {
140        super(api);
141        this.name = name;
142        this.id = id;
143    }
144
145    /**
146     * Prints the ASM source code to generate the given class to the standard
147     * output.
148     * <p>
149     * Usage: ASMifier [-debug] &lt;binary class name or class file name&gt;
150     *
151     * @param args
152     *            the command line arguments.
153     *
154     * @throws Exception
155     *             if the class cannot be found, or if an IO exception occurs.
156     */
157    public static void main(final String[] args) throws Exception {
158        int i = 0;
159        int flags = ClassReader.SKIP_DEBUG;
160
161        boolean ok = true;
162        if (args.length < 1 || args.length > 2) {
163            ok = false;
164        }
165        if (ok && "-debug".equals(args[0])) {
166            i = 1;
167            flags = 0;
168            if (args.length != 2) {
169                ok = false;
170            }
171        }
172        if (!ok) {
173            System.err
174                    .println("Prints the ASM code to generate the given class.");
175            System.err.println("Usage: ASMifier [-debug] "
176                    + "<fully qualified class name or class file name>");
177            return;
178        }
179        ClassReader cr;
180        if (args[i].endsWith(".class") || args[i].indexOf('\\') > -1
181                || args[i].indexOf('/') > -1) {
182            cr = new ClassReader(new FileInputStream(args[i]));
183        } else {
184            cr = new ClassReader(args[i]);
185        }
186        cr.accept(new TraceClassVisitor(null, new ASMifier(), new PrintWriter(
187                System.out)), flags);
188    }
189
190    // ------------------------------------------------------------------------
191    // Classes
192    // ------------------------------------------------------------------------
193
194    @Override
195    public void visit(final int version, final int access, final String name,
196            final String signature, final String superName,
197            final String[] interfaces) {
198        String simpleName;
199        int n = name.lastIndexOf('/');
200        if (n == -1) {
201            simpleName = name;
202        } else {
203            text.add("package asm." + name.substring(0, n).replace('/', '.')
204                    + ";\n");
205            simpleName = name.substring(n + 1);
206        }
207        text.add("import java.util.*;\n");
208        text.add("import jdk.internal.org.objectweb.asm.*;\n");
209        text.add("public class " + simpleName + "Dump implements Opcodes {\n\n");
210        text.add("public static byte[] dump () throws Exception {\n\n");
211        text.add("ClassWriter cw = new ClassWriter(0);\n");
212        text.add("FieldVisitor fv;\n");
213        text.add("MethodVisitor mv;\n");
214        text.add("AnnotationVisitor av0;\n\n");
215
216        buf.setLength(0);
217        buf.append("cw.visit(");
218        switch (version) {
219        case Opcodes.V1_1:
220            buf.append("V1_1");
221            break;
222        case Opcodes.V1_2:
223            buf.append("V1_2");
224            break;
225        case Opcodes.V1_3:
226            buf.append("V1_3");
227            break;
228        case Opcodes.V1_4:
229            buf.append("V1_4");
230            break;
231        case Opcodes.V1_5:
232            buf.append("V1_5");
233            break;
234        case Opcodes.V1_6:
235            buf.append("V1_6");
236            break;
237        case Opcodes.V1_7:
238            buf.append("V1_7");
239            break;
240        default:
241            buf.append(version);
242            break;
243        }
244        buf.append(", ");
245        appendAccess(access | ACCESS_CLASS);
246        buf.append(", ");
247        appendConstant(name);
248        buf.append(", ");
249        appendConstant(signature);
250        buf.append(", ");
251        appendConstant(superName);
252        buf.append(", ");
253        if (interfaces != null && interfaces.length > 0) {
254            buf.append("new String[] {");
255            for (int i = 0; i < interfaces.length; ++i) {
256                buf.append(i == 0 ? " " : ", ");
257                appendConstant(interfaces[i]);
258            }
259            buf.append(" }");
260        } else {
261            buf.append("null");
262        }
263        buf.append(");\n\n");
264        text.add(buf.toString());
265    }
266
267    @Override
268    public void visitSource(final String file, final String debug) {
269        buf.setLength(0);
270        buf.append("cw.visitSource(");
271        appendConstant(file);
272        buf.append(", ");
273        appendConstant(debug);
274        buf.append(");\n\n");
275        text.add(buf.toString());
276    }
277
278    @Override
279    public void visitOuterClass(final String owner, final String name,
280            final String desc) {
281        buf.setLength(0);
282        buf.append("cw.visitOuterClass(");
283        appendConstant(owner);
284        buf.append(", ");
285        appendConstant(name);
286        buf.append(", ");
287        appendConstant(desc);
288        buf.append(");\n\n");
289        text.add(buf.toString());
290    }
291
292    @Override
293    public ASMifier visitClassAnnotation(final String desc,
294            final boolean visible) {
295        return visitAnnotation(desc, visible);
296    }
297
298    @Override
299    public ASMifier visitClassTypeAnnotation(final int typeRef,
300            final TypePath typePath, final String desc, final boolean visible) {
301        return visitTypeAnnotation(typeRef, typePath, desc, visible);
302    }
303
304    @Override
305    public void visitClassAttribute(final Attribute attr) {
306        visitAttribute(attr);
307    }
308
309    @Override
310    public void visitInnerClass(final String name, final String outerName,
311            final String innerName, final int access) {
312        buf.setLength(0);
313        buf.append("cw.visitInnerClass(");
314        appendConstant(name);
315        buf.append(", ");
316        appendConstant(outerName);
317        buf.append(", ");
318        appendConstant(innerName);
319        buf.append(", ");
320        appendAccess(access | ACCESS_INNER);
321        buf.append(");\n\n");
322        text.add(buf.toString());
323    }
324
325    @Override
326    public ASMifier visitField(final int access, final String name,
327            final String desc, final String signature, final Object value) {
328        buf.setLength(0);
329        buf.append("{\n");
330        buf.append("fv = cw.visitField(");
331        appendAccess(access | ACCESS_FIELD);
332        buf.append(", ");
333        appendConstant(name);
334        buf.append(", ");
335        appendConstant(desc);
336        buf.append(", ");
337        appendConstant(signature);
338        buf.append(", ");
339        appendConstant(value);
340        buf.append(");\n");
341        text.add(buf.toString());
342        ASMifier a = createASMifier("fv", 0);
343        text.add(a.getText());
344        text.add("}\n");
345        return a;
346    }
347
348    @Override
349    public ASMifier visitMethod(final int access, final String name,
350            final String desc, final String signature, final String[] exceptions) {
351        buf.setLength(0);
352        buf.append("{\n");
353        buf.append("mv = cw.visitMethod(");
354        appendAccess(access);
355        buf.append(", ");
356        appendConstant(name);
357        buf.append(", ");
358        appendConstant(desc);
359        buf.append(", ");
360        appendConstant(signature);
361        buf.append(", ");
362        if (exceptions != null && exceptions.length > 0) {
363            buf.append("new String[] {");
364            for (int i = 0; i < exceptions.length; ++i) {
365                buf.append(i == 0 ? " " : ", ");
366                appendConstant(exceptions[i]);
367            }
368            buf.append(" }");
369        } else {
370            buf.append("null");
371        }
372        buf.append(");\n");
373        text.add(buf.toString());
374        ASMifier a = createASMifier("mv", 0);
375        text.add(a.getText());
376        text.add("}\n");
377        return a;
378    }
379
380    @Override
381    public void visitClassEnd() {
382        text.add("cw.visitEnd();\n\n");
383        text.add("return cw.toByteArray();\n");
384        text.add("}\n");
385        text.add("}\n");
386    }
387
388    // ------------------------------------------------------------------------
389    // Annotations
390    // ------------------------------------------------------------------------
391
392    @Override
393    public void visit(final String name, final Object value) {
394        buf.setLength(0);
395        buf.append("av").append(id).append(".visit(");
396        appendConstant(buf, name);
397        buf.append(", ");
398        appendConstant(buf, value);
399        buf.append(");\n");
400        text.add(buf.toString());
401    }
402
403    @Override
404    public void visitEnum(final String name, final String desc,
405            final String value) {
406        buf.setLength(0);
407        buf.append("av").append(id).append(".visitEnum(");
408        appendConstant(buf, name);
409        buf.append(", ");
410        appendConstant(buf, desc);
411        buf.append(", ");
412        appendConstant(buf, value);
413        buf.append(");\n");
414        text.add(buf.toString());
415    }
416
417    @Override
418    public ASMifier visitAnnotation(final String name, final String desc) {
419        buf.setLength(0);
420        buf.append("{\n");
421        buf.append("AnnotationVisitor av").append(id + 1).append(" = av");
422        buf.append(id).append(".visitAnnotation(");
423        appendConstant(buf, name);
424        buf.append(", ");
425        appendConstant(buf, desc);
426        buf.append(");\n");
427        text.add(buf.toString());
428        ASMifier a = createASMifier("av", id + 1);
429        text.add(a.getText());
430        text.add("}\n");
431        return a;
432    }
433
434    @Override
435    public ASMifier visitArray(final String name) {
436        buf.setLength(0);
437        buf.append("{\n");
438        buf.append("AnnotationVisitor av").append(id + 1).append(" = av");
439        buf.append(id).append(".visitArray(");
440        appendConstant(buf, name);
441        buf.append(");\n");
442        text.add(buf.toString());
443        ASMifier a = createASMifier("av", id + 1);
444        text.add(a.getText());
445        text.add("}\n");
446        return a;
447    }
448
449    @Override
450    public void visitAnnotationEnd() {
451        buf.setLength(0);
452        buf.append("av").append(id).append(".visitEnd();\n");
453        text.add(buf.toString());
454    }
455
456    // ------------------------------------------------------------------------
457    // Fields
458    // ------------------------------------------------------------------------
459
460    @Override
461    public ASMifier visitFieldAnnotation(final String desc,
462            final boolean visible) {
463        return visitAnnotation(desc, visible);
464    }
465
466    @Override
467    public ASMifier visitFieldTypeAnnotation(final int typeRef,
468            final TypePath typePath, final String desc, final boolean visible) {
469        return visitTypeAnnotation(typeRef, typePath, desc, visible);
470    }
471
472    @Override
473    public void visitFieldAttribute(final Attribute attr) {
474        visitAttribute(attr);
475    }
476
477    @Override
478    public void visitFieldEnd() {
479        buf.setLength(0);
480        buf.append(name).append(".visitEnd();\n");
481        text.add(buf.toString());
482    }
483
484    // ------------------------------------------------------------------------
485    // Methods
486    // ------------------------------------------------------------------------
487
488    @Override
489    public void visitParameter(String parameterName, int access) {
490        buf.setLength(0);
491        buf.append(name).append(".visitParameter(");
492        appendString(buf, parameterName);
493        buf.append(", ");
494        appendAccess(access);
495        text.add(buf.append(");\n").toString());
496    }
497
498    @Override
499    public ASMifier visitAnnotationDefault() {
500        buf.setLength(0);
501        buf.append("{\n").append("av0 = ").append(name)
502                .append(".visitAnnotationDefault();\n");
503        text.add(buf.toString());
504        ASMifier a = createASMifier("av", 0);
505        text.add(a.getText());
506        text.add("}\n");
507        return a;
508    }
509
510    @Override
511    public ASMifier visitMethodAnnotation(final String desc,
512            final boolean visible) {
513        return visitAnnotation(desc, visible);
514    }
515
516    @Override
517    public ASMifier visitMethodTypeAnnotation(final int typeRef,
518            final TypePath typePath, final String desc, final boolean visible) {
519        return visitTypeAnnotation(typeRef, typePath, desc, visible);
520    }
521
522    @Override
523    public ASMifier visitParameterAnnotation(final int parameter,
524            final String desc, final boolean visible) {
525        buf.setLength(0);
526        buf.append("{\n").append("av0 = ").append(name)
527                .append(".visitParameterAnnotation(").append(parameter)
528                .append(", ");
529        appendConstant(desc);
530        buf.append(", ").append(visible).append(");\n");
531        text.add(buf.toString());
532        ASMifier a = createASMifier("av", 0);
533        text.add(a.getText());
534        text.add("}\n");
535        return a;
536    }
537
538    @Override
539    public void visitMethodAttribute(final Attribute attr) {
540        visitAttribute(attr);
541    }
542
543    @Override
544    public void visitCode() {
545        text.add(name + ".visitCode();\n");
546    }
547
548    @Override
549    public void visitFrame(final int type, final int nLocal,
550            final Object[] local, final int nStack, final Object[] stack) {
551        buf.setLength(0);
552        switch (type) {
553        case Opcodes.F_NEW:
554        case Opcodes.F_FULL:
555            declareFrameTypes(nLocal, local);
556            declareFrameTypes(nStack, stack);
557            if (type == Opcodes.F_NEW) {
558                buf.append(name).append(".visitFrame(Opcodes.F_NEW, ");
559            } else {
560                buf.append(name).append(".visitFrame(Opcodes.F_FULL, ");
561            }
562            buf.append(nLocal).append(", new Object[] {");
563            appendFrameTypes(nLocal, local);
564            buf.append("}, ").append(nStack).append(", new Object[] {");
565            appendFrameTypes(nStack, stack);
566            buf.append('}');
567            break;
568        case Opcodes.F_APPEND:
569            declareFrameTypes(nLocal, local);
570            buf.append(name).append(".visitFrame(Opcodes.F_APPEND,")
571                    .append(nLocal).append(", new Object[] {");
572            appendFrameTypes(nLocal, local);
573            buf.append("}, 0, null");
574            break;
575        case Opcodes.F_CHOP:
576            buf.append(name).append(".visitFrame(Opcodes.F_CHOP,")
577                    .append(nLocal).append(", null, 0, null");
578            break;
579        case Opcodes.F_SAME:
580            buf.append(name).append(
581                    ".visitFrame(Opcodes.F_SAME, 0, null, 0, null");
582            break;
583        case Opcodes.F_SAME1:
584            declareFrameTypes(1, stack);
585            buf.append(name).append(
586                    ".visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[] {");
587            appendFrameTypes(1, stack);
588            buf.append('}');
589            break;
590        }
591        buf.append(");\n");
592        text.add(buf.toString());
593    }
594
595    @Override
596    public void visitInsn(final int opcode) {
597        buf.setLength(0);
598        buf.append(name).append(".visitInsn(").append(OPCODES[opcode])
599                .append(");\n");
600        text.add(buf.toString());
601    }
602
603    @Override
604    public void visitIntInsn(final int opcode, final int operand) {
605        buf.setLength(0);
606        buf.append(name)
607                .append(".visitIntInsn(")
608                .append(OPCODES[opcode])
609                .append(", ")
610                .append(opcode == Opcodes.NEWARRAY ? TYPES[operand] : Integer
611                        .toString(operand)).append(");\n");
612        text.add(buf.toString());
613    }
614
615    @Override
616    public void visitVarInsn(final int opcode, final int var) {
617        buf.setLength(0);
618        buf.append(name).append(".visitVarInsn(").append(OPCODES[opcode])
619                .append(", ").append(var).append(");\n");
620        text.add(buf.toString());
621    }
622
623    @Override
624    public void visitTypeInsn(final int opcode, final String type) {
625        buf.setLength(0);
626        buf.append(name).append(".visitTypeInsn(").append(OPCODES[opcode])
627                .append(", ");
628        appendConstant(type);
629        buf.append(");\n");
630        text.add(buf.toString());
631    }
632
633    @Override
634    public void visitFieldInsn(final int opcode, final String owner,
635            final String name, final String desc) {
636        buf.setLength(0);
637        buf.append(this.name).append(".visitFieldInsn(")
638                .append(OPCODES[opcode]).append(", ");
639        appendConstant(owner);
640        buf.append(", ");
641        appendConstant(name);
642        buf.append(", ");
643        appendConstant(desc);
644        buf.append(");\n");
645        text.add(buf.toString());
646    }
647
648    @Deprecated
649    @Override
650    public void visitMethodInsn(final int opcode, final String owner,
651            final String name, final String desc) {
652        if (api >= Opcodes.ASM5) {
653            super.visitMethodInsn(opcode, owner, name, desc);
654            return;
655        }
656        doVisitMethodInsn(opcode, owner, name, desc,
657                opcode == Opcodes.INVOKEINTERFACE);
658    }
659
660    @Override
661    public void visitMethodInsn(final int opcode, final String owner,
662            final String name, final String desc, final boolean itf) {
663        if (api < Opcodes.ASM5) {
664            super.visitMethodInsn(opcode, owner, name, desc, itf);
665            return;
666        }
667        doVisitMethodInsn(opcode, owner, name, desc, itf);
668    }
669
670    private void doVisitMethodInsn(final int opcode, final String owner,
671            final String name, final String desc, final boolean itf) {
672        buf.setLength(0);
673        buf.append(this.name).append(".visitMethodInsn(")
674                .append(OPCODES[opcode]).append(", ");
675        appendConstant(owner);
676        buf.append(", ");
677        appendConstant(name);
678        buf.append(", ");
679        appendConstant(desc);
680        buf.append(", ");
681        buf.append(itf ? "true" : "false");
682        buf.append(");\n");
683        text.add(buf.toString());
684    }
685
686    @Override
687    public void visitInvokeDynamicInsn(String name, String desc, Handle bsm,
688            Object... bsmArgs) {
689        buf.setLength(0);
690        buf.append(this.name).append(".visitInvokeDynamicInsn(");
691        appendConstant(name);
692        buf.append(", ");
693        appendConstant(desc);
694        buf.append(", ");
695        appendConstant(bsm);
696        buf.append(", new Object[]{");
697        for (int i = 0; i < bsmArgs.length; ++i) {
698            appendConstant(bsmArgs[i]);
699            if (i != bsmArgs.length - 1) {
700                buf.append(", ");
701            }
702        }
703        buf.append("});\n");
704        text.add(buf.toString());
705    }
706
707    @Override
708    public void visitJumpInsn(final int opcode, final Label label) {
709        buf.setLength(0);
710        declareLabel(label);
711        buf.append(name).append(".visitJumpInsn(").append(OPCODES[opcode])
712                .append(", ");
713        appendLabel(label);
714        buf.append(");\n");
715        text.add(buf.toString());
716    }
717
718    @Override
719    public void visitLabel(final Label label) {
720        buf.setLength(0);
721        declareLabel(label);
722        buf.append(name).append(".visitLabel(");
723        appendLabel(label);
724        buf.append(");\n");
725        text.add(buf.toString());
726    }
727
728    @Override
729    public void visitLdcInsn(final Object cst) {
730        buf.setLength(0);
731        buf.append(name).append(".visitLdcInsn(");
732        appendConstant(cst);
733        buf.append(");\n");
734        text.add(buf.toString());
735    }
736
737    @Override
738    public void visitIincInsn(final int var, final int increment) {
739        buf.setLength(0);
740        buf.append(name).append(".visitIincInsn(").append(var).append(", ")
741                .append(increment).append(");\n");
742        text.add(buf.toString());
743    }
744
745    @Override
746    public void visitTableSwitchInsn(final int min, final int max,
747            final Label dflt, final Label... labels) {
748        buf.setLength(0);
749        for (int i = 0; i < labels.length; ++i) {
750            declareLabel(labels[i]);
751        }
752        declareLabel(dflt);
753
754        buf.append(name).append(".visitTableSwitchInsn(").append(min)
755                .append(", ").append(max).append(", ");
756        appendLabel(dflt);
757        buf.append(", new Label[] {");
758        for (int i = 0; i < labels.length; ++i) {
759            buf.append(i == 0 ? " " : ", ");
760            appendLabel(labels[i]);
761        }
762        buf.append(" });\n");
763        text.add(buf.toString());
764    }
765
766    @Override
767    public void visitLookupSwitchInsn(final Label dflt, final int[] keys,
768            final Label[] labels) {
769        buf.setLength(0);
770        for (int i = 0; i < labels.length; ++i) {
771            declareLabel(labels[i]);
772        }
773        declareLabel(dflt);
774
775        buf.append(name).append(".visitLookupSwitchInsn(");
776        appendLabel(dflt);
777        buf.append(", new int[] {");
778        for (int i = 0; i < keys.length; ++i) {
779            buf.append(i == 0 ? " " : ", ").append(keys[i]);
780        }
781        buf.append(" }, new Label[] {");
782        for (int i = 0; i < labels.length; ++i) {
783            buf.append(i == 0 ? " " : ", ");
784            appendLabel(labels[i]);
785        }
786        buf.append(" });\n");
787        text.add(buf.toString());
788    }
789
790    @Override
791    public void visitMultiANewArrayInsn(final String desc, final int dims) {
792        buf.setLength(0);
793        buf.append(name).append(".visitMultiANewArrayInsn(");
794        appendConstant(desc);
795        buf.append(", ").append(dims).append(");\n");
796        text.add(buf.toString());
797    }
798
799    @Override
800    public ASMifier visitInsnAnnotation(final int typeRef,
801            final TypePath typePath, final String desc, final boolean visible) {
802        return visitTypeAnnotation("visitInsnAnnotation", typeRef, typePath,
803                desc, visible);
804    }
805
806    @Override
807    public void visitTryCatchBlock(final Label start, final Label end,
808            final Label handler, final String type) {
809        buf.setLength(0);
810        declareLabel(start);
811        declareLabel(end);
812        declareLabel(handler);
813        buf.append(name).append(".visitTryCatchBlock(");
814        appendLabel(start);
815        buf.append(", ");
816        appendLabel(end);
817        buf.append(", ");
818        appendLabel(handler);
819        buf.append(", ");
820        appendConstant(type);
821        buf.append(");\n");
822        text.add(buf.toString());
823    }
824
825    @Override
826    public ASMifier visitTryCatchAnnotation(final int typeRef,
827            final TypePath typePath, final String desc, final boolean visible) {
828        return visitTypeAnnotation("visitTryCatchAnnotation", typeRef,
829                typePath, desc, visible);
830    }
831
832    @Override
833    public void visitLocalVariable(final String name, final String desc,
834            final String signature, final Label start, final Label end,
835            final int index) {
836        buf.setLength(0);
837        buf.append(this.name).append(".visitLocalVariable(");
838        appendConstant(name);
839        buf.append(", ");
840        appendConstant(desc);
841        buf.append(", ");
842        appendConstant(signature);
843        buf.append(", ");
844        appendLabel(start);
845        buf.append(", ");
846        appendLabel(end);
847        buf.append(", ").append(index).append(");\n");
848        text.add(buf.toString());
849    }
850
851    @Override
852    public Printer visitLocalVariableAnnotation(int typeRef, TypePath typePath,
853            Label[] start, Label[] end, int[] index, String desc,
854            boolean visible) {
855        buf.setLength(0);
856        buf.append("{\n").append("av0 = ").append(name)
857                .append(".visitLocalVariableAnnotation(");
858        buf.append(typeRef);
859        if (typePath == null) {
860            buf.append(", null, ");
861        } else {
862            buf.append(", TypePath.fromString(\"").append(typePath).append("\"), ");
863        }
864        buf.append("new Label[] {");
865        for (int i = 0; i < start.length; ++i) {
866            buf.append(i == 0 ? " " : ", ");
867            appendLabel(start[i]);
868        }
869        buf.append(" }, new Label[] {");
870        for (int i = 0; i < end.length; ++i) {
871            buf.append(i == 0 ? " " : ", ");
872            appendLabel(end[i]);
873        }
874        buf.append(" }, new int[] {");
875        for (int i = 0; i < index.length; ++i) {
876            buf.append(i == 0 ? " " : ", ").append(index[i]);
877        }
878        buf.append(" }, ");
879        appendConstant(desc);
880        buf.append(", ").append(visible).append(");\n");
881        text.add(buf.toString());
882        ASMifier a = createASMifier("av", 0);
883        text.add(a.getText());
884        text.add("}\n");
885        return a;
886    }
887
888    @Override
889    public void visitLineNumber(final int line, final Label start) {
890        buf.setLength(0);
891        buf.append(name).append(".visitLineNumber(").append(line).append(", ");
892        appendLabel(start);
893        buf.append(");\n");
894        text.add(buf.toString());
895    }
896
897    @Override
898    public void visitMaxs(final int maxStack, final int maxLocals) {
899        buf.setLength(0);
900        buf.append(name).append(".visitMaxs(").append(maxStack).append(", ")
901                .append(maxLocals).append(");\n");
902        text.add(buf.toString());
903    }
904
905    @Override
906    public void visitMethodEnd() {
907        buf.setLength(0);
908        buf.append(name).append(".visitEnd();\n");
909        text.add(buf.toString());
910    }
911
912    // ------------------------------------------------------------------------
913    // Common methods
914    // ------------------------------------------------------------------------
915
916    public ASMifier visitAnnotation(final String desc, final boolean visible) {
917        buf.setLength(0);
918        buf.append("{\n").append("av0 = ").append(name)
919                .append(".visitAnnotation(");
920        appendConstant(desc);
921        buf.append(", ").append(visible).append(");\n");
922        text.add(buf.toString());
923        ASMifier a = createASMifier("av", 0);
924        text.add(a.getText());
925        text.add("}\n");
926        return a;
927    }
928
929    public ASMifier visitTypeAnnotation(final int typeRef,
930            final TypePath typePath, final String desc, final boolean visible) {
931        return visitTypeAnnotation("visitTypeAnnotation", typeRef, typePath,
932                desc, visible);
933    }
934
935    public ASMifier visitTypeAnnotation(final String method, final int typeRef,
936            final TypePath typePath, final String desc, final boolean visible) {
937        buf.setLength(0);
938        buf.append("{\n").append("av0 = ").append(name).append(".")
939                .append(method).append("(");
940        buf.append(typeRef);
941        if (typePath == null) {
942            buf.append(", null, ");
943        } else {
944            buf.append(", TypePath.fromString(\"").append(typePath).append("\"), ");
945        }
946        appendConstant(desc);
947        buf.append(", ").append(visible).append(");\n");
948        text.add(buf.toString());
949        ASMifier a = createASMifier("av", 0);
950        text.add(a.getText());
951        text.add("}\n");
952        return a;
953    }
954
955    public void visitAttribute(final Attribute attr) {
956        buf.setLength(0);
957        buf.append("// ATTRIBUTE ").append(attr.type).append('\n');
958        if (attr instanceof ASMifiable) {
959            if (labelNames == null) {
960                labelNames = new HashMap<Label, String>();
961            }
962            buf.append("{\n");
963            ((ASMifiable) attr).asmify(buf, "attr", labelNames);
964            buf.append(name).append(".visitAttribute(attr);\n");
965            buf.append("}\n");
966        }
967        text.add(buf.toString());
968    }
969
970    // ------------------------------------------------------------------------
971    // Utility methods
972    // ------------------------------------------------------------------------
973
974    protected ASMifier createASMifier(final String name, final int id) {
975        return new ASMifier(Opcodes.ASM5, name, id);
976    }
977
978    /**
979     * Appends a string representation of the given access modifiers to
980     * {@link #buf buf}.
981     *
982     * @param access
983     *            some access modifiers.
984     */
985    void appendAccess(final int access) {
986        boolean first = true;
987        if ((access & Opcodes.ACC_PUBLIC) != 0) {
988            buf.append("ACC_PUBLIC");
989            first = false;
990        }
991        if ((access & Opcodes.ACC_PRIVATE) != 0) {
992            buf.append("ACC_PRIVATE");
993            first = false;
994        }
995        if ((access & Opcodes.ACC_PROTECTED) != 0) {
996            buf.append("ACC_PROTECTED");
997            first = false;
998        }
999        if ((access & Opcodes.ACC_FINAL) != 0) {
1000            if (!first) {
1001                buf.append(" + ");
1002            }
1003            buf.append("ACC_FINAL");
1004            first = false;
1005        }
1006        if ((access & Opcodes.ACC_STATIC) != 0) {
1007            if (!first) {
1008                buf.append(" + ");
1009            }
1010            buf.append("ACC_STATIC");
1011            first = false;
1012        }
1013        if ((access & Opcodes.ACC_SYNCHRONIZED) != 0) {
1014            if (!first) {
1015                buf.append(" + ");
1016            }
1017            if ((access & ACCESS_CLASS) == 0) {
1018                buf.append("ACC_SYNCHRONIZED");
1019            } else {
1020                buf.append("ACC_SUPER");
1021            }
1022            first = false;
1023        }
1024        if ((access & Opcodes.ACC_VOLATILE) != 0
1025                && (access & ACCESS_FIELD) != 0) {
1026            if (!first) {
1027                buf.append(" + ");
1028            }
1029            buf.append("ACC_VOLATILE");
1030            first = false;
1031        }
1032        if ((access & Opcodes.ACC_BRIDGE) != 0 && (access & ACCESS_CLASS) == 0
1033                && (access & ACCESS_FIELD) == 0) {
1034            if (!first) {
1035                buf.append(" + ");
1036            }
1037            buf.append("ACC_BRIDGE");
1038            first = false;
1039        }
1040        if ((access & Opcodes.ACC_VARARGS) != 0 && (access & ACCESS_CLASS) == 0
1041                && (access & ACCESS_FIELD) == 0) {
1042            if (!first) {
1043                buf.append(" + ");
1044            }
1045            buf.append("ACC_VARARGS");
1046            first = false;
1047        }
1048        if ((access & Opcodes.ACC_TRANSIENT) != 0
1049                && (access & ACCESS_FIELD) != 0) {
1050            if (!first) {
1051                buf.append(" + ");
1052            }
1053            buf.append("ACC_TRANSIENT");
1054            first = false;
1055        }
1056        if ((access & Opcodes.ACC_NATIVE) != 0 && (access & ACCESS_CLASS) == 0
1057                && (access & ACCESS_FIELD) == 0) {
1058            if (!first) {
1059                buf.append(" + ");
1060            }
1061            buf.append("ACC_NATIVE");
1062            first = false;
1063        }
1064        if ((access & Opcodes.ACC_ENUM) != 0
1065                && ((access & ACCESS_CLASS) != 0
1066                        || (access & ACCESS_FIELD) != 0 || (access & ACCESS_INNER) != 0)) {
1067            if (!first) {
1068                buf.append(" + ");
1069            }
1070            buf.append("ACC_ENUM");
1071            first = false;
1072        }
1073        if ((access & Opcodes.ACC_ANNOTATION) != 0
1074                && ((access & ACCESS_CLASS) != 0 || (access & ACCESS_INNER) != 0)) {
1075            if (!first) {
1076                buf.append(" + ");
1077            }
1078            buf.append("ACC_ANNOTATION");
1079            first = false;
1080        }
1081        if ((access & Opcodes.ACC_ABSTRACT) != 0) {
1082            if (!first) {
1083                buf.append(" + ");
1084            }
1085            buf.append("ACC_ABSTRACT");
1086            first = false;
1087        }
1088        if ((access & Opcodes.ACC_INTERFACE) != 0) {
1089            if (!first) {
1090                buf.append(" + ");
1091            }
1092            buf.append("ACC_INTERFACE");
1093            first = false;
1094        }
1095        if ((access & Opcodes.ACC_STRICT) != 0) {
1096            if (!first) {
1097                buf.append(" + ");
1098            }
1099            buf.append("ACC_STRICT");
1100            first = false;
1101        }
1102        if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
1103            if (!first) {
1104                buf.append(" + ");
1105            }
1106            buf.append("ACC_SYNTHETIC");
1107            first = false;
1108        }
1109        if ((access & Opcodes.ACC_DEPRECATED) != 0) {
1110            if (!first) {
1111                buf.append(" + ");
1112            }
1113            buf.append("ACC_DEPRECATED");
1114            first = false;
1115        }
1116        if ((access & Opcodes.ACC_MANDATED) != 0) {
1117            if (!first) {
1118                buf.append(" + ");
1119            }
1120            buf.append("ACC_MANDATED");
1121            first = false;
1122        }
1123        if (first) {
1124            buf.append('0');
1125        }
1126    }
1127
1128    /**
1129     * Appends a string representation of the given constant to the given
1130     * buffer.
1131     *
1132     * @param cst
1133     *            an {@link Integer}, {@link Float}, {@link Long},
1134     *            {@link Double} or {@link String} object. May be <tt>null</tt>.
1135     */
1136    protected void appendConstant(final Object cst) {
1137        appendConstant(buf, cst);
1138    }
1139
1140    /**
1141     * Appends a string representation of the given constant to the given
1142     * buffer.
1143     *
1144     * @param buf
1145     *            a string buffer.
1146     * @param cst
1147     *            an {@link Integer}, {@link Float}, {@link Long},
1148     *            {@link Double} or {@link String} object. May be <tt>null</tt>.
1149     */
1150    static void appendConstant(final StringBuffer buf, final Object cst) {
1151        if (cst == null) {
1152            buf.append("null");
1153        } else if (cst instanceof String) {
1154            appendString(buf, (String) cst);
1155        } else if (cst instanceof Type) {
1156            buf.append("Type.getType(\"");
1157            buf.append(((Type) cst).getDescriptor());
1158            buf.append("\")");
1159        } else if (cst instanceof Handle) {
1160            buf.append("new Handle(");
1161            Handle h = (Handle) cst;
1162            buf.append("Opcodes.").append(HANDLE_TAG[h.getTag()])
1163                    .append(", \"");
1164            buf.append(h.getOwner()).append("\", \"");
1165            buf.append(h.getName()).append("\", \"");
1166            buf.append(h.getDesc()).append("\")");
1167        } else if (cst instanceof Byte) {
1168            buf.append("new Byte((byte)").append(cst).append(')');
1169        } else if (cst instanceof Boolean) {
1170            buf.append(((Boolean) cst).booleanValue() ? "Boolean.TRUE"
1171                    : "Boolean.FALSE");
1172        } else if (cst instanceof Short) {
1173            buf.append("new Short((short)").append(cst).append(')');
1174        } else if (cst instanceof Character) {
1175            int c = ((Character) cst).charValue();
1176            buf.append("new Character((char)").append(c).append(')');
1177        } else if (cst instanceof Integer) {
1178            buf.append("new Integer(").append(cst).append(')');
1179        } else if (cst instanceof Float) {
1180            buf.append("new Float(\"").append(cst).append("\")");
1181        } else if (cst instanceof Long) {
1182            buf.append("new Long(").append(cst).append("L)");
1183        } else if (cst instanceof Double) {
1184            buf.append("new Double(\"").append(cst).append("\")");
1185        } else if (cst instanceof byte[]) {
1186            byte[] v = (byte[]) cst;
1187            buf.append("new byte[] {");
1188            for (int i = 0; i < v.length; i++) {
1189                buf.append(i == 0 ? "" : ",").append(v[i]);
1190            }
1191            buf.append('}');
1192        } else if (cst instanceof boolean[]) {
1193            boolean[] v = (boolean[]) cst;
1194            buf.append("new boolean[] {");
1195            for (int i = 0; i < v.length; i++) {
1196                buf.append(i == 0 ? "" : ",").append(v[i]);
1197            }
1198            buf.append('}');
1199        } else if (cst instanceof short[]) {
1200            short[] v = (short[]) cst;
1201            buf.append("new short[] {");
1202            for (int i = 0; i < v.length; i++) {
1203                buf.append(i == 0 ? "" : ",").append("(short)").append(v[i]);
1204            }
1205            buf.append('}');
1206        } else if (cst instanceof char[]) {
1207            char[] v = (char[]) cst;
1208            buf.append("new char[] {");
1209            for (int i = 0; i < v.length; i++) {
1210                buf.append(i == 0 ? "" : ",").append("(char)")
1211                        .append((int) v[i]);
1212            }
1213            buf.append('}');
1214        } else if (cst instanceof int[]) {
1215            int[] v = (int[]) cst;
1216            buf.append("new int[] {");
1217            for (int i = 0; i < v.length; i++) {
1218                buf.append(i == 0 ? "" : ",").append(v[i]);
1219            }
1220            buf.append('}');
1221        } else if (cst instanceof long[]) {
1222            long[] v = (long[]) cst;
1223            buf.append("new long[] {");
1224            for (int i = 0; i < v.length; i++) {
1225                buf.append(i == 0 ? "" : ",").append(v[i]).append('L');
1226            }
1227            buf.append('}');
1228        } else if (cst instanceof float[]) {
1229            float[] v = (float[]) cst;
1230            buf.append("new float[] {");
1231            for (int i = 0; i < v.length; i++) {
1232                buf.append(i == 0 ? "" : ",").append(v[i]).append('f');
1233            }
1234            buf.append('}');
1235        } else if (cst instanceof double[]) {
1236            double[] v = (double[]) cst;
1237            buf.append("new double[] {");
1238            for (int i = 0; i < v.length; i++) {
1239                buf.append(i == 0 ? "" : ",").append(v[i]).append('d');
1240            }
1241            buf.append('}');
1242        }
1243    }
1244
1245    private void declareFrameTypes(final int n, final Object[] o) {
1246        for (int i = 0; i < n; ++i) {
1247            if (o[i] instanceof Label) {
1248                declareLabel((Label) o[i]);
1249            }
1250        }
1251    }
1252
1253    private void appendFrameTypes(final int n, final Object[] o) {
1254        for (int i = 0; i < n; ++i) {
1255            if (i > 0) {
1256                buf.append(", ");
1257            }
1258            if (o[i] instanceof String) {
1259                appendConstant(o[i]);
1260            } else if (o[i] instanceof Integer) {
1261                switch (((Integer) o[i]).intValue()) {
1262                case 0:
1263                    buf.append("Opcodes.TOP");
1264                    break;
1265                case 1:
1266                    buf.append("Opcodes.INTEGER");
1267                    break;
1268                case 2:
1269                    buf.append("Opcodes.FLOAT");
1270                    break;
1271                case 3:
1272                    buf.append("Opcodes.DOUBLE");
1273                    break;
1274                case 4:
1275                    buf.append("Opcodes.LONG");
1276                    break;
1277                case 5:
1278                    buf.append("Opcodes.NULL");
1279                    break;
1280                case 6:
1281                    buf.append("Opcodes.UNINITIALIZED_THIS");
1282                    break;
1283                }
1284            } else {
1285                appendLabel((Label) o[i]);
1286            }
1287        }
1288    }
1289
1290    /**
1291     * Appends a declaration of the given label to {@link #buf buf}. This
1292     * declaration is of the form "Label lXXX = new Label();". Does nothing if
1293     * the given label has already been declared.
1294     *
1295     * @param l
1296     *            a label.
1297     */
1298    protected void declareLabel(final Label l) {
1299        if (labelNames == null) {
1300            labelNames = new HashMap<Label, String>();
1301        }
1302        String name = labelNames.get(l);
1303        if (name == null) {
1304            name = "l" + labelNames.size();
1305            labelNames.put(l, name);
1306            buf.append("Label ").append(name).append(" = new Label();\n");
1307        }
1308    }
1309
1310    /**
1311     * Appends the name of the given label to {@link #buf buf}. The given label
1312     * <i>must</i> already have a name. One way to ensure this is to always call
1313     * {@link #declareLabel declared} before calling this method.
1314     *
1315     * @param l
1316     *            a label.
1317     */
1318    protected void appendLabel(final Label l) {
1319        buf.append(labelNames.get(l));
1320    }
1321}
1322