001 package net.minecraftforge.transformers; 002 003 import java.util.List; 004 005 import net.minecraftforge.event.Event; 006 import net.minecraftforge.event.ListenerList; 007 008 import org.objectweb.asm.*; 009 import org.objectweb.asm.tree.*; 010 import static org.objectweb.asm.Opcodes.*; 011 import static org.objectweb.asm.Type.*; 012 import static org.objectweb.asm.ClassWriter.*; 013 014 import cpw.mods.fml.relauncher.IClassTransformer; 015 016 public class EventTransformer implements IClassTransformer 017 { 018 public EventTransformer() 019 { 020 } 021 022 @Override 023 public byte[] transform(String name, byte[] bytes) 024 { 025 if (name.equals("net.minecraftforge.event.Event") || name.startsWith("net.minecraft.") || name.indexOf('.') == -1) 026 { 027 return bytes; 028 } 029 ClassReader cr = new ClassReader(bytes); 030 ClassNode classNode = new ClassNode(); 031 cr.accept(classNode, 0); 032 033 try 034 { 035 if (buildEvents(classNode)) 036 { 037 ClassWriter cw = new ClassWriter(COMPUTE_MAXS | COMPUTE_FRAMES); 038 classNode.accept(cw); 039 return cw.toByteArray(); 040 } 041 return bytes; 042 } 043 catch (Exception e) 044 { 045 e.printStackTrace(); 046 } 047 048 return bytes; 049 } 050 051 @SuppressWarnings("unchecked") 052 private boolean buildEvents(ClassNode classNode) throws Exception 053 { 054 Class<?> parent = this.getClass().getClassLoader().loadClass(classNode.superName.replace('/', '.')); 055 if (!Event.class.isAssignableFrom(parent)) 056 { 057 return false; 058 } 059 060 boolean hasSetup = false; 061 boolean hasGetListenerList = false; 062 boolean hasDefaultCtr = false; 063 064 Class<?> listenerListClazz = Class.forName("net.minecraftforge.event.ListenerList", false, getClass().getClassLoader()); 065 Type tList = Type.getType(listenerListClazz); 066 067 for (MethodNode method : (List<MethodNode>)classNode.methods) 068 { 069 if (method.name.equals("setup") && 070 method.desc.equals(Type.getMethodDescriptor(VOID_TYPE)) && 071 (method.access & ACC_PROTECTED) == ACC_PROTECTED) 072 { 073 hasSetup = true; 074 } 075 if (method.name.equals("getListenerList") && 076 method.desc.equals(Type.getMethodDescriptor(tList)) && 077 (method.access & ACC_PUBLIC) == ACC_PUBLIC) 078 { 079 hasGetListenerList = true; 080 } 081 if (method.name.equals("<init>") && 082 method.desc.equals(Type.getMethodDescriptor(VOID_TYPE))) 083 { 084 hasDefaultCtr = true; 085 } 086 } 087 088 if (hasSetup) 089 { 090 if (!hasGetListenerList) 091 { 092 throw new RuntimeException("Event class defines setup() but does not define getListenerList! " + classNode.name); 093 } 094 else 095 { 096 return false; 097 } 098 } 099 100 Type tSuper = Type.getType(classNode.superName); 101 102 //Add private static ListenerList LISTENER_LIST 103 classNode.fields.add(new FieldNode(ACC_PRIVATE | ACC_STATIC, "LISTENER_LIST", tList.getDescriptor(), null, null)); 104 105 /*Add: 106 * public <init>() 107 * { 108 * super(); 109 * } 110 */ 111 MethodNode method = new MethodNode(ASM4, ACC_PUBLIC, "<init>", getMethodDescriptor(VOID_TYPE), null, null); 112 method.instructions.add(new VarInsnNode(ALOAD, 0)); 113 method.instructions.add(new MethodInsnNode(INVOKESPECIAL, tSuper.getInternalName(), "<init>", getMethodDescriptor(VOID_TYPE))); 114 method.instructions.add(new InsnNode(RETURN)); 115 if (!hasDefaultCtr) 116 { 117 classNode.methods.add(method); 118 } 119 120 /*Add: 121 * protected void setup() 122 * { 123 * super.setup(); 124 * if (LISTENER_LIST != NULL) 125 * { 126 * return; 127 * } 128 * LISTENER_LIST = new ListenerList(super.getListenerList()); 129 * } 130 */ 131 method = new MethodNode(ASM4, ACC_PROTECTED, "setup", getMethodDescriptor(VOID_TYPE), null, null); 132 method.instructions.add(new VarInsnNode(ALOAD, 0)); 133 method.instructions.add(new MethodInsnNode(INVOKESPECIAL, tSuper.getInternalName(), "setup", getMethodDescriptor(VOID_TYPE))); 134 method.instructions.add(new FieldInsnNode(GETSTATIC, classNode.name, "LISTENER_LIST", tList.getDescriptor())); 135 LabelNode initLisitener = new LabelNode(); 136 method.instructions.add(new JumpInsnNode(IFNULL, initLisitener)); 137 method.instructions.add(new InsnNode(RETURN)); 138 method.instructions.add(initLisitener); 139 method.instructions.add(new FrameNode(F_SAME, 0, null, 0, null)); 140 method.instructions.add(new TypeInsnNode(NEW, tList.getInternalName())); 141 method.instructions.add(new InsnNode(DUP)); 142 method.instructions.add(new VarInsnNode(ALOAD, 0)); 143 method.instructions.add(new MethodInsnNode(INVOKESPECIAL, tSuper.getInternalName(), "getListenerList", getMethodDescriptor(tList))); 144 method.instructions.add(new MethodInsnNode(INVOKESPECIAL, tList.getInternalName(), "<init>", getMethodDescriptor(VOID_TYPE, tList))); 145 method.instructions.add(new FieldInsnNode(PUTSTATIC, classNode.name, "LISTENER_LIST", tList.getDescriptor())); 146 method.instructions.add(new InsnNode(RETURN)); 147 classNode.methods.add(method); 148 149 /*Add: 150 * public ListenerList getListenerList() 151 * { 152 * return this.LISTENER_LIST; 153 * } 154 */ 155 method = new MethodNode(ASM4, ACC_PUBLIC, "getListenerList", getMethodDescriptor(tList), null, null); 156 method.instructions.add(new FieldInsnNode(GETSTATIC, classNode.name, "LISTENER_LIST", tList.getDescriptor())); 157 method.instructions.add(new InsnNode(ARETURN)); 158 classNode.methods.add(method); 159 return true; 160 } 161 162 }