/******************************************************************************* * Copyright (c) 2010 Eric Bodden. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Eric Bodden - initial API and implementation ******************************************************************************/ package de.bodden.tamiflex.playout; import static org.objectweb.asm.Opcodes.ALOAD; import static org.objectweb.asm.Opcodes.INVOKESTATIC; import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.IllegalClassFormatException; import java.security.ProtectionDomain; import org.objectweb.asm.ClassAdapter; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.MethodAdapter; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; public class ReflectionMonitor implements ClassFileTransformer { public byte[] transform(ClassLoader loader, final String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { if(!className.equals("java/lang/Class") && !className.equals("java/lang/reflect/Method") && !className.equals("java/lang/reflect/Constructor")) return null; try { // scan class binary format to find fields for toString() method ClassReader creader = new ClassReader(classfileBuffer); ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS); ClassVisitor visitor = new ClassAdapter(writer) { public MethodVisitor visitMethod(int access, String methodName, String desc, String signature, String[] exceptions) { //delegate MethodVisitor mv = cv.visitMethod(access, methodName, desc, signature, exceptions); if(className.equals("java/lang/Class") && methodName.equals("forName")) { mv = new ClassForNameAdapter(mv); } else if(className.equals("java/lang/Class") && methodName.equals("newInstance0")) { mv = new ClassNewInstanceAdapter(mv); } else if(className.equals("java/lang/reflect/Method") && methodName.equals("invoke")) { mv = new MethodInvokeAdapter(mv); } else if(className.equals("java/lang/reflect/Constructor") && methodName.equals("newInstance")) { mv = new ConstructorNewInstanceAdapter(mv); } return mv; }; }; creader.accept(visitor, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); return writer.toByteArray(); } catch (IllegalStateException e) { throw new IllegalClassFormatException("Error: " + e.getMessage() + " on class " + className); } catch(RuntimeException e) { e.printStackTrace(); throw e; } } static class ClassForNameAdapter extends MethodAdapter { public ClassForNameAdapter(MethodVisitor mv) { super(mv); } @Override public void visitInsn(int opcode) { if(opcode==Opcodes.ARETURN) { //load first argument on stack, i.e. the name of the class to be loaded mv.visitVarInsn(ALOAD, 0); //call logging method with that Class object as argument mv.visitMethodInsn(INVOKESTATIC, "de/bodden/tamiflex/playout/rt/ReflLogger", "classForName", "(Ljava/lang/String;)V"); } super.visitInsn(opcode); } } static class ClassNewInstanceAdapter extends MethodAdapter { public ClassNewInstanceAdapter(MethodVisitor mv) { super(mv); } @Override public void visitInsn(int opcode) { if(opcode==Opcodes.ARETURN) { //load "this" on stack, i.e. the Class object mv.visitVarInsn(ALOAD, 0); //call logging method with that Class object as argument mv.visitMethodInsn(INVOKESTATIC, "de/bodden/tamiflex/playout/rt/ReflLogger", "classNewInstance", "(Ljava/lang/Class;)V"); // sthg missing here!! } super.visitInsn(opcode); } } static class MethodInvokeAdapter extends MethodAdapter { public MethodInvokeAdapter(MethodVisitor mv) { super(mv); } @Override public void visitInsn(int opcode) { if(opcode==Opcodes.ARETURN) { //load first parameter on the stack, i.e. the designated receiver object mv.visitVarInsn(ALOAD, 1); //load "this" on stack, i.e. the Method object mv.visitVarInsn(ALOAD, 0); //call logging method with that Method object as argument mv.visitMethodInsn(INVOKESTATIC, "de/bodden/tamiflex/playout/rt/ReflLogger", "methodInvoke", "(Ljava/lang/Object;Ljava/lang/reflect/Method;)V"); } super.visitInsn(opcode); } } static class ConstructorNewInstanceAdapter extends MethodAdapter { public ConstructorNewInstanceAdapter(MethodVisitor mv) { super(mv); } @Override public void visitInsn(int opcode) { if(opcode==Opcodes.ARETURN) { //load "this" on stack, i.e. the Constructor object mv.visitVarInsn(ALOAD, 0); //call logging method with that Method object as argument mv.visitMethodInsn(INVOKESTATIC, "de/bodden/tamiflex/playout/rt/ReflLogger", "constructorNewInstance", "(Ljava/lang/reflect/Constructor;)V"); } super.visitInsn(opcode); } } //Comment creation /* static class CommentAdapter extends MethodAdapter { public CommentAdapter(MethodVisitor mv) { super(mv); } @Override public void visitInsn(int opcode) { if(opcode==Opcodes.ARETURN) { //load first parameter on the stack, i.e. the designated receiver object mv.visitVarInsn(ALOAD, 1); //load "this" on stack, i.e. the Method object mv.visitVarInsn(ALOAD, 0); //call logging method with that Method object as argument mv.visitMethodInsn(INVOKESTATIC, "de/bodden/tamiflex/playout/rt/ReflLogger", "Comment", "(Ljava/lang/Object;Ljava/lang/reflect/Comment;)V"); //not sure about that!! } super.visitInsn(opcode); } }*/ }