All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jadex.bytecode.Proxy Maven / Gradle / Ivy

package jadex.bytecode;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.PrintWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.atomic.AtomicInteger;

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.FieldNode;
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.objectweb.asm.util.ASMifier;
import org.objectweb.asm.util.TraceClassVisitor;

import jadex.commons.SReflect;
import jadex.commons.SUtil;
import jadex.commons.Tuple2;
import jadex.commons.collection.WeakValueMap;

/**
 *  Proxy class allows for generating proxy objects for
 *  interfaces and/or one class. Both sides are optional.
 *  
 *  Uses the InvocationHandler from standard Java interface
 *  proxy mechanism.
 */
public class Proxy
{
	public static final Set OBJECTMETHODS = new HashSet();
	
	static
	{
		OBJECTMETHODS.add("equals");
		OBJECTMETHODS.add("toString");
		OBJECTMETHODS.add("hashCode");
	}

	public static final AtomicInteger COUNTER = new AtomicInteger();
	
	public static final Map>>, Class> CLASSCACHE = Collections.synchronizedMap(new WeakValueMap>>, Class>());
	
	/**
     *  Get the invocation handler of a proxy.
     *  @param proxy
     *  @return The handler
     */
    public static InvocationHandler getInvocationHandler(Object proxy) 
    {
    	try
    	{
    		proxy.getClass().getField("isproxy");
    		Field f = proxy.getClass().getField("handler");
    		return (InvocationHandler)f.get(proxy);
    	}
    	catch(Exception e)
    	{
    		SUtil.rethrowAsUnchecked(e);
    		return null;
    	}
    }
	
	/**
	 *  Generate a proxy for an existing class.
	 *  @param loader The class loader.
	 *  @param ifaces The interfaces (may contain one clazz).
	 *  @param handler The invocation handler.
	 *  @return The new proxy extending the clazz and implementing all interfaces.
	 */
	public static Object newProxyInstance(ClassLoader loader, final Class[] ifaces, InvocationHandler handler) 
	{
		try
		{
			Class clazz = null;
			List> ifs = new ArrayList>();
			for(Class iface: ifaces)
			{
				if(!iface.isInterface())
				{
					clazz = iface;
				}
				else
				{
					ifs.add(iface);
				}
			}
			Class[] ifsa = ifs.toArray(new Class[ifs.size()]);
			return newProxyInstance(loader, clazz, ifsa, handler);
		}
		catch(Exception e)
		{
			SUtil.rethrowAsUnchecked(e);
			return null;
		}
	}
	
