ASMifierClassVisitor.java revision 674060f01e9090cd21b3c5656cc3204912ad17a6
1/***
2 * ASM: a very small and fast Java bytecode manipulation framework
3 * Copyright (c) 2000-2007 INRIA, France Telecom
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the copyright holders nor the names of its
15 *    contributors may be used to endorse or promote products derived from
16 *    this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
28 * THE POSSIBILITY OF SUCH DAMAGE.
29 */
30package org.mockito.asm.util;
31
32import java.io.FileInputStream;
33import java.io.PrintWriter;
34
35import org.mockito.asm.AnnotationVisitor;
36import org.mockito.asm.ClassReader;
37import org.mockito.asm.ClassVisitor;
38import org.mockito.asm.FieldVisitor;
39import org.mockito.asm.MethodVisitor;
40import org.mockito.asm.Opcodes;
41
42/**
43 * A {@link ClassVisitor} that prints the ASM code that generates the classes it
44 * visits. This class visitor can be used to quickly write ASM code to generate
45 * some given bytecode: <ul> <li>write the Java source code equivalent to the
46 * bytecode you want to generate;</li> <li>compile it with <tt>javac</tt>;</li>
47 * <li>make a {@link ASMifierClassVisitor} visit this compiled class (see the
48 * {@link #main main} method);</li> <li>edit the generated source code, if
49 * necessary.</li> </ul> The source code printed when visiting the
50 * <tt>Hello</tt> class is the following: <p> <blockquote>
51 *
52 * <pre>
53 * import org.mockito.asm.*;
54 *
55 * public class HelloDump implements Opcodes {
56 *
57 *     public static byte[] dump() throws Exception {
58 *
59 *         ClassWriter cw = new ClassWriter(0);
60 *         FieldVisitor fv;
61 *         MethodVisitor mv;
62 *         AnnotationVisitor av0;
63 *
64 *         cw.visit(49,
65 *                 ACC_PUBLIC + ACC_SUPER,
66 *                 &quot;Hello&quot;,
67 *                 null,
68 *                 &quot;java/lang/Object&quot;,
69 *                 null);
70 *
71 *         cw.visitSource(&quot;Hello.java&quot;, null);
72 *
73 *         {
74 *             mv = cw.visitMethod(ACC_PUBLIC, &quot;&lt;init&gt;&quot;, &quot;()V&quot;, null, null);
75 *             mv.visitVarInsn(ALOAD, 0);
76 *             mv.visitMethodInsn(INVOKESPECIAL,
77 *                     &quot;java/lang/Object&quot;,
78 *                     &quot;&lt;init&gt;&quot;,
79 *                     &quot;()V&quot;);
80 *             mv.visitInsn(RETURN);
81 *             mv.visitMaxs(1, 1);
82 *             mv.visitEnd();
83 *         }
84 *         {
85 *             mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC,
86 *                     &quot;main&quot;,
87 *                     &quot;([Ljava/lang/String;)V&quot;,
88 *                     null,
89 *                     null);
90 *             mv.visitFieldInsn(GETSTATIC,
91 *                     &quot;java/lang/System&quot;,
92 *                     &quot;out&quot;,
93 *                     &quot;Ljava/io/PrintStream;&quot;);
94 *             mv.visitLdcInsn(&quot;hello&quot;);
95 *             mv.visitMethodInsn(INVOKEVIRTUAL,
96 *                     &quot;java/io/PrintStream&quot;,
97 *                     &quot;println&quot;,
98 *                     &quot;(Ljava/lang/String;)V&quot;);
99 *             mv.visitInsn(RETURN);
100 *             mv.visitMaxs(2, 1);
101 *             mv.visitEnd();
102 *         }
103 *         cw.visitEnd();
104 *
105 *         return cw.toByteArray();
106 *     }
107 * }
108 *
109 * </pre>
110 *
111 * </blockquote> where <tt>Hello</tt> is defined by: <p> <blockquote>
112 *
113 * <pre>
114 * public class Hello {
115 *
116 *     public static void main(String[] args) {
117 *         System.out.println(&quot;hello&quot;);
118 *     }
119 * }
120 * </pre>
121 *
122 * </blockquote>
123 *
124 * @author Eric Bruneton
125 * @author Eugene Kuleshov
126 */
127public class ASMifierClassVisitor extends ASMifierAbstractVisitor implements
128        ClassVisitor
129{
130
131    /**
132     * Pseudo access flag used to distinguish class access flags.
133     */
134    private static final int ACCESS_CLASS = 262144;
135
136    /**
137     * Pseudo access flag used to distinguish field access flags.
138     */
139    private static final int ACCESS_FIELD = 524288;
140
141    /**
142     * Pseudo access flag used to distinguish inner class flags.
143     */
144    private static final int ACCESS_INNER = 1048576;
145
146    /**
147     * The print writer to be used to print the class.
148     */
149    protected final PrintWriter pw;
150
151    /**
152     * Prints the ASM source code to generate the given class to the standard
153     * output. <p> Usage: ASMifierClassVisitor [-debug] &lt;fully qualified
154     * class name or class file name&gt;
155     *
156     * @param args the command line arguments.
157     *
158     * @throws Exception if the class cannot be found, or if an IO exception
159     *         occurs.
160     */
161    public static void main(final String[] args) throws Exception {
162        int i = 0;
163        int flags = ClassReader.SKIP_DEBUG;
164
165        boolean ok = true;
166        if (args.length < 1 || args.length > 2) {
167            ok = false;
168        }
169        if (ok && "-debug".equals(args[0])) {
170            i = 1;
171            flags = 0;
172            if (args.length != 2) {
173                ok = false;
174            }
175        }
176        if (!ok) {
177            System.err.println("Prints the ASM code to generate the given class.");
178            System.err.println("Usage: ASMifierClassVisitor [-debug] "
179                    + "<fully qualified class name or class file name>");
180            return;
181        }
182        ClassReader cr;
183        if (args[i].endsWith(".class") || args[i].indexOf('\\') > -1
184                || args[i].indexOf('/') > -1)
185        {
186            cr = new ClassReader(new FileInputStream(args[i]));
187        } else {
188            cr = new ClassReader(args[i]);
189        }
190        cr.accept(new ASMifierClassVisitor(new PrintWriter(System.out)),
191                getDefaultAttributes(),
192                flags);
193    }
194
195    /**
196     * Constructs a new {@link ASMifierClassVisitor} object.
197     *
198     * @param pw the print writer to be used to print the class.
199     */
200    public ASMifierClassVisitor(final PrintWriter pw) {
201        super("cw");
202        this.pw = pw;
203    }
204
205    // ------------------------------------------------------------------------
206    // Implementation of the ClassVisitor interface
207    // ------------------------------------------------------------------------
208
209    public void visit(
210        final int version,
211        final int access,
212        final String name,
213        final String signature,
214        final String superName,
215        final String[] interfaces)
216    {
217        String simpleName;
218        int n = name.lastIndexOf('/');
219        if (n == -1) {
220            simpleName = name;
221        } else {
222            text.add("package asm." + name.substring(0, n).replace('/', '.')
223                    + ";\n");
224            simpleName = name.substring(n + 1);
225        }
226        text.add("import java.util.*;\n");
227        text.add("import org.mockito.asm.*;\n");
228        text.add("import org.mockito.asm.attrs.*;\n");
229        text.add("public class " + simpleName + "Dump implements Opcodes {\n\n");
230        text.add("public static byte[] dump () throws Exception {\n\n");
231        text.add("ClassWriter cw = new ClassWriter(0);\n");
232        text.add("FieldVisitor fv;\n");
233        text.add("MethodVisitor mv;\n");
234        text.add("AnnotationVisitor av0;\n\n");
235
236        buf.setLength(0);
237        buf.append("cw.visit(");
238        switch (version) {
239            case Opcodes.V1_1:
240                buf.append("V1_1");
241                break;
242            case Opcodes.V1_2:
243                buf.append("V1_2");
244                break;
245            case Opcodes.V1_3:
246                buf.append("V1_3");
247                break;
248            case Opcodes.V1_4:
249                buf.append("V1_4");
250                break;
251            case Opcodes.V1_5:
252                buf.append("V1_5");
253                break;
254            case Opcodes.V1_6:
255                buf.append("V1_6");
256                break;
257            default:
258                buf.append(version);
259                break;
260        }
261        buf.append(", ");
262        appendAccess(access | ACCESS_CLASS);
263        buf.append(", ");
264        appendConstant(name);
265        buf.append(", ");
266        appendConstant(signature);
267        buf.append(", ");
268        appendConstant(superName);
269        buf.append(", ");
270        if (interfaces != null && interfaces.length > 0) {
271            buf.append("new String[] {");
272            for (int i = 0; i < interfaces.length; ++i) {
273                buf.append(i == 0 ? " " : ", ");
274                appendConstant(interfaces[i]);
275            }
276            buf.append(" }");
277        } else {
278            buf.append("null");
279        }
280        buf.append(");\n\n");
281        text.add(buf.toString());
282    }
283
284    public void visitSource(final String file, final String debug) {
285        buf.setLength(0);
286        buf.append("cw.visitSource(");
287        appendConstant(file);
288        buf.append(", ");
289        appendConstant(debug);
290        buf.append(");\n\n");
291        text.add(buf.toString());
292    }
293
294    public void visitOuterClass(
295        final String owner,
296        final String name,
297        final String desc)
298    {
299        buf.setLength(0);
300        buf.append("cw.visitOuterClass(");
301        appendConstant(owner);
302        buf.append(", ");
303        appendConstant(name);
304        buf.append(", ");
305        appendConstant(desc);
306        buf.append(");\n\n");
307        text.add(buf.toString());
308    }
309
310    public void visitInnerClass(
311        final String name,
312        final String outerName,
313        final String innerName,
314        final int access)
315    {
316        buf.setLength(0);
317        buf.append("cw.visitInnerClass(");
318        appendConstant(name);
319        buf.append(", ");
320        appendConstant(outerName);
321        buf.append(", ");
322        appendConstant(innerName);
323        buf.append(", ");
324        appendAccess(access | ACCESS_INNER);
325        buf.append(");\n\n");
326        text.add(buf.toString());
327    }
328
329    public FieldVisitor visitField(
330        final int access,
331        final String name,
332        final String desc,
333        final String signature,
334        final Object value)
335    {
336        buf.setLength(0);
337        buf.append("{\n");
338        buf.append("fv = cw.visitField(");
339        appendAccess(access | ACCESS_FIELD);
340        buf.append(", ");
341        appendConstant(name);
342        buf.append(", ");
343        appendConstant(desc);
344        buf.append(", ");
345        appendConstant(signature);
346        buf.append(", ");
347        appendConstant(value);
348        buf.append(");\n");
349        text.add(buf.toString());
350        ASMifierFieldVisitor aav = new ASMifierFieldVisitor();
351        text.add(aav.getText());
352        text.add("}\n");
353        return aav;
354    }
355
356    public MethodVisitor visitMethod(
357        final int access,
358        final String name,
359        final String desc,
360        final String signature,
361        final String[] exceptions)
362    {
363        buf.setLength(0);
364        buf.append("{\n");
365        buf.append("mv = cw.visitMethod(");
366        appendAccess(access);
367        buf.append(", ");
368        appendConstant(name);
369        buf.append(", ");
370        appendConstant(desc);
371        buf.append(", ");
372        appendConstant(signature);
373        buf.append(", ");
374        if (exceptions != null && exceptions.length > 0) {
375            buf.append("new String[] {");
376            for (int i = 0; i < exceptions.length; ++i) {
377                buf.append(i == 0 ? " " : ", ");
378                appendConstant(exceptions[i]);
379            }
380            buf.append(" }");
381        } else {
382            buf.append("null");
383        }
384        buf.append(");\n");
385        text.add(buf.toString());
386        ASMifierMethodVisitor acv = createASMifierMethodVisitor();
387        text.add(acv.getText());
388        text.add("}\n");
389        return acv;
390    }
391
392    protected ASMifierMethodVisitor createASMifierMethodVisitor() {
393        return new ASMifierMethodVisitor();
394    }
395
396    public AnnotationVisitor visitAnnotation(
397        final String desc,
398        final boolean visible)
399    {
400        buf.setLength(0);
401        buf.append("{\n");
402        buf.append("av0 = cw.visitAnnotation(");
403        appendConstant(desc);
404        buf.append(", ");
405        buf.append(visible);
406        buf.append(");\n");
407        text.add(buf.toString());
408        ASMifierAnnotationVisitor av = new ASMifierAnnotationVisitor(0);
409        text.add(av.getText());
410        text.add("}\n");
411        return av;
412    }
413
414    public void visitEnd() {
415        text.add("cw.visitEnd();\n\n");
416        text.add("return cw.toByteArray();\n");
417        text.add("}\n");
418        text.add("}\n");
419        printList(pw, text);
420        pw.flush();
421    }
422
423    // ------------------------------------------------------------------------
424    // Utility methods
425    // ------------------------------------------------------------------------
426
427    /**
428     * Appends a string representation of the given access modifiers to {@link
429     * #buf buf}.
430     *
431     * @param access some access modifiers.
432     */
433    void appendAccess(final int access) {
434        boolean first = true;
435        if ((access & Opcodes.ACC_PUBLIC) != 0) {
436            buf.append("ACC_PUBLIC");
437            first = false;
438        }
439        if ((access & Opcodes.ACC_PRIVATE) != 0) {
440            buf.append("ACC_PRIVATE");
441            first = false;
442        }
443        if ((access & Opcodes.ACC_PROTECTED) != 0) {
444            buf.append("ACC_PROTECTED");
445            first = false;
446        }
447        if ((access & Opcodes.ACC_FINAL) != 0) {
448            if (!first) {
449                buf.append(" + ");
450            }
451            buf.append("ACC_FINAL");
452            first = false;
453        }
454        if ((access & Opcodes.ACC_STATIC) != 0) {
455            if (!first) {
456                buf.append(" + ");
457            }
458            buf.append("ACC_STATIC");
459            first = false;
460        }
461        if ((access & Opcodes.ACC_SYNCHRONIZED) != 0) {
462            if (!first) {
463                buf.append(" + ");
464            }
465            if ((access & ACCESS_CLASS) == 0) {
466                buf.append("ACC_SYNCHRONIZED");
467            } else {
468                buf.append("ACC_SUPER");
469            }
470            first = false;
471        }
472        if ((access & Opcodes.ACC_VOLATILE) != 0
473                && (access & ACCESS_FIELD) != 0)
474        {
475            if (!first) {
476                buf.append(" + ");
477            }
478            buf.append("ACC_VOLATILE");
479            first = false;
480        }
481        if ((access & Opcodes.ACC_BRIDGE) != 0 && (access & ACCESS_CLASS) == 0
482                && (access & ACCESS_FIELD) == 0)
483        {
484            if (!first) {
485                buf.append(" + ");
486            }
487            buf.append("ACC_BRIDGE");
488            first = false;
489        }
490        if ((access & Opcodes.ACC_VARARGS) != 0 && (access & ACCESS_CLASS) == 0
491                && (access & ACCESS_FIELD) == 0)
492        {
493            if (!first) {
494                buf.append(" + ");
495            }
496            buf.append("ACC_VARARGS");
497            first = false;
498        }
499        if ((access & Opcodes.ACC_TRANSIENT) != 0
500                && (access & ACCESS_FIELD) != 0)
501        {
502            if (!first) {
503                buf.append(" + ");
504            }
505            buf.append("ACC_TRANSIENT");
506            first = false;
507        }
508        if ((access & Opcodes.ACC_NATIVE) != 0 && (access & ACCESS_CLASS) == 0
509                && (access & ACCESS_FIELD) == 0)
510        {
511            if (!first) {
512                buf.append(" + ");
513            }
514            buf.append("ACC_NATIVE");
515            first = false;
516        }
517        if ((access & Opcodes.ACC_ENUM) != 0
518                && ((access & ACCESS_CLASS) != 0
519                        || (access & ACCESS_FIELD) != 0 || (access & ACCESS_INNER) != 0))
520        {
521            if (!first) {
522                buf.append(" + ");
523            }
524            buf.append("ACC_ENUM");
525            first = false;
526        }
527        if ((access & Opcodes.ACC_ANNOTATION) != 0
528                && (access & ACCESS_CLASS) != 0)
529        {
530            if (!first) {
531                buf.append(" + ");
532            }
533            buf.append("ACC_ANNOTATION");
534            first = false;
535        }
536        if ((access & Opcodes.ACC_ABSTRACT) != 0) {
537            if (!first) {
538                buf.append(" + ");
539            }
540            buf.append("ACC_ABSTRACT");
541            first = false;
542        }
543        if ((access & Opcodes.ACC_INTERFACE) != 0) {
544            if (!first) {
545                buf.append(" + ");
546            }
547            buf.append("ACC_INTERFACE");
548            first = false;
549        }
550        if ((access & Opcodes.ACC_STRICT) != 0) {
551            if (!first) {
552                buf.append(" + ");
553            }
554            buf.append("ACC_STRICT");
555            first = false;
556        }
557        if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
558            if (!first) {
559                buf.append(" + ");
560            }
561            buf.append("ACC_SYNTHETIC");
562            first = false;
563        }
564        if ((access & Opcodes.ACC_DEPRECATED) != 0) {
565            if (!first) {
566                buf.append(" + ");
567            }
568            buf.append("ACC_DEPRECATED");
569            first = false;
570        }
571        if (first) {
572            buf.append('0');
573        }
574    }
575}
576