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.uwyn.rife.asm.util;
030:
031: import java.io.FileInputStream;
032: import java.io.PrintWriter;
033:
034: import com.uwyn.rife.asm.AnnotationVisitor;
035: import com.uwyn.rife.asm.ClassReader;
036: import com.uwyn.rife.asm.ClassVisitor;
037: import com.uwyn.rife.asm.FieldVisitor;
038: import com.uwyn.rife.asm.MethodVisitor;
039: import com.uwyn.rife.asm.Opcodes;
040: import com.uwyn.rife.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 com.uwyn.rife.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: 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 com.uwyn.rife.asm.*;\n");
223: text.add("import com.uwyn.rife.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(false);\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 = new ASMifierMethodVisitor();
369: text.add(acv.getText());
370: text.add("}\n");
371: return acv;
372: }
373:
374: public AnnotationVisitor visitAnnotation(final String desc,
375: final boolean visible) {
376: buf.setLength(0);
377: buf.append("{\n");
378: buf.append("av0 = cw.visitAnnotation(");
379: appendConstant(desc);
380: buf.append(", ");
381: buf.append(visible);
382: buf.append(");\n");
383: text.add(buf.toString());
384: ASMifierAnnotationVisitor av = new ASMifierAnnotationVisitor(0);
385: text.add(av.getText());
386: text.add("}\n");
387: return av;
388: }
389:
390: public void visitEnd() {
391: text.add("cw.visitEnd();\n\n");
392: text.add("return cw.toByteArray();\n");
393: text.add("}\n");
394: text.add("}\n");
395: printList(pw, text);
396: pw.flush();
397: }
398:
399: // ------------------------------------------------------------------------
400: // Utility methods
401: // ------------------------------------------------------------------------
402:
403: /**
404: * Appends a string representation of the given access modifiers to {@link
405: * #buf buf}.
406: *
407: * @param access some access modifiers.
408: */
409: void appendAccess(final int access) {
410: boolean first = true;
411: if ((access & Opcodes.ACC_PUBLIC) != 0) {
412: buf.append("ACC_PUBLIC");
413: first = false;
414: }
415: if ((access & Opcodes.ACC_PRIVATE) != 0) {
416: if (!first) {
417: buf.append(" + ");
418: }
419: buf.append("ACC_PRIVATE");
420: first = false;
421: }
422: if ((access & Opcodes.ACC_PROTECTED) != 0) {
423: if (!first) {
424: buf.append(" + ");
425: }
426: buf.append("ACC_PROTECTED");
427: first = false;
428: }
429: if ((access & Opcodes.ACC_FINAL) != 0) {
430: if (!first) {
431: buf.append(" + ");
432: }
433: buf.append("ACC_FINAL");
434: first = false;
435: }
436: if ((access & Opcodes.ACC_STATIC) != 0) {
437: if (!first) {
438: buf.append(" + ");
439: }
440: buf.append("ACC_STATIC");
441: first = false;
442: }
443: if ((access & Opcodes.ACC_SYNCHRONIZED) != 0) {
444: if (!first) {
445: buf.append(" + ");
446: }
447: if ((access & ACCESS_CLASS) != 0) {
448: buf.append("ACC_SUPER");
449: } else {
450: buf.append("ACC_SYNCHRONIZED");
451: }
452: first = false;
453: }
454: if ((access & Opcodes.ACC_VOLATILE) != 0
455: && (access & ACCESS_FIELD) != 0) {
456: if (!first) {
457: buf.append(" + ");
458: }
459: buf.append("ACC_VOLATILE");
460: first = false;
461: }
462: if ((access & Opcodes.ACC_BRIDGE) != 0
463: && (access & ACCESS_CLASS) == 0
464: && (access & ACCESS_FIELD) == 0) {
465: if (!first) {
466: buf.append(" + ");
467: }
468: buf.append("ACC_BRIDGE");
469: first = false;
470: }
471: if ((access & Opcodes.ACC_VARARGS) != 0
472: && (access & ACCESS_CLASS) == 0
473: && (access & ACCESS_FIELD) == 0) {
474: if (!first) {
475: buf.append(" + ");
476: }
477: buf.append("ACC_VARARGS");
478: first = false;
479: }
480: if ((access & Opcodes.ACC_TRANSIENT) != 0
481: && (access & ACCESS_FIELD) != 0) {
482: if (!first) {
483: buf.append(" + ");
484: }
485: buf.append("ACC_TRANSIENT");
486: first = false;
487: }
488: if ((access & Opcodes.ACC_NATIVE) != 0
489: && (access & ACCESS_CLASS) == 0
490: && (access & ACCESS_FIELD) == 0) {
491: if (!first) {
492: buf.append(" + ");
493: }
494: buf.append("ACC_NATIVE");
495: first = false;
496: }
497: if ((access & Opcodes.ACC_ENUM) != 0
498: && ((access & ACCESS_CLASS) != 0
499: || (access & ACCESS_FIELD) != 0 || (access & ACCESS_INNER) != 0)) {
500: if (!first) {
501: buf.append(" + ");
502: }
503: buf.append("ACC_ENUM");
504: first = false;
505: }
506: if ((access & Opcodes.ACC_ANNOTATION) != 0
507: && ((access & ACCESS_CLASS) != 0)) {
508: if (!first) {
509: buf.append(" + ");
510: }
511: buf.append("ACC_ANNOTATION");
512: first = false;
513: }
514: if ((access & Opcodes.ACC_ABSTRACT) != 0) {
515: if (!first) {
516: buf.append(" + ");
517: }
518: buf.append("ACC_ABSTRACT");
519: first = false;
520: }
521: if ((access & Opcodes.ACC_INTERFACE) != 0) {
522: if (!first) {
523: buf.append(" + ");
524: }
525: buf.append("ACC_INTERFACE");
526: first = false;
527: }
528: if ((access & Opcodes.ACC_STRICT) != 0) {
529: if (!first) {
530: buf.append(" + ");
531: }
532: buf.append("ACC_STRICT");
533: first = false;
534: }
535: if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
536: if (!first) {
537: buf.append(" + ");
538: }
539: buf.append("ACC_SYNTHETIC");
540: first = false;
541: }
542: if ((access & Opcodes.ACC_DEPRECATED) != 0) {
543: if (!first) {
544: buf.append(" + ");
545: }
546: buf.append("ACC_DEPRECATED");
547: first = false;
548: }
549: if (first) {
550: buf.append("0");
551: }
552: }
553:
554: /**
555: * Appends a string representation of the given constant to the given
556: * buffer.
557: *
558: * @param buf a string buffer.
559: * @param cst an {@link java.lang.Integer Integer}, {@link java.lang.Float
560: * Float}, {@link java.lang.Long Long},
561: * {@link java.lang.Double Double} or {@link String String} object.
562: * May be <tt>null</tt>.
563: */
564: static void appendConstant(final StringBuffer buf, final Object cst) {
565: if (cst == null) {
566: buf.append("null");
567: } else if (cst instanceof String) {
568: AbstractVisitor.appendString(buf, (String) cst);
569: } else if (cst instanceof Type) {
570: buf.append("Type.getType(\"").append(
571: ((Type) cst).getDescriptor()).append("\")");
572: } else if (cst instanceof Integer) {
573: buf.append("new Integer(").append(cst).append(")");
574: } else if (cst instanceof Float) {
575: buf.append("new Float(\"").append(cst).append("\")");
576: } else if (cst instanceof Long) {
577: buf.append("new Long(").append(cst).append("L)");
578: } else if (cst instanceof Double) {
579: buf.append("new Double(\"").append(cst).append("\")");
580: }
581: }
582: }
|