    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}