001package net.minecraftforge.event;
002
003import static org.objectweb.asm.Opcodes.*;
004
005import java.lang.reflect.Method;
006
007
008import org.objectweb.asm.ClassWriter;
009import org.objectweb.asm.MethodVisitor;
010import org.objectweb.asm.Type;
011
012
013public class ASMEventHandler implements IEventListener
014{
015    private static int IDs = 0;
016    private static final String HANDLER_DESC = Type.getInternalName(IEventListener.class);
017    private static final String HANDLER_FUNC_DESC = Type.getMethodDescriptor(IEventListener.class.getDeclaredMethods()[0]);    
018    private static final ASMClassLoader LOADER = new ASMClassLoader();
019    
020    private final IEventListener handler;
021    private final ForgeSubscribe subInfo;
022    public ASMEventHandler(Object target, Method method) throws Exception
023    {
024        handler = (IEventListener)createWrapper(method).getConstructor(Object.class).newInstance(target);
025        subInfo = method.getAnnotation(ForgeSubscribe.class);
026    }
027
028    @Override
029    public void invoke(Event event)
030    {
031        if (handler != null)
032        {
033            if (!event.isCancelable() || !event.isCanceled() || subInfo.receiveCanceled())
034            {
035                handler.invoke(event);
036            }
037        }
038    }
039    
040    public EventPriority getPriority()
041    {
042        return subInfo.priority();
043    }
044    
045    public Class<?> createWrapper(Method callback)
046    {
047        ClassWriter cw = new ClassWriter(0);
048        MethodVisitor mv;
049        
050        String name = getUniqueName(callback);
051        String desc = name.replace('.',  '/');
052        String instType = Type.getInternalName(callback.getDeclaringClass());
053        String eventType = Type.getInternalName(callback.getParameterTypes()[0]);
054        
055        /*
056        System.out.println("Name:     " + name);
057        System.out.println("Desc:     " + desc);
058        System.out.println("InstType: " + instType);
059        System.out.println("Callback: " + callback.getName() + Type.getMethodDescriptor(callback));
060        System.out.println("Event:    " + eventType);
061        */
062        
063        cw.visit(V1_6, ACC_PUBLIC | ACC_SUPER, desc, null, "java/lang/Object", new String[]{ HANDLER_DESC });
064
065        cw.visitSource(".dynamic", null);
066        {
067            cw.visitField(ACC_PUBLIC, "instance", "Ljava/lang/Object;", null, null).visitEnd();
068        }
069        {
070            mv = cw.visitMethod(ACC_PUBLIC, "<init>", "(Ljava/lang/Object;)V", null, null);
071            mv.visitCode();
072            mv.visitVarInsn(ALOAD, 0);
073            mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
074            mv.visitVarInsn(ALOAD, 0);
075            mv.visitVarInsn(ALOAD, 1);
076            mv.visitFieldInsn(PUTFIELD, desc, "instance", "Ljava/lang/Object;");
077            mv.visitInsn(RETURN);
078            mv.visitMaxs(2, 2);
079            mv.visitEnd();
080        }
081        {
082            mv = cw.visitMethod(ACC_PUBLIC, "invoke", HANDLER_FUNC_DESC, null, null);
083            mv.visitCode();
084            mv.visitVarInsn(ALOAD, 0);
085            mv.visitFieldInsn(GETFIELD, desc, "instance", "Ljava/lang/Object;");
086            mv.visitTypeInsn(CHECKCAST, instType);
087            mv.visitVarInsn(ALOAD, 1);
088            mv.visitTypeInsn(CHECKCAST, eventType);
089            mv.visitMethodInsn(INVOKEVIRTUAL, instType, callback.getName(), Type.getMethodDescriptor(callback));
090            mv.visitInsn(RETURN);
091            mv.visitMaxs(2, 2);
092            mv.visitEnd();
093        }
094        cw.visitEnd();
095        return LOADER.define(name, cw.toByteArray());
096    }
097    
098    private String getUniqueName(Method callback)
099    {
100        return String.format("%s_%d_%s_%s_%s", getClass().getName(), IDs++, 
101                callback.getDeclaringClass().getSimpleName(), 
102                callback.getName(), 
103                callback.getParameterTypes()[0].getSimpleName());
104    }
105    
106    private static class ASMClassLoader extends ClassLoader
107    {
108        private ASMClassLoader()
109        {
110            super(ASMClassLoader.class.getClassLoader());
111        }
112        
113        public Class<?> define(String name, byte[] data)
114        {
115            return defineClass(name, data, 0, data.length);
116        }
117    }
118
119}