/* * * ASM: a very small and fast Java bytecode manipulation framework * Copyright (c) 2000-2007 INRIA, France Telecom * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ package org.powermock.api.mockito.repackaged.asm.util; import org.powermock.api.mockito.repackaged.asm.AnnotationVisitor; import org.powermock.api.mockito.repackaged.asm.ClassReader; import org.powermock.api.mockito.repackaged.asm.ClassVisitor; import org.powermock.api.mockito.repackaged.asm.FieldVisitor; import org.powermock.api.mockito.repackaged.asm.MethodVisitor; import org.powermock.api.mockito.repackaged.asm.Opcodes; import java.io.FileInputStream; import java.io.PrintWriter; /** * A {@link ClassVisitor} that prints the ASM code that generates the classes it * visits. This class visitor can be used to quickly write ASM code to generate * some given bytecode: <ul> <li>write the Java source code equivalent to the * bytecode you want to generate;</li> <li>compile it with <tt>javac</tt>;</li> * <li>make a {@link ASMifierClassVisitor} visit this compiled class (see the * {@link #main main} method);</li> <li>edit the generated source code, if * necessary.</li> </ul> The source code printed when visiting the * <tt>Hello</tt> class is the following: <p> <blockquote> * * <pre> * import org.powermock.api.mockito.repackaged.asm.*; * * public class HelloDump implements Opcodes { * * public static byte[] dump() throws Exception { * * ClassWriter cw = new ClassWriter(0); * FieldVisitor fv; * MethodVisitor mv; * AnnotationVisitor av0; * * cw.visit(49, * ACC_PUBLIC + ACC_SUPER, * "Hello", * null, * "java/lang/Object", * null); * * cw.visitSource("Hello.java", null); * * { * mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); * mv.visitVarInsn(ALOAD, 0); * mv.visitMethodInsn(INVOKESPECIAL, * "java/lang/Object", * "<init>", * "()V"); * mv.visitInsn(RETURN); * mv.visitMaxs(1, 1); * mv.visitEnd(); * } * { * mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, * "main", * "([Ljava/lang/String;)V", * null, * null); * mv.visitFieldInsn(GETSTATIC, * "java/lang/System", * "out", * "Ljava/io/PrintStream;"); * mv.visitLdcInsn("hello"); * mv.visitMethodInsn(INVOKEVIRTUAL, * "java/io/PrintStream", * "println", * "(Ljava/lang/String;)V"); * mv.visitInsn(RETURN); * mv.visitMaxs(2, 1); * mv.visitEnd(); * } * cw.visitEnd(); * * return cw.toByteArray(); * } * } * * </pre> * * </blockquote> where <tt>Hello</tt> is defined by: <p> <blockquote> * * <pre> * public class Hello { * * public static void main(String[] args) { * System.out.println("hello"); * } * } * </pre> * * </blockquote> * * @author Eric Bruneton * @author Eugene Kuleshov */ public class ASMifierClassVisitor extends ASMifierAbstractVisitor implements ClassVisitor { /** * Pseudo access flag used to distinguish class access flags. */ private static final int ACCESS_CLASS = 262144; /** * Pseudo access flag used to distinguish field access flags. */ private static final int ACCESS_FIELD = 524288; /** * Pseudo access flag used to distinguish inner class flags. */ private static final int ACCESS_INNER = 1048576; /** * The print writer to be used to print the class. */ protected final PrintWriter pw; /** * Constructs a new object. * * @param pw the print writer to be used to print the class. */ public ASMifierClassVisitor(final PrintWriter pw) { super("cw"); this.pw = pw; } /** * Prints the ASM source code to generate the given class to the standard * output. <p> Usage: ASMifierClassVisitor [-debug] <fully qualified * class name or class file name> * * @param args the command line arguments. * * @throws Exception if the class cannot be found, or if an IO exception * occurs. */ public static void main(final String[] args) throws Exception { int i = 0; int flags = ClassReader.SKIP_DEBUG; boolean ok = true; if (args.length < 1 || args.length > 2) { ok = false; } if (ok && "-debug".equals(args[0])) { i = 1; flags = 0; if (args.length != 2) { ok = false; } } if (!ok) { System.err.println("Prints the ASM code to generate the given class."); System.err.println("Usage: ASMifierClassVisitor [-debug] " + "<fully qualified class name or class file name>"); return; } ClassReader cr; if (args[i].endsWith(".class") || args[i].indexOf('\\') > -1 || args[i].indexOf('/') > -1) { cr = new ClassReader(new FileInputStream(args[i])); } else { cr = new ClassReader(args[i]); } cr.accept(new ASMifierClassVisitor(new PrintWriter(System.out)), getDefaultAttributes(), flags); } // ------------------------------------------------------------------------ // Implementation of the ClassVisitor interface // ------------------------------------------------------------------------ public void visit( final int version, final int access, final String name, final String signature, final String superName, final String[] interfaces) { String simpleName; int n = name.lastIndexOf('/'); if (n == -1) { simpleName = name; } else { text.add("package asm." + name.substring(0, n).replace('/', '.') + ";\n"); simpleName = name.substring(n + 1); } text.add("import java.util.*;\n"); text.add("import org.powermock.api.mockito.repackaged.asm.*;\n"); text.add("import org.powermock.api.mockito.repackaged.asm.attrs.*;\n"); text.add("public class " + simpleName + "Dump implements Opcodes {\n\n"); text.add("public static byte[] dump () throws Exception {\n\n"); text.add("ClassWriter cw = new ClassWriter(0);\n"); text.add("FieldVisitor fv;\n"); text.add("MethodVisitor mv;\n"); text.add("AnnotationVisitor av0;\n\n"); buf.setLength(0); buf.append("cw.visit("); switch (version) { case Opcodes.V1_1: buf.append("V1_1"); break; case Opcodes.V1_2: buf.append("V1_2"); break; case Opcodes.V1_3: buf.append("V1_3"); break; case Opcodes.V1_4: buf.append("V1_4"); break; case Opcodes.V1_5: buf.append("V1_5"); break; case Opcodes.V1_6: buf.append("V1_6"); break; default: buf.append(version); break; } buf.append(", "); appendAccess(access | ACCESS_CLASS); buf.append(", "); appendConstant(name); buf.append(", "); appendConstant(signature); buf.append(", "); appendConstant(superName); buf.append(", "); if (interfaces != null && interfaces.length > 0) { buf.append("new String[] {"); for (int i = 0; i < interfaces.length; ++i) { buf.append(i == 0 ? " " : ", "); appendConstant(interfaces[i]); } buf.append(" }"); } else { buf.append("null"); } buf.append(");\n\n"); text.add(buf.toString()); } public void visitSource(final String file, final String debug) { buf.setLength(0); buf.append("cw.visitSource("); appendConstant(file); buf.append(", "); appendConstant(debug); buf.append(");\n\n"); text.add(buf.toString()); } public void visitOuterClass( final String owner, final String name, final String desc) { buf.setLength(0); buf.append("cw.visitOuterClass("); appendConstant(owner); buf.append(", "); appendConstant(name); buf.append(", "); appendConstant(desc); buf.append(");\n\n"); text.add(buf.toString()); } public void visitInnerClass( final String name, final String outerName, final String innerName, final int access) { buf.setLength(0); buf.append("cw.visitInnerClass("); appendConstant(name); buf.append(", "); appendConstant(outerName); buf.append(", "); appendConstant(innerName); buf.append(", "); appendAccess(access | ACCESS_INNER); buf.append(");\n\n"); text.add(buf.toString()); } public FieldVisitor visitField( final int access, final String name, final String desc, final String signature, final Object value) { buf.setLength(0); buf.append("{\n"); buf.append("fv = cw.visitField("); appendAccess(access | ACCESS_FIELD); buf.append(", "); appendConstant(name); buf.append(", "); appendConstant(desc); buf.append(", "); appendConstant(signature); buf.append(", "); appendConstant(value); buf.append(");\n"); text.add(buf.toString()); ASMifierFieldVisitor aav = new ASMifierFieldVisitor(); text.add(aav.getText()); text.add("}\n"); return aav; } public MethodVisitor visitMethod( final int access, final String name, final String desc, final String signature, final String[] exceptions) { buf.setLength(0); buf.append("{\n"); buf.append("mv = cw.visitMethod("); appendAccess(access); buf.append(", "); appendConstant(name); buf.append(", "); appendConstant(desc); buf.append(", "); appendConstant(signature); buf.append(", "); if (exceptions != null && exceptions.length > 0) { buf.append("new String[] {"); for (int i = 0; i < exceptions.length; ++i) { buf.append(i == 0 ? " " : ", "); appendConstant(exceptions[i]); } buf.append(" }"); } else { buf.append("null"); } buf.append(");\n"); text.add(buf.toString()); ASMifierMethodVisitor acv = createASMifierMethodVisitor(); text.add(acv.getText()); text.add("}\n"); return acv; } protected ASMifierMethodVisitor createASMifierMethodVisitor() { return new ASMifierMethodVisitor(); } public AnnotationVisitor visitAnnotation( final String desc, final boolean visible) { buf.setLength(0); buf.append("{\n"); buf.append("av0 = cw.visitAnnotation("); appendConstant(desc); buf.append(", "); buf.append(visible); buf.append(");\n"); text.add(buf.toString()); ASMifierAnnotationVisitor av = new ASMifierAnnotationVisitor(0); text.add(av.getText()); text.add("}\n"); return av; } public void visitEnd() { text.add("cw.visitEnd();\n\n"); text.add("return cw.toByteArray();\n"); text.add("}\n"); text.add("}\n"); printList(pw, text); pw.flush(); } // ------------------------------------------------------------------------ // Utility methods // ------------------------------------------------------------------------ /** * Appends a string representation of the given access modifiers to {@link * #buf buf}. * * @param access some access modifiers. */ void appendAccess(final int access) { boolean first = true; if ((access & Opcodes.ACC_PUBLIC) != 0) { buf.append("ACC_PUBLIC"); first = false; } if ((access & Opcodes.ACC_PRIVATE) != 0) { buf.append("ACC_PRIVATE"); first = false; } if ((access & Opcodes.ACC_PROTECTED) != 0) { buf.append("ACC_PROTECTED"); first = false; } if ((access & Opcodes.ACC_FINAL) != 0) { if (!first) { buf.append(" + "); } buf.append("ACC_FINAL"); first = false; } if ((access & Opcodes.ACC_STATIC) != 0) { if (!first) { buf.append(" + "); } buf.append("ACC_STATIC"); first = false; } if ((access & Opcodes.ACC_SYNCHRONIZED) != 0) { if (!first) { buf.append(" + "); } if ((access & ACCESS_CLASS) == 0) { buf.append("ACC_SYNCHRONIZED"); } else { buf.append("ACC_SUPER"); } first = false; } if ((access & Opcodes.ACC_VOLATILE) != 0 && (access & ACCESS_FIELD) != 0) { if (!first) { buf.append(" + "); } buf.append("ACC_VOLATILE"); first = false; } if ((access & Opcodes.ACC_BRIDGE) != 0 && (access & ACCESS_CLASS) == 0 && (access & ACCESS_FIELD) == 0) { if (!first) { buf.append(" + "); } buf.append("ACC_BRIDGE"); first = false; } if ((access & Opcodes.ACC_VARARGS) != 0 && (access & ACCESS_CLASS) == 0 && (access & ACCESS_FIELD) == 0) { if (!first) { buf.append(" + "); } buf.append("ACC_VARARGS"); first = false; } if ((access & Opcodes.ACC_TRANSIENT) != 0 && (access & ACCESS_FIELD) != 0) { if (!first) { buf.append(" + "); } buf.append("ACC_TRANSIENT"); first = false; } if ((access & Opcodes.ACC_NATIVE) != 0 && (access & ACCESS_CLASS) == 0 && (access & ACCESS_FIELD) == 0) { if (!first) { buf.append(" + "); } buf.append("ACC_NATIVE"); first = false; } if ((access & Opcodes.ACC_ENUM) != 0 && ((access & ACCESS_CLASS) != 0 || (access & ACCESS_FIELD) != 0 || (access & ACCESS_INNER) != 0)) { if (!first) { buf.append(" + "); } buf.append("ACC_ENUM"); first = false; } if ((access & Opcodes.ACC_ANNOTATION) != 0 && (access & ACCESS_CLASS) != 0) { if (!first) { buf.append(" + "); } buf.append("ACC_ANNOTATION"); first = false; } if ((access & Opcodes.ACC_ABSTRACT) != 0) { if (!first) { buf.append(" + "); } buf.append("ACC_ABSTRACT"); first = false; } if ((access & Opcodes.ACC_INTERFACE) != 0) { if (!first) { buf.append(" + "); } buf.append("ACC_INTERFACE"); first = false; } if ((access & Opcodes.ACC_STRICT) != 0) { if (!first) { buf.append(" + "); } buf.append("ACC_STRICT"); first = false; } if ((access & Opcodes.ACC_SYNTHETIC) != 0) { if (!first) { buf.append(" + "); } buf.append("ACC_SYNTHETIC"); first = false; } if ((access & Opcodes.ACC_DEPRECATED) != 0) { if (!first) { buf.append(" + "); } buf.append("ACC_DEPRECATED"); first = false; } if (first) { buf.append('0'); } } }