001    package net.minecraftforge.event;
002    
003    import static org.objectweb.asm.Opcodes.*;
004    
005    import java.lang.reflect.Method;
006    
007    
008    import org.objectweb.asm.ClassWriter;
009    import org.objectweb.asm.MethodVisitor;
010    import org.objectweb.asm.Type;
011    
012    
013    public 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    }