	/**
	 *  Generate a proxy for an existing class.
	 *  @param loader The class loader.
	 *  @param clazz The clazz.
	 *  @param ifaces The interfaces.
	 *  @param handler The invocation handler.
	 *  @return The new proxy extending the clazz and implementing all interfaces.
	 */
	public static Object newProxyInstance(ClassLoader loader, final Class clazz, final Class[] ifaces, InvocationHandler handler) 
	{
		Set> def = SUtil.arrayToSet(ifaces);
		if(clazz!=null)
			def.add(clazz);
		loader = loader==null? Proxy.class.getClassLoader(): loader;

		// Try fetch from cache (use handler class loader to enable sharing between multiple platforms, todo: extra class loader to get rid of proxies?)
		Tuple2>> key = new Tuple2>>(handler.getClass().getClassLoader(), def);
//		Tuple2>> key = new Tuple2>>(loader, def);
		Class ret = CLASSCACHE.get(key);
		if(ret!=null)
		{
//			System.out.println("cache hit: "+key);
			try
			{
//				System.out.println("Cache hit: "+ret+" "+def);
				Constructor c = ret.getConstructor(new Class[]{InvocationHandler.class});
				Object o = c.newInstance(handler);
				return o;
			}
			catch(Exception e)
			{
				SUtil.rethrowAsUnchecked(e);
			}
		}
		
//		System.out.println("cache miss: "+key);
		
		ClassNode cn = new ClassNode();
		try
		{
			ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
	//		ClassReader cr = new ClassReader(clazz.getName());
	//		TraceClassVisitor tcv = new TraceClassVisitor(cw, new PrintWriter(System.out));
	//		TraceClassVisitor tcv = new TraceClassVisitor(cw, new ASMifier(), new PrintWriter(System.out));
			
			cn.version = Opcodes.V1_5;
			cn.access = Opcodes.ACC_PUBLIC;
			cn.name = (clazz!=null? Type.getInternalName(clazz)+"Proxy": "Proxy")+COUNTER.getAndIncrement();
			cn.superName = clazz!=null? Type.getInternalName(clazz): Type.getType(Object.class).getInternalName();
			cn.fields.add(new FieldNode(Opcodes.ACC_PUBLIC, "handler", "Ljava/lang/reflect/InvocationHandler;", null, null));
			cn.fields.add(new FieldNode(Opcodes.ACC_PUBLIC, "isproxy", Type.getType(Boolean.class).getDescriptor(), null, Boolean.TRUE));
			
			Class[] allifs = ifaces!=null? SReflect.getSuperInterfaces(ifaces): null;
			if(ifaces!=null)
			{
//				for(int i=0; i", "(Ljava/lang/reflect/InvocationHandler;)V", null, null);
			
			mn.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
			mn.instructions.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, clazz!=null? Type.getInternalName(clazz): Type.getInternalName(Object.class), "", "()V"));
			mn.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
			mn.instructions.add(new VarInsnNode(Opcodes.ALOAD, 1));
			mn.instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, cn.name, "handler", "Ljava/lang/reflect/InvocationHandler;"));
			mn.instructions.add(new InsnNode(Opcodes.RETURN));
			
			cn.methods.add(mn);
			
			// The done methods
			Set done = new HashSet();
			
			ClassNode cns = SASM.getClassNode(Object.class, loader);
	//		crs.accept(new TraceClassVisitor(cns, new PrintWriter(System.out)), 0);
	//		crs.accept(new TraceClassVisitor(cns, new ASMifier(), new PrintWriter(System.out)), 0);
			
			List ms = new ArrayList(cns.methods);
			
			for(MethodNode m: ms)
			{
				if(OBJECTMETHODS.contains(m.name))
				{
					MethodNode nmn = genrateInvocationCode(m, cn.name, null, loader);
					if(nmn!=null && !done.contains(nmn.name+nmn.desc))
					{
						cn.methods.add(nmn);
						done.add(nmn.name+nmn.desc);
	//						System.out.println(nmn.name+nmn.desc);
					}
	//					else if(nmn!=null)
	//					{
	//						System.out.println("OMITTED: "+nmn.name+nmn.desc);
	//					}
				}
			}
			
			if(clazz!=null)
			{
				cns = SASM.getClassNode(clazz, loader);
		//		crs.accept(new TraceClassVisitor(cns, new PrintWriter(System.out)), 0);
		//		crs.accept(new TraceClassVisitor(cns, new ASMifier(), new PrintWriter(System.out)), 0);
				
				ms = new ArrayList(cns.methods);
				
				for(MethodNode m: ms)
				{
					MethodNode nmn = genrateInvocationCode(m, cn.name, null, loader);
					if(nmn!=null && !done.contains(nmn.name+nmn.desc))
					{
						cn.methods.add(nmn);
						done.add(nmn.name+nmn.desc);
//						System.out.println(nmn.name+nmn.desc);
					}
//					else if(nmn!=null)
//					{
//						System.out.println("OMITTED: "+nmn.name+nmn.desc);
//					}
				}
			}
			
			
			if(ifaces!=null)
			{				
				for(int i=0; i tms = tcn.methods;
					for(MethodNode m: tms)
					{
						MethodNode nmn = genrateInvocationCode(m, cn.name, allifs[i], loader);
						if(nmn!=null && !done.contains(nmn.name+nmn.desc))
						{
							cn.methods.add(nmn);
							done.add(nmn.name+nmn.desc);
//							System.out.println(nmn.name+nmn.desc);
						}
//						else if(nmn!=null)
//						{
//							System.out.println("OMITTED: "+nmn.name+nmn.desc);
//						}
					}
				}
			}
			
			cn.accept(cw);
	//		cn.accept(tcv);
	//		cn.accept(new CheckClassAdapter(tcv));
//			cn.accept(new TraceClassVisitor(cw, new ASMifier(), new PrintWriter(System.out)));
			
	//		ByteClassLoader bcl = new ByteClassLoader(loader!=null? loader: SASM.class.getClassLoader());
			Class cl = SASM.toClass(cn.name.replace("/", "."), cw.toByteArray(), new URLClassLoader(new URL[0], loader), null);
	//		Class cl = bcl.loadClass(cn.name, cw.toByteArray(), true);
			Constructor c = cl.getConstructor(new Class[]{InvocationHandler.class});
			Object o = c.newInstance(handler);
		
//			CLASSCACHE.put(new Tuple2>>(loader, def), cl);
			CLASSCACHE.put(key, cl);
