Home | History | Annotate | Download | only in util
      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  */
     30 package org.mockito.asm.util;
     31 
     32 import java.io.FileInputStream;
     33 import java.io.PrintWriter;
     34 
     35 import org.mockito.asm.AnnotationVisitor;
     36 import org.mockito.asm.Attribute;
     37 import org.mockito.asm.ClassReader;
     38 import org.mockito.asm.ClassVisitor;
     39 import org.mockito.asm.FieldVisitor;
     40 import org.mockito.asm.MethodVisitor;
     41 import org.mockito.asm.Opcodes;
     42 import org.mockito.asm.signature.SignatureReader;
     43 
     44 /**
     45  * A {@link ClassVisitor} that prints a disassembled view of the classes it
     46  * visits. This class visitor can be used alone (see the {@link #main main}
     47  * method) to disassemble a class. It can also be used in the middle of class
     48  * visitor chain to trace the class that is visited at a given point in this
     49  * chain. This may be uselful for debugging purposes. <p> The trace printed when
     50  * visiting the <tt>Hello</tt> class is the following: <p> <blockquote>
     51  *
     52  * <pre>
     53  * // class version 49.0 (49)
     54  * // access flags 33
     55  * public class Hello {
     56  *
     57  *  // compiled from: Hello.java
     58  *
     59  *   // access flags 1
     60  *   public &lt;init&gt; ()V
     61  *     ALOAD 0
     62  *     INVOKESPECIAL java/lang/Object &lt;init&gt; ()V
     63  *     RETURN
     64  *     MAXSTACK = 1
     65  *     MAXLOCALS = 1
     66  *
     67  *   // access flags 9
     68  *   public static main ([Ljava/lang/String;)V
     69  *     GETSTATIC java/lang/System out Ljava/io/PrintStream;
     70  *     LDC &quot;hello&quot;
     71  *     INVOKEVIRTUAL java/io/PrintStream println (Ljava/lang/String;)V
     72  *     RETURN
     73  *     MAXSTACK = 2
     74  *     MAXLOCALS = 1
     75  * }
     76  * </pre>
     77  *
     78  * </blockquote> where <tt>Hello</tt> is defined by: <p> <blockquote>
     79  *
     80  * <pre>
     81  * public class Hello {
     82  *
     83  *     public static void main(String[] args) {
     84  *         System.out.println(&quot;hello&quot;);
     85  *     }
     86  * }
     87  * </pre>
     88  *
     89  * </blockquote>
     90  *
     91  * @author Eric Bruneton
     92  * @author Eugene Kuleshov
     93  */
     94 public class TraceClassVisitor extends TraceAbstractVisitor implements
     95         ClassVisitor
     96 {
     97 
     98     /**
     99      * The {@link ClassVisitor} to which this visitor delegates calls. May be
    100      * <tt>null</tt>.
    101      */
    102     protected final ClassVisitor cv;
    103 
    104     /**
    105      * The print writer to be used to print the class.
    106      */
    107     protected final PrintWriter pw;
    108 
    109     /**
    110      * Prints a disassembled view of the given class to the standard output. <p>
    111      * Usage: TraceClassVisitor [-debug] &lt;fully qualified class name or class
    112      * file name &gt;
    113      *
    114      * @param args the command line arguments.
    115      *
    116      * @throws Exception if the class cannot be found, or if an IO exception
    117      *         occurs.
    118      */
    119     public static void main(final String[] args) throws Exception {
    120         int i = 0;
    121         int flags = ClassReader.SKIP_DEBUG;
    122 
    123         boolean ok = true;
    124         if (args.length < 1 || args.length > 2) {
    125             ok = false;
    126         }
    127         if (ok && "-debug".equals(args[0])) {
    128             i = 1;
    129             flags = 0;
    130             if (args.length != 2) {
    131                 ok = false;
    132             }
    133         }
    134         if (!ok) {
    135             System.err.println("Prints a disassembled view of the given class.");
    136             System.err.println("Usage: TraceClassVisitor [-debug] "
    137                     + "<fully qualified class name or class file name>");
    138             return;
    139         }
    140         ClassReader cr;
    141         if (args[i].endsWith(".class") || args[i].indexOf('\\') > -1
    142                 || args[i].indexOf('/') > -1)
    143         {
    144             cr = new ClassReader(new FileInputStream(args[i]));
    145         } else {
    146             cr = new ClassReader(args[i]);
    147         }
    148         cr.accept(new TraceClassVisitor(new PrintWriter(System.out)),
    149                 getDefaultAttributes(),
    150                 flags);
    151     }
    152 
    153     /**
    154      * Constructs a new {@link TraceClassVisitor}.
    155      *
    156      * @param pw the print writer to be used to print the class.
    157      */
    158     public TraceClassVisitor(final PrintWriter pw) {
    159         this(null, pw);
    160     }
    161 
    162     /**
    163      * Constructs a new {@link TraceClassVisitor}.
    164      *
    165      * @param cv the {@link ClassVisitor} to which this visitor delegates calls.
    166      *        May be <tt>null</tt>.
    167      * @param pw the print writer to be used to print the class.
    168      */
    169     public TraceClassVisitor(final ClassVisitor cv, final PrintWriter pw) {
    170         this.cv = cv;
    171         this.pw = pw;
    172     }
    173 
    174     // ------------------------------------------------------------------------
    175     // Implementation of the ClassVisitor interface
    176     // ------------------------------------------------------------------------
    177 
    178     public void visit(
    179         final int version,
    180         final int access,
    181         final String name,
    182         final String signature,
    183         final String superName,
    184         final String[] interfaces)
    185     {
    186         int major = version & 0xFFFF;
    187         int minor = version >>> 16;
    188         buf.setLength(0);
    189         buf.append("// class version ")
    190                 .append(major)
    191                 .append('.')
    192                 .append(minor)
    193                 .append(" (")
    194                 .append(version)
    195                 .append(")\n");
    196         if ((access & Opcodes.ACC_DEPRECATED) != 0) {
    197             buf.append("// DEPRECATED\n");
    198         }
    199         buf.append("// access flags ").append(access).append('\n');
    200 
    201         appendDescriptor(CLASS_SIGNATURE, signature);
    202         if (signature != null) {
    203             TraceSignatureVisitor sv = new TraceSignatureVisitor(access);
    204             SignatureReader r = new SignatureReader(signature);
    205             r.accept(sv);
    206             buf.append("// declaration: ")
    207                     .append(name)
    208                     .append(sv.getDeclaration())
    209                     .append('\n');
    210         }
    211 
    212         appendAccess(access & ~Opcodes.ACC_SUPER);
    213         if ((access & Opcodes.ACC_ANNOTATION) != 0) {
    214             buf.append("@interface ");
    215         } else if ((access & Opcodes.ACC_INTERFACE) != 0) {
    216             buf.append("interface ");
    217         } else if ((access & Opcodes.ACC_ENUM) == 0) {
    218             buf.append("class ");
    219         }
    220         appendDescriptor(INTERNAL_NAME, name);
    221 
    222         if (superName != null && !"java/lang/Object".equals(superName)) {
    223             buf.append(" extends ");
    224             appendDescriptor(INTERNAL_NAME, superName);
    225             buf.append(' ');
    226         }
    227         if (interfaces != null && interfaces.length > 0) {
    228             buf.append(" implements ");
    229             for (int i = 0; i < interfaces.length; ++i) {
    230                 appendDescriptor(INTERNAL_NAME, interfaces[i]);
    231                 buf.append(' ');
    232             }
    233         }
    234         buf.append(" {\n\n");
    235 
    236         text.add(buf.toString());
    237 
    238         if (cv != null) {
    239             cv.visit(version, access, name, signature, superName, interfaces);
    240         }
    241     }
    242 
    243     public void visitSource(final String file, final String debug) {
    244         buf.setLength(0);
    245         if (file != null) {
    246             buf.append(tab)
    247                     .append("// compiled from: ")
    248                     .append(file)
    249                     .append('\n');
    250         }
    251         if (debug != null) {
    252             buf.append(tab)
    253                     .append("// debug info: ")
    254                     .append(debug)
    255                     .append('\n');
    256         }
    257         if (buf.length() > 0) {
    258             text.add(buf.toString());
    259         }
    260 
    261         if (cv != null) {
    262             cv.visitSource(file, debug);
    263         }
    264     }
    265 
    266     public void visitOuterClass(
    267         final String owner,
    268         final String name,
    269         final String desc)
    270     {
    271         buf.setLength(0);
    272         buf.append(tab).append("OUTERCLASS ");
    273         appendDescriptor(INTERNAL_NAME, owner);
    274         buf.append(' ');
    275         if (name != null) {
    276             buf.append(name).append(' ');
    277         }
    278         appendDescriptor(METHOD_DESCRIPTOR, desc);
    279         buf.append('\n');
    280         text.add(buf.toString());
    281 
    282         if (cv != null) {
    283             cv.visitOuterClass(owner, name, desc);
    284         }
    285     }
    286 
    287     public AnnotationVisitor visitAnnotation(
    288         final String desc,
    289         final boolean visible)
    290     {
    291         text.add("\n");
    292         AnnotationVisitor tav = super.visitAnnotation(desc, visible);
    293         if (cv != null) {
    294             ((TraceAnnotationVisitor) tav).av = cv.visitAnnotation(desc,
    295                     visible);
    296         }
    297         return tav;
    298     }
    299 
    300     public void visitAttribute(final Attribute attr) {
    301         text.add("\n");
    302         super.visitAttribute(attr);
    303 
    304         if (cv != null) {
    305             cv.visitAttribute(attr);
    306         }
    307     }
    308 
    309     public void visitInnerClass(
    310         final String name,
    311         final String outerName,
    312         final String innerName,
    313         final int access)
    314     {
    315         buf.setLength(0);
    316         buf.append(tab).append("// access flags ");
    317         buf.append(access & ~Opcodes.ACC_SUPER).append('\n');
    318         buf.append(tab);
    319         appendAccess(access);
    320         buf.append("INNERCLASS ");
    321         appendDescriptor(INTERNAL_NAME, name);
    322         buf.append(' ');
    323         appendDescriptor(INTERNAL_NAME, outerName);
    324         buf.append(' ');
    325         appendDescriptor(INTERNAL_NAME, innerName);
    326         buf.append('\n');
    327         text.add(buf.toString());
    328 
    329         if (cv != null) {
    330             cv.visitInnerClass(name, outerName, innerName, access);
    331         }
    332     }
    333 
    334     public FieldVisitor visitField(
    335         final int access,
    336         final String name,
    337         final String desc,
    338         final String signature,
    339         final Object value)
    340     {
    341         buf.setLength(0);
    342         buf.append('\n');
    343         if ((access & Opcodes.ACC_DEPRECATED) != 0) {
    344             buf.append(tab).append("// DEPRECATED\n");
    345         }
    346         buf.append(tab).append("// access flags ").append(access).append('\n');
    347         if (signature != null) {
    348             buf.append(tab);
    349             appendDescriptor(FIELD_SIGNATURE, signature);
    350 
    351             TraceSignatureVisitor sv = new TraceSignatureVisitor(0);
    352             SignatureReader r = new SignatureReader(signature);
    353             r.acceptType(sv);
    354             buf.append(tab)
    355                     .append("// declaration: ")
    356                     .append(sv.getDeclaration())
    357                     .append('\n');
    358         }
    359 
    360         buf.append(tab);
    361         appendAccess(access);
    362 
    363         appendDescriptor(FIELD_DESCRIPTOR, desc);
    364         buf.append(' ').append(name);
    365         if (value != null) {
    366             buf.append(" = ");
    367             if (value instanceof String) {
    368                 buf.append('\"').append(value).append('\"');
    369             } else {
    370                 buf.append(value);
    371             }
    372         }
    373 
    374         buf.append('\n');
    375         text.add(buf.toString());
    376 
    377         TraceFieldVisitor tav = createTraceFieldVisitor();
    378         text.add(tav.getText());
    379 
    380         if (cv != null) {
    381             tav.fv = cv.visitField(access, name, desc, signature, value);
    382         }
    383 
    384         return tav;
    385     }
    386 
    387     public MethodVisitor visitMethod(
    388         final int access,
    389         final String name,
    390         final String desc,
    391         final String signature,
    392         final String[] exceptions)
    393     {
    394         buf.setLength(0);
    395         buf.append('\n');
    396         if ((access & Opcodes.ACC_DEPRECATED) != 0) {
    397             buf.append(tab).append("// DEPRECATED\n");
    398         }
    399         buf.append(tab).append("// access flags ").append(access).append('\n');
    400 
    401         if (signature != null) {
    402             buf.append(tab);
    403             appendDescriptor(METHOD_SIGNATURE, signature);
    404 
    405             TraceSignatureVisitor v = new TraceSignatureVisitor(0);
    406             SignatureReader r = new SignatureReader(signature);
    407             r.accept(v);
    408             String genericDecl = v.getDeclaration();
    409             String genericReturn = v.getReturnType();
    410             String genericExceptions = v.getExceptions();
    411 
    412             buf.append(tab)
    413                     .append("// declaration: ")
    414                     .append(genericReturn)
    415                     .append(' ')
    416                     .append(name)
    417                     .append(genericDecl);
    418             if (genericExceptions != null) {
    419                 buf.append(" throws ").append(genericExceptions);
    420             }
    421             buf.append('\n');
    422         }
    423 
    424         buf.append(tab);
    425         appendAccess(access);
    426         if ((access & Opcodes.ACC_NATIVE) != 0) {
    427             buf.append("native ");
    428         }
    429         if ((access & Opcodes.ACC_VARARGS) != 0) {
    430             buf.append("varargs ");
    431         }
    432         if ((access & Opcodes.ACC_BRIDGE) != 0) {
    433             buf.append("bridge ");
    434         }
    435 
    436         buf.append(name);
    437         appendDescriptor(METHOD_DESCRIPTOR, desc);
    438         if (exceptions != null && exceptions.length > 0) {
    439             buf.append(" throws ");
    440             for (int i = 0; i < exceptions.length; ++i) {
    441                 appendDescriptor(INTERNAL_NAME, exceptions[i]);
    442                 buf.append(' ');
    443             }
    444         }
    445 
    446         buf.append('\n');
    447         text.add(buf.toString());
    448 
    449         TraceMethodVisitor tcv = createTraceMethodVisitor();
    450         text.add(tcv.getText());
    451 
    452         if (cv != null) {
    453             tcv.mv = cv.visitMethod(access, name, desc, signature, exceptions);
    454         }
    455 
    456         return tcv;
    457     }
    458 
    459     public void visitEnd() {
    460         text.add("}\n");
    461 
    462         print(pw);
    463         pw.flush();
    464 
    465         if (cv != null) {
    466             cv.visitEnd();
    467         }
    468     }
    469 
    470     // ------------------------------------------------------------------------
    471     // Utility methods
    472     // ------------------------------------------------------------------------
    473 
    474     protected TraceFieldVisitor createTraceFieldVisitor() {
    475         return new TraceFieldVisitor();
    476     }
    477 
    478     protected TraceMethodVisitor createTraceMethodVisitor() {
    479         return new TraceMethodVisitor();
    480     }
    481 
    482     /**
    483      * Appends a string representation of the given access modifiers to {@link
    484      * #buf buf}.
    485      *
    486      * @param access some access modifiers.
    487      */
    488     private void appendAccess(final int access) {
    489         if ((access & Opcodes.ACC_PUBLIC) != 0) {
    490             buf.append("public ");
    491         }
    492         if ((access & Opcodes.ACC_PRIVATE) != 0) {
    493             buf.append("private ");
    494         }
    495         if ((access & Opcodes.ACC_PROTECTED) != 0) {
    496             buf.append("protected ");
    497         }
    498         if ((access & Opcodes.ACC_FINAL) != 0) {
    499             buf.append("final ");
    500         }
    501         if ((access & Opcodes.ACC_STATIC) != 0) {
    502             buf.append("static ");
    503         }
    504         if ((access & Opcodes.ACC_SYNCHRONIZED) != 0) {
    505             buf.append("synchronized ");
    506         }
    507         if ((access & Opcodes.ACC_VOLATILE) != 0) {
    508             buf.append("volatile ");
    509         }
    510         if ((access & Opcodes.ACC_TRANSIENT) != 0) {
    511             buf.append("transient ");
    512         }
    513         if ((access & Opcodes.ACC_ABSTRACT) != 0) {
    514             buf.append("abstract ");
    515         }
    516         if ((access & Opcodes.ACC_STRICT) != 0) {
    517             buf.append("strictfp ");
    518         }
    519         if ((access & Opcodes.ACC_ENUM) != 0) {
    520             buf.append("enum ");
    521         }
    522     }
    523 }
    524