    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    }