001: /*
002: * ASM: a very small and fast Java bytecode manipulation framework
003: * Copyright (c) 2000-2005 INRIA, France Telecom
004: * All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions
008: * are met:
009: * 1. Redistributions of source code must retain the above copyright
010: * notice, this list of conditions and the following disclaimer.
011: * 2. Redistributions in binary form must reproduce the above copyright
012: * notice, this list of conditions and the following disclaimer in the
013: * documentation and/or other materials provided with the distribution.
014: * 3. Neither the name of the copyright holders nor the names of its
015: * contributors may be used to endorse or promote products derived from
016: * this software without specific prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
020: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
021: * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
022: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
023: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
024: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
025: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
026: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
027: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
028: * THE POSSIBILITY OF SUCH DAMAGE.
029: */
030: package org.objectweb.asm.util;
031:
032: import java.io.FileInputStream;
033: import java.io.PrintWriter;
034:
035: import org.objectweb.asm.AnnotationVisitor;
036: import org.objectweb.asm.Attribute;
037: import org.objectweb.asm.ClassReader;
038: import org.objectweb.asm.ClassVisitor;
039: import org.objectweb.asm.MethodVisitor;
040: import org.objectweb.asm.Opcodes;
041: import org.objectweb.asm.FieldVisitor;
042: import org.objectweb.asm.signature.SignatureReader;
043:
044: /**
045: * A {@link ClassVisitor} that prints a disassembled view of the classes it
046: * visits. This class visitor can be used alone (see the {@link #main main}
047: * method) to disassemble a class. It can also be used in the middle of class
048: * visitor chain to trace the class that is visited at a given point in this
049: * chain. This may be uselful for debugging purposes. <p> The trace printed when
050: * visiting the <tt>Hello</tt> class is the following: <p> <blockquote>
051: *
052: * <pre>
053: * // class version 49.0 (49)
054: * // access flags 33
055: * public class Hello {
056: *
057: * // compiled from: Hello.java
058: *
059: * // access flags 1
060: * public <init> ()V
061: * ALOAD 0
062: * INVOKESPECIAL java/lang/Object <init> ()V
063: * RETURN
064: * MAXSTACK = 1
065: * MAXLOCALS = 1
066: *
067: * // access flags 9
068: * public static main ([Ljava/lang/String;)V
069: * GETSTATIC java/lang/System out Ljava/io/PrintStream;
070: * LDC "hello"
071: * INVOKEVIRTUAL java/io/PrintStream println (Ljava/lang/String;)V
072: * RETURN
073: * MAXSTACK = 2
074: * MAXLOCALS = 1
075: * }
076: * </pre>
077: *
078: * </blockquote> where <tt>Hello</tt> is defined by: <p> <blockquote>
079: *
080: * <pre>
081: * public class Hello {
082: *
083: * public static void main(String[] args) {
084: * System.out.println("hello");
085: * }
086: * }
087: * </pre>
088: *
089: * </blockquote>
090: *
091: * @author Eric Bruneton
092: * @author Eugene Kuleshov
093: */
094: @SuppressWarnings("unchecked")
095: public class TraceClassVisitor extends TraceAbstractVisitor implements
096: ClassVisitor {
097:
098: /**
099: * 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] <fully qualified class name or class
112: * file name >
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 && args[0].equals("-debug")) {
128: i = 1;
129: flags = 0;
130: if (args.length != 2) {
131: ok = false;
132: }
133: }
134: if (!ok) {
135: System.err
136: .println("Prints a disassembled view of the given class.");
137: System.err
138: .println("Usage: TraceClassVisitor [-debug] "
139: + "<fully qualified class name or class file name>");
140: return;
141: }
142: ClassReader cr;
143: if (args[i].endsWith(".class") || args[i].indexOf('\\') > -1
144: || args[i].indexOf('/') > -1) {
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(), 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(final int version, final int access,
179: final String name, final String signature,
180: final String super Name, final String[] interfaces) {
181: int major = version & 0xFFFF;
182: int minor = version >>> 16;
183: buf.setLength(0);
184: buf.append("// class version ").append(major).append('.')
185: .append(minor).append(" (").append(version).append(
186: ")\n");
187: if ((access & Opcodes.ACC_DEPRECATED) != 0) {
188: buf.append("// DEPRECATED\n");
189: }
190: buf.append("// access flags ").append(access).append('\n');
191:
192: appendDescriptor(CLASS_SIGNATURE, signature);
193: if (signature != null) {
194: TraceSignatureVisitor sv = new TraceSignatureVisitor(access);
195: SignatureReader r = new SignatureReader(signature);
196: r.accept(sv);
197: buf.append("// declaration: ").append(name).append(
198: sv.getDeclaration()).append('\n');
199: }
200:
201: appendAccess(access & ~Opcodes.ACC_SUPER);
202: if ((access & Opcodes.ACC_ANNOTATION) != 0) {
203: buf.append("@interface ");
204: } else if ((access & Opcodes.ACC_INTERFACE) != 0) {
205: buf.append("interface ");
206: } else if ((access & Opcodes.ACC_ENUM) == 0) {
207: buf.append("class ");
208: }
209: appendDescriptor(INTERNAL_NAME, name);
210:
211: if (super Name != null && !super Name.equals("java/lang/Object")) {
212: buf.append(" extends ");
213: appendDescriptor(INTERNAL_NAME, super Name);
214: buf.append(' ');
215: }
216: if (interfaces != null && interfaces.length > 0) {
217: buf.append(" implements ");
218: for (int i = 0; i < interfaces.length; ++i) {
219: appendDescriptor(INTERNAL_NAME, interfaces[i]);
220: buf.append(' ');
221: }
222: }
223: buf.append(" {\n\n");
224:
225: text.add(buf.toString());
226:
227: if (cv != null) {
228: cv.visit(version, access, name, signature, super Name,
229: interfaces);
230: }
231: }
232:
233: public void visitSource(final String file, final String debug) {
234: buf.setLength(0);
235: if (file != null) {
236: buf.append(tab).append("// compiled from: ").append(file)
237: .append('\n');
238: }
239: if (debug != null) {
240: buf.append(tab).append("// debug info: ").append(debug)
241: .append('\n');
242: }
243: if (buf.length() > 0) {
244: text.add(buf.toString());
245: }
246:
247: if (cv != null) {
248: cv.visitSource(file, debug);
249: }
250: }
251:
252: public void visitOuterClass(final String owner, final String name,
253: final String desc) {
254: buf.setLength(0);
255: buf.append(tab).append("OUTERCLASS ");
256: appendDescriptor(INTERNAL_NAME, owner);
257: buf.append(' ');
258: if (name != null) {
259: buf.append(name).append(' ');
260: }
261: appendDescriptor(METHOD_DESCRIPTOR, desc);
262: buf.append('\n');
263: text.add(buf.toString());
264:
265: if (cv != null) {
266: cv.visitOuterClass(owner, name, desc);
267: }
268: }
269:
270: public AnnotationVisitor visitAnnotation(final String desc,
271: final boolean visible) {
272: text.add("\n");
273: AnnotationVisitor tav = super .visitAnnotation(desc, visible);
274: if (cv != null) {
275: ((TraceAnnotationVisitor) tav).av = cv.visitAnnotation(
276: desc, visible);
277: }
278: return tav;
279: }
280:
281: public void visitAttribute(final Attribute attr) {
282: text.add("\n");
283: super .visitAttribute(attr);
284:
285: if (cv != null) {
286: cv.visitAttribute(attr);
287: }
288: }
289:
290: public void visitInnerClass(final String name,
291: final String outerName, final String innerName,
292: final int access) {
293: buf.setLength(0);
294: buf.append(tab).append("// access flags ");
295: buf.append(access & ~Opcodes.ACC_SUPER).append('\n');
296: buf.append(tab);
297: appendAccess(access);
298: buf.append("INNERCLASS ");
299: appendDescriptor(INTERNAL_NAME, name);
300: buf.append(' ');
301: appendDescriptor(INTERNAL_NAME, outerName);
302: buf.append(' ');
303: appendDescriptor(INTERNAL_NAME, innerName);
304: buf.append('\n');
305: text.add(buf.toString());
306:
307: if (cv != null) {
308: cv.visitInnerClass(name, outerName, innerName, access);
309: }
310: }
311:
312: public FieldVisitor visitField(final int access, final String name,
313: final String desc, final String signature,
314: final Object value) {
315: buf.setLength(0);
316: buf.append('\n');
317: if ((access & Opcodes.ACC_DEPRECATED) != 0) {
318: buf.append(tab).append("// DEPRECATED\n");
319: }
320: buf.append(tab).append("// access flags ").append(access)
321: .append('\n');
322: if (signature != null) {
323: buf.append(tab);
324: appendDescriptor(FIELD_SIGNATURE, signature);
325:
326: TraceSignatureVisitor sv = new TraceSignatureVisitor(0);
327: SignatureReader r = new SignatureReader(signature);
328: r.acceptType(sv);
329: buf.append(tab).append("// declaration: ").append(
330: sv.getDeclaration()).append('\n');
331: }
332:
333: buf.append(tab);
334: appendAccess(access);
335:
336: appendDescriptor(FIELD_DESCRIPTOR, desc);
337: buf.append(' ').append(name);
338: if (value != null) {
339: buf.append(" = ");
340: if (value instanceof String) {
341: buf.append("\"").append(value).append("\"");
342: } else {
343: buf.append(value);
344: }
345: }
346:
347: buf.append('\n');
348: text.add(buf.toString());
349:
350: TraceFieldVisitor tav = createTraceFieldVisitor();
351: text.add(tav.getText());
352:
353: if (cv != null) {
354: tav.fv = cv
355: .visitField(access, name, desc, signature, value);
356: }
357:
358: return tav;
359: }
360:
361: public MethodVisitor visitMethod(final int access,
362: final String name, final String desc,
363: final String signature, final String[] exceptions) {
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)
370: .append('\n');
371:
372: if (signature != null) {
373: buf.append(tab);
374: appendDescriptor(METHOD_SIGNATURE, signature);
375:
376: TraceSignatureVisitor v = new TraceSignatureVisitor(0);
377: SignatureReader r = new SignatureReader(signature);
378: r.accept(v);
379: String genericDecl = v.getDeclaration();
380: String genericReturn = v.getReturnType();
381: String genericExceptions = v.getExceptions();
382:
383: buf.append(tab).append("// declaration: ").append(
384: genericReturn).append(' ').append(name).append(
385: genericDecl);
386: if (genericExceptions != null) {
387: buf.append(" throws ").append(genericExceptions);
388: }
389: buf.append('\n');
390: }
391:
392: buf.append(tab);
393: appendAccess(access);
394: if ((access & Opcodes.ACC_NATIVE) != 0) {
395: buf.append("native ");
396: }
397: if ((access & Opcodes.ACC_VARARGS) != 0) {
398: buf.append("varargs ");
399: }
400: if ((access & Opcodes.ACC_BRIDGE) != 0) {
401: buf.append("bridge ");
402: }
403:
404: buf.append(name);
405: appendDescriptor(METHOD_DESCRIPTOR, desc);
406: if (exceptions != null && exceptions.length > 0) {
407: buf.append(" throws ");
408: for (int i = 0; i < exceptions.length; ++i) {
409: appendDescriptor(INTERNAL_NAME, exceptions[i]);
410: buf.append(' ');
411: }
412: }
413:
414: buf.append('\n');
415: text.add(buf.toString());
416:
417: TraceMethodVisitor tcv = createTraceMethodVisitor();
418: text.add(tcv.getText());
419:
420: if (cv != null) {
421: tcv.mv = cv.visitMethod(access, name, desc, signature,
422: exceptions);
423: }
424:
425: return tcv;
426: }
427:
428: public void visitEnd() {
429: text.add("}\n");
430:
431: printList(pw, text);
432: pw.flush();
433:
434: if (cv != null) {
435: cv.visitEnd();
436: }
437: }
438:
439: // ------------------------------------------------------------------------
440: // Utility methods
441: // ------------------------------------------------------------------------
442:
443: protected TraceFieldVisitor createTraceFieldVisitor() {
444: return new TraceFieldVisitor();
445: }
446:
447: protected TraceMethodVisitor createTraceMethodVisitor() {
448: return new TraceMethodVisitor();
449: }
450:
451: /**
452: * Appends a string representation of the given access modifiers to {@link
453: * #buf buf}.
454: *
455: * @param access some access modifiers.
456: */
457: private void appendAccess(final int access) {
458: if ((access & Opcodes.ACC_PUBLIC) != 0) {
459: buf.append("public ");
460: }
461: if ((access & Opcodes.ACC_PRIVATE) != 0) {
462: buf.append("private ");
463: }
464: if ((access & Opcodes.ACC_PROTECTED) != 0) {
465: buf.append("protected ");
466: }
467: if ((access & Opcodes.ACC_FINAL) != 0) {
468: buf.append("final ");
469: }
470: if ((access & Opcodes.ACC_STATIC) != 0) {
471: buf.append("static ");
472: }
473: if ((access & Opcodes.ACC_SYNCHRONIZED) != 0) {
474: buf.append("synchronized ");
475: }
476: if ((access & Opcodes.ACC_VOLATILE) != 0) {
477: buf.append("volatile ");
478: }
479: if ((access & Opcodes.ACC_TRANSIENT) != 0) {
480: buf.append("transient ");
481: }
482: if ((access & Opcodes.ACC_ABSTRACT) != 0) {
483: buf.append("abstract ");
484: }
485: if ((access & Opcodes.ACC_STRICT) != 0) {
486: buf.append("strictfp ");
487: }
488: if ((access & Opcodes.ACC_ENUM) != 0) {
489: buf.append("enum ");
490: }
491: }
492: }
|