All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.nerdvision.agent.inst.asm.PluginVisitor Maven / Gradle / Ivy
package com.nerdvision.agent.inst.asm;
import com.nerdvision.agent.api.plugins.IMatcher;
import com.nerdvision.agent.plugins.MethodHook;
import java.com.nerdvision.agent.snapshot.ProxyMethodHook;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.JSRInlinerAdapter;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TypeInsnNode;
import org.objectweb.asm.tree.VarInsnNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static com.nerdvision.agent.inst.asm.Visitor.loadVariable;
import static java.lang.reflect.Modifier.isStatic;
import static org.objectweb.asm.Opcodes.ACONST_NULL;
import static org.objectweb.asm.Opcodes.ALOAD;
import static org.objectweb.asm.Opcodes.ASM7;
import static org.objectweb.asm.Opcodes.ASM8;
import static org.objectweb.asm.Opcodes.DUP;
import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
import static org.objectweb.asm.Opcodes.INVOKESTATIC;
import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
import static org.objectweb.asm.Opcodes.NEW;
import static org.objectweb.asm.Opcodes.POP;
/**
* This visitor will process plugin calls to inject hooks into methods
*/
public class PluginVisitor extends ClassVisitor
{
private static final Logger LOGGER = LoggerFactory.getLogger( PluginVisitor.class );
public static final Class> CALLBACK_CLASS;
static
{
// this is here to make the tests easier.
// we cannot use java. classes in the tests without screwing with the class loaders
// so in the tests we use the 'nv.hook.class' which is the MethodHook.class
// at runtime we use the ProxyMethodHook.class so we can bypass the osgi classloading restrictions
final String property = System.getProperty( "nv.hook.class" );
if( property == null )
{
CALLBACK_CLASS = ProxyMethodHook.class;
}
else
{
Class> callbackClass;
try
{
callbackClass = Class.forName( property );
}
catch( ClassNotFoundException e )
{
callbackClass = ProxyMethodHook.class;
}
CALLBACK_CLASS = callbackClass;
}
}
private final String className;
private final IMatcher plugin;
private boolean changed = false;
public PluginVisitor( final ClassVisitor v, final String className, final IMatcher plugin )
{
super( ASM8, v );
this.className = className;
this.plugin = plugin;
}
public MethodVisitor visitMethod( int access, String name, String desc, String signature, String[] exceptions )
{
// we need to ignore abstract methods
if( TransformerUtils.isAbstract( access ) )
{
return super.visitMethod( access, name, desc, signature, exceptions );
}
// is this a method we want?
final IMatcher.IMethodHook iMethodHook = this.plugin.matchesMethod( name, desc );
if( iMethodHook == null )
{
return super.visitMethod( access, name, desc, signature, exceptions );
}
else
{
// register the extension and inject call
final String uuid = UUID.randomUUID().toString();
MethodHook.register( uuid, iMethodHook );
final MethodVisitor methodVisitor = super.visitMethod( access, name, desc, signature, exceptions );
final JSRInlinerAdapter jsrInlinerAdapter = new JSRInlinerAdapter( methodVisitor, access, name, desc, signature,
exceptions );
return new MethodNode( ASM7, access, name, desc, signature, exceptions )
{
@Override
public void visitEnd()
{
final InsnList hook = new InsnList();
// add id
hook.add( new LdcInsnNode( uuid ) );
// add this
final boolean isStatic = isStatic( access );
if( !isStatic )
{
// var = this
hook.add( new VarInsnNode( ALOAD, 0 ) );
}
else
{
// var = null
hook.add( new InsnNode( ACONST_NULL ) );
}
// list = new ArrayList();
hook.add( new TypeInsnNode( NEW, Type.getInternalName( ArrayList.class ) ) );
hook.add( new InsnNode( DUP ) );
hook.add( new MethodInsnNode( INVOKESPECIAL, Type.getInternalName( ArrayList.class ), "",
"()V", false ) );
final Type[] argumentTypes = Type.getArgumentTypes( desc );
for( int i = 0; i < argumentTypes.length; i++ )
{
// list.add(param);
final Type argumentType = argumentTypes[i];
LOGGER.debug( "visitMethod capture param {} {}", className, name );
hook.add( new InsnNode( DUP ) ); // we need a ptr to our list
hook.add( loadVariable( argumentType, i + 1 ) ); // value
// call list.add(value)
hook.add( new MethodInsnNode( INVOKEVIRTUAL, Type.getInternalName( ArrayList.class ), "add",
"(Ljava/lang/Object;)Z" ) );
hook.add( new InsnNode( POP ) ); // dont care about return
}
// MethodHook.callHook(id, this, list)
hook.add( new MethodInsnNode( INVOKESTATIC, Type.getInternalName( CALLBACK_CLASS ),
"callHook",
Type.getMethodDescriptor( Type.VOID_TYPE, Type.getType( String.class ), Type.getType( Object.class ),
Type.getType( List.class ) ),
false ) );
changed = true;
instructions.insert( hook );
this.accept( jsrInlinerAdapter );
super.visitEnd();
}
};
}
}
public boolean wasChanged()
{
return changed;
}
}