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.objectweb.asm.util;
030:
031: import java.io.FileInputStream;
032: import java.io.PrintWriter;
033:
034: import org.objectweb.asm.AnnotationVisitor;
035: import org.objectweb.asm.ClassReader;
036: import org.objectweb.asm.ClassVisitor;
037: import org.objectweb.asm.FieldVisitor;
038: import org.objectweb.asm.MethodVisitor;
039: import org.objectweb.asm.Opcodes;
040:
041: /**
042: * A {@link ClassVisitor} that prints the ASM code that generates the classes it
043: * visits. This class visitor can be used to quickly write ASM code to generate
044: * some given bytecode: <ul> <li>write the Java source code equivalent to the
045: * bytecode you want to generate;</li> <li>compile it with <tt>javac</tt>;</li>
046: * <li>make a {@link ASMifierClassVisitor} visit this compiled class (see the
047: * {@link #main main} method);</li> <li>edit the generated source code, if
048: * necessary.</li> </ul> The source code printed when visiting the
049: * <tt>Hello</tt> class is the following: <p> <blockquote>
050: *
051: * <pre>
052: * import org.objectweb.asm.*;
053: *
054: * public class HelloDump implements Opcodes {
055: *
056: * public static byte[] dump() throws Exception {
057: *
058: * ClassWriter cw = new ClassWriter(0);
059: * FieldVisitor fv;
060: * MethodVisitor mv;
061: * AnnotationVisitor av0;
062: *
063: * cw.visit(49,
064: * ACC_PUBLIC + ACC_SUPER,
065: * "Hello",
066: * null,
067: * "java/lang/Object",
068: * null);
069: *
070: * cw.visitSource("Hello.java", null);
071: *
072: * {
073: * mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
074: * mv.visitVarInsn(ALOAD, 0);
075: * mv.visitMethodInsn(INVOKESPECIAL,
076: * "java/lang/Object",
077: * "<init>",
078: * "()V");
079: * mv.visitInsn(RETURN);
080: * mv.visitMaxs(1, 1);
081: * mv.visitEnd();
082: * }
083: * {
084: * mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC,
085: * "main",
086: * "([Ljava/lang/String;)V",
087: * null,
088: * null);
089: * mv.visitFieldInsn(GETSTATIC,
090: * "java/lang/System",
091: * "out",
092: * "Ljava/io/PrintStream;");
093: * mv.visitLdcInsn("hello");
094: * mv.visitMethodInsn(INVOKEVIRTUAL,
095: * "java/io/PrintStream",
096: * "println",
097: * "(Ljava/lang/String;)V");
098: * mv.visitInsn(RETURN);
099: * mv.visitMaxs(2, 1);
100: * mv.visitEnd();
101: * }
102: * cw.visitEnd();
103: *
104: * return cw.toByteArray();
105: * }
106: * }
107: *
108: * </pre>
109: *
110: * </blockquote> where <tt>Hello</tt> is defined by: <p> <blockquote>
111: *
112: * <pre>
113: * public class Hello {
114: *
115: * public static void main(String[] args) {
116: * System.out.println("hello");
117: * }
118: * }
119: * </pre>
120: *
121: * </blockquote>
122: *
123: * @author Eric Bruneton
124: * @author Eugene Kuleshov
125: */
126: @SuppressWarnings("unchecked")
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: int flags = ClassReader.SKIP_DEBUG;
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: flags = 0;
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(), flags);
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: int n = name.lastIndexOf('/');
214: if (n != -1) {
215: text.add("package asm."
216: + name.substring(0, n).replace('/', '.') + ";\n");
217: simpleName = name.substring(n + 1);
218: } else {
219: simpleName = name;
220: }
221: text.add("import java.util.*;\n");
222: text.add("import org.objectweb.asm.*;\n");
223: text.add("import org.objectweb.asm.attrs.*;\n");
224: text.add("public class " + simpleName
225: + "Dump implements Opcodes {\n\n");
226: text.add("public static byte[] dump () throws Exception {\n\n");
227: text.add("ClassWriter cw = new ClassWriter(0);\n");
228: text.add("FieldVisitor fv;\n");
229: text.add("MethodVisitor mv;\n");
230: text.add("AnnotationVisitor av0;\n\n");
231:
232: buf.setLength(0);
233: buf.append("cw.visit(");
234: switch (version) {
235: case Opcodes.V1_1:
236: buf.append("V1_1");
237: break;
238: case Opcodes.V1_2:
239: buf.append("V1_2");
240: break;
241: case Opcodes.V1_3:
242: buf.append("V1_3");
243: break;
244: case Opcodes.V1_4:
245: buf.append("V1_4");
246: break;
247: case Opcodes.V1_5:
248: buf.append("V1_5");
249: break;
250: case Opcodes.V1_6:
251: buf.append("V1_6");
252: break;
253: default:
254: buf.append(version);
255: break;
256: }
257: buf.append(", ");
258: appendAccess(access | ACCESS_CLASS);
259: buf.append(", ");
260: appendConstant(name);
261: buf.append(", ");
262: appendConstant(signature);
263: buf.append(", ");
264: appendConstant(super Name);
265: buf.append(", ");
266: if (interfaces != null && interfaces.length > 0) {
267: buf.append("new String[] {");
268: for (int i = 0; i < interfaces.length; ++i) {
269: buf.append(i == 0 ? " " : ", ");
270: appendConstant(interfaces[i]);
271: }
272: buf.append(" }");
273: } else {
274: buf.append("null");
275: }
276: buf.append(");\n\n");
277: text.add(buf.toString());
278: }
279:
280: public void visitSource(final String file, final String debug) {
281: buf.setLength(0);
282: buf.append("cw.visitSource(");
283: appendConstant(file);
284: buf.append(", ");
285: appendConstant(debug);
286: buf.append(");\n\n");
287: text.add(buf.toString());
288: }
289:
290: public void visitOuterClass(final String owner, final String name,
291: final String desc) {
292: buf.setLength(0);
293: buf.append("cw.visitOuterClass(");
294: appendConstant(owner);
295: buf.append(", ");
296: appendConstant(name);
297: buf.append(", ");
298: appendConstant(desc);
299: buf.append(");\n\n");
300: text.add(buf.toString());
301: }
302:
303: public void visitInnerClass(final String name,
304: final String outerName, final String innerName,
305: final int access) {
306: buf.setLength(0);
307: buf.append("cw.visitInnerClass(");
308: appendConstant(name);
309: buf.append(", ");
310: appendConstant(outerName);
311: buf.append(", ");
312: appendConstant(innerName);
313: buf.append(", ");
314: appendAccess(access | ACCESS_INNER);
315: buf.append(");\n\n");
316: text.add(buf.toString());
317: }
318:
319: public FieldVisitor visitField(final int access, final String name,
320: final String desc, final String signature,
321: final Object value) {
322: buf.setLength(0);
323: buf.append("{\n");
324: buf.append("fv = cw.visitField(");
325: appendAccess(access | ACCESS_FIELD);
326: buf.append(", ");
327: appendConstant(name);
328: buf.append(", ");
329: appendConstant(desc);
330: buf.append(", ");
331: appendConstant(signature);
332: buf.append(", ");
333: appendConstant(value);
334: buf.append(");\n");
335: text.add(buf.toString());
336: ASMifierFieldVisitor aav = new ASMifierFieldVisitor();
337: text.add(aav.getText());
338: text.add("}\n");
339: return aav;
340: }
341:
342: public MethodVisitor visitMethod(final int access,
343: final String name, final String desc,
344: final String signature, final String[] exceptions) {
345: buf.setLength(0);
346: buf.append("{\n");
347: buf.append("mv = cw.visitMethod(");
348: appendAccess(access);
349: buf.append(", ");
350: appendConstant(name);
351: buf.append(", ");
352: appendConstant(desc);
353: buf.append(", ");
354: appendConstant(signature);
355: buf.append(", ");
356: if (exceptions != null && exceptions.length > 0) {
357: buf.append("new String[] {");
358: for (int i = 0; i < exceptions.length; ++i) {
359: buf.append(i == 0 ? " " : ", ");
360: appendConstant(exceptions[i]);
361: }
362: buf.append(" }");
363: } else {
364: buf.append("null");
365: }
366: buf.append(");\n");
367: text.add(buf.toString());
368: ASMifierMethodVisitor acv = createASMifierMethodVisitor();
369: text.add(acv.getText());
370: text.add("}\n");
371: return acv;
372: }
373:
374: protected ASMifierMethodVisitor createASMifierMethodVisitor() {
375: return new ASMifierMethodVisitor();
376: }
377:
378: public AnnotationVisitor visitAnnotation(final String desc,
379: final boolean visible) {
380: buf.setLength(0);
381: buf.append("{\n");
382: buf.append("av0 = cw.visitAnnotation(");
383: appendConstant(desc);
384: buf.append(", ");
385: buf.append(visible);
386: buf.append(");\n");
387: text.add(buf.toString());
388: ASMifierAnnotationVisitor av = new ASMifierAnnotationVisitor(0);
389: text.add(av.getText());
390: text.add("}\n");
391: return av;
392: }
393:
394: public void visitEnd() {
395: text.add("cw.visitEnd();\n\n");
396: text.add("return cw.toByteArray();\n");
397: text.add("}\n");
398: text.add("}\n");
399: printList(pw, text);
400: pw.flush();
401: }
402:
403: // ------------------------------------------------------------------------
404: // Utility methods
405: // ------------------------------------------------------------------------
406:
407: /**
408: * Appends a string representation of the given access modifiers to {@link
409: * #buf buf}.
410: *
411: * @param access some access modifiers.
412: */
413: void appendAccess(final int access) {
414: boolean first = true;
415: if ((access & Opcodes.ACC_PUBLIC) != 0) {
416: buf.append("ACC_PUBLIC");
417: first = false;
418: }
419: if ((access & Opcodes.ACC_PRIVATE) != 0) {
420: buf.append("ACC_PRIVATE");
421: first = false;
422: }
423: if ((access & Opcodes.ACC_PROTECTED) != 0) {
424: buf.append("ACC_PROTECTED");
425: first = false;
426: }
427: if ((access & Opcodes.ACC_FINAL) != 0) {
428: if (!first) {
429: buf.append(" + ");
430: }
431: buf.append("ACC_FINAL");
432: first = false;
433: }
434: if ((access & Opcodes.ACC_STATIC) != 0) {
435: if (!first) {
436: buf.append(" + ");
437: }
438: buf.append("ACC_STATIC");
439: first = false;
440: }
441: if ((access & Opcodes.ACC_SYNCHRONIZED) != 0) {
442: if (!first) {
443: buf.append(" + ");
444: }
445: if ((access & ACCESS_CLASS) != 0) {
446: buf.append("ACC_SUPER");
447: } else {
448: buf.append("ACC_SYNCHRONIZED");
449: }
450: first = false;
451: }
452: if ((access & Opcodes.ACC_VOLATILE) != 0
453: && (access & ACCESS_FIELD) != 0) {
454: if (!first) {
455: buf.append(" + ");
456: }
457: buf.append("ACC_VOLATILE");
458: first = false;
459: }
460: if ((access & Opcodes.ACC_BRIDGE) != 0
461: && (access & ACCESS_CLASS) == 0
462: && (access & ACCESS_FIELD) == 0) {
463: if (!first) {
464: buf.append(" + ");
465: }
466: buf.append("ACC_BRIDGE");
467: first = false;
468: }
469: if ((access & Opcodes.ACC_VARARGS) != 0
470: && (access & ACCESS_CLASS) == 0
471: && (access & ACCESS_FIELD) == 0) {
472: if (!first) {
473: buf.append(" + ");
474: }
475: buf.append("ACC_VARARGS");
476: first = false;
477: }
478: if ((access & Opcodes.ACC_TRANSIENT) != 0
479: && (access & ACCESS_FIELD) != 0) {
480: if (!first) {
481: buf.append(" + ");
482: }
483: buf.append("ACC_TRANSIENT");
484: first = false;
485: }
486: if ((access & Opcodes.ACC_NATIVE) != 0
487: && (access & ACCESS_CLASS) == 0
488: && (access & ACCESS_FIELD) == 0) {
489: if (!first) {
490: buf.append(" + ");
491: }
492: buf.append("ACC_NATIVE");
493: first = false;
494: }
495: if ((access & Opcodes.ACC_ENUM) != 0
496: && ((access & ACCESS_CLASS) != 0
497: || (access & ACCESS_FIELD) != 0 || (access & ACCESS_INNER) != 0)) {
498: if (!first) {
499: buf.append(" + ");
500: }
501: buf.append("ACC_ENUM");
502: first = false;
503: }
504: if ((access & Opcodes.ACC_ANNOTATION) != 0
505: && (access & ACCESS_CLASS) != 0) {
506: if (!first) {
507: buf.append(" + ");
508: }
509: buf.append("ACC_ANNOTATION");
510: first = false;
511: }
512: if ((access & Opcodes.ACC_ABSTRACT) != 0) {
513: if (!first) {
514: buf.append(" + ");
515: }
516: buf.append("ACC_ABSTRACT");
517: first = false;
518: }
519: if ((access & Opcodes.ACC_INTERFACE) != 0) {
520: if (!first) {
521: buf.append(" + ");
522: }
523: buf.append("ACC_INTERFACE");
524: first = false;
525: }
526: if ((access & Opcodes.ACC_STRICT) != 0) {
527: if (!first) {
528: buf.append(" + ");
529: }
530: buf.append("ACC_STRICT");
531: first = false;
532: }
533: if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
534: if (!first) {
535: buf.append(" + ");
536: }
537: buf.append("ACC_SYNTHETIC");
538: first = false;
539: }
540: if ((access & Opcodes.ACC_DEPRECATED) != 0) {
541: if (!first) {
542: buf.append(" + ");
543: }
544: buf.append("ACC_DEPRECATED");
545: first = false;
546: }
547: if (first) {
548: buf.append("0");
549: }
550: }
551: }
|