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;
|
||||
|
||||
public class EventBus {
|
||||
private final Map<Class<?>, CopyOnWriteArrayList<ListenerMethod>> listeners =
|
||||
new ConcurrentHashMap<>();
|
||||
private final Map<Class<?>, CopyOnWriteArrayList<Listener>> listeners = new ConcurrentHashMap<>();
|
||||
|
||||
public void register(Object listener) {
|
||||
for (Method method : listener.getClass().getDeclaredMethods()) {
|
||||
public void register(Object host) {
|
||||
for (Method method : host.getClass().getDeclaredMethods()) {
|
||||
if (method.isAnnotationPresent(Subscribe.class)) {
|
||||
Class<?>[] params = method.getParameterTypes();
|
||||
if (params.length == 1) {
|
||||
Class<?> eventType = params[0];
|
||||
|
||||
listeners
|
||||
.computeIfAbsent(eventType, k -> new CopyOnWriteArrayList<>())
|
||||
.add(new ListenerMethod(listener, method));
|
||||
listeners.computeIfAbsent(eventType, k -> new CopyOnWriteArrayList<>())
|
||||
.add(Listener.of(host, method));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void unregister(Object listener) {
|
||||
for (CopyOnWriteArrayList<ListenerMethod> list : listeners.values()) {
|
||||
list.removeIf(lm -> lm.owner.equals(listener));
|
||||
public void unregister(Object host) {
|
||||
for (CopyOnWriteArrayList<Listener> list : listeners.values()) {
|
||||
list.removeIf(listener -> listener.getHost().equals(host));
|
||||
}
|
||||
}
|
||||
|
||||
public void post(Event event) {
|
||||
List<ListenerMethod> list = listeners.get(event.getClass());
|
||||
List<Listener> list = listeners.get(event.getClass());
|
||||
if (list == null) return;
|
||||
|
||||
for (ListenerMethod lm : list) {
|
||||
for (Listener listener : list) {
|
||||
if (event.isCancelled()) return;
|
||||
try {
|
||||
lm.method.setAccessible(true);
|
||||
lm.method.invoke(lm.owner, event);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
listener.invoke(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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