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.src.") || 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 Type tList = Type.getType(ListenerList.class); 065 066 for (MethodNode method : (List<MethodNode>)classNode.methods) 067 { 068 if (method.name.equals("setup") && 069 method.desc.equals(Type.getMethodDescriptor(VOID_TYPE)) && 070 (method.access & ACC_PROTECTED) == ACC_PROTECTED) 071 { 072 hasSetup = true; 073 } 074 if (method.name.equals("getListenerList") && 075 method.desc.equals(Type.getMethodDescriptor(tList)) && 076 (method.access & ACC_PUBLIC) == ACC_PUBLIC) 077 { 078 hasGetListenerList = true; 079 } 080 if (method.name.equals("<init>") && 081 method.desc.equals(Type.getMethodDescriptor(VOID_TYPE))) 082 { 083 hasDefaultCtr = true; 084 } 085 } 086 087 if (hasSetup) 088 { 089 if (!hasGetListenerList) 090 { 091 throw new RuntimeException("Event class defines setup() but does not define getListenerList! " + classNode.name); 092 } 093 else 094 { 095 return false; 096 } 097 } 098 099 Type tSuper = Type.getType(classNode.superName); 100 101 //Add private static ListenerList LISTENER_LIST 102 classNode.fields.add(new FieldNode(ACC_PRIVATE | ACC_STATIC, "LISTENER_LIST", tList.getDescriptor(), null, null)); 103 104 /*Add: 105 * public <init>() 106 * { 107 * super(); 108 * } 109 */ 110 MethodNode method = new MethodNode(ASM4, ACC_PUBLIC, "<init>", getMethodDescriptor(VOID_TYPE), null, null); 111 method.instructions.add(new VarInsnNode(ALOAD, 0)); 112 method.instructions.add(new MethodInsnNode(INVOKESPECIAL, tSuper.getInternalName(), "<init>", getMethodDescriptor(VOID_TYPE))); 113 method.instructions.add(new InsnNode(RETURN)); 114 if (!hasDefaultCtr) 115 { 116 classNode.methods.add(method); 117 } 118 119 /*Add: 120 * protected void setup() 121 * { 122 * super.setup(); 123 * if (LISTENER_LIST != NULL) 124 * { 125 * return; 126 * } 127 * LISTENER_LIST = new ListenerList(super.getListenerList()); 128 * } 129 */ 130 method = new MethodNode(ASM4, ACC_PROTECTED, "setup", getMethodDescriptor(VOID_TYPE), null, null); 131 method.instructions.add(new VarInsnNode(ALOAD, 0)); 132 method.instructions.add(new MethodInsnNode(INVOKESPECIAL, tSuper.getInternalName(), "setup", getMethodDescriptor(VOID_TYPE))); 133 method.instructions.add(new FieldInsnNode(GETSTATIC, classNode.name, "LISTENER_LIST", tList.getDescriptor())); 134 LabelNode initLisitener = new LabelNode(); 135 method.instructions.add(new JumpInsnNode(IFNULL, initLisitener)); 136 method.instructions.add(new InsnNode(RETURN)); 137 method.instructions.add(initLisitener); 138 method.instructions.add(new FrameNode(F_SAME, 0, null, 0, null)); 139 method.instructions.add(new TypeInsnNode(NEW, tList.getInternalName())); 140 method.instructions.add(new InsnNode(DUP)); 141 method.instructions.add(new VarInsnNode(ALOAD, 0)); 142 method.instructions.add(new MethodInsnNode(INVOKESPECIAL, tSuper.getInternalName(), "getListenerList", getMethodDescriptor(tList))); 143 method.instructions.add(new MethodInsnNode(INVOKESPECIAL, tList.getInternalName(), "<init>", getMethodDescriptor(VOID_TYPE, tList))); 144 method.instructions.add(new FieldInsnNode(PUTSTATIC, classNode.name, "LISTENER_LIST", tList.getDescriptor())); 145 method.instructions.add(new InsnNode(RETURN)); 146 classNode.methods.add(method); 147 148 /*Add: 149 * public ListenerList getListenerList() 150 * { 151 * return this.LISTENER_LIST; 152 * } 153 */ 154 method = new MethodNode(ASM4, ACC_PUBLIC, "getListenerList", getMethodDescriptor(tList), null, null); 155 method.instructions.add(new FieldInsnNode(GETSTATIC, classNode.name, "LISTENER_LIST", tList.getDescriptor())); 156 method.instructions.add(new InsnNode(ARETURN)); 157 classNode.methods.add(method); 158 return true; 159 } 160 161 }