| /*** |
| * ASM examples: examples showing how ASM can be used |
| * Copyright (c) 2000-2011 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. |
| */ |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.lang.reflect.Method; |
| |
| import org.objectweb.asm.ClassReader; |
| import org.objectweb.asm.ClassVisitor; |
| import org.objectweb.asm.ClassWriter; |
| import org.objectweb.asm.FieldVisitor; |
| import org.objectweb.asm.MethodVisitor; |
| import org.objectweb.asm.Opcodes; |
| import org.objectweb.asm.Type; |
| |
| /** |
| * @author Eric Bruneton |
| */ |
| public class Adapt extends ClassLoader { |
| |
| @Override |
| protected synchronized Class<?> loadClass(final String name, |
| final boolean resolve) throws ClassNotFoundException { |
| if (name.startsWith("java.")) { |
| System.err.println("Adapt: loading class '" + name |
| + "' without on the fly adaptation"); |
| return super.loadClass(name, resolve); |
| } else { |
| System.err.println("Adapt: loading class '" + name |
| + "' with on the fly adaptation"); |
| } |
| |
| // gets an input stream to read the bytecode of the class |
| String resource = name.replace('.', '/') + ".class"; |
| InputStream is = getResourceAsStream(resource); |
| byte[] b; |
| |
| // adapts the class on the fly |
| try { |
| ClassReader cr = new ClassReader(is); |
| ClassWriter cw = new ClassWriter(0); |
| ClassVisitor cv = new TraceFieldClassAdapter(cw); |
| cr.accept(cv, 0); |
| b = cw.toByteArray(); |
| } catch (Exception e) { |
| throw new ClassNotFoundException(name, e); |
| } |
| |
| // optional: stores the adapted class on disk |
| try { |
| FileOutputStream fos = new FileOutputStream(resource + ".adapted"); |
| fos.write(b); |
| fos.close(); |
| } catch (IOException e) { |
| } |
| |
| // returns the adapted class |
| return defineClass(name, b, 0, b.length); |
| } |
| |
| public static void main(final String args[]) throws Exception { |
| // loads the application class (in args[0]) with an Adapt class loader |
| ClassLoader loader = new Adapt(); |
| Class<?> c = loader.loadClass(args[0]); |
| // calls the 'main' static method of this class with the |
| // application arguments (in args[1] ... args[n]) as parameter |
| Method m = c.getMethod("main", new Class<?>[] { String[].class }); |
| String[] applicationArgs = new String[args.length - 1]; |
| System.arraycopy(args, 1, applicationArgs, 0, applicationArgs.length); |
| m.invoke(null, new Object[] { applicationArgs }); |
| } |
| } |
| |
| class TraceFieldClassAdapter extends ClassVisitor implements Opcodes { |
| |
| private String owner; |
| |
| public TraceFieldClassAdapter(final ClassVisitor cv) { |
| super(Opcodes.ASM4, cv); |
| } |
| |
| @Override |
| public void visit(final int version, final int access, final String name, |
| final String signature, final String superName, |
| final String[] interfaces) { |
| owner = name; |
| super.visit(version, access, name, signature, superName, interfaces); |
| } |
| |
| @Override |
| public FieldVisitor visitField(final int access, final String name, |
| final String desc, final String signature, final Object value) { |
| FieldVisitor fv = super |
| .visitField(access, name, desc, signature, value); |
| if ((access & ACC_STATIC) == 0) { |
| Type t = Type.getType(desc); |
| int size = t.getSize(); |
| |
| // generates getter method |
| String gDesc = "()" + desc; |
| MethodVisitor gv = cv.visitMethod(ACC_PRIVATE, "_get" + name, |
| gDesc, null, null); |
| gv.visitFieldInsn(GETSTATIC, "java/lang/System", "err", |
| "Ljava/io/PrintStream;"); |
| gv.visitLdcInsn("_get" + name + " called"); |
| gv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", |
| "(Ljava/lang/String;)V"); |
| gv.visitVarInsn(ALOAD, 0); |
| gv.visitFieldInsn(GETFIELD, owner, name, desc); |
| gv.visitInsn(t.getOpcode(IRETURN)); |
| gv.visitMaxs(1 + size, 1); |
| gv.visitEnd(); |
| |
| // generates setter method |
| String sDesc = "(" + desc + ")V"; |
| MethodVisitor sv = cv.visitMethod(ACC_PRIVATE, "_set" + name, |
| sDesc, null, null); |
| sv.visitFieldInsn(GETSTATIC, "java/lang/System", "err", |
| "Ljava/io/PrintStream;"); |
| sv.visitLdcInsn("_set" + name + " called"); |
| sv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", |
| "(Ljava/lang/String;)V"); |
| sv.visitVarInsn(ALOAD, 0); |
| sv.visitVarInsn(t.getOpcode(ILOAD), 1); |
| sv.visitFieldInsn(PUTFIELD, owner, name, desc); |
| sv.visitInsn(RETURN); |
| sv.visitMaxs(1 + size, 1 + size); |
| sv.visitEnd(); |
| } |
| return fv; |
| } |
| |
| @Override |
| public MethodVisitor visitMethod(final int access, final String name, |
| final String desc, final String signature, final String[] exceptions) { |
| MethodVisitor mv = cv.visitMethod(access, name, desc, signature, |
| exceptions); |
| return mv == null ? null : new TraceFieldCodeAdapter(mv, owner); |
| } |
| } |
| |
| class TraceFieldCodeAdapter extends MethodVisitor implements Opcodes { |
| |
| private String owner; |
| |
| public TraceFieldCodeAdapter(final MethodVisitor mv, final String owner) { |
| super(Opcodes.ASM4, mv); |
| this.owner = owner; |
| } |
| |
| @Override |
| public void visitFieldInsn(final int opcode, final String owner, |
| final String name, final String desc) { |
| if (owner.equals(this.owner)) { |
| if (opcode == GETFIELD) { |
| // replaces GETFIELD f by INVOKESPECIAL _getf |
| String gDesc = "()" + desc; |
| visitMethodInsn(INVOKESPECIAL, owner, "_get" + name, gDesc); |
| return; |
| } else if (opcode == PUTFIELD) { |
| // replaces PUTFIELD f by INVOKESPECIAL _setf |
| String sDesc = "(" + desc + ")V"; |
| visitMethodInsn(INVOKESPECIAL, owner, "_set" + name, sDesc); |
| return; |
| } |
| } |
| super.visitFieldInsn(opcode, owner, name, desc); |
| } |
| } |