    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    }