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 }