1/***
2 * ASM: a very small and fast Java bytecode manipulation framework
3 * Copyright (c) 2000-2005 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.objectweb.asm.util;
31
32import java.io.FileInputStream;
33import java.io.PrintWriter;
34
35import org.objectweb.asm.AnnotationVisitor;
36import org.objectweb.asm.ClassReader;
37import org.objectweb.asm.ClassVisitor;
38import org.objectweb.asm.TypeAnnotationVisitor;
39import org.objectweb.asm.FieldVisitor;
40import org.objectweb.asm.MethodVisitor;
41import org.objectweb.asm.Opcodes;
42import org.objectweb.asm.Type;
43
44/**
45 * A {@link ClassVisitor} that prints the ASM code that generates the classes it
46 * visits. This class visitor can be used to quickly write ASM code to generate
47 * some given bytecode: <ul> <li>write the Java source code equivalent to the
48 * bytecode you want to generate;</li> <li>compile it with <tt>javac</tt>;</li>
49 * <li>make a {@link ASMifierClassVisitor} visit this compiled class (see the
50 * {@link #main main} method);</li> <li>edit the generated source code, if
51 * necessary.</li> </ul> The source code printed when visiting the
52 * <tt>Hello</tt> class is the following: <p> <blockquote>
53 *
54 * <pre>
55 * import org.objectweb.asm.*;
56 *
57 * public class HelloDump implements Opcodes {
58 *
59 *     public static byte[] dump() throws Exception {
60 *
61 *         ClassWriter cw = new ClassWriter(false);
62 *         FieldVisitor fv;
63 *         MethodVisitor mv;
64 *         AnnotationVisitor av0;
65 *
66 *         cw.visit(49,
67 *                 ACC_PUBLIC + ACC_SUPER,
68 *                 &quot;Hello&quot;,
69 *                 null,
70 *                 &quot;java/lang/Object&quot;,
71 *                 null);
72 *
73 *         cw.visitSource(&quot;Hello.java&quot;, null);
74 *
75 *         {
76 *             mv = cw.visitMethod(ACC_PUBLIC, &quot;&lt;init&gt;&quot;, &quot;()V&quot;, null, null);
77 *             mv.visitVarInsn(ALOAD, 0);
78 *             mv.visitMethodInsn(INVOKESPECIAL,
79 *                     &quot;java/lang/Object&quot;,
80 *                     &quot;&lt;init&gt;&quot;,
81 *                     &quot;()V&quot;);
82 *             mv.visitInsn(RETURN);
83 *             mv.visitMaxs(1, 1);
84 *             mv.visitEnd();
85 *         }
86 *         {
87 *             mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC,
88 *                     &quot;main&quot;,
89 *                     &quot;([Ljava/lang/String;)V&quot;,
90 *                     null,
91 *                     null);
92 *             mv.visitFieldInsn(GETSTATIC,
93 *                     &quot;java/lang/System&quot;,
94 *                     &quot;out&quot;,
95 *                     &quot;Ljava/io/PrintStream;&quot;);
96 *             mv.visitLdcInsn(&quot;hello&quot;);
97 *             mv.visitMethodInsn(INVOKEVIRTUAL,
98 *                     &quot;java/io/PrintStream&quot;,
99 *                     &quot;println&quot;,
100 *                     &quot;(Ljava/lang/String;)V&quot;);
101 *             mv.visitInsn(RETURN);
102 *             mv.visitMaxs(2, 1);
103 *             mv.visitEnd();
104 *         }
105 *         cw.visitEnd();
106 *
107 *         return cw.toByteArray();
108 *     }
109 * }
110 *
111 * </pre>
112 *
113 * </blockquote> where <tt>Hello</tt> is defined by: <p> <blockquote>
114 *
115 * <pre>
116 * public class Hello {
117 *
118 *     public static void main(String[] args) {
119 *         System.out.println(&quot;hello&quot;);
120 *     }
121 * }
122 * </pre>
123 *
124 * </blockquote>
125 *
126 * @author Eric Bruneton
127 * @author Eugene Kuleshov
128 */
129public class ASMifierClassVisitor extends ASMifierAbstractVisitor implements
130        ClassVisitor
131{
132    /**
133     * Pseudo access flag used to distinguish class access flags.
134     */
135    private final static int ACCESS_CLASS = 262144;
136
137    /**
138     * Pseudo access flag used to distinguish field access flags.
139     */
140    private final static int ACCESS_FIELD = 524288;
141
142    /**
143     * Pseudo access flag used to distinguish inner class flags.
144     */
145    private static final int ACCESS_INNER = 1048576;
146
147    /**
148     * The print writer to be used to print the class.
149     */
150    protected final PrintWriter pw;
151
152    /**
153     * Prints the ASM source code to generate the given class to the standard
154     * output. <p> Usage: ASMifierClassVisitor [-debug] &lt;fully qualified
155     * class name or class file name&gt;
156     *
157     * @param args the command line arguments.
158     *
159     * @throws Exception if the class cannot be found, or if an IO exception
160     *         occurs.
161     */
162    public static void main(final String[] args) throws Exception {
163        int i = 0;
164        boolean skipDebug = true;
165
166        boolean ok = true;
167        if (args.length < 1 || args.length > 2) {
168            ok = false;
169        }
170        if (ok && args[0].equals("-debug")) {
171            i = 1;
172            skipDebug = false;
173            if (args.length != 2) {
174                ok = false;
175            }
176        }
177        if (!ok) {
178            System.err.println("Prints the ASM code to generate the given class.");
179            System.err.println("Usage: ASMifierClassVisitor [-debug] "
180                    + "<fully qualified class name or class file name>");
181            return;
182        }
183        ClassReader cr;
184        if (args[i].endsWith(".class") || args[i].indexOf('\\') > -1
185                || args[i].indexOf('/') > -1) {
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                skipDebug);
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            text.add("package asm." + name.substring(0, n).replace('/', '.')
221                    + ";\n");
222            simpleName = name.substring(n + 1);
223        } else {
224            simpleName = name;
225        }
226        text.add("import java.util.*;\n");
227        text.add("import org.objectweb.asm.*;\n");
228        text.add("import org.objectweb.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(false);\n");
232        text.add("FieldVisitor fv;\n");
233        text.add("MethodVisitor mv;\n");
234        text.add("AnnotationVisitor av0;\n");
235        text.add("TypeAnnotationVisitor xav0;\n\n");
236
237        buf.setLength(0);
238        buf.append("cw.visit(");
239        switch (version) {
240            case Opcodes.V1_1:
241                buf.append("V1_1");
242                break;
243            case Opcodes.V1_2:
244                buf.append("V1_2");
245                break;
246            case Opcodes.V1_3:
247                buf.append("V1_3");
248                break;
249            case Opcodes.V1_4:
250                buf.append("V1_4");
251                break;
252            case Opcodes.V1_5:
253                buf.append("V1_5");
254                break;
255            case Opcodes.V1_6:
256                buf.append("V1_6");
257                break;
258            default:
259                buf.append(version);
260                break;
261        }
262        buf.append(", ");
263        appendAccess(access | ACCESS_CLASS);
264        buf.append(", ");
265        appendConstant(name);
266        buf.append(", ");
267        appendConstant(signature);
268        buf.append(", ");
269        appendConstant(superName);
270        buf.append(", ");
271        if (interfaces != null && interfaces.length > 0) {
272            buf.append("new String[] {");
273            for (int i = 0; i < interfaces.length; ++i) {
274                buf.append(i == 0 ? " " : ", ");
275                appendConstant(interfaces[i]);
276            }
277            buf.append(" }");
278        } else {
279            buf.append("null");
280        }
281        buf.append(");\n\n");
282        text.add(buf.toString());
283    }
284
285    public void visitSource(final String file, final String debug) {
286        buf.setLength(0);
287        buf.append("cw.visitSource(");
288        appendConstant(file);
289        buf.append(", ");
290        appendConstant(debug);
291        buf.append(");\n\n");
292        text.add(buf.toString());
293    }
294
295    public void visitOuterClass(
296        final String owner,
297        final String name,
298        final String desc)
299    {
300        buf.setLength(0);
301        buf.append("cw.visitOuterClass(");
302        appendConstant(owner);
303        buf.append(", ");
304        appendConstant(name);
305        buf.append(", ");
306        appendConstant(desc);
307        buf.append(");\n\n");
308        text.add(buf.toString());
309    }
310
311    public void visitInnerClass(
312        final String name,
313        final String outerName,
314        final String innerName,
315        final int access)
316    {
317        buf.setLength(0);
318        buf.append("cw.visitInnerClass(");
319        appendConstant(name);
320        buf.append(", ");
321        appendConstant(outerName);
322        buf.append(", ");
323        appendConstant(innerName);
324        buf.append(", ");
325        appendAccess(access | ACCESS_INNER);
326        buf.append(");\n\n");
327        text.add(buf.toString());
328    }
329
330    public FieldVisitor visitField(
331        final int access,
332        final String name,
333        final String desc,
334        final String signature,
335        final Object value)
336    {
337        buf.setLength(0);
338        buf.append("{\n");
339        buf.append("fv = cw.visitField(");
340        appendAccess(access | ACCESS_FIELD);
341        buf.append(", ");
342        appendConstant(name);
343        buf.append(", ");
344        appendConstant(desc);
345        buf.append(", ");
346        appendConstant(signature);
347        buf.append(", ");
348        appendConstant(value);
349        buf.append(");\n");
350        text.add(buf.toString());
351        ASMifierFieldVisitor aav = new ASMifierFieldVisitor();
352        text.add(aav.getText());
353        text.add("}\n");
354        return aav;
355    }
356
357    public MethodVisitor visitMethod(
358        final int access,
359        final String name,
360        final String desc,
361        final String signature,
362        final String[] exceptions)
363    {
364        buf.setLength(0);
365        buf.append("{\n");
366        buf.append("mv = cw.visitMethod(");
367        appendAccess(access);
368        buf.append(", ");
369        appendConstant(name);
370        buf.append(", ");
371        appendConstant(desc);
372        buf.append(", ");
373        appendConstant(signature);
374        buf.append(", ");
375        if (exceptions != null && exceptions.length > 0) {
376            buf.append("new String[] {");
377            for (int i = 0; i < exceptions.length; ++i) {
378                buf.append(i == 0 ? " " : ", ");
379                appendConstant(exceptions[i]);
380            }
381            buf.append(" }");
382        } else {
383            buf.append("null");
384        }
385        buf.append(");\n");
386        text.add(buf.toString());
387        ASMifierMethodVisitor acv = new ASMifierMethodVisitor();
388        text.add(acv.getText());
389        text.add("}\n");
390        return acv;
391    }
392
393    public AnnotationVisitor visitAnnotation(
394        final String desc,
395        final boolean visible)
396    {
397        buf.setLength(0);
398        buf.append("{\n");
399        buf.append("av0 = cw.visitAnnotation(");
400        appendConstant(desc);
401        buf.append(", ");
402        buf.append(visible);
403        buf.append(");\n");
404        text.add(buf.toString());
405        ASMifierAnnotationVisitor av = new ASMifierAnnotationVisitor(0);
406        text.add(av.getText());
407        text.add("}\n");
408        return av;
409    }
410
411    public TypeAnnotationVisitor visitTypeAnnotation(
412        final String desc,
413        final boolean visible,
414        final boolean inCode)
415    {
416      buf.setLength(0);
417      buf.append("{\n");
418      buf.append("xav0 = cw.visitTypeAnnotation(");
419      appendConstant(desc);
420      buf.append(", ");
421      buf.append(visible);
422      buf.append(", ");
423      buf.append(inCode);
424      buf.append(");\n");
425      text.add(buf.toString());
426      ASMifierTypeAnnotationVisitor xav =
427        new ASMifierTypeAnnotationVisitor(0);
428      text.add(xav.getText());
429      text.add("}\n");
430      return xav;
431    }
432
433    public void visitEnd() {
434        text.add("cw.visitEnd();\n\n");
435        text.add("return cw.toByteArray();\n");
436        text.add("}\n");
437        text.add("}\n");
438        printList(pw, text);
439        pw.flush();
440    }
441
442    // ------------------------------------------------------------------------
443    // Utility methods
444    // ------------------------------------------------------------------------
445
446    /**
447     * Appends a string representation of the given access modifiers to {@link
448     * #buf buf}.
449     *
450     * @param access some access modifiers.
451     */
452    void appendAccess(final int access) {
453        boolean first = true;
454        if ((access & Opcodes.ACC_PUBLIC) != 0) {
455            buf.append("ACC_PUBLIC");
456            first = false;
457        }
458        if ((access & Opcodes.ACC_PRIVATE) != 0) {
459            if (!first) {
460                buf.append(" + ");
461            }
462            buf.append("ACC_PRIVATE");
463            first = false;
464        }
465        if ((access & Opcodes.ACC_PROTECTED) != 0) {
466            if (!first) {
467                buf.append(" + ");
468            }
469            buf.append("ACC_PROTECTED");
470            first = false;
471        }
472        if ((access & Opcodes.ACC_FINAL) != 0) {
473            if (!first) {
474                buf.append(" + ");
475            }
476            buf.append("ACC_FINAL");
477            first = false;
478        }
479        if ((access & Opcodes.ACC_STATIC) != 0) {
480            if (!first) {
481                buf.append(" + ");
482            }
483            buf.append("ACC_STATIC");
484            first = false;
485        }
486        if ((access & Opcodes.ACC_SYNCHRONIZED) != 0) {
487            if (!first) {
488                buf.append(" + ");
489            }
490            if ((access & ACCESS_CLASS) != 0) {
491                buf.append("ACC_SUPER");
492            } else {
493                buf.append("ACC_SYNCHRONIZED");
494            }
495            first = false;
496        }
497        if ((access & Opcodes.ACC_VOLATILE) != 0
498                && (access & ACCESS_FIELD) != 0)
499        {
500            if (!first) {
501                buf.append(" + ");
502            }
503            buf.append("ACC_VOLATILE");
504            first = false;
505        }
506        if ((access & Opcodes.ACC_BRIDGE) != 0 && (access & ACCESS_CLASS) == 0
507                && (access & ACCESS_FIELD) == 0)
508        {
509            if (!first) {
510                buf.append(" + ");
511            }
512            buf.append("ACC_BRIDGE");
513            first = false;
514        }
515        if ((access & Opcodes.ACC_VARARGS) != 0 && (access & ACCESS_CLASS) == 0
516                && (access & ACCESS_FIELD) == 0)
517        {
518            if (!first) {
519                buf.append(" + ");
520            }
521            buf.append("ACC_VARARGS");
522            first = false;
523        }
524        if ((access & Opcodes.ACC_TRANSIENT) != 0
525                && (access & ACCESS_FIELD) != 0)
526        {
527            if (!first) {
528                buf.append(" + ");
529            }
530            buf.append("ACC_TRANSIENT");
531            first = false;
532        }
533        if ((access & Opcodes.ACC_NATIVE) != 0 && (access & ACCESS_CLASS) == 0
534                && (access & ACCESS_FIELD) == 0)
535        {
536            if (!first) {
537                buf.append(" + ");
538            }
539            buf.append("ACC_NATIVE");
540            first = false;
541        }
542        if ((access & Opcodes.ACC_ENUM) != 0
543                && ((access & ACCESS_CLASS) != 0
544                        || (access & ACCESS_FIELD) != 0 || (access & ACCESS_INNER) != 0))
545        {
546            if (!first) {
547                buf.append(" + ");
548            }
549            buf.append("ACC_ENUM");
550            first = false;
551        }
552        if ((access & Opcodes.ACC_ANNOTATION) != 0
553                && ((access & ACCESS_CLASS) != 0))
554        {
555            if (!first) {
556                buf.append(" + ");
557            }
558            buf.append("ACC_ANNOTATION");
559            first = false;
560        }
561        if ((access & Opcodes.ACC_ABSTRACT) != 0) {
562            if (!first) {
563                buf.append(" + ");
564            }
565            buf.append("ACC_ABSTRACT");
566            first = false;
567        }
568        if ((access & Opcodes.ACC_INTERFACE) != 0) {
569            if (!first) {
570                buf.append(" + ");
571            }
572            buf.append("ACC_INTERFACE");
573            first = false;
574        }
575        if ((access & Opcodes.ACC_STRICT) != 0) {
576            if (!first) {
577                buf.append(" + ");
578            }
579            buf.append("ACC_STRICT");
580            first = false;
581        }
582        if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
583            if (!first) {
584                buf.append(" + ");
585            }
586            buf.append("ACC_SYNTHETIC");
587            first = false;
588        }
589        if ((access & Opcodes.ACC_DEPRECATED) != 0) {
590            if (!first) {
591                buf.append(" + ");
592            }
593            buf.append("ACC_DEPRECATED");
594            first = false;
595        }
596        if (first) {
597            buf.append("0");
598        }
599    }
600
601    /**
602     * Appends a string representation of the given constant to the given
603     * buffer.
604     *
605     * @param buf a string buffer.
606     * @param cst an {@link java.lang.Integer Integer}, {@link java.lang.Float
607     *        Float}, {@link java.lang.Long Long},
608     *        {@link java.lang.Double Double} or {@link String String} object.
609     *        May be <tt>null</tt>.
610     */
611    static void appendConstant(final StringBuffer buf, final Object cst) {
612        if (cst == null) {
613            buf.append("null");
614        } else if (cst instanceof String) {
615            AbstractVisitor.appendString(buf, (String) cst);
616        } else if (cst instanceof Type) {
617            buf.append("Type.getType(\"")
618                    .append(((Type) cst).getDescriptor())
619                    .append("\")");
620        } else if (cst instanceof Integer) {
621            buf.append("new Integer(").append(cst).append(")");
622        } else if (cst instanceof Float) {
623            buf.append("new Float(\"").append(cst).append("\")");
624        } else if (cst instanceof Long) {
625            buf.append("new Long(").append(cst).append("L)");
626        } else if (cst instanceof Double) {
627            buf.append("new Double(\"").append(cst).append("\")");
628        }
629    }
630}
631