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 (ClassNotFoundException ex) 044 { 045 // Discard silently- it's just noise 046 } 047 catch (Exception e) 048 { 049 e.printStackTrace(); 050 } 051 052 return bytes; 053 } 054 055 @SuppressWarnings("unchecked") 056 private boolean buildEvents(ClassNode classNode) throws Exception 057 { 058 Class<?> parent = this.getClass().getClassLoader().loadClass(classNode.superName.replace('/', '.')); 059 if (!Event.class.isAssignableFrom(parent)) 060 { 061 return false; 062 } 063 064 boolean hasSetup = false; 065 boolean hasGetListenerList = false; 066 boolean hasDefaultCtr = false; 067 068 Class<?> listenerListClazz = Class.forName("net.minecraftforge.event.ListenerList", false, getClass().getClassLoader()); 069 Type tList = Type.getType(listenerListClazz); 070 071 for (MethodNode method : (List<MethodNode>)classNode.methods) 072 { 073 if (method.name.equals("setup") && 074 method.desc.equals(Type.getMethodDescriptor(VOID_TYPE)) && 075 (method.access & ACC_PROTECTED) == ACC_PROTECTED) 076 { 077 hasSetup = true; 078 } 079 if (method.name.equals("getListenerList") && 080 method.desc.equals(Type.getMethodDescriptor(tList)) && 081 (method.access & ACC_PUBLIC) == ACC_PUBLIC) 082 { 083 hasGetListenerList = true; 084 } 085 if (method.name.equals("<init>") && 086 method.desc.equals(Type.getMethodDescriptor(VOID_TYPE))) 087 { 088 hasDefaultCtr = true; 089 } 090 } 091 092 if (hasSetup) 093 { 094 if (!hasGetListenerList) 095 { 096 throw new RuntimeException("Event class defines setup() but does not define getListenerList! " + classNode.name); 097 } 098 else 099 { 100 return false; 101 } 102 } 103 104 Type tSuper = Type.getType(classNode.superName); 105 106 //Add private static ListenerList LISTENER_LIST 107 classNode.fields.add(new FieldNode(ACC_PRIVATE | ACC_STATIC, "LISTENER_LIST", tList.getDescriptor(), null, null)); 108 109 /*Add: 110 * public <init>() 111 * { 112 * super(); 113 * } 114 */ 115 MethodNode method = new MethodNode(ASM4, ACC_PUBLIC, "<init>", getMethodDescriptor(VOID_TYPE), null, null); 116 method.instructions.add(new VarInsnNode(ALOAD, 0)); 117 method.instructions.add(new MethodInsnNode(INVOKESPECIAL, tSuper.getInternalName(), "<init>", getMethodDescriptor(VOID_TYPE))); 118 method.instructions.add(new InsnNode(RETURN)); 119 if (!hasDefaultCtr) 120 { 121 classNode.methods.add(method); 122 } 123 124 /*Add: 125 * protected void setup() 126 * { 127 * super.setup(); 128 * if (LISTENER_LIST != NULL) 129 * { 130 * return; 131 * } 132 * LISTENER_LIST = new ListenerList(super.getListenerList()); 133 * } 134 */ 135 method = new MethodNode(ASM4, ACC_PROTECTED, "setup", getMethodDescriptor(VOID_TYPE), null, null); 136 method.instructions.add(new VarInsnNode(ALOAD, 0)); 137 method.instructions.add(new MethodInsnNode(INVOKESPECIAL, tSuper.getInternalName(), "setup", getMethodDescriptor(VOID_TYPE))); 138 method.instructions.add(new FieldInsnNode(GETSTATIC, classNode.name, "LISTENER_LIST", tList.getDescriptor())); 139 LabelNode initLisitener = new LabelNode(); 140 method.instructions.add(new JumpInsnNode(IFNULL, initLisitener)); 141 method.instructions.add(new InsnNode(RETURN)); 142 method.instructions.add(initLisitener); 143 method.instructions.add(new FrameNode(F_SAME, 0, null, 0, null)); 144 method.instructions.add(new TypeInsnNode(NEW, tList.getInternalName())); 145 method.instructions.add(new InsnNode(DUP)); 146 method.instructions.add(new VarInsnNode(ALOAD, 0)); 147 method.instructions.add(new MethodInsnNode(INVOKESPECIAL, tSuper.getInternalName(), "getListenerList", getMethodDescriptor(tList))); 148 method.instructions.add(new MethodInsnNode(INVOKESPECIAL, tList.getInternalName(), "<init>", getMethodDescriptor(VOID_TYPE, tList))); 149 method.instructions.add(new FieldInsnNode(PUTSTATIC, classNode.name, "LISTENER_LIST", tList.getDescriptor())); 150 method.instructions.add(new InsnNode(RETURN)); 151 classNode.methods.add(method); 152 153 /*Add: 154 * public ListenerList getListenerList() 155 * { 156 * return this.LISTENER_LIST; 157 * } 158 */ 159 method = new MethodNode(ASM4, ACC_PUBLIC, "getListenerList", getMethodDescriptor(tList), null, null); 160 method.instructions.add(new FieldInsnNode(GETSTATIC, classNode.name, "LISTENER_LIST", tList.getDescriptor())); 161 method.instructions.add(new InsnNode(ARETURN)); 162 classNode.methods.add(method); 163 return true; 164 } 165 166 }