//			System.out.println("put into proxy class cache: "+key+", "+loader);
			return o;
		}
		catch(Throwable t)
		{
			ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
			//		TraceClassVisitor tcv = new TraceClassVisitor(cw, new PrintWriter(System.out));
			TraceClassVisitor tcv = new TraceClassVisitor(cw, new ASMifier(), new PrintWriter(System.out));
			cn.accept(tcv);
			
			SUtil.rethrowAsUnchecked(t);
			return null;
		}
	}
	
	/**
	 *  Generate the code for delegating the call to the invocation handler.
	 *  @param m The methodnode.
	 *  @param classname The class name.
	 *  @param iface The interface (null means the class is owner of the method)
	 *  @return The new method node (or null).
	 */
	protected static MethodNode genrateInvocationCode(MethodNode m, String classname, Class iface, ClassLoader loader) throws Exception
	{
		MethodNode ret = null;
		
		if(!"".equals(m.name) && !"".equals(m.name))
		{
			// todo: exceptions
//			MethodNode nm = new MethodNode(m.access, m.name, m.desc, m.signature, null);
			ret = new MethodNode(Opcodes.ACC_PUBLIC, m.name, m.desc, m.signature, null);
//			System.out.println(m.name+" "+m.desc+" "+m.signature);
			Type[] ptypes = Type.getArgumentTypes(ret.desc);
							
			// Object on which method is invoked (handler)
			
			ret.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
			ret.instructions.add(new FieldInsnNode(Opcodes.GETFIELD, classname, "handler", Type.getDescriptor(InvocationHandler.class)));
			
			// Arguments proxy, method, args
			
			// Proxy
			ret.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
			
//			nm.instructions.add(new FieldInsnNode(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"));
//			nm.instructions.add(new LdcInsnNode("0ppppppp"));
//			nm.instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false));
			
			// Method
			if(iface==null)
			{
				ret.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
				ret.instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;", false));
				ret.instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/lang/Class", "getSuperclass", "()Ljava/lang/Class;", false));
			}
			else
			{
				ret.instructions.add(new LdcInsnNode(Type.getType(iface)));
//				ret.instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;", false));
			}
			ret.instructions.add(new LdcInsnNode(m.name));
			ret.instructions.add(new LdcInsnNode(ptypes.length));
			ret.instructions.add(new TypeInsnNode(Opcodes.ANEWARRAY, "java/lang/Class"));				
			for(int i=0; i cl = SReflect.findClass(ptypes[i].getClassName(), null, loader);
				
				if(cl.isPrimitive())
				{
					// Special hack to get primitive class
//					nm.instructions.add(new LdcInsnNode(Type.getType(SReflect.getWrappedType(cl))));
					ret.instructions.add(new FieldInsnNode(Opcodes.GETSTATIC, Type.getInternalName(SReflect.getWrappedType(cl)), "TYPE", "Ljava/lang/Class;"));
				}
				else
				{
					ret.instructions.add(new LdcInsnNode(ptypes[i]));
				}
				ret.instructions.add(new InsnNode(Opcodes.AASTORE));
			}
			ret.instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/lang/Class", "getMethod", "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;", false));

			// Arguments
			ret.instructions.add(new LdcInsnNode(ptypes.length));
			ret.instructions.add(new TypeInsnNode(Opcodes.ANEWARRAY, "java/lang/Object"));
			int pos = 1;
			for(int i=0; i[]{ActionListener.class},
//			new InvocationHandler()
//		{
//			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
//			{
//				System.out.println("Handler called: "+proxy+" "+method+" "+args);
//				try
//				{
//					return method.invoke(mtc, args);
//				}
//				catch(Exception e)
//				{
//					System.out.println("Could not delegate to original object: "+e.getMessage());
//					return null;
//				}
//			}
//		});
//		
//		System.out.println("o: "+proxy+" "+proxy.getClass().getField("handler").get(proxy));
//		
//		try
//		{
//			proxy.call2("hallo", 12);
//			System.out.println(proxy.add(1, 2));
//			((ActionListener)proxy).actionPerformed(new ActionEvent(new Object(), 1, null));
//		}
//		catch(Throwable t)
//		{
//			t.printStackTrace();
//		}
		
		
		ActionListener proxy2 = (ActionListener)newProxyInstance(null, null, 
			new Class[]{ActionListener.class},
			new InvocationHandler()
		{
			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
			{
				System.out.println("Handler called: "+proxy+" "+method+" "+args);
				return null;
			}
		});
		
		try
		{
			proxy2.actionPerformed(new ActionEvent(new Object(), 1, null));
		}
		catch(Throwable t)
		{
			t.printStackTrace();
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy