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