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:
parent
79ca5bbf86
commit
aeb4b03bf4
2 changed files with 81 additions and 75 deletions
|
@ -17,7 +17,10 @@ import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
|
|||
import static org.objectweb.asm.Opcodes.NEW;
|
||||
import static org.objectweb.asm.Opcodes.PUTSTATIC;
|
||||
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.BOOLEAN_TYPE;
|
||||
import static org.objectweb.asm.Type.getMethodDescriptor;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -28,6 +31,7 @@ import net.minecraftforge.fml.common.eventhandler.Event;
|
|||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.tree.AnnotationNode;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
import org.objectweb.asm.tree.FieldInsnNode;
|
||||
import org.objectweb.asm.tree.FieldNode;
|
||||
|
@ -81,56 +85,87 @@ public class EventSubscriptionTransformer implements IClassTransformer
|
|||
|
||||
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('/', '.'));
|
||||
if (!Event.class.isAssignableFrom(parent))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean hasSetup = false;
|
||||
boolean hasGetListenerList = false;
|
||||
boolean hasDefaultCtr = false;
|
||||
//Class<?> listenerListClazz = Class.forName("net.minecraftforge.fml.common.eventhandler.ListenerList", false, getClass().getClassLoader());
|
||||
Type tList = Type.getType("Lnet/minecraftforge/fml/common/eventhandler/ListenerList;");
|
||||
|
||||
Class<?> listenerListClazz = Class.forName("net.minecraftforge.fml.common.eventhandler.ListenerList", false, getClass().getClassLoader());
|
||||
Type tList = Type.getType(listenerListClazz);
|
||||
boolean edited = false;
|
||||
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)
|
||||
{
|
||||
if (method.name.equals("setup") &&
|
||||
method.desc.equals(Type.getMethodDescriptor(VOID_TYPE)) &&
|
||||
(method.access & ACC_PROTECTED) == ACC_PROTECTED)
|
||||
if (method.name.equals("setup") && method.desc.equals(voidDesc) && (method.access & ACC_PROTECTED) == ACC_PROTECTED) hasSetup = true;
|
||||
if ((method.access & ACC_PUBLIC) == ACC_PUBLIC)
|
||||
{
|
||||
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("Lnet/minecraftforge/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") &&
|
||||
method.desc.equals(Type.getMethodDescriptor(tList)) &&
|
||||
(method.access & ACC_PUBLIC) == ACC_PUBLIC)
|
||||
else if (!hasCancelable && node.desc.equals("Lnet/minecraftforge/fml/common/eventhandler/Cancelable;"))
|
||||
{
|
||||
hasGetListenerList = true;
|
||||
}
|
||||
if (method.name.equals("<init>") &&
|
||||
method.desc.equals(Type.getMethodDescriptor(VOID_TYPE)))
|
||||
{
|
||||
hasDefaultCtr = true;
|
||||
/* Add:
|
||||
* public boolean isCancelable()
|
||||
* {
|
||||
* return 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 (!hasGetListenerList)
|
||||
{
|
||||
throw new RuntimeException("Event class defines setup() but does not define getListenerList! " + classNode.name);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!hasGetListenerList)
|
||||
throw new RuntimeException("Event class defines setup() but does not define getListenerList! " + classNode.name);
|
||||
else
|
||||
return edited;
|
||||
}
|
||||
|
||||
Type tSuper = Type.getType(classNode.superName);
|
||||
|
||||
//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:
|
||||
* public <init>()
|
||||
|
@ -138,12 +173,12 @@ public class EventSubscriptionTransformer implements IClassTransformer
|
|||
* 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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -158,10 +193,10 @@ public class EventSubscriptionTransformer implements IClassTransformer
|
|||
* 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 MethodInsnNode(INVOKESPECIAL, tSuper.getInternalName(), "setup", getMethodDescriptor(VOID_TYPE), false));
|
||||
method.instructions.add(new FieldInsnNode(GETSTATIC, classNode.name, "LISTENER_LIST", tList.getDescriptor()));
|
||||
method.instructions.add(new MethodInsnNode(INVOKESPECIAL, tSuper.getInternalName(), "setup", voidDesc, false));
|
||||
method.instructions.add(new FieldInsnNode(GETSTATIC, classNode.name, "LISTENER_LIST", listDesc));
|
||||
LabelNode initLisitener = new LabelNode();
|
||||
method.instructions.add(new JumpInsnNode(IFNULL, initLisitener));
|
||||
method.instructions.add(new InsnNode(RETURN));
|
||||
|
@ -170,9 +205,9 @@ public class EventSubscriptionTransformer implements IClassTransformer
|
|||
method.instructions.add(new TypeInsnNode(NEW, tList.getInternalName()));
|
||||
method.instructions.add(new InsnNode(DUP));
|
||||
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 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));
|
||||
classNode.methods.add(method);
|
||||
|
||||
|
@ -182,8 +217,8 @@ public class EventSubscriptionTransformer implements IClassTransformer
|
|||
* return this.LISTENER_LIST;
|
||||
* }
|
||||
*/
|
||||
method = new MethodNode(ASM5, ACC_PUBLIC, "getListenerList", getMethodDescriptor(tList), null, null);
|
||||
method.instructions.add(new FieldInsnNode(GETSTATIC, classNode.name, "LISTENER_LIST", tList.getDescriptor()));
|
||||
method = new MethodNode(ACC_PUBLIC, "getListenerList", listDescM, null, null);
|
||||
method.instructions.add(new FieldInsnNode(GETSTATIC, classNode.name, "LISTENER_LIST", listDesc));
|
||||
method.instructions.add(new InsnNode(ARETURN));
|
||||
classNode.methods.add(method);
|
||||
return true;
|
||||
|
|
|
@ -32,9 +32,7 @@ public class Event
|
|||
}
|
||||
|
||||
private boolean isCanceled = false;
|
||||
private final boolean isCancelable;
|
||||
private Result result = Result.DEFAULT;
|
||||
private final boolean hasResult;
|
||||
private static ListenerList listeners = new ListenerList();
|
||||
private EventPriority phase = null;
|
||||
|
||||
|
@ -43,48 +41,18 @@ public class Event
|
|||
public Event()
|
||||
{
|
||||
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.
|
||||
* @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()
|
||||
{
|
||||
return isCancelable;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -115,10 +83,13 @@ public class Event
|
|||
|
||||
/**
|
||||
* 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()
|
||||
{
|
||||
return hasResult;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in a new issue