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: */package org.ejb3unit.asm.util;
030:
031: import java.io.FileInputStream;
032: import java.io.PrintWriter;
033:
034: import org.ejb3unit.asm.AnnotationVisitor;
035: import org.ejb3unit.asm.Attribute;
036: import org.ejb3unit.asm.ClassReader;
037: import org.ejb3unit.asm.ClassVisitor;
038: import org.ejb3unit.asm.FieldVisitor;
039: import org.ejb3unit.asm.MethodVisitor;
040: import org.ejb3unit.asm.Opcodes;
041: import org.ejb3unit.asm.signature.SignatureReader;
042:
043: /**
044: * A {@link ClassVisitor} that prints a disassembled view of the classes it
045: * visits. This class visitor can be used alone (see the {@link #main main}
046: * method) to disassemble a class. It can also be used in the middle of class
047: * visitor chain to trace the class that is visited at a given point in this
048: * chain. This may be uselful for debugging purposes. <p> The trace printed when
049: * visiting the <tt>Hello</tt> class is the following: <p> <blockquote>
050: *
051: * <pre>
052: * // class version 49.0 (49)
053: * // access flags 33
054: * public class Hello {
055: *
056: * // compiled from: Hello.java
057: *
058: * // access flags 1
059: * public <init> ()V
060: * ALOAD 0
061: * INVOKESPECIAL java/lang/Object <init> ()V
062: * RETURN
063: * MAXSTACK = 1
064: * MAXLOCALS = 1
065: *
066: * // access flags 9
067: * public static main ([Ljava/lang/String;)V
068: * GETSTATIC java/lang/System out Ljava/io/PrintStream;
069: * LDC "hello"
070: * INVOKEVIRTUAL java/io/PrintStream println (Ljava/lang/String;)V
071: * RETURN
072: * MAXSTACK = 2
073: * MAXLOCALS = 1
074: * }
075: * </pre>
076: *
077: * </blockquote> where <tt>Hello</tt> is defined by: <p> <blockquote>
078: *
079: * <pre>
080: * public class Hello {
081: *
082: * public static void main(String[] args) {
083: * System.out.println("hello");
084: * }
085: * }
086: * </pre>
087: *
088: * </blockquote>
089: *
090: * @author Eric Bruneton
091: * @author Eugene Kuleshov
092: */
093: public class TraceClassVisitor extends TraceAbstractVisitor implements
094: ClassVisitor {
095:
096: /**
097: * The {@link ClassVisitor} to which this visitor delegates calls. May be
098: * <tt>null</tt>.
099: */
100: protected final ClassVisitor cv;
101:
102: /**
103: * The print writer to be used to print the class.
104: */
105: protected final PrintWriter pw;
106:
107: /**
108: * Prints a disassembled view of the given class to the standard output. <p>
109: * Usage: TraceClassVisitor [-debug] <fully qualified class name or class
110: * file name >
111: *
112: * @param args the command line arguments.
113: *
114: * @throws Exception if the class cannot be found, or if an IO exception
115: * occurs.
116: */
117: public static void main(final String[] args) throws Exception {
118: int i = 0;
119: int flags = ClassReader.SKIP_DEBUG;
120:
121: boolean ok = true;
122: if (args.length < 1 || args.length > 2) {
123: ok = false;
124: }
125: if (ok && args[0].equals("-debug")) {
126: i = 1;
127: flags = 0;
128: if (args.length != 2) {
129: ok = false;
130: }
131: }
132: if (!ok) {
133: System.err
134: .println("Prints a disassembled view of the given class.");
135: System.err
136: .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: cr = new ClassReader(new FileInputStream(args[i]));
144: } else {
145: cr = new ClassReader(args[i]);
146: }
147: cr.accept(new TraceClassVisitor(new PrintWriter(System.out)),
148: getDefaultAttributes(), flags);
149: }
150:
151: /**
152: * Constructs a new {@link TraceClassVisitor}.
153: *
154: * @param pw the print writer to be used to print the class.
155: */
156: public TraceClassVisitor(final PrintWriter pw) {
157: this (null, pw);
158: }
159:
160: /**
161: * Constructs a new {@link TraceClassVisitor}.
162: *
163: * @param cv the {@link ClassVisitor} to which this visitor delegates calls.
164: * May be <tt>null</tt>.
165: * @param pw the print writer to be used to print the class.
166: */
167: public TraceClassVisitor(final ClassVisitor cv, final PrintWriter pw) {
168: this .cv = cv;
169: this .pw = pw;
170: }
171:
172: // ------------------------------------------------------------------------
173: // Implementation of the ClassVisitor interface
174: // ------------------------------------------------------------------------
175:
176: public void visit(final int version, final int access,
177: final String name, final String signature,
178: final String super Name, final String[] interfaces) {
179: int major = version & 0xFFFF;
180: int minor = version >>> 16;
181: buf.setLength(0);
182: buf.append("// class version ").append(major).append('.')
183: .append(minor).append(" (").append(version).append(
184: ")\n");
185: if ((access & Opcodes.ACC_DEPRECATED) != 0) {
186: buf.append("// DEPRECATED\n");
187: }
188: buf.append("// access flags ").append(access).append('\n');
189:
190: appendDescriptor(CLASS_SIGNATURE, signature);
191: if (signature != null) {
192: TraceSignatureVisitor sv = new TraceSignatureVisitor(access);
193: SignatureReader r = new SignatureReader(signature);
194: r.accept(sv);
195: buf.append("// declaration: ").append(name).append(
196: sv.getDeclaration()).append('\n');
197: }
198:
199: appendAccess(access & ~Opcodes.ACC_SUPER);
200: if ((access & Opcodes.ACC_ANNOTATION) != 0) {
201: buf.append("@interface ");
202: } else if ((access & Opcodes.ACC_INTERFACE) != 0) {
203: buf.append("interface ");
204: } else if ((access & Opcodes.ACC_ENUM) == 0) {
205: buf.append("class ");
206: }
207: appendDescriptor(INTERNAL_NAME, name);
208:
209: if (super Name != null && !super Name.equals("java/lang/Object")) {
210: buf.append(" extends ");
211: appendDescriptor(INTERNAL_NAME, super Name);
212: buf.append(' ');
213: }
214: if (interfaces != null && interfaces.length > 0) {
215: buf.append(" implements ");
216: for (int i = 0; i < interfaces.length; ++i) {
217: appendDescriptor(INTERNAL_NAME, interfaces[i]);
218: buf.append(' ');
219: }
220: }
221: buf.append(" {\n\n");
222:
223: text.add(buf.toString());
224:
225: if (cv != null) {
226: cv.visit(version, access, name, signature, super Name,
227: interfaces);
228: }
229: }
230:
231: public void visitSource(final String file, final String debug) {
232: buf.setLength(0);
233: if (file != null) {
234: buf.append(tab).append("// compiled from: ").append(file)
235: .append('\n');
236: }
237: if (debug != null) {
238: buf.append(tab).append("// debug info: ").append(debug)
239: .append('\n');
240: }
241: if (buf.length() > 0) {
242: text.add(buf.toString());
243: }
244:
245: if (cv != null) {
246: cv.visitSource(file, debug);
247: }
248: }
249:
250: public void visitOuterClass(final String owner, final String name,
251: final String desc) {
252: buf.setLength(0);
253: buf.append(tab).append("OUTERCLASS ");
254: appendDescriptor(INTERNAL_NAME, owner);
255: buf.append(' ');
256: if (name != null) {
257: buf.append(name).append(' ');
258: }
259: appendDescriptor(METHOD_DESCRIPTOR, desc);
260: buf.append('\n');
261: text.add(buf.toString());
262:
263: if (cv != null) {
264: cv.visitOuterClass(owner, name, desc);
265: }
266: }
267:
268: public AnnotationVisitor visitAnnotation(final String desc,
269: final boolean visible) {
270: text.add("\n");
271: AnnotationVisitor tav = super .visitAnnotation(desc, visible);
272: if (cv != null) {
273: ((TraceAnnotationVisitor) tav).av = cv.visitAnnotation(
274: desc, visible);
275: }
276: return tav;
277: }
278:
279: public void visitAttribute(final Attribute attr) {
280: text.add("\n");
281: super .visitAttribute(attr);
282:
283: if (cv != null) {
284: cv.visitAttribute(attr);
285: }
286: }
287:
288: public void visitInnerClass(final String name,
289: final String outerName, final String innerName,
290: final int access) {
291: buf.setLength(0);
292: buf.append(tab).append("// access flags ");
293: buf.append(access & ~Opcodes.ACC_SUPER).append('\n');
294: buf.append(tab);
295: appendAccess(access);
296: buf.append("INNERCLASS ");
297: appendDescriptor(INTERNAL_NAME, name);
298: buf.append(' ');
299: appendDescriptor(INTERNAL_NAME, outerName);
300: buf.append(' ');
301: appendDescriptor(INTERNAL_NAME, innerName);
302: buf.append('\n');
303: text.add(buf.toString());
304:
305: if (cv != null) {
306: cv.visitInnerClass(name, outerName, innerName, access);
307: }
308: }
309:
310: public FieldVisitor visitField(final int access, final String name,
311: final String desc, final String signature,
312: final Object value) {
313: buf.setLength(0);
314: buf.append('\n');
315: if ((access & Opcodes.ACC_DEPRECATED) != 0) {
316: buf.append(tab).append("// DEPRECATED\n");
317: }
318: buf.append(tab).append("// access flags ").append(access)
319: .append('\n');
320: if (signature != null) {
321: buf.append(tab);
322: appendDescriptor(FIELD_SIGNATURE, signature);
323:
324: TraceSignatureVisitor sv = new TraceSignatureVisitor(0);
325: SignatureReader r = new SignatureReader(signature);
326: r.acceptType(sv);
327: buf.append(tab).append("// declaration: ").append(
328: sv.getDeclaration()).append('\n');
329: }
330:
331: buf.append(tab);
332: appendAccess(access);
333:
334: appendDescriptor(FIELD_DESCRIPTOR, desc);
335: buf.append(' ').append(name);
336: if (value != null) {
337: buf.append(" = ");
338: if (value instanceof String) {
339: buf.append("\"").append(value).append("\"");
340: } else {
341: buf.append(value);
342: }
343: }
344:
345: buf.append('\n');
346: text.add(buf.toString());
347:
348: TraceFieldVisitor tav = createTraceFieldVisitor();
349: text.add(tav.getText());
350:
351: if (cv != null) {
352: tav.fv = cv
353: .visitField(access, name, desc, signature, value);
354: }
355:
356: return tav;
357: }
358:
359: public MethodVisitor visitMethod(final int access,
360: final String name, final String desc,
361: final String signature, final String[] exceptions) {
362: buf.setLength(0);
363: buf.append('\n');
364: if ((access & Opcodes.ACC_DEPRECATED) != 0) {
365: buf.append(tab).append("// DEPRECATED\n");
366: }
367: buf.append(tab).append("// access flags ").append(access)
368: .append('\n');
369:
370: if (signature != null) {
371: buf.append(tab);
372: appendDescriptor(METHOD_SIGNATURE, signature);
373:
374: TraceSignatureVisitor v = new TraceSignatureVisitor(0);
375: SignatureReader r = new SignatureReader(signature);
376: r.accept(v);
377: String genericDecl = v.getDeclaration();
378: String genericReturn = v.getReturnType();
379: String genericExceptions = v.getExceptions();
380:
381: buf.append(tab).append("// declaration: ").append(
382: genericReturn).append(' ').append(name).append(
383: genericDecl);
384: if (genericExceptions != null) {
385: buf.append(" throws ").append(genericExceptions);
386: }
387: buf.append('\n');
388: }
389:
390: buf.append(tab);
391: appendAccess(access);
392: if ((access & Opcodes.ACC_NATIVE) != 0) {
393: buf.append("native ");
394: }
395: if ((access & Opcodes.ACC_VARARGS) != 0) {
396: buf.append("varargs ");
397: }
398: if ((access & Opcodes.ACC_BRIDGE) != 0) {
399: buf.append("bridge ");
400: }
401:
402: buf.append(name);
403: appendDescriptor(METHOD_DESCRIPTOR, desc);
404: if (exceptions != null && exceptions.length > 0) {
405: buf.append(" throws ");
406: for (int i = 0; i < exceptions.length; ++i) {
407: appendDescriptor(INTERNAL_NAME, exceptions[i]);
408: buf.append(' ');
409: }
410: }
411:
412: buf.append('\n');
413: text.add(buf.toString());
414:
415: TraceMethodVisitor tcv = createTraceMethodVisitor();
416: text.add(tcv.getText());
417:
418: if (cv != null) {
419: tcv.mv = cv.visitMethod(access, name, desc, signature,
420: exceptions);
421: }
422:
423: return tcv;
424: }
425:
426: public void visitEnd() {
427: text.add("}\n");
428:
429: print(pw);
430: pw.flush();
431:
432: if (cv != null) {
433: cv.visitEnd();
434: }
435: }
436:
437: // ------------------------------------------------------------------------
438: // Utility methods
439: // ------------------------------------------------------------------------
440:
441: protected TraceFieldVisitor createTraceFieldVisitor() {
442: return new TraceFieldVisitor();
443: }
444:
445: protected TraceMethodVisitor createTraceMethodVisitor() {
446: return new TraceMethodVisitor();
447: }
448:
449: /**
450: * Appends a string representation of the given access modifiers to {@link
451: * #buf buf}.
452: *
453: * @param access some access modifiers.
454: */
455: private void appendAccess(final int access) {
456: if ((access & Opcodes.ACC_PUBLIC) != 0) {
457: buf.append("public ");
458: }
459: if ((access & Opcodes.ACC_PRIVATE) != 0) {
460: buf.append("private ");
461: }
462: if ((access & Opcodes.ACC_PROTECTED) != 0) {
463: buf.append("protected ");
464: }
465: if ((access & Opcodes.ACC_FINAL) != 0) {
466: buf.append("final ");
467: }
468: if ((access & Opcodes.ACC_STATIC) != 0) {
469: buf.append("static ");
470: }
471: if ((access & Opcodes.ACC_SYNCHRONIZED) != 0) {
472: buf.append("synchronized ");
473: }
474: if ((access & Opcodes.ACC_VOLATILE) != 0) {
475: buf.append("volatile ");
476: }
477: if ((access & Opcodes.ACC_TRANSIENT) != 0) {
478: buf.append("transient ");
479: }
480: if ((access & Opcodes.ACC_ABSTRACT) != 0) {
481: buf.append("abstract ");
482: }
483: if ((access & Opcodes.ACC_STRICT) != 0) {
484: buf.append("strictfp ");
485: }
486: if ((access & Opcodes.ACC_ENUM) != 0) {
487: buf.append("enum ");
488: }
489: }
490: }
|