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.ClassReader;
036: import org.drools.asm.ClassVisitor;
037: import org.drools.asm.FieldVisitor;
038: import org.drools.asm.MethodVisitor;
039: import org.drools.asm.Opcodes;
040: import org.drools.asm.Type;
041:
042: /**
043: * A {@link ClassVisitor} that prints the ASM code that generates the classes it
044: * visits. This class visitor can be used to quickly write ASM code to generate
045: * some given bytecode: <ul> <li>write the Java source code equivalent to the
046: * bytecode you want to generate;</li> <li>compile it with <tt>javac</tt>;</li>
047: * <li>make a {@link ASMifierClassVisitor} visit this compiled class (see the
048: * {@link #main main} method);</li> <li>edit the generated source code, if
049: * necessary.</li> </ul> The source code printed when visiting the
050: * <tt>Hello</tt> class is the following: <p> <blockquote>
051: *
052: * <pre>
053: * import org.objectweb.asm.*;
054: *
055: * public class HelloDump implements Opcodes {
056: *
057: * public static byte[] dump() throws Exception {
058: *
059: * ClassWriter cw = new ClassWriter(false);
060: * FieldVisitor fv;
061: * MethodVisitor mv;
062: * AnnotationVisitor av0;
063: *
064: * cw.visit(49,
065: * ACC_PUBLIC + ACC_SUPER,
066: * "Hello",
067: * null,
068: * "java/lang/Object",
069: * null);
070: *
071: * cw.visitSource("Hello.java", null);
072: *
073: * {
074: * mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
075: * mv.visitVarInsn(ALOAD, 0);
076: * mv.visitMethodInsn(INVOKESPECIAL,
077: * "java/lang/Object",
078: * "<init>",
079: * "()V");
080: * mv.visitInsn(RETURN);
081: * mv.visitMaxs(1, 1);
082: * mv.visitEnd();
083: * }
084: * {
085: * mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC,
086: * "main",
087: * "([Ljava/lang/String;)V",
088: * null,
089: * null);
090: * mv.visitFieldInsn(GETSTATIC,
091: * "java/lang/System",
092: * "out",
093: * "Ljava/io/PrintStream;");
094: * mv.visitLdcInsn("hello");
095: * mv.visitMethodInsn(INVOKEVIRTUAL,
096: * "java/io/PrintStream",
097: * "println",
098: * "(Ljava/lang/String;)V");
099: * mv.visitInsn(RETURN);
100: * mv.visitMaxs(2, 1);
101: * mv.visitEnd();
102: * }
103: * cw.visitEnd();
104: *
105: * return cw.toByteArray();
106: * }
107: * }
108: *
109: * </pre>
110: *
111: * </blockquote> where <tt>Hello</tt> is defined by: <p> <blockquote>
112: *
113: * <pre>
114: * public class Hello {
115: *
116: * public static void main(String[] args) {
117: * System.out.println("hello");
118: * }
119: * }
120: * </pre>
121: *
122: * </blockquote>
123: *
124: * @author Eric Bruneton
125: * @author Eugene Kuleshov
126: */
127: public class ASMifierClassVisitor extends ASMifierAbstractVisitor
128: implements ClassVisitor {
129:
130: /**
131: * Pseudo access flag used to distinguish class access flags.
132: */
133: private final static int ACCESS_CLASS = 262144;
134:
135: /**
136: * Pseudo access flag used to distinguish field access flags.
137: */
138: private final static int ACCESS_FIELD = 524288;
139:
140: /**
141: * Pseudo access flag used to distinguish inner class flags.
142: */
143: private static final int ACCESS_INNER = 1048576;
144:
145: /**
146: * The print writer to be used to print the class.
147: */
148: protected final PrintWriter pw;
149:
150: /**
151: * Prints the ASM source code to generate the given class to the standard
152: * output. <p> Usage: ASMifierClassVisitor [-debug] <fully qualified
153: * class name or class file name>
154: *
155: * @param args the command line arguments.
156: *
157: * @throws Exception if the class cannot be found, or if an IO exception
158: * occurs.
159: */
160: public static void main(final String[] args) throws Exception {
161: int i = 0;
162: boolean skipDebug = true;
163:
164: boolean ok = true;
165: if (args.length < 1 || args.length > 2) {
166: ok = false;
167: }
168: if (ok && args[0].equals("-debug")) {
169: i = 1;
170: skipDebug = false;
171: if (args.length != 2) {
172: ok = false;
173: }
174: }
175: if (!ok) {
176: System.err
177: .println("Prints the ASM code to generate the given class.");
178: System.err
179: .println("Usage: ASMifierClassVisitor [-debug] "
180: + "<fully qualified class name or class file name>");
181: return;
182: }
183: ClassReader cr;
184: if (args[i].endsWith(".class") || args[i].indexOf('\\') > -1
185: || args[i].indexOf('/') > -1) {
186: cr = new ClassReader(new FileInputStream(args[i]));
187: } else {
188: cr = new ClassReader(args[i]);
189: }
190: cr.accept(
191: new ASMifierClassVisitor(new PrintWriter(System.out)),
192: getDefaultAttributes(), skipDebug);
193: }
194:
195: /**
196: * Constructs a new {@link ASMifierClassVisitor} object.
197: *
198: * @param pw the print writer to be used to print the class.
199: */
200: public ASMifierClassVisitor(final PrintWriter pw) {
201: super ("cw");
202: this .pw = pw;
203: }
204:
205: // ------------------------------------------------------------------------
206: // Implementation of the ClassVisitor interface
207: // ------------------------------------------------------------------------
208:
209: public void visit(final int version, final int access,
210: final String name, final String signature,
211: final String super Name, final String[] interfaces) {
212: String simpleName;
213: final int n = name.lastIndexOf('/');
214: if (n != -1) {
215: this .text.add("package asm."
216: + name.substring(0, n).replace('/', '.') + ";\n");
217: simpleName = name.substring(n + 1);
218: } else {
219: simpleName = name;
220: }
221: this .text.add("import java.util.*;\n");
222: this .text.add("import org.objectweb.asm.*;\n");
223: this .text.add("import org.objectweb.asm.attrs.*;\n");
224: this .text.add("public class " + simpleName
225: + "Dump implements Opcodes {\n\n");
226: this .text
227: .add("public static byte[] dump () throws Exception {\n\n");
228: this .text.add("ClassWriter cw = new ClassWriter(false);\n");
229: this .text.add("FieldVisitor fv;\n");
230: this .text.add("MethodVisitor mv;\n");
231: this .text.add("AnnotationVisitor av0;\n\n");
232:
233: this .buf.setLength(0);
234: this .buf.append("cw.visit(");
235: switch (version) {
236: case Opcodes.V1_1:
237: this .buf.append("V1_1");
238: break;
239: case Opcodes.V1_2:
240: this .buf.append("V1_2");
241: break;
242: case Opcodes.V1_3:
243: this .buf.append("V1_3");
244: break;
245: case Opcodes.V1_4:
246: this .buf.append("V1_4");
247: break;
248: case Opcodes.V1_5:
249: this .buf.append("V1_5");
250: break;
251: case Opcodes.V1_6:
252: this .buf.append("V1_6");
253: break;
254: default:
255: this .buf.append(version);
256: break;
257: }
258: this .buf.append(", ");
259: appendAccess(access | ASMifierClassVisitor.ACCESS_CLASS);
260: this .buf.append(", ");
261: appendConstant(name);
262: this .buf.append(", ");
263: appendConstant(signature);
264: this .buf.append(", ");
265: appendConstant(super Name);
266: this .buf.append(", ");
267: if (interfaces != null && interfaces.length > 0) {
268: this .buf.append("new String[] {");
269: for (int i = 0; i < interfaces.length; ++i) {
270: this .buf.append(i == 0 ? " " : ", ");
271: appendConstant(interfaces[i]);
272: }
273: this .buf.append(" }");
274: } else {
275: this .buf.append("null");
276: }
277: this .buf.append(");\n\n");
278: this .text.add(this .buf.toString());
279: }
280:
281: public void visitSource(final String file, final String debug) {
282: this .buf.setLength(0);
283: this .buf.append("cw.visitSource(");
284: appendConstant(file);
285: this .buf.append(", ");
286: appendConstant(debug);
287: this .buf.append(");\n\n");
288: this .text.add(this .buf.toString());
289: }
290:
291: public void visitOuterClass(final String owner, final String name,
292: final String desc) {
293: this .buf.setLength(0);
294: this .buf.append("cw.visitOuterClass(");
295: appendConstant(owner);
296: this .buf.append(", ");
297: appendConstant(name);
298: this .buf.append(", ");
299: appendConstant(desc);
300: this .buf.append(");\n\n");
301: this .text.add(this .buf.toString());
302: }
303:
304: public void visitInnerClass(final String name,
305: final String outerName, final String innerName,
306: final int access) {
307: this .buf.setLength(0);
308: this .buf.append("cw.visitInnerClass(");
309: appendConstant(name);
310: this .buf.append(", ");
311: appendConstant(outerName);
312: this .buf.append(", ");
313: appendConstant(innerName);
314: this .buf.append(", ");
315: appendAccess(access | ASMifierClassVisitor.ACCESS_INNER);
316: this .buf.append(");\n\n");
317: this .text.add(this .buf.toString());
318: }
319:
320: public FieldVisitor visitField(final int access, final String name,
321: final String desc, final String signature,
322: final Object value) {
323: this .buf.setLength(0);
324: this .buf.append("{\n");
325: this .buf.append("fv = cw.visitField(");
326: appendAccess(access | ASMifierClassVisitor.ACCESS_FIELD);
327: this .buf.append(", ");
328: appendConstant(name);
329: this .buf.append(", ");
330: appendConstant(desc);
331: this .buf.append(", ");
332: appendConstant(signature);
333: this .buf.append(", ");
334: appendConstant(value);
335: this .buf.append(");\n");
336: this .text.add(this .buf.toString());
337: final ASMifierFieldVisitor aav = new ASMifierFieldVisitor();
338: this .text.add(aav.getText());
339: this .text.add("}\n");
340: return aav;
341: }
342:
343: public MethodVisitor visitMethod(final int access,
344: final String name, final String desc,
345: final String signature, final String[] exceptions) {
346: this .buf.setLength(0);
347: this .buf.append("{\n");
348: this .buf.append("mv = cw.visitMethod(");
349: appendAccess(access);
350: this .buf.append(", ");
351: appendConstant(name);
352: this .buf.append(", ");
353: appendConstant(desc);
354: this .buf.append(", ");
355: appendConstant(signature);
356: this .buf.append(", ");
357: if (exceptions != null && exceptions.length > 0) {
358: this .buf.append("new String[] {");
359: for (int i = 0; i < exceptions.length; ++i) {
360: this .buf.append(i == 0 ? " " : ", ");
361: appendConstant(exceptions[i]);
362: }
363: this .buf.append(" }");
364: } else {
365: this .buf.append("null");
366: }
367: this .buf.append(");\n");
368: this .text.add(this .buf.toString());
369: final ASMifierMethodVisitor acv = new ASMifierMethodVisitor();
370: this .text.add(acv.getText());
371: this .text.add("}\n");
372: return acv;
373: }
374:
375: public AnnotationVisitor visitAnnotation(final String desc,
376: final boolean visible) {
377: this .buf.setLength(0);
378: this .buf.append("{\n");
379: this .buf.append("av0 = cw.visitAnnotation(");
380: appendConstant(desc);
381: this .buf.append(", ");
382: this .buf.append(visible);
383: this .buf.append(");\n");
384: this .text.add(this .buf.toString());
385: final ASMifierAnnotationVisitor av = new ASMifierAnnotationVisitor(
386: 0);
387: this .text.add(av.getText());
388: this .text.add("}\n");
389: return av;
390: }
391:
392: public void visitEnd() {
393: this .text.add("cw.visitEnd();\n\n");
394: this .text.add("return cw.toByteArray();\n");
395: this .text.add("}\n");
396: this .text.add("}\n");
397: printList(this .pw, this .text);
398: this .pw.flush();
399: }
400:
401: // ------------------------------------------------------------------------
402: // Utility methods
403: // ------------------------------------------------------------------------
404:
405: /**
406: * Appends a string representation of the given access modifiers to {@link
407: * #buf buf}.
408: *
409: * @param access some access modifiers.
410: */
411: void appendAccess(final int access) {
412: boolean first = true;
413: if ((access & Opcodes.ACC_PUBLIC) != 0) {
414: this .buf.append("ACC_PUBLIC");
415: first = false;
416: }
417: if ((access & Opcodes.ACC_PRIVATE) != 0) {
418: if (!first) {
419: this .buf.append(" + ");
420: }
421: this .buf.append("ACC_PRIVATE");
422: first = false;
423: }
424: if ((access & Opcodes.ACC_PROTECTED) != 0) {
425: if (!first) {
426: this .buf.append(" + ");
427: }
428: this .buf.append("ACC_PROTECTED");
429: first = false;
430: }
431: if ((access & Opcodes.ACC_FINAL) != 0) {
432: if (!first) {
433: this .buf.append(" + ");
434: }
435: this .buf.append("ACC_FINAL");
436: first = false;
437: }
438: if ((access & Opcodes.ACC_STATIC) != 0) {
439: if (!first) {
440: this .buf.append(" + ");
441: }
442: this .buf.append("ACC_STATIC");
443: first = false;
444: }
445: if ((access & Opcodes.ACC_SYNCHRONIZED) != 0) {
446: if (!first) {
447: this .buf.append(" + ");
448: }
449: if ((access & ASMifierClassVisitor.ACCESS_CLASS) != 0) {
450: this .buf.append("ACC_SUPER");
451: } else {
452: this .buf.append("ACC_SYNCHRONIZED");
453: }
454: first = false;
455: }
456: if ((access & Opcodes.ACC_VOLATILE) != 0
457: && (access & ASMifierClassVisitor.ACCESS_FIELD) != 0) {
458: if (!first) {
459: this .buf.append(" + ");
460: }
461: this .buf.append("ACC_VOLATILE");
462: first = false;
463: }
464: if ((access & Opcodes.ACC_BRIDGE) != 0
465: && (access & ASMifierClassVisitor.ACCESS_CLASS) == 0
466: && (access & ASMifierClassVisitor.ACCESS_FIELD) == 0) {
467: if (!first) {
468: this .buf.append(" + ");
469: }
470: this .buf.append("ACC_BRIDGE");
471: first = false;
472: }
473: if ((access & Opcodes.ACC_VARARGS) != 0
474: && (access & ASMifierClassVisitor.ACCESS_CLASS) == 0
475: && (access & ASMifierClassVisitor.ACCESS_FIELD) == 0) {
476: if (!first) {
477: this .buf.append(" + ");
478: }
479: this .buf.append("ACC_VARARGS");
480: first = false;
481: }
482: if ((access & Opcodes.ACC_TRANSIENT) != 0
483: && (access & ASMifierClassVisitor.ACCESS_FIELD) != 0) {
484: if (!first) {
485: this .buf.append(" + ");
486: }
487: this .buf.append("ACC_TRANSIENT");
488: first = false;
489: }
490: if ((access & Opcodes.ACC_NATIVE) != 0
491: && (access & ASMifierClassVisitor.ACCESS_CLASS) == 0
492: && (access & ASMifierClassVisitor.ACCESS_FIELD) == 0) {
493: if (!first) {
494: this .buf.append(" + ");
495: }
496: this .buf.append("ACC_NATIVE");
497: first = false;
498: }
499: if ((access & Opcodes.ACC_ENUM) != 0
500: && ((access & ASMifierClassVisitor.ACCESS_CLASS) != 0
501: || (access & ASMifierClassVisitor.ACCESS_FIELD) != 0 || (access & ASMifierClassVisitor.ACCESS_INNER) != 0)) {
502: if (!first) {
503: this .buf.append(" + ");
504: }
505: this .buf.append("ACC_ENUM");
506: first = false;
507: }
508: if ((access & Opcodes.ACC_ANNOTATION) != 0
509: && ((access & ASMifierClassVisitor.ACCESS_CLASS) != 0)) {
510: if (!first) {
511: this .buf.append(" + ");
512: }
513: this .buf.append("ACC_ANNOTATION");
514: first = false;
515: }
516: if ((access & Opcodes.ACC_ABSTRACT) != 0) {
517: if (!first) {
518: this .buf.append(" + ");
519: }
520: this .buf.append("ACC_ABSTRACT");
521: first = false;
522: }
523: if ((access & Opcodes.ACC_INTERFACE) != 0) {
524: if (!first) {
525: this .buf.append(" + ");
526: }
527: this .buf.append("ACC_INTERFACE");
528: first = false;
529: }
530: if ((access & Opcodes.ACC_STRICT) != 0) {
531: if (!first) {
532: this .buf.append(" + ");
533: }
534: this .buf.append("ACC_STRICT");
535: first = false;
536: }
537: if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
538: if (!first) {
539: this .buf.append(" + ");
540: }
541: this .buf.append("ACC_SYNTHETIC");
542: first = false;
543: }
544: if ((access & Opcodes.ACC_DEPRECATED) != 0) {
545: if (!first) {
546: this .buf.append(" + ");
547: }
548: this .buf.append("ACC_DEPRECATED");
549: first = false;
550: }
551: if (first) {
552: this .buf.append("0");
553: }
554: }
555:
556: /**
557: * Appends a string representation of the given constant to the given
558: * buffer.
559: *
560: * @param buf a string buffer.
561: * @param cst an {@link java.lang.Integer Integer}, {@link java.lang.Float
562: * Float}, {@link java.lang.Long Long},
563: * {@link java.lang.Double Double} or {@link String String} object.
564: * May be <tt>null</tt>.
565: */
566: static void appendConstant(final StringBuffer buf, final Object cst) {
567: if (cst == null) {
568: buf.append("null");
569: } else if (cst instanceof String) {
570: AbstractVisitor.appendString(buf, (String) cst);
571: } else if (cst instanceof Type) {
572: buf.append("Type.getType(\"").append(
573: ((Type) cst).getDescriptor()).append("\")");
574: } else if (cst instanceof Integer) {
575: buf.append("new Integer(").append(cst).append(")");
576: } else if (cst instanceof Float) {
577: buf.append("new Float(\"").append(cst).append("\")");
578: } else if (cst instanceof Long) {
579: buf.append("new Long(").append(cst).append("L)");
580: } else if (cst instanceof Double) {
581: buf.append("new Double(\"").append(cst).append("\")");
582: }
583: }
584: }
|