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 }