Skip to content

Commit 05e8c12

Browse files
authored
Skip redundant wrapped post-time listener checks in more cases (#98)
Enhances the optimisation made in #85 to support also checking the permitted subclasses when the event type is sealed and running on Java 17+
1 parent 48e2711 commit 05e8c12

File tree

3 files changed

+73
-2
lines changed

3 files changed

+73
-2
lines changed

src/main/java/net/minecraftforge/eventbus/ASMEventHandler.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package net.minecraftforge.eventbus;
66

77
import net.minecraftforge.eventbus.api.*;
8+
import net.minecraftforge.eventbus.internal.InternalUtils;
89

910
import java.lang.reflect.*;
1011
import static org.objectweb.asm.Type.getMethodDescriptor;
@@ -77,7 +78,7 @@ public static ASMEventHandler of(IEventListenerFactory factory, Object target, M
7778
var subInfo = method.getAnnotation(SubscribeEvent.class);
7879
assert subInfo != null;
7980
var eventType = method.getParameterTypes()[0];
80-
if (isGeneric || !Modifier.isFinal(eventType.getModifiers()) || EventListenerHelper.isCancelable(eventType))
81+
if (InternalUtils.couldBeCancelled(eventType, isGeneric))
8182
return new ASMEventHandler(factory, target, method, isGeneric, subInfo);
8283

8384
// If we get to this point, no post-time checks are needed, so strip them out

src/main/java/net/minecraftforge/eventbus/EventBus.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import net.jodah.typetools.TypeResolver;
88
import net.minecraftforge.eventbus.api.*;
9+
import net.minecraftforge.eventbus.internal.InternalUtils;
910
import org.apache.logging.log4j.LogManager;
1011
import org.apache.logging.log4j.Logger;
1112
import org.objectweb.asm.Type;
@@ -252,7 +253,7 @@ private <T extends Event> void addListener(final EventPriority priority, final P
252253
}
253254

254255
@SuppressWarnings("unchecked")
255-
IEventListener listener = Modifier.isFinal(eventClass.getModifiers()) && (filter == checkCancelled || filter == null) && !EventListenerHelper.isCancelable(eventClass)
256+
IEventListener listener = (filter == checkCancelled || filter == null) && !InternalUtils.couldBeCancelled(eventClass)
256257
? e -> consumer.accept((T) e)
257258
: e -> doCastFilter(filter, eventClass, consumer, e);
258259

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
* Copyright (c) Forge Development LLC
3+
* SPDX-License-Identifier: LGPL-2.1-only
4+
*/
5+
package net.minecraftforge.eventbus.internal;
6+
7+
import net.minecraftforge.eventbus.api.EventListenerHelper;
8+
import net.minecraftforge.eventbus.api.IGenericEvent;
9+
10+
import java.lang.invoke.MethodHandle;
11+
import java.lang.invoke.MethodHandles;
12+
import java.lang.invoke.MethodType;
13+
import java.lang.reflect.Modifier;
14+
15+
public final class InternalUtils {
16+
private InternalUtils() {}
17+
18+
private static final MethodHandle GET_PERMITTED_SUBCLASSES;
19+
static {
20+
var lookup = MethodHandles.lookup();
21+
var methodType = MethodType.methodType(Class[].class);
22+
if (Runtime.version().feature() >= 17) {
23+
try {
24+
GET_PERMITTED_SUBCLASSES = lookup
25+
.findVirtual(Class.class, "getPermittedSubclasses", methodType)
26+
.asType(methodType.insertParameterTypes(0, Class.class));
27+
} catch (Exception e) {
28+
throw new ExceptionInInitializerError("Failed to find Class#getPermittedSubclasses(Class): " + e);
29+
}
30+
} else {
31+
GET_PERMITTED_SUBCLASSES = MethodHandles.empty(methodType.insertParameterTypes(0, Class.class));
32+
}
33+
}
34+
35+
public static Class<?>[] getPermittedSubclasses(Class<?> clazz) {
36+
try {
37+
return (Class<?>[]) GET_PERMITTED_SUBCLASSES.invokeExact(clazz);
38+
} catch (Throwable e) {
39+
throw new RuntimeException(e);
40+
}
41+
}
42+
43+
/**
44+
* @param eventType the event type to check
45+
* @return true if the given event type could be cancelled by a listener for a subclass of the given event type
46+
*/
47+
public static boolean couldBeCancelled(Class<?> eventType) {
48+
return couldBeCancelled(eventType, IGenericEvent.class.isAssignableFrom(eventType));
49+
}
50+
51+
/**
52+
* @param eventType the event type to check
53+
* @param isGenericEvent true if the given event type is a generic event type
54+
* @return true if the given event type could be cancelled by a listener for a subclass of the given event type
55+
*/
56+
public static boolean couldBeCancelled(Class<?> eventType, boolean isGenericEvent) {
57+
if (isGenericEvent || EventListenerHelper.isCancelable(eventType)) return true;
58+
if (Modifier.isFinal(eventType.getModifiers())) return false;
59+
60+
var permittedSubclasses = getPermittedSubclasses(eventType);
61+
if (permittedSubclasses == null || permittedSubclasses.length == 0) return true;
62+
63+
for (var subclass : permittedSubclasses)
64+
if (couldBeCancelled(subclass))
65+
return true;
66+
67+
return false;
68+
}
69+
}

0 commit comments

Comments
 (0)