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 com.tc.asm.util;
030:
031: import java.io.FileInputStream;
032: import java.io.PrintWriter;
033:
034: import com.tc.asm.AnnotationVisitor;
035: import com.tc.asm.ClassReader;
036: import com.tc.asm.ClassVisitor;
037: import com.tc.asm.FieldVisitor;
038: import com.tc.asm.MethodVisitor;
039: import com.tc.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 com.tc.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: public class ASMifierClassVisitor extends ASMifierAbstractVisitor
127: implements ClassVisitor {
128:
129: /**
130: * Pseudo access flag used to distinguish class access flags.
131: */
132: private final static int ACCESS_CLASS = 262144;
133:
134: /**
135: * Pseudo access flag used to distinguish field access flags.
136: */
137: private final static int ACCESS_FIELD = 524288;
138:
139: /**
140: * Pseudo access flag used to distinguish inner class flags.
141: */
142: private static final int ACCESS_INNER = 1048576;
143:
144: /**
145: * The print writer to be used to print the class.
146: */
147: protected final PrintWriter pw;
148:
149: /**
150: * Prints the ASM source code to generate the given class to the standard
151: * output. <p> Usage: ASMifierClassVisitor [-debug] <fully qualified
152: * class name or class file name>
153: *
154: * @param args the command line arguments.
155: *
156: * @throws Exception if the class cannot be found, or if an IO exception
157: * occurs.
158: */
159: public static void main(final String[] args) throws Exception {
160: int i = 0;
161: int flags = ClassReader.SKIP_DEBUG;
162:
163: boolean ok = true;
164: if (args.length < 1 || args.length > 2) {
165: ok = false;
166: }
167: if (ok && args[0].equals("-debug")) {
168: i = 1;
169: flags = 0;
170: if (args.length != 2) {
171: ok = false;
172: }
173: }
174: if (!ok) {
175: System.err
176: .println("Prints the ASM code to generate the given class.");
177: System.err
178: .println("Usage: ASMifierClassVisitor [-debug] "
179: + "<fully qualified class name or class file name>");
180: return;
181: }
182: ClassReader cr;
183: if (args[i].endsWith(".class") || args[i].indexOf('\\') > -1
184: || args[i].indexOf('/') > -1) {
185: cr = new ClassReader(new FileInputStream(args[i]));
186: } else {
187: cr = new ClassReader(args[i]);
188: }
189: cr.accept(
190: new ASMifierClassVisitor(new PrintWriter(System.out)),
191: getDefaultAttributes(), flags);
192: }
193:
194: /**
195: * Constructs a new {@link ASMifierClassVisitor} object.
196: *
197: * @param pw the print writer to be used to print the class.
198: */
199: public ASMifierClassVisitor(final PrintWriter pw) {
200: super ("cw");
201: this .pw = pw;
202: }
203:
204: // ------------------------------------------------------------------------
205: // Implementation of the ClassVisitor interface
206: // ------------------------------------------------------------------------
207:
208: public void visit(final int version, final int access,
209: final String name, final String signature,
210: final String super Name, final String[] interfaces) {
211: String simpleName;
212: int n = name.lastIndexOf('/');
213: if (n != -1) {
214: text.add("package asm."
215: + name.substring(0, n).replace('/', '.') + ";\n");
216: simpleName = name.substring(n + 1);
217: } else {
218: simpleName = name;
219: }
220: text.add("import java.util.*;\n");
221: text.add("import com.tc.asm.*;\n");
222: text.add("import com.tc.asm.attrs.*;\n");
223: text.add("public class " + simpleName
224: + "Dump implements Opcodes {\n\n");
225: text.add("public static byte[] dump () throws Exception {\n\n");
226: text.add("ClassWriter cw = new ClassWriter(0);\n");
227: text.add("FieldVisitor fv;\n");
228: text.add("MethodVisitor mv;\n");
229: text.add("AnnotationVisitor av0;\n\n");
230:
231: buf.setLength(0);
232: buf.append("cw.visit(");
233: switch (version) {
234: case Opcodes.V1_1:
235: buf.append("V1_1");
236: break;
237: case Opcodes.V1_2:
238: buf.append("V1_2");
239: break;
240: case Opcodes.V1_3:
241: buf.append("V1_3");
242: break;
243: case Opcodes.V1_4:
244: buf.append("V1_4");
245: break;
246: case Opcodes.V1_5:
247: buf.append("V1_5");
248: break;
249: case Opcodes.V1_6:
250: buf.append("V1_6");
251: break;
252: default:
253: buf.append(version);
254: break;
255: }
256: buf.append(", ");
257: appendAccess(access | ACCESS_CLASS);
258: buf.append(", ");
259: appendConstant(name);
260: buf.append(", ");
261: appendConstant(signature);
262: buf.append(", ");
263: appendConstant(super Name);
264: buf.append(", ");
265: if (interfaces != null && interfaces.length > 0) {
266: buf.append("new String[] {");
267: for (int i = 0; i < interfaces.length; ++i) {
268: buf.append(i == 0 ? " " : ", ");
269: appendConstant(interfaces[i]);
270: }
271: buf.append(" }");
272: } else {
273: buf.append("null");
274: }
275: buf.append(");\n\n");
276: text.add(buf.toString());
277: }
278:
279: public void visitSource(final String file, final String debug) {
280: buf.setLength(0);
281: buf.append("cw.visitSource(");
282: appendConstant(file);
283: buf.append(", ");
284: appendConstant(debug);
285: buf.append(");\n\n");
286: text.add(buf.toString());
287: }
288:
289: public void visitOuterClass(final String owner, final String name,
290: final String desc) {
291: buf.setLength(0);
292: buf.append("cw.visitOuterClass(");
293: appendConstant(owner);
294: buf.append(", ");
295: appendConstant(name);
296: buf.append(", ");
297: appendConstant(desc);
298: buf.append(");\n\n");
299: text.add(buf.toString());
300: }
301:
302: public void visitInnerClass(final String name,
303: final String outerName, final String innerName,
304: final int access) {
305: buf.setLength(0);
306: buf.append("cw.visitInnerClass(");
307: appendConstant(name);
308: buf.append(", ");
309: appendConstant(outerName);
310: buf.append(", ");
311: appendConstant(innerName);
312: buf.append(", ");
313: appendAccess(access | ACCESS_INNER);
314: buf.append(");\n\n");
315: text.add(buf.toString());
316: }
317:
318: public FieldVisitor visitField(final int access, final String name,
319: final String desc, final String signature,
320: final Object value) {
321: buf.setLength(0);
322: buf.append("{\n");
323: buf.append("fv = cw.visitField(");
324: appendAccess(access | ACCESS_FIELD);
325: buf.append(", ");
326: appendConstant(name);
327: buf.append(", ");
328: appendConstant(desc);
329: buf.append(", ");
330: appendConstant(signature);
331: buf.append(", ");
332: appendConstant(value);
333: buf.append(");\n");
334: text.add(buf.toString());
335: ASMifierFieldVisitor aav = new ASMifierFieldVisitor();
336: text.add(aav.getText());
337: text.add("}\n");
338: return aav;
339: }
340:
341: public MethodVisitor visitMethod(final int access,
342: final String name, final String desc,
343: final String signature, final String[] exceptions) {
344: buf.setLength(0);
345: buf.append("{\n");
346: buf.append("mv = cw.visitMethod(");
347: appendAccess(access);
348: buf.append(", ");
349: appendConstant(name);
350: buf.append(", ");
351: appendConstant(desc);
352: buf.append(", ");
353: appendConstant(signature);
354: buf.append(", ");
355: if (exceptions != null && exceptions.length > 0) {
356: buf.append("new String[] {");
357: for (int i = 0; i < exceptions.length; ++i) {
358: buf.append(i == 0 ? " " : ", ");
359: appendConstant(exceptions[i]);
360: }
361: buf.append(" }");
362: } else {
363: buf.append("null");
364: }
365: buf.append(");\n");
366: text.add(buf.toString());
367: ASMifierMethodVisitor acv = createASMifierMethodVisitor();
368: text.add(acv.getText());
369: text.add("}\n");
370: return acv;
371: }
372:
373: protected ASMifierMethodVisitor createASMifierMethodVisitor() {
374: return new ASMifierMethodVisitor();
375: }
376:
377: public AnnotationVisitor visitAnnotation(final String desc,
378: final boolean visible) {
379: buf.setLength(0);
380: buf.append("{\n");
381: buf.append("av0 = cw.visitAnnotation(");
382: appendConstant(desc);
383: buf.append(", ");
384: buf.append(visible);
385: buf.append(");\n");
386: text.add(buf.toString());
387: ASMifierAnnotationVisitor av = new ASMifierAnnotationVisitor(0);
388: text.add(av.getText());
389: text.add("}\n");
390: return av;
391: }
392:
393: public void visitEnd() {
394: text.add("cw.visitEnd();\n\n");
395: text.add("return cw.toByteArray();\n");
396: text.add("}\n");
397: text.add("}\n");
398: printList(pw, text);
399: pw.flush();
400: }
401:
402: // ------------------------------------------------------------------------
403: // Utility methods
404: // ------------------------------------------------------------------------
405:
406: /**
407: * Appends a string representation of the given access modifiers to {@link
408: * #buf buf}.
409: *
410: * @param access some access modifiers.
411: */
412: void appendAccess(final int access) {
413: boolean first = true;
414: if ((access & Opcodes.ACC_PUBLIC) != 0) {
415: buf.append("ACC_PUBLIC");
416: first = false;
417: }
418: if ((access & Opcodes.ACC_PRIVATE) != 0) {
419: buf.append("ACC_PRIVATE");
420: first = false;
421: }
422: if ((access & Opcodes.ACC_PROTECTED) != 0) {
423: buf.append("ACC_PROTECTED");
424: first = false;
425: }
426: if ((access & Opcodes.ACC_FINAL) != 0) {
427: if (!first) {
428: buf.append(" + ");
429: }
430: buf.append("ACC_FINAL");
431: first = false;
432: }
433: if ((access & Opcodes.ACC_STATIC) != 0) {
434: if (!first) {
435: buf.append(" + ");
436: }
437: buf.append("ACC_STATIC");
438: first = false;
439: }
440: if ((access & Opcodes.ACC_SYNCHRONIZED) != 0) {
441: if (!first) {
442: buf.append(" + ");
443: }
444: if ((access & ACCESS_CLASS) != 0) {
445: buf.append("ACC_SUPER");
446: } else {
447: buf.append("ACC_SYNCHRONIZED");
448: }
449: first = false;
450: }
451: if ((access & Opcodes.ACC_VOLATILE) != 0
452: && (access & ACCESS_FIELD) != 0) {
453: if (!first) {
454: buf.append(" + ");
455: }
456: buf.append("ACC_VOLATILE");
457: first = false;
458: }
459: if ((access & Opcodes.ACC_BRIDGE) != 0
460: && (access & ACCESS_CLASS) == 0
461: && (access & ACCESS_FIELD) == 0) {
462: if (!first) {
463: buf.append(" + ");
464: }
465: buf.append("ACC_BRIDGE");
466: first = false;
467: }
468: if ((access & Opcodes.ACC_VARARGS) != 0
469: && (access & ACCESS_CLASS) == 0
470: && (access & ACCESS_FIELD) == 0) {
471: if (!first) {
472: buf.append(" + ");
473: }
474: buf.append("ACC_VARARGS");
475: first = false;
476: }
477: if ((access & Opcodes.ACC_TRANSIENT) != 0
478: && (access & ACCESS_FIELD) != 0) {
479: if (!first) {
480: buf.append(" + ");
481: }
482: buf.append("ACC_TRANSIENT");
483: first = false;
484: }
485: if ((access & Opcodes.ACC_NATIVE) != 0
486: && (access & ACCESS_CLASS) == 0
487: && (access & ACCESS_FIELD) == 0) {
488: if (!first) {
489: buf.append(" + ");
490: }
491: buf.append("ACC_NATIVE");
492: first = false;
493: }
494: if ((access & Opcodes.ACC_ENUM) != 0
495: && ((access & ACCESS_CLASS) != 0
496: || (access & ACCESS_FIELD) != 0 || (access & ACCESS_INNER) != 0)) {
497: if (!first) {
498: buf.append(" + ");
499: }
500: buf.append("ACC_ENUM");
501: first = false;
502: }
503: if ((access & Opcodes.ACC_ANNOTATION) != 0
504: && (access & ACCESS_CLASS) != 0) {
505: if (!first) {
506: buf.append(" + ");
507: }
508: buf.append("ACC_ANNOTATION");
509: first = false;
510: }
511: if ((access & Opcodes.ACC_ABSTRACT) != 0) {
512: if (!first) {
513: buf.append(" + ");
514: }
515: buf.append("ACC_ABSTRACT");
516: first = false;
517: }
518: if ((access & Opcodes.ACC_INTERFACE) != 0) {
519: if (!first) {
520: buf.append(" + ");
521: }
522: buf.append("ACC_INTERFACE");
523: first = false;
524: }
525: if ((access & Opcodes.ACC_STRICT) != 0) {
526: if (!first) {
527: buf.append(" + ");
528: }
529: buf.append("ACC_STRICT");
530: first = false;
531: }
532: if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
533: if (!first) {
534: buf.append(" + ");
535: }
536: buf.append("ACC_SYNTHETIC");
537: first = false;
538: }
539: if ((access & Opcodes.ACC_DEPRECATED) != 0) {
540: if (!first) {
541: buf.append(" + ");
542: }
543: buf.append("ACC_DEPRECATED");
544: first = false;
545: }
546: if (first) {
547: buf.append("0");
548: }
549: }
550: }
|