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.Attribute;
37import org.mockito.asm.ClassReader;
38import org.mockito.asm.ClassVisitor;
39import org.mockito.asm.FieldVisitor;
40import org.mockito.asm.MethodVisitor;
41import org.mockito.asm.Opcodes;
42import 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 */
94public 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