Home | History | Annotate | Download | only in util
      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  */
     30 package org.objectweb.asm.util;
     31 
     32 import java.io.FileInputStream;
     33 import java.io.PrintWriter;
     34 
     35 import org.objectweb.asm.AnnotationVisitor;
     36 import org.objectweb.asm.ClassReader;
     37 import org.objectweb.asm.ClassVisitor;
     38 import org.objectweb.asm.TypeAnnotationVisitor;
     39 import org.objectweb.asm.FieldVisitor;
     40 import org.objectweb.asm.MethodVisitor;
     41 import org.objectweb.asm.Opcodes;
     42 import 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  */
    129 public 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