Rework EventSubscriptionTransformer to bake @Cancelable and @HasResult values, should increase EventBus performance even more by removing logic from Event constructors.

This commit is contained in:
Lex Manos 2015-04-10 03:28:41 -07:00 committed by cpw
parent f6c50cc3af
commit 4be96044d0
2 changed files with 81 additions and 75 deletions

View File

@ -17,7 +17,10 @@ import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
import static org.objectweb.asm.Opcodes.NEW; import static org.objectweb.asm.Opcodes.NEW;
import static org.objectweb.asm.Opcodes.PUTSTATIC; import static org.objectweb.asm.Opcodes.PUTSTATIC;
import static org.objectweb.asm.Opcodes.RETURN; import static org.objectweb.asm.Opcodes.RETURN;
import static org.objectweb.asm.Opcodes.IRETURN;
import static org.objectweb.asm.Opcodes.ICONST_1;
import static org.objectweb.asm.Type.VOID_TYPE; import static org.objectweb.asm.Type.VOID_TYPE;
import static org.objectweb.asm.Type.BOOLEAN_TYPE;
import static org.objectweb.asm.Type.getMethodDescriptor; import static org.objectweb.asm.Type.getMethodDescriptor;
import java.util.List; import java.util.List;
@ -27,6 +30,7 @@ import net.minecraft.launchwrapper.IClassTransformer;
import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter; import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Type; import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AnnotationNode;
import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldInsnNode; import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.FieldNode; import org.objectweb.asm.tree.FieldNode;
@ -82,56 +86,87 @@ public class EventSubscriptionTransformer implements IClassTransformer
private boolean buildEvents(ClassNode classNode) throws Exception private boolean buildEvents(ClassNode classNode) throws Exception
{ {
// Yes, this recursively loads classes until we get this base class. THIS IS NOT A ISSUE. Coremods should handle re-entry just fine.
// If they do not this a COREMOD issue NOT a Forge/LaunchWrapper issue.
Class<?> parent = this.getClass().getClassLoader().loadClass(classNode.superName.replace('/', '.')); Class<?> parent = this.getClass().getClassLoader().loadClass(classNode.superName.replace('/', '.'));
if (!Event.class.isAssignableFrom(parent)) if (!Event.class.isAssignableFrom(parent))
{ {
return false; return false;
} }
boolean hasSetup = false; //Class<?> listenerListClazz = Class.forName("cpw.mods.fml.common.eventhandler.ListenerList", false, getClass().getClassLoader());
boolean hasGetListenerList = false; Type tList = Type.getType("Lcpw/mods/fml/common/eventhandler/ListenerList;");
boolean hasDefaultCtr = false;
Class<?> listenerListClazz = Class.forName("cpw.mods.fml.common.eventhandler.ListenerList", false, getClass().getClassLoader()); boolean edited = false;
Type tList = Type.getType(listenerListClazz); boolean hasSetup = false;
boolean hasGetListenerList = false;
boolean hasDefaultCtr = false;
boolean hasCancelable = false;
boolean hasResult = false;
String voidDesc = Type.getMethodDescriptor(VOID_TYPE);
String boolDesc = Type.getMethodDescriptor(BOOLEAN_TYPE);
String listDesc = tList.getDescriptor();
String listDescM = Type.getMethodDescriptor(tList);
for (MethodNode method : (List<MethodNode>)classNode.methods) for (MethodNode method : (List<MethodNode>)classNode.methods)
{ {
if (method.name.equals("setup") && if (method.name.equals("setup") && method.desc.equals(voidDesc) && (method.access & ACC_PROTECTED) == ACC_PROTECTED) hasSetup = true;
method.desc.equals(Type.getMethodDescriptor(VOID_TYPE)) && if ((method.access & ACC_PUBLIC) == ACC_PUBLIC)
(method.access & ACC_PROTECTED) == ACC_PROTECTED) {
if (method.name.equals("getListenerList") && method.desc.equals(listDescM)) hasGetListenerList = true;
if (method.name.equals("isCancelable") && method.desc.equals(boolDesc)) hasCancelable = true;
if (method.name.equals("hasResult") && method.desc.equals(boolDesc)) hasResult = true;
}
if (method.name.equals("<init>") && method.desc.equals(voidDesc)) hasDefaultCtr = true;
}
if (classNode.visibleAnnotations != null)
{
for (AnnotationNode node : classNode.visibleAnnotations)
{
if (!hasResult && node.desc.equals("Lcpw/mods/fml/common/eventhandler/Event$HasResult;"))
{ {
hasSetup = true; /* Add:
* public boolean hasResult()
* {
* return true;
* }
*/
MethodNode method = new MethodNode(ACC_PUBLIC, "hasResult", boolDesc, null, null);
method.instructions.add(new InsnNode(ICONST_1));
method.instructions.add(new InsnNode(IRETURN));
classNode.methods.add(method);
edited = true;
} }
if (method.name.equals("getListenerList") && else if (!hasCancelable && node.desc.equals("Lcpw/mods/fml/common/eventhandler/Cancelable;"))
method.desc.equals(Type.getMethodDescriptor(tList)) &&
(method.access & ACC_PUBLIC) == ACC_PUBLIC)
{ {
hasGetListenerList = true; /* Add:
} * public boolean isCancelable()
if (method.name.equals("<init>") && * {
method.desc.equals(Type.getMethodDescriptor(VOID_TYPE))) * return true;
{ * }
hasDefaultCtr = true; */
MethodNode method = new MethodNode(ACC_PUBLIC, "isCancelable", boolDesc, null, null);
method.instructions.add(new InsnNode(ICONST_1));
method.instructions.add(new InsnNode(IRETURN));
classNode.methods.add(method);
edited = true;
} }
}
} }
if (hasSetup) if (hasSetup)
{ {
if (!hasGetListenerList) if (!hasGetListenerList)
{ throw new RuntimeException("Event class defines setup() but does not define getListenerList! " + classNode.name);
throw new RuntimeException("Event class defines setup() but does not define getListenerList! " + classNode.name); else
} return edited;
else
{
return false;
}
} }
Type tSuper = Type.getType(classNode.superName); Type tSuper = Type.getType(classNode.superName);
//Add private static ListenerList LISTENER_LIST //Add private static ListenerList LISTENER_LIST
classNode.fields.add(new FieldNode(ACC_PRIVATE | ACC_STATIC, "LISTENER_LIST", tList.getDescriptor(), null, null)); classNode.fields.add(new FieldNode(ACC_PRIVATE | ACC_STATIC, "LISTENER_LIST", listDesc, null, null));
/*Add: /*Add:
* public <init>() * public <init>()
@ -139,12 +174,12 @@ public class EventSubscriptionTransformer implements IClassTransformer
* super(); * super();
* } * }
*/ */
MethodNode method = new MethodNode(ASM5, ACC_PUBLIC, "<init>", getMethodDescriptor(VOID_TYPE), null, null);
method.instructions.add(new VarInsnNode(ALOAD, 0));
method.instructions.add(new MethodInsnNode(INVOKESPECIAL, tSuper.getInternalName(), "<init>", getMethodDescriptor(VOID_TYPE), false));
method.instructions.add(new InsnNode(RETURN));
if (!hasDefaultCtr) if (!hasDefaultCtr)
{ {
MethodNode method = new MethodNode(ACC_PUBLIC, "<init>", voidDesc, null, null);
method.instructions.add(new VarInsnNode(ALOAD, 0));
method.instructions.add(new MethodInsnNode(INVOKESPECIAL, tSuper.getInternalName(), "<init>", voidDesc, false));
method.instructions.add(new InsnNode(RETURN));
classNode.methods.add(method); classNode.methods.add(method);
} }
@ -159,10 +194,10 @@ public class EventSubscriptionTransformer implements IClassTransformer
* LISTENER_LIST = new ListenerList(super.getListenerList()); * LISTENER_LIST = new ListenerList(super.getListenerList());
* } * }
*/ */
method = new MethodNode(ASM5, ACC_PROTECTED, "setup", getMethodDescriptor(VOID_TYPE), null, null); MethodNode method = new MethodNode(ACC_PROTECTED, "setup", voidDesc, null, null);
method.instructions.add(new VarInsnNode(ALOAD, 0)); method.instructions.add(new VarInsnNode(ALOAD, 0));
method.instructions.add(new MethodInsnNode(INVOKESPECIAL, tSuper.getInternalName(), "setup", getMethodDescriptor(VOID_TYPE), false)); method.instructions.add(new MethodInsnNode(INVOKESPECIAL, tSuper.getInternalName(), "setup", voidDesc, false));
method.instructions.add(new FieldInsnNode(GETSTATIC, classNode.name, "LISTENER_LIST", tList.getDescriptor())); method.instructions.add(new FieldInsnNode(GETSTATIC, classNode.name, "LISTENER_LIST", listDesc));
LabelNode initLisitener = new LabelNode(); LabelNode initLisitener = new LabelNode();
method.instructions.add(new JumpInsnNode(IFNULL, initLisitener)); method.instructions.add(new JumpInsnNode(IFNULL, initLisitener));
method.instructions.add(new InsnNode(RETURN)); method.instructions.add(new InsnNode(RETURN));
@ -171,9 +206,9 @@ public class EventSubscriptionTransformer implements IClassTransformer
method.instructions.add(new TypeInsnNode(NEW, tList.getInternalName())); method.instructions.add(new TypeInsnNode(NEW, tList.getInternalName()));
method.instructions.add(new InsnNode(DUP)); method.instructions.add(new InsnNode(DUP));
method.instructions.add(new VarInsnNode(ALOAD, 0)); method.instructions.add(new VarInsnNode(ALOAD, 0));
method.instructions.add(new MethodInsnNode(INVOKESPECIAL, tSuper.getInternalName(), "getListenerList", getMethodDescriptor(tList), false)); method.instructions.add(new MethodInsnNode(INVOKESPECIAL, tSuper.getInternalName(), "getListenerList", listDescM, false));
method.instructions.add(new MethodInsnNode(INVOKESPECIAL, tList.getInternalName(), "<init>", getMethodDescriptor(VOID_TYPE, tList), false)); method.instructions.add(new MethodInsnNode(INVOKESPECIAL, tList.getInternalName(), "<init>", getMethodDescriptor(VOID_TYPE, tList), false));
method.instructions.add(new FieldInsnNode(PUTSTATIC, classNode.name, "LISTENER_LIST", tList.getDescriptor())); method.instructions.add(new FieldInsnNode(PUTSTATIC, classNode.name, "LISTENER_LIST", listDesc));
method.instructions.add(new InsnNode(RETURN)); method.instructions.add(new InsnNode(RETURN));
classNode.methods.add(method); classNode.methods.add(method);
@ -183,8 +218,8 @@ public class EventSubscriptionTransformer implements IClassTransformer
* return this.LISTENER_LIST; * return this.LISTENER_LIST;
* } * }
*/ */
method = new MethodNode(ASM5, ACC_PUBLIC, "getListenerList", getMethodDescriptor(tList), null, null); method = new MethodNode(ACC_PUBLIC, "getListenerList", listDescM, null, null);
method.instructions.add(new FieldInsnNode(GETSTATIC, classNode.name, "LISTENER_LIST", tList.getDescriptor())); method.instructions.add(new FieldInsnNode(GETSTATIC, classNode.name, "LISTENER_LIST", listDesc));
method.instructions.add(new InsnNode(ARETURN)); method.instructions.add(new InsnNode(ARETURN));
classNode.methods.add(method); classNode.methods.add(method);
return true; return true;

View File

@ -32,9 +32,7 @@ public class Event
} }
private boolean isCanceled = false; private boolean isCanceled = false;
private final boolean isCancelable;
private Result result = Result.DEFAULT; private Result result = Result.DEFAULT;
private final boolean hasResult;
private static ListenerList listeners = new ListenerList(); private static ListenerList listeners = new ListenerList();
private EventPriority phase = null; private EventPriority phase = null;
@ -43,48 +41,18 @@ public class Event
public Event() public Event()
{ {
setup(); setup();
isCancelable = hasAnnotation(Cancelable.class);
hasResult = hasAnnotation(HasResult.class);
}
private boolean hasAnnotation(Class<? extends Annotation> annotation)
{
Class<?> me = this.getClass();
Map<Class<?>, Boolean> list = annotationMap.get(me);
if (list == null)
{
list = new ConcurrentHashMap<Class<?>, Boolean>();
annotationMap.put(me, list);
}
Boolean cached = list.get(annotation);
if (cached != null)
{
return cached;
}
Class<?> cls = me;
while (cls != Event.class)
{
if (cls.isAnnotationPresent(annotation))
{
list.put(annotation, true);
return true;
}
cls = cls.getSuperclass();
}
list.put(annotation, false);
return false;
} }
/** /**
* Determine if this function is cancelable at all. * Determine if this function is cancelable at all.
* @return If access to setCanceled should be allowed * @return If access to setCanceled should be allowed
*
* Note:
* Events with the Cancelable annotation will have this method automatically added to return true.
*/ */
public boolean isCancelable() public boolean isCancelable()
{ {
return isCancelable; return false;
} }
/** /**
@ -115,10 +83,13 @@ public class Event
/** /**
* Determines if this event expects a significant result value. * Determines if this event expects a significant result value.
*
* Note:
* Events with the HasResult annotation will have this method automatically added to return true.
*/ */
public boolean hasResult() public boolean hasResult()
{ {
return hasResult; return false;
} }
/** /**