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.drools.asm.util;
030:
031: import java.io.FileInputStream;
032: import java.io.PrintWriter;
033:
034: import org.drools.asm.AnnotationVisitor;
035: import org.drools.asm.Attribute;
036: import org.drools.asm.ClassReader;
037: import org.drools.asm.ClassVisitor;
038: import org.drools.asm.FieldVisitor;
039: import org.drools.asm.MethodVisitor;
040: import org.drools.asm.Opcodes;
041: import org.drools.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: boolean skipDebug = true;
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: skipDebug = false;
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(), skipDebug);
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: final int major = version & 0xFFFF;
180: final int minor = version >>> 16;
181: this .buf.setLength(0);
182: this .buf.append("// class version ").append(major).append('.')
183: .append(minor).append(" (").append(version).append(
184: ")\n");
185: if ((access & Opcodes.ACC_DEPRECATED) != 0) {
186: this .buf.append("// DEPRECATED\n");
187: }
188: this .buf.append("// access flags ").append(access).append('\n');
189:
190: appendDescriptor(TraceAbstractVisitor.CLASS_SIGNATURE,
191: signature);
192: if (signature != null) {
193: final TraceSignatureVisitor sv = new TraceSignatureVisitor(
194: access);
195: final SignatureReader r = new SignatureReader(signature);
196: r.accept(sv);
197: this .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: this .buf.append("@interface ");
204: } else if ((access & Opcodes.ACC_INTERFACE) != 0) {
205: this .buf.append("interface ");
206: } else if ((access & Opcodes.ACC_ENUM) != 0) {
207: this .buf.append("enum ");
208: } else {
209: this .buf.append("class ");
210: }
211: appendDescriptor(TraceAbstractVisitor.INTERNAL_NAME, name);
212:
213: if (super Name != null && !super Name.equals("java/lang/Object")) {
214: this .buf.append(" extends ");
215: appendDescriptor(TraceAbstractVisitor.INTERNAL_NAME,
216: super Name);
217: this .buf.append(' ');
218: }
219: if (interfaces != null && interfaces.length > 0) {
220: this .buf.append(" implements ");
221: for (int i = 0; i < interfaces.length; ++i) {
222: appendDescriptor(TraceAbstractVisitor.INTERNAL_NAME,
223: interfaces[i]);
224: this .buf.append(' ');
225: }
226: }
227: this .buf.append(" {\n\n");
228:
229: this .text.add(this .buf.toString());
230:
231: if (this .cv != null) {
232: this .cv.visit(version, access, name, signature, super Name,
233: interfaces);
234: }
235: }
236:
237: public void visitSource(final String file, final String debug) {
238: this .buf.setLength(0);
239: if (file != null) {
240: this .buf.append(this .tab).append("// compiled from: ")
241: .append(file).append('\n');
242: }
243: if (debug != null) {
244: this .buf.append(this .tab).append("// debug info: ").append(
245: debug).append('\n');
246: }
247: if (this .buf.length() > 0) {
248: this .text.add(this .buf.toString());
249: }
250:
251: if (this .cv != null) {
252: this .cv.visitSource(file, debug);
253: }
254: }
255:
256: public void visitOuterClass(final String owner, final String name,
257: final String desc) {
258: this .buf.setLength(0);
259: this .buf.append(this .tab).append("OUTERCLASS ");
260: appendDescriptor(TraceAbstractVisitor.INTERNAL_NAME, owner);
261: // if enclosing name is null, so why should we show this info?
262: if (name != null) {
263: this .buf.append(' ').append(name).append(' ');
264: } else {
265: this .buf.append(' ');
266: }
267: appendDescriptor(TraceAbstractVisitor.METHOD_DESCRIPTOR, desc);
268: this .buf.append('\n');
269: this .text.add(this .buf.toString());
270:
271: if (this .cv != null) {
272: this .cv.visitOuterClass(owner, name, desc);
273: }
274: }
275:
276: public AnnotationVisitor visitAnnotation(final String desc,
277: final boolean visible) {
278: this .text.add("\n");
279: final AnnotationVisitor tav = super .visitAnnotation(desc,
280: visible);
281: if (this .cv != null) {
282: ((TraceAnnotationVisitor) tav).av = this .cv
283: .visitAnnotation(desc, visible);
284: }
285: return tav;
286: }
287:
288: public void visitAttribute(final Attribute attr) {
289: this .text.add("\n");
290: super .visitAttribute(attr);
291:
292: if (this .cv != null) {
293: this .cv.visitAttribute(attr);
294: }
295: }
296:
297: public void visitInnerClass(final String name,
298: final String outerName, final String innerName,
299: final int access) {
300: this .buf.setLength(0);
301: this .buf.append(this .tab).append("// access flags ").append(
302: access & ~Opcodes.ACC_SUPER).append('\n');
303: this .buf.append(this .tab);
304: appendAccess(access);
305: this .buf.append("INNERCLASS ");
306: if ((access & Opcodes.ACC_ENUM) != 0) {
307: this .buf.append("enum ");
308: }
309: appendDescriptor(TraceAbstractVisitor.INTERNAL_NAME, name);
310: this .buf.append(' ');
311: appendDescriptor(TraceAbstractVisitor.INTERNAL_NAME, outerName);
312: this .buf.append(' ');
313: appendDescriptor(TraceAbstractVisitor.INTERNAL_NAME, innerName);
314: this .buf.append('\n');
315: this .text.add(this .buf.toString());
316:
317: if (this .cv != null) {
318: this .cv.visitInnerClass(name, outerName, innerName, access);
319: }
320: }
321:
322: public FieldVisitor visitField(final int access, final String name,
323: final String desc, final String signature,
324: final Object value) {
325: this .buf.setLength(0);
326: this .buf.append('\n');
327: if ((access & Opcodes.ACC_DEPRECATED) != 0) {
328: this .buf.append(this .tab).append("// DEPRECATED\n");
329: }
330: this .buf.append(this .tab).append("// access flags ").append(
331: access).append('\n');
332: if (signature != null) {
333: this .buf.append(this .tab);
334: appendDescriptor(TraceAbstractVisitor.FIELD_SIGNATURE,
335: signature);
336:
337: final TraceSignatureVisitor sv = new TraceSignatureVisitor(
338: 0);
339: final SignatureReader r = new SignatureReader(signature);
340: r.acceptType(sv);
341: this .buf.append(this .tab).append("// declaration: ")
342: .append(sv.getDeclaration()).append('\n');
343: }
344:
345: this .buf.append(this .tab);
346: appendAccess(access);
347: if ((access & Opcodes.ACC_ENUM) != 0) {
348: this .buf.append("enum ");
349: }
350:
351: appendDescriptor(TraceAbstractVisitor.FIELD_DESCRIPTOR, desc);
352: this .buf.append(' ').append(name);
353: if (value != null) {
354: this .buf.append(" = ");
355: if (value instanceof String) {
356: this .buf.append("\"").append(value).append("\"");
357: } else {
358: this .buf.append(value);
359: }
360: }
361:
362: this .buf.append('\n');
363: this .text.add(this .buf.toString());
364:
365: final TraceFieldVisitor tav = createTraceFieldVisitor();
366: this .text.add(tav.getText());
367:
368: if (this .cv != null) {
369: tav.fv = this .cv.visitField(access, name, desc, signature,
370: value);
371: }
372:
373: return tav;
374: }
375:
376: public MethodVisitor visitMethod(final int access,
377: final String name, final String desc,
378: final String signature, final String[] exceptions) {
379: this .buf.setLength(0);
380: this .buf.append('\n');
381: if ((access & Opcodes.ACC_DEPRECATED) != 0) {
382: this .buf.append(this .tab).append("// DEPRECATED\n");
383: }
384: this .buf.append(this .tab).append("// access flags ").append(
385: access).append('\n');
386: this .buf.append(this .tab);
387: appendDescriptor(TraceAbstractVisitor.METHOD_SIGNATURE,
388: signature);
389:
390: if (signature != null) {
391: final TraceSignatureVisitor v = new TraceSignatureVisitor(0);
392: final SignatureReader r = new SignatureReader(signature);
393: r.accept(v);
394: final String genericDecl = v.getDeclaration();
395: final String genericReturn = v.getReturnType();
396: final String genericExceptions = v.getExceptions();
397:
398: this .buf.append(this .tab).append("// declaration: ")
399: .append(genericReturn).append(' ').append(name)
400: .append(genericDecl);
401: if (genericExceptions != null) {
402: this .buf.append(" throws ").append(genericExceptions);
403: }
404: this .buf.append('\n');
405: }
406:
407: appendAccess(access);
408: if ((access & Opcodes.ACC_NATIVE) != 0) {
409: this .buf.append("native ");
410: }
411: if ((access & Opcodes.ACC_VARARGS) != 0) {
412: this .buf.append("varargs ");
413: }
414: if ((access & Opcodes.ACC_BRIDGE) != 0) {
415: this .buf.append("bridge ");
416: }
417:
418: this .buf.append(name);
419: appendDescriptor(TraceAbstractVisitor.METHOD_DESCRIPTOR, desc);
420: if (exceptions != null && exceptions.length > 0) {
421: this .buf.append(" throws ");
422: for (int i = 0; i < exceptions.length; ++i) {
423: appendDescriptor(TraceAbstractVisitor.INTERNAL_NAME,
424: exceptions[i]);
425: this .buf.append(' ');
426: }
427: }
428:
429: this .buf.append('\n');
430: this .text.add(this .buf.toString());
431:
432: final TraceMethodVisitor tcv = createTraceMethodVisitor();
433: this .text.add(tcv.getText());
434:
435: if (this .cv != null) {
436: tcv.mv = this .cv.visitMethod(access, name, desc, signature,
437: exceptions);
438: }
439:
440: return tcv;
441: }
442:
443: public void visitEnd() {
444: this .text.add("}\n");
445:
446: printList(this .pw, this .text);
447: this .pw.flush();
448:
449: if (this .cv != null) {
450: this .cv.visitEnd();
451: }
452: }
453:
454: // ------------------------------------------------------------------------
455: // Utility methods
456: // ------------------------------------------------------------------------
457:
458: protected TraceFieldVisitor createTraceFieldVisitor() {
459: return new TraceFieldVisitor();
460: }
461:
462: protected TraceMethodVisitor createTraceMethodVisitor() {
463: return new TraceMethodVisitor();
464: }
465:
466: /**
467: * Appends a string representation of the given access modifiers to {@link
468: * #buf buf}.
469: *
470: * @param access some access modifiers.
471: */
472: private void appendAccess(final int access) {
473: if ((access & Opcodes.ACC_PUBLIC) != 0) {
474: this .buf.append("public ");
475: }
476: if ((access & Opcodes.ACC_PRIVATE) != 0) {
477: this .buf.append("private ");
478: }
479: if ((access & Opcodes.ACC_PROTECTED) != 0) {
480: this .buf.append("protected ");
481: }
482: if ((access & Opcodes.ACC_FINAL) != 0) {
483: this .buf.append("final ");
484: }
485: if ((access & Opcodes.ACC_STATIC) != 0) {
486: this .buf.append("static ");
487: }
488: if ((access & Opcodes.ACC_SYNCHRONIZED) != 0) {
489: this .buf.append("synchronized ");
490: }
491: if ((access & Opcodes.ACC_VOLATILE) != 0) {
492: this .buf.append("volatile ");
493: }
494: if ((access & Opcodes.ACC_TRANSIENT) != 0) {
495: this .buf.append("transient ");
496: }
497: // if ((access & Constants.ACC_NATIVE) != 0) {
498: // buf.append("native ");
499: // }
500: if ((access & Opcodes.ACC_ABSTRACT) != 0) {
501: this .buf.append("abstract ");
502: }
503: if ((access & Opcodes.ACC_STRICT) != 0) {
504: this .buf.append("strictfp ");
505: }
506: if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
507: this .buf.append("synthetic ");
508: }
509: }
510: }
|