perf(EventBus): replace reflection call with invokedynamic
This commit is contained in:
@@ -9,44 +9,35 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
|
||||||
public class EventBus {
|
public class EventBus {
|
||||||
private final Map<Class<?>, CopyOnWriteArrayList<ListenerMethod>> listeners =
|
private final Map<Class<?>, CopyOnWriteArrayList<Listener>> listeners = new ConcurrentHashMap<>();
|
||||||
new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
public void register(Object listener) {
|
public void register(Object host) {
|
||||||
for (Method method : listener.getClass().getDeclaredMethods()) {
|
for (Method method : host.getClass().getDeclaredMethods()) {
|
||||||
if (method.isAnnotationPresent(Subscribe.class)) {
|
if (method.isAnnotationPresent(Subscribe.class)) {
|
||||||
Class<?>[] params = method.getParameterTypes();
|
Class<?>[] params = method.getParameterTypes();
|
||||||
if (params.length == 1) {
|
if (params.length == 1) {
|
||||||
Class<?> eventType = params[0];
|
Class<?> eventType = params[0];
|
||||||
|
|
||||||
listeners
|
listeners.computeIfAbsent(eventType, k -> new CopyOnWriteArrayList<>())
|
||||||
.computeIfAbsent(eventType, k -> new CopyOnWriteArrayList<>())
|
.add(Listener.of(host, method));
|
||||||
.add(new ListenerMethod(listener, method));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unregister(Object listener) {
|
public void unregister(Object host) {
|
||||||
for (CopyOnWriteArrayList<ListenerMethod> list : listeners.values()) {
|
for (CopyOnWriteArrayList<Listener> list : listeners.values()) {
|
||||||
list.removeIf(lm -> lm.owner.equals(listener));
|
list.removeIf(listener -> listener.getHost().equals(host));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void post(Event event) {
|
public void post(Event event) {
|
||||||
List<ListenerMethod> list = listeners.get(event.getClass());
|
List<Listener> list = listeners.get(event.getClass());
|
||||||
if (list == null) return;
|
if (list == null) return;
|
||||||
|
|
||||||
for (ListenerMethod lm : list) {
|
for (Listener listener : list) {
|
||||||
if (event.isCancelled()) return;
|
if (event.isCancelled()) return;
|
||||||
try {
|
listener.invoke(event);
|
||||||
lm.method.setAccessible(true);
|
|
||||||
lm.method.invoke(lm.owner, event);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private record ListenerMethod(Object owner, Method method) {}
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package me.alpha432.oyvey.event.system;
|
||||||
|
|
||||||
|
import java.lang.invoke.*;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public record Listener(Object host, Consumer<Object> consumer) {
|
||||||
|
public static Listener of(Object host, Method method) {
|
||||||
|
return new Listener(host, buildLambdaMetafactory(host, method));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void invoke(Object event) {
|
||||||
|
consumer.accept(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getHost() {
|
||||||
|
return host;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Consumer<Object> buildLambdaMetafactory(Object host, Method method) {
|
||||||
|
try {
|
||||||
|
MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(host.getClass(), MethodHandles.lookup());
|
||||||
|
CallSite site = LambdaMetafactory.metafactory(
|
||||||
|
lookup,
|
||||||
|
"accept",
|
||||||
|
MethodType.methodType(Consumer.class, host.getClass()),
|
||||||
|
MethodType.methodType(void.class, Object.class),
|
||||||
|
lookup.unreflect(method),
|
||||||
|
MethodType.methodType(void.class, method.getParameterTypes()[0])
|
||||||
|
);
|
||||||
|
MethodHandle target = site.getTarget();
|
||||||
|
return (Consumer<Object>) (target.invoke(host));
|
||||||
|
} catch (Throwable e) {
|
||||||
|
throw new RuntimeException("Failed to build lambda from %s method".formatted(method.getName()), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user