From e927c7317f461a065e344ce4d5931d8b3498fcc1 Mon Sep 17 00:00:00 2001 From: Crosby <32882447+crosby-moe@users.noreply.github.com> Date: Mon, 17 Aug 2026 18:33:19 -0400 Subject: [PATCH 01/17] replace lookup factory lambdas with a simpler struct --- .../meteordevelopment/orbit/EventBus.java | 27 ++++------- .../meteordevelopment/orbit/IEventBus.java | 10 ++-- .../meteordevelopment/orbit/LookupInfo.java | 13 +++++ .../orbit/listeners/LambdaListener.java | 47 +++---------------- src/test/java/test/Main.java | 4 +- 5 files changed, 36 insertions(+), 65 deletions(-) create mode 100644 src/main/java/meteordevelopment/orbit/LookupInfo.java diff --git a/src/main/java/meteordevelopment/orbit/EventBus.java b/src/main/java/meteordevelopment/orbit/EventBus.java index ba7b951..636ee4c 100644 --- a/src/main/java/meteordevelopment/orbit/EventBus.java +++ b/src/main/java/meteordevelopment/orbit/EventBus.java @@ -3,6 +3,7 @@ import meteordevelopment.orbit.listeners.IListener; import meteordevelopment.orbit.listeners.LambdaListener; +import java.lang.invoke.MethodHandles; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; @@ -15,27 +16,17 @@ * Default implementation of {@link IEventBus}. */ public class EventBus implements IEventBus { - private static class LambdaFactoryInfo { - public final String packagePrefix; - public final LambdaListener.Factory factory; - - public LambdaFactoryInfo(String packagePrefix, LambdaListener.Factory factory) { - this.packagePrefix = packagePrefix; - this.factory = factory; - } - } - private final Map> listenerCache = new ConcurrentHashMap<>(); private final Map, List> staticListenerCache = new ConcurrentHashMap<>(); private final Map, List> listenerMap = new ConcurrentHashMap<>(); - private final List lambdaFactoryInfos = new ArrayList<>(); + private final List lookupInfos = new ArrayList<>(); @Override - public void registerLambdaFactory(String packagePrefix, LambdaListener.Factory factory) { - synchronized (lambdaFactoryInfos) { - lambdaFactoryInfos.add(new LambdaFactoryInfo(packagePrefix, factory)); + public void registerLookup(String packagePrefix, MethodHandles.Lookup lookup) { + synchronized (lookupInfos) { + lookupInfos.add(new LookupInfo(packagePrefix, lookup)); } } @@ -178,10 +169,10 @@ private boolean isValid(Method method) { return !method.getParameters()[0].getType().isPrimitive(); } - private LambdaListener.Factory getLambdaFactory(Class klass) { - synchronized (lambdaFactoryInfos) { - for (LambdaFactoryInfo info : lambdaFactoryInfos) { - if (klass.getName().startsWith(info.packagePrefix)) return info.factory; + private MethodHandles.Lookup getLambdaFactory(Class klass) { + synchronized (lookupInfos) { + for (LookupInfo info : lookupInfos) { + if (klass.getName().startsWith(info.packagePrefix)) return info.lookup; } } diff --git a/src/main/java/meteordevelopment/orbit/IEventBus.java b/src/main/java/meteordevelopment/orbit/IEventBus.java index d2718de..1788b9a 100644 --- a/src/main/java/meteordevelopment/orbit/IEventBus.java +++ b/src/main/java/meteordevelopment/orbit/IEventBus.java @@ -1,18 +1,20 @@ package meteordevelopment.orbit; import meteordevelopment.orbit.listeners.IListener; -import meteordevelopment.orbit.listeners.LambdaListener; + +import java.lang.invoke.MethodHandles; /** * Manages event listeners. */ public interface IEventBus { /** - * Registers a lambda factory to use with the specified package. + * Registers a lookup allowing orbit to reflect into private members inside the provided package. You can obtain a + * lookup instance by calling {@link MethodHandles#lookup()}. * @param packagePrefix Package prefix that this factory will be used for, eg "meteordevelopment.orbit" - * @param factory The factory to use + * @param lookup The lookup to use. */ - void registerLambdaFactory(String packagePrefix, LambdaListener.Factory factory); + void registerLookup(String packagePrefix, MethodHandles.Lookup lookup); /** * Returns whether at least one event listener is currently registered for this event type. diff --git a/src/main/java/meteordevelopment/orbit/LookupInfo.java b/src/main/java/meteordevelopment/orbit/LookupInfo.java new file mode 100644 index 0000000..f6ce2d1 --- /dev/null +++ b/src/main/java/meteordevelopment/orbit/LookupInfo.java @@ -0,0 +1,13 @@ +package meteordevelopment.orbit; + +import java.lang.invoke.MethodHandles; + +public class LookupInfo { + public final String packagePrefix; + public final MethodHandles.Lookup lookup; + + public LookupInfo(String packagePrefix, MethodHandles.Lookup lookup) { + this.packagePrefix = packagePrefix; + this.lookup = lookup; + } +} diff --git a/src/main/java/meteordevelopment/orbit/listeners/LambdaListener.java b/src/main/java/meteordevelopment/orbit/listeners/LambdaListener.java index 0337264..2020508 100644 --- a/src/main/java/meteordevelopment/orbit/listeners/LambdaListener.java +++ b/src/main/java/meteordevelopment/orbit/listeners/LambdaListener.java @@ -6,8 +6,6 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.function.Consumer; @@ -16,14 +14,6 @@ * Default implementation of a {@link IListener} that creates a lambda at runtime to call the target method. */ public class LambdaListener implements IListener { - public interface Factory { - MethodHandles.Lookup create(Method lookupInMethod, Class klass) throws InvocationTargetException, IllegalAccessException; - } - - private static boolean isJava1dot8; - private static Constructor lookupConstructor; - private static Method privateLookupInMethod; - private final Class target; private final boolean isStatic; private final int priority; @@ -36,40 +26,30 @@ public interface Factory { * @param method Method to create lambda for */ @SuppressWarnings("unchecked") - public LambdaListener(Factory factory, Class klass, Object object, Method method) { + public LambdaListener(MethodHandles.Lookup lookup, Class klass, Object object, Method method) { this.target = method.getParameters()[0].getType(); this.isStatic = Modifier.isStatic(method.getModifiers()); this.priority = method.getAnnotation(EventHandler.class).priority(); try { String name = method.getName(); - MethodHandles.Lookup lookup; - - if (isJava1dot8) { - boolean a = lookupConstructor.isAccessible(); - lookupConstructor.setAccessible(true); - lookup = lookupConstructor.newInstance(klass); - lookupConstructor.setAccessible(a); - } - else { - lookup = factory.create(privateLookupInMethod, klass); - } - MethodType methodType = MethodType.methodType(void.class, method.getParameters()[0].getType()); MethodHandle methodHandle; MethodType invokedType; + MethodHandles.Lookup innerLookup = lookup.in(klass); + if (isStatic) { - methodHandle = lookup.findStatic(klass, name, methodType); + methodHandle = innerLookup.findStatic(klass, name, methodType); invokedType = MethodType.methodType(Consumer.class); } else { - methodHandle = lookup.findVirtual(klass, name, methodType); + methodHandle = innerLookup.findVirtual(klass, name, methodType); invokedType = MethodType.methodType(Consumer.class, klass); } - MethodHandle lambdaFactory = LambdaMetafactory.metafactory(lookup, "accept", invokedType, MethodType.methodType(void.class, Object.class), methodHandle, methodType).getTarget(); + MethodHandle lambdaFactory = LambdaMetafactory.metafactory(innerLookup, "accept", invokedType, MethodType.methodType(void.class, Object.class), methodHandle, methodType).getTarget(); if (isStatic) this.executor = (Consumer) lambdaFactory.invoke(); else this.executor = (Consumer) lambdaFactory.invoke(object); @@ -97,19 +77,4 @@ public int getPriority() { public boolean isStatic() { return isStatic; } - - static { - try { - isJava1dot8 = System.getProperty("java.version").startsWith("1.8"); - - if (isJava1dot8) { - lookupConstructor = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class); - } - else { - privateLookupInMethod = MethodHandles.class.getDeclaredMethod("privateLookupIn", Class.class, MethodHandles.Lookup.class); - } - } catch (NoSuchMethodException e) { - e.printStackTrace(); - } - } } diff --git a/src/test/java/test/Main.java b/src/test/java/test/Main.java index 0c1dd2e..487d74f 100644 --- a/src/test/java/test/Main.java +++ b/src/test/java/test/Main.java @@ -33,7 +33,7 @@ static class Abc2 extends Abc { } public static void main(String[] args) { System.out.println("-- WITHOUT INSTANCE --"); IEventBus bus = new EventBus(); - bus.registerLambdaFactory("test", (lookupInMethod, klass) -> (MethodHandles.Lookup) lookupInMethod.invoke(null, klass, MethodHandles.lookup())); + bus.registerLookup("test", MethodHandles.lookup()); // Subscribes only static methods bus.subscribe(Main.class); @@ -57,7 +57,7 @@ public static void main(String[] args) { public Main() { IEventBus bus = new EventBus(); - bus.registerLambdaFactory("test", (lookupInMethod, klass) -> (MethodHandles.Lookup) lookupInMethod.invoke(null, klass, MethodHandles.lookup())); + bus.registerLookup("test", MethodHandles.lookup()); // Subscribes both static and normal methods bus.subscribe(this); From 8dcb2a6f39fb9b2fa75c2cfdcb8c173e6789e4f5 Mon Sep 17 00:00:00 2001 From: Crosby <32882447+crosby-moe@users.noreply.github.com> Date: Mon, 17 Aug 2026 18:34:24 -0400 Subject: [PATCH 02/17] make lambda listener fail-fast --- .../meteordevelopment/orbit/listeners/LambdaListener.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/meteordevelopment/orbit/listeners/LambdaListener.java b/src/main/java/meteordevelopment/orbit/listeners/LambdaListener.java index 2020508..93e6c60 100644 --- a/src/main/java/meteordevelopment/orbit/listeners/LambdaListener.java +++ b/src/main/java/meteordevelopment/orbit/listeners/LambdaListener.java @@ -17,7 +17,7 @@ public class LambdaListener implements IListener { private final Class target; private final boolean isStatic; private final int priority; - private Consumer executor; + private final Consumer executor; /** * Creates a new lambda listener, can be used for both static and non-static methods. @@ -54,7 +54,7 @@ public LambdaListener(MethodHandles.Lookup lookup, Class klass, Object object if (isStatic) this.executor = (Consumer) lambdaFactory.invoke(); else this.executor = (Consumer) lambdaFactory.invoke(object); } catch (Throwable throwable) { - throwable.printStackTrace(); + throw new IllegalStateException("Error creating lambda listener", throwable); } } From 0fd97b5dd075f3ee6f430e0a26ccd087dfaeb7dd Mon Sep 17 00:00:00 2001 From: Crosby <32882447+crosby-moe@users.noreply.github.com> Date: Mon, 17 Aug 2026 18:36:23 -0400 Subject: [PATCH 03/17] remove `IListener#isStatic` --- src/main/java/meteordevelopment/orbit/EventBus.java | 5 +++-- .../orbit/listeners/ConsumerListener.java | 5 ----- .../java/meteordevelopment/orbit/listeners/IListener.java | 7 ------- .../meteordevelopment/orbit/listeners/LambdaListener.java | 1 - 4 files changed, 3 insertions(+), 15 deletions(-) diff --git a/src/main/java/meteordevelopment/orbit/EventBus.java b/src/main/java/meteordevelopment/orbit/EventBus.java index 636ee4c..a5c403a 100644 --- a/src/main/java/meteordevelopment/orbit/EventBus.java +++ b/src/main/java/meteordevelopment/orbit/EventBus.java @@ -84,7 +84,8 @@ private void subscribe(List listeners, boolean onlyStatic) { private void subscribe(IListener listener, boolean onlyStatic) { if (onlyStatic) { - if (listener.isStatic()) insert(listenerMap.computeIfAbsent(listener.getTarget(), aClass -> new CopyOnWriteArrayList<>()), listener); + if (listener instanceof LambdaListener && ((LambdaListener) listener).isStatic()) + insert(listenerMap.computeIfAbsent(listener.getTarget(), aClass -> new CopyOnWriteArrayList<>()), listener); } else { insert(listenerMap.computeIfAbsent(listener.getTarget(), aClass -> new CopyOnWriteArrayList<>()), listener); @@ -124,7 +125,7 @@ private void unsubscribe(IListener listener, boolean staticOnly) { if (l != null) { if (staticOnly) { - if (listener.isStatic()) l.remove(listener); + if (listener instanceof LambdaListener && ((LambdaListener) listener).isStatic()) l.remove(listener); } else l.remove(listener); } diff --git a/src/main/java/meteordevelopment/orbit/listeners/ConsumerListener.java b/src/main/java/meteordevelopment/orbit/listeners/ConsumerListener.java index c048ed5..ec7e8b3 100644 --- a/src/main/java/meteordevelopment/orbit/listeners/ConsumerListener.java +++ b/src/main/java/meteordevelopment/orbit/listeners/ConsumerListener.java @@ -37,9 +37,4 @@ public Class getTarget() { public int getPriority() { return priority; } - - @Override - public boolean isStatic() { - return false; - } } diff --git a/src/main/java/meteordevelopment/orbit/listeners/IListener.java b/src/main/java/meteordevelopment/orbit/listeners/IListener.java index f08e3f9..fd83bcd 100644 --- a/src/main/java/meteordevelopment/orbit/listeners/IListener.java +++ b/src/main/java/meteordevelopment/orbit/listeners/IListener.java @@ -19,11 +19,4 @@ public interface IListener { * @return The priority for this listener */ int getPriority(); - - /** - * @return True if this listener is for static methods - * @deprecated Will be removed in a future version - */ - @Deprecated - boolean isStatic(); } diff --git a/src/main/java/meteordevelopment/orbit/listeners/LambdaListener.java b/src/main/java/meteordevelopment/orbit/listeners/LambdaListener.java index 93e6c60..bf507ff 100644 --- a/src/main/java/meteordevelopment/orbit/listeners/LambdaListener.java +++ b/src/main/java/meteordevelopment/orbit/listeners/LambdaListener.java @@ -73,7 +73,6 @@ public int getPriority() { return priority; } - @Override public boolean isStatic() { return isStatic; } From 581eb8e106ff2cdc3748f9fe61c251b0d29edce3 Mon Sep 17 00:00:00 2001 From: Crosby <32882447+crosby-moe@users.noreply.github.com> Date: Mon, 17 Aug 2026 18:44:36 -0400 Subject: [PATCH 04/17] use a sync identity map to make `listenerMap` not linear time --- .../java/meteordevelopment/orbit/EventBus.java | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/src/main/java/meteordevelopment/orbit/EventBus.java b/src/main/java/meteordevelopment/orbit/EventBus.java index a5c403a..1bf1c27 100644 --- a/src/main/java/meteordevelopment/orbit/EventBus.java +++ b/src/main/java/meteordevelopment/orbit/EventBus.java @@ -5,9 +5,7 @@ import java.lang.invoke.MethodHandles; import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.Function; @@ -16,7 +14,7 @@ * Default implementation of {@link IEventBus}. */ public class EventBus implements IEventBus { - private final Map> listenerCache = new ConcurrentHashMap<>(); + private final Map> listenerCache = Collections.synchronizedMap(new IdentityHashMap<>()); private final Map, List> staticListenerCache = new ConcurrentHashMap<>(); private final Map, List> listenerMap = new ConcurrentHashMap<>(); @@ -141,15 +139,7 @@ private List getListeners(Class klass, Object object) { }; if (object == null) return staticListenerCache.computeIfAbsent(klass, func); - - // We need to check if the instances are the same and avoid using .equals() and .hashCode() - for (Object key : listenerCache.keySet()) { - if (key == object) return listenerCache.get(object); - } - - List listeners = func.apply(object); - listenerCache.put(object, listeners); - return listeners; + else return listenerCache.computeIfAbsent(object, func); } private void getListeners(List listeners, Class klass, Object object) { From 9634e7e6fe2b324283f381aaa04070ee5369fe94 Mon Sep 17 00:00:00 2001 From: Crosby <32882447+crosby-moe@users.noreply.github.com> Date: Mon, 17 Aug 2026 23:25:53 -0400 Subject: [PATCH 05/17] add a lambda factory cache --- .../orbit/listeners/LambdaListener.java | 40 +++++++++---------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/src/main/java/meteordevelopment/orbit/listeners/LambdaListener.java b/src/main/java/meteordevelopment/orbit/listeners/LambdaListener.java index bf507ff..ce42d89 100644 --- a/src/main/java/meteordevelopment/orbit/listeners/LambdaListener.java +++ b/src/main/java/meteordevelopment/orbit/listeners/LambdaListener.java @@ -2,18 +2,19 @@ import meteordevelopment.orbit.EventHandler; -import java.lang.invoke.LambdaMetafactory; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; +import java.lang.invoke.*; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; /** * Default implementation of a {@link IListener} that creates a lambda at runtime to call the target method. */ public class LambdaListener implements IListener { + private static final Map LAMBDA_FACTORY_CACHE = new ConcurrentHashMap<>(); + private final Class target; private final boolean isStatic; private final int priority; @@ -32,24 +33,21 @@ public LambdaListener(MethodHandles.Lookup lookup, Class klass, Object object this.priority = method.getAnnotation(EventHandler.class).priority(); try { - String name = method.getName(); - MethodType methodType = MethodType.methodType(void.class, method.getParameters()[0].getType()); - - MethodHandle methodHandle; - MethodType invokedType; - - MethodHandles.Lookup innerLookup = lookup.in(klass); - - if (isStatic) { - methodHandle = innerLookup.findStatic(klass, name, methodType); - invokedType = MethodType.methodType(Consumer.class); - } - else { - methodHandle = innerLookup.findVirtual(klass, name, methodType); - invokedType = MethodType.methodType(Consumer.class, klass); - } + MethodHandle lambdaFactory = LAMBDA_FACTORY_CACHE.computeIfAbsent(method, innerMethod -> { + try { + MethodHandles.Lookup innerLookup = lookup.in(klass); - MethodHandle lambdaFactory = LambdaMetafactory.metafactory(innerLookup, "accept", invokedType, MethodType.methodType(void.class, Object.class), methodHandle, methodType).getTarget(); + return LambdaMetafactory.metafactory( + innerLookup, "accept", + isStatic ? MethodType.methodType(Consumer.class) : MethodType.methodType(Consumer.class, klass), + MethodType.methodType(void.class, Object.class), + innerLookup.unreflect(innerMethod), + MethodType.methodType(void.class, innerMethod.getParameters()[0].getType()) + ).getTarget(); + } catch (IllegalAccessException | LambdaConversionException e) { + throw new RuntimeException(e); + } + }); if (isStatic) this.executor = (Consumer) lambdaFactory.invoke(); else this.executor = (Consumer) lambdaFactory.invoke(object); From 053584c6d00f63c481a24c069b1a4c4043569153 Mon Sep 17 00:00:00 2001 From: Crosby <32882447+crosby-moe@users.noreply.github.com> Date: Mon, 17 Aug 2026 23:43:11 -0400 Subject: [PATCH 06/17] patch memory leak, caches now only store in-use listeners --- .../meteordevelopment/orbit/EventBus.java | 28 ++++++++----------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/src/main/java/meteordevelopment/orbit/EventBus.java b/src/main/java/meteordevelopment/orbit/EventBus.java index 1bf1c27..4062eea 100644 --- a/src/main/java/meteordevelopment/orbit/EventBus.java +++ b/src/main/java/meteordevelopment/orbit/EventBus.java @@ -101,32 +101,26 @@ private void insert(List listeners, IListener listener) { @Override public void unsubscribe(Object object) { - unsubscribe(getListeners(object.getClass(), object), false); + List listeners = listenerCache.remove(object); + if (listeners != null) unsubscribe(listeners); + // for backwards-compatibility + else unsubscribe(object.getClass()); } @Override public void unsubscribe(Class klass) { - unsubscribe(getListeners(klass, null), true); + List staticListeners = staticListenerCache.remove(klass); + if (staticListeners != null) unsubscribe(staticListeners); } - @Override - public void unsubscribe(IListener listener) { - unsubscribe(listener, false); + private void unsubscribe(List listeners) { + for (IListener listener : listeners) unsubscribe(listener); } - private void unsubscribe(List listeners, boolean staticOnly) { - for (IListener listener : listeners) unsubscribe(listener, staticOnly); - } - - private void unsubscribe(IListener listener, boolean staticOnly) { + @Override + public void unsubscribe(IListener listener) { List l = listenerMap.get(listener.getTarget()); - - if (l != null) { - if (staticOnly) { - if (listener instanceof LambdaListener && ((LambdaListener) listener).isStatic()) l.remove(listener); - } - else l.remove(listener); - } + if (l != null) l.remove(listener); } private List getListeners(Class klass, Object object) { From 641538b7436d95fbc963620c2616e2f63e7d65a5 Mon Sep 17 00:00:00 2001 From: Crosby <32882447+crosby-moe@users.noreply.github.com> Date: Mon, 17 Aug 2026 23:52:17 -0400 Subject: [PATCH 07/17] use a weak map for lambda listener caching to allow class gc --- .../orbit/listeners/LambdaListener.java | 52 ++++++++++++------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/src/main/java/meteordevelopment/orbit/listeners/LambdaListener.java b/src/main/java/meteordevelopment/orbit/listeners/LambdaListener.java index ce42d89..caa1d49 100644 --- a/src/main/java/meteordevelopment/orbit/listeners/LambdaListener.java +++ b/src/main/java/meteordevelopment/orbit/listeners/LambdaListener.java @@ -2,18 +2,23 @@ import meteordevelopment.orbit.EventHandler; -import java.lang.invoke.*; +import java.lang.invoke.LambdaMetafactory; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.ref.WeakReference; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.util.Collections; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; +import java.util.WeakHashMap; import java.util.function.Consumer; /** * Default implementation of a {@link IListener} that creates a lambda at runtime to call the target method. */ public class LambdaListener implements IListener { - private static final Map LAMBDA_FACTORY_CACHE = new ConcurrentHashMap<>(); + private static final Map> LAMBDA_FACTORY_CACHE = Collections.synchronizedMap(new WeakHashMap<>()); private final Class target; private final boolean isStatic; @@ -22,7 +27,8 @@ public class LambdaListener implements IListener { /** * Creates a new lambda listener, can be used for both static and non-static methods. - * @param klass Class of the object + * + * @param klass Class of the object * @param object Object, null if static * @param method Method to create lambda for */ @@ -32,27 +38,35 @@ public LambdaListener(MethodHandles.Lookup lookup, Class klass, Object object this.isStatic = Modifier.isStatic(method.getModifiers()); this.priority = method.getAnnotation(EventHandler.class).priority(); + WeakReference lambdaFactoryRef = LAMBDA_FACTORY_CACHE.get(method); + MethodHandle lambdaFactory; try { - MethodHandle lambdaFactory = LAMBDA_FACTORY_CACHE.computeIfAbsent(method, innerMethod -> { - try { - MethodHandles.Lookup innerLookup = lookup.in(klass); + if (lambdaFactoryRef != null && lambdaFactoryRef.get() != null) { + lambdaFactory = lambdaFactoryRef.get(); + } else { + MethodHandles.Lookup innerLookup = lookup.in(klass); - return LambdaMetafactory.metafactory( - innerLookup, "accept", - isStatic ? MethodType.methodType(Consumer.class) : MethodType.methodType(Consumer.class, klass), - MethodType.methodType(void.class, Object.class), - innerLookup.unreflect(innerMethod), - MethodType.methodType(void.class, innerMethod.getParameters()[0].getType()) - ).getTarget(); - } catch (IllegalAccessException | LambdaConversionException e) { - throw new RuntimeException(e); - } - }); + lambdaFactory = LambdaMetafactory.metafactory( + innerLookup, "accept", + isStatic ? MethodType.methodType(Consumer.class) : MethodType.methodType(Consumer.class, klass), + MethodType.methodType(void.class, Object.class), + innerLookup.unreflect(method), + MethodType.methodType(void.class, method.getParameters()[0].getType()) + ).getTarget(); + + LAMBDA_FACTORY_CACHE.put(method, new WeakReference<>(lambdaFactory)); + } + + assert lambdaFactory != null; if (isStatic) this.executor = (Consumer) lambdaFactory.invoke(); else this.executor = (Consumer) lambdaFactory.invoke(object); } catch (Throwable throwable) { - throw new IllegalStateException("Error creating lambda listener", throwable); + String message = String.format( + "Could not create lambda listener for '%s.%s(%s)'.", + klass.getSimpleName(), method.getName(), target.getSimpleName() + ); + throw new IllegalStateException(message, throwable); } } From 0298c43af85a1264f5c8c122f08ef45de335e17b Mon Sep 17 00:00:00 2001 From: Crosby <32882447+crosby-moe@users.noreply.github.com> Date: Mon, 17 Aug 2026 23:56:10 -0400 Subject: [PATCH 08/17] slightly speed up lambda listener creation --- .../meteordevelopment/orbit/listeners/LambdaListener.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/meteordevelopment/orbit/listeners/LambdaListener.java b/src/main/java/meteordevelopment/orbit/listeners/LambdaListener.java index caa1d49..d60bc76 100644 --- a/src/main/java/meteordevelopment/orbit/listeners/LambdaListener.java +++ b/src/main/java/meteordevelopment/orbit/listeners/LambdaListener.java @@ -54,13 +54,17 @@ public LambdaListener(MethodHandles.Lookup lookup, Class klass, Object object MethodType.methodType(void.class, method.getParameters()[0].getType()) ).getTarget(); + if (!isStatic) { + lambdaFactory = lambdaFactory.asType(MethodType.methodType(Consumer.class, Object.class)); + } + LAMBDA_FACTORY_CACHE.put(method, new WeakReference<>(lambdaFactory)); } assert lambdaFactory != null; - if (isStatic) this.executor = (Consumer) lambdaFactory.invoke(); - else this.executor = (Consumer) lambdaFactory.invoke(object); + if (isStatic) this.executor = (Consumer) lambdaFactory.invokeExact(); + else this.executor = (Consumer) lambdaFactory.invokeExact(object); } catch (Throwable throwable) { String message = String.format( "Could not create lambda listener for '%s.%s(%s)'.", From da1731825552053ed4c4a78e797ab141910307e8 Mon Sep 17 00:00:00 2001 From: Crosby <32882447+crosby-moe@users.noreply.github.com> Date: Tue, 18 Aug 2026 00:03:55 -0400 Subject: [PATCH 09/17] avoid creating lambda listeners for methods that wont be registered --- .../meteordevelopment/orbit/EventBus.java | 35 +++++++------------ 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/src/main/java/meteordevelopment/orbit/EventBus.java b/src/main/java/meteordevelopment/orbit/EventBus.java index 4062eea..78a5907 100644 --- a/src/main/java/meteordevelopment/orbit/EventBus.java +++ b/src/main/java/meteordevelopment/orbit/EventBus.java @@ -5,6 +5,7 @@ import java.lang.invoke.MethodHandles; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; @@ -63,31 +64,21 @@ public T post(T event) { @Override public void subscribe(Object object) { - subscribe(getListeners(object.getClass(), object), false); + subscribe(getListeners(object.getClass(), object, false)); } @Override public void subscribe(Class klass) { - subscribe(getListeners(klass, null), true); + subscribe(getListeners(klass, null, true)); } - @Override - public void subscribe(IListener listener) { - subscribe(listener, false); - } - - private void subscribe(List listeners, boolean onlyStatic) { - for (IListener listener : listeners) subscribe(listener, onlyStatic); + private void subscribe(List listeners) { + for (IListener listener : listeners) subscribe(listener); } - private void subscribe(IListener listener, boolean onlyStatic) { - if (onlyStatic) { - if (listener instanceof LambdaListener && ((LambdaListener) listener).isStatic()) - insert(listenerMap.computeIfAbsent(listener.getTarget(), aClass -> new CopyOnWriteArrayList<>()), listener); - } - else { - insert(listenerMap.computeIfAbsent(listener.getTarget(), aClass -> new CopyOnWriteArrayList<>()), listener); - } + @Override + public void subscribe(IListener listener) { + insert(listenerMap.computeIfAbsent(listener.getTarget(), aClass -> new CopyOnWriteArrayList<>()), listener); } private void insert(List listeners, IListener listener) { @@ -123,11 +114,11 @@ public void unsubscribe(IListener listener) { if (l != null) l.remove(listener); } - private List getListeners(Class klass, Object object) { + private List getListeners(Class klass, Object object, boolean onlyStatic) { Function> func = o -> { List listeners = new CopyOnWriteArrayList<>(); - getListeners(listeners, klass, object); + getListeners(listeners, klass, object, onlyStatic); return listeners; }; @@ -136,14 +127,14 @@ private List getListeners(Class klass, Object object) { else return listenerCache.computeIfAbsent(object, func); } - private void getListeners(List listeners, Class klass, Object object) { + private void getListeners(List listeners, Class klass, Object object, boolean onlyStatic) { for (Method method : klass.getDeclaredMethods()) { - if (isValid(method)) { + if (isValid(method) && (!onlyStatic || Modifier.isStatic(method.getModifiers()))) { listeners.add(new LambdaListener(getLambdaFactory(klass), klass, object, method)); } } - if (klass.getSuperclass() != null) getListeners(listeners, klass.getSuperclass(), object); + if (klass.getSuperclass() != null) getListeners(listeners, klass.getSuperclass(), object, onlyStatic); } private boolean isValid(Method method) { From 3ff51d527444b4e4e895dc2e83c289560634c1c5 Mon Sep 17 00:00:00 2001 From: Crosby <32882447+crosby-moe@users.noreply.github.com> Date: Tue, 18 Aug 2026 00:06:13 -0400 Subject: [PATCH 10/17] move code to avoid redundant capturing lambda allocation --- .../java/meteordevelopment/orbit/EventBus.java | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/src/main/java/meteordevelopment/orbit/EventBus.java b/src/main/java/meteordevelopment/orbit/EventBus.java index 78a5907..f0a27df 100644 --- a/src/main/java/meteordevelopment/orbit/EventBus.java +++ b/src/main/java/meteordevelopment/orbit/EventBus.java @@ -9,7 +9,6 @@ import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; -import java.util.function.Function; /** * Default implementation of {@link IEventBus}. @@ -64,12 +63,12 @@ public T post(T event) { @Override public void subscribe(Object object) { - subscribe(getListeners(object.getClass(), object, false)); + subscribe(listenerCache.computeIfAbsent(object, o -> getListeners(o.getClass(), o, false))); } @Override public void subscribe(Class klass) { - subscribe(getListeners(klass, null, true)); + subscribe(staticListenerCache.computeIfAbsent(klass, k -> getListeners(k, null, true))); } private void subscribe(List listeners) { @@ -115,16 +114,9 @@ public void unsubscribe(IListener listener) { } private List getListeners(Class klass, Object object, boolean onlyStatic) { - Function> func = o -> { - List listeners = new CopyOnWriteArrayList<>(); - - getListeners(listeners, klass, object, onlyStatic); - - return listeners; - }; - - if (object == null) return staticListenerCache.computeIfAbsent(klass, func); - else return listenerCache.computeIfAbsent(object, func); + List listeners = new CopyOnWriteArrayList<>(); + getListeners(listeners, klass, object, onlyStatic); + return listeners; } private void getListeners(List listeners, Class klass, Object object, boolean onlyStatic) { From bafaaab2a279c83a3c87aa1dc6693e27ab25953e Mon Sep 17 00:00:00 2001 From: Crosby <32882447+crosby-moe@users.noreply.github.com> Date: Tue, 18 Aug 2026 00:07:01 -0400 Subject: [PATCH 11/17] reduce allocation rate for listener heavy classes --- src/main/java/meteordevelopment/orbit/EventBus.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/meteordevelopment/orbit/EventBus.java b/src/main/java/meteordevelopment/orbit/EventBus.java index f0a27df..2c912a3 100644 --- a/src/main/java/meteordevelopment/orbit/EventBus.java +++ b/src/main/java/meteordevelopment/orbit/EventBus.java @@ -63,12 +63,12 @@ public T post(T event) { @Override public void subscribe(Object object) { - subscribe(listenerCache.computeIfAbsent(object, o -> getListeners(o.getClass(), o, false))); + subscribe(listenerCache.computeIfAbsent(object, o -> getListeners(o.getClass(), o))); } @Override public void subscribe(Class klass) { - subscribe(staticListenerCache.computeIfAbsent(klass, k -> getListeners(k, null, true))); + subscribe(staticListenerCache.computeIfAbsent(klass, k -> getListeners(k, null))); } private void subscribe(List listeners) { @@ -113,10 +113,10 @@ public void unsubscribe(IListener listener) { if (l != null) l.remove(listener); } - private List getListeners(Class klass, Object object, boolean onlyStatic) { - List listeners = new CopyOnWriteArrayList<>(); - getListeners(listeners, klass, object, onlyStatic); - return listeners; + private List getListeners(Class klass, Object object) { + List listeners = new ArrayList<>(); + getListeners(listeners, klass, object, object == null); + return new CopyOnWriteArrayList<>(listeners); } private void getListeners(List listeners, Class klass, Object object, boolean onlyStatic) { From 60c89d14403228d839ebf2c297dda7851c6df44b Mon Sep 17 00:00:00 2001 From: Crosby <32882447+crosby-moe@users.noreply.github.com> Date: Tue, 18 Aug 2026 00:09:31 -0400 Subject: [PATCH 12/17] optimize listener collection --- .../java/meteordevelopment/orbit/EventBus.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/java/meteordevelopment/orbit/EventBus.java b/src/main/java/meteordevelopment/orbit/EventBus.java index 2c912a3..762d58b 100644 --- a/src/main/java/meteordevelopment/orbit/EventBus.java +++ b/src/main/java/meteordevelopment/orbit/EventBus.java @@ -115,18 +115,18 @@ public void unsubscribe(IListener listener) { private List getListeners(Class klass, Object object) { List listeners = new ArrayList<>(); - getListeners(listeners, klass, object, object == null); - return new CopyOnWriteArrayList<>(listeners); - } - private void getListeners(List listeners, Class klass, Object object, boolean onlyStatic) { - for (Method method : klass.getDeclaredMethods()) { - if (isValid(method) && (!onlyStatic || Modifier.isStatic(method.getModifiers()))) { - listeners.add(new LambdaListener(getLambdaFactory(klass), klass, object, method)); + while (klass != Object.class) { + for (Method method : klass.getDeclaredMethods()) { + if (isValid(method) && (object != null || Modifier.isStatic(method.getModifiers()))) { + listeners.add(new LambdaListener(getLambdaFactory(klass), klass, object, method)); + } } + + klass = klass.getSuperclass(); } - if (klass.getSuperclass() != null) getListeners(listeners, klass.getSuperclass(), object, onlyStatic); + return new CopyOnWriteArrayList<>(listeners); } private boolean isValid(Method method) { From 9b71bac4b47d70fe2622808124fc65d1efb9cc41 Mon Sep 17 00:00:00 2001 From: Crosby <32882447+crosby-moe@users.noreply.github.com> Date: Tue, 18 Aug 2026 00:16:43 -0400 Subject: [PATCH 13/17] improve lookupinfo correctness --- src/main/java/meteordevelopment/orbit/EventBus.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/meteordevelopment/orbit/EventBus.java b/src/main/java/meteordevelopment/orbit/EventBus.java index 762d58b..e8a850a 100644 --- a/src/main/java/meteordevelopment/orbit/EventBus.java +++ b/src/main/java/meteordevelopment/orbit/EventBus.java @@ -24,7 +24,12 @@ public class EventBus implements IEventBus { @Override public void registerLookup(String packagePrefix, MethodHandles.Lookup lookup) { synchronized (lookupInfos) { - lookupInfos.add(new LookupInfo(packagePrefix, lookup)); + // to ensure the lookups are used correctly, they are ordered from longest to shortest + int i = 0; + while (lookupInfos.get(i).packagePrefix.length() > packagePrefix.length()) { + i++; + } + lookupInfos.add(i, new LookupInfo(packagePrefix, lookup)); } } From 7dfa13733007ba6f9f43429a61595517004eebbc Mon Sep 17 00:00:00 2001 From: Crosby <32882447+crosby-moe@users.noreply.github.com> Date: Tue, 18 Aug 2026 00:21:43 -0400 Subject: [PATCH 14/17] add new query functions to IEventBus --- .../meteordevelopment/orbit/EventBus.java | 16 ++++++++++++++ .../meteordevelopment/orbit/IEventBus.java | 21 +++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/src/main/java/meteordevelopment/orbit/EventBus.java b/src/main/java/meteordevelopment/orbit/EventBus.java index e8a850a..6a290d8 100644 --- a/src/main/java/meteordevelopment/orbit/EventBus.java +++ b/src/main/java/meteordevelopment/orbit/EventBus.java @@ -39,6 +39,22 @@ public boolean isListening(Class eventKlass) { return listeners != null && !listeners.isEmpty(); } + @Override + public boolean isSubscribed(Object object) { + return listenerCache.containsKey(object); + } + + @Override + public boolean isSubscribed(Class klass) { + return staticListenerCache.containsKey(klass); + } + + @Override + public boolean isSubscribed(IListener listener) { + List listeners = listenerMap.get(listener.getTarget()); + return listeners != null && listeners.contains(listener); + } + @Override public T post(T event) { List listeners = listenerMap.get(event.getClass()); diff --git a/src/main/java/meteordevelopment/orbit/IEventBus.java b/src/main/java/meteordevelopment/orbit/IEventBus.java index 1788b9a..023ad2c 100644 --- a/src/main/java/meteordevelopment/orbit/IEventBus.java +++ b/src/main/java/meteordevelopment/orbit/IEventBus.java @@ -24,6 +24,27 @@ public interface IEventBus { */ boolean isListening(Class eventClass); + /** + * Returns whether the object is currently subscribed to the event bus. + * @param object The object to query + * @return whether the object is currently subscribed to the event bus + */ + boolean isSubscribed(Object object); + + /** + * Returns whether the class is currently subscribed to the event bus. + * @param klass The class to query + * @return whether the class is currently subscribed to the event bus + */ + boolean isSubscribed(Class klass); + + /** + * Returns whether the listener is currently subscribed to the event bus. + * @param listener The listener to query + * @return whether the listener is currently subscribed to the event bus + */ + boolean isSubscribed(IListener listener); + /** * Posts an event to all subscribed event listeners. * @param event Event to post From d793feca149f374cae006252906bff6a6585261d Mon Sep 17 00:00:00 2001 From: Crosby <32882447+crosby-moe@users.noreply.github.com> Date: Tue, 18 Aug 2026 00:34:43 -0400 Subject: [PATCH 15/17] fix index overflow lol --- src/main/java/meteordevelopment/orbit/EventBus.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/meteordevelopment/orbit/EventBus.java b/src/main/java/meteordevelopment/orbit/EventBus.java index 6a290d8..cc5f3bd 100644 --- a/src/main/java/meteordevelopment/orbit/EventBus.java +++ b/src/main/java/meteordevelopment/orbit/EventBus.java @@ -26,7 +26,7 @@ public void registerLookup(String packagePrefix, MethodHandles.Lookup lookup) { synchronized (lookupInfos) { // to ensure the lookups are used correctly, they are ordered from longest to shortest int i = 0; - while (lookupInfos.get(i).packagePrefix.length() > packagePrefix.length()) { + while (i < lookupInfos.size() && lookupInfos.get(i).packagePrefix.length() > packagePrefix.length()) { i++; } lookupInfos.add(i, new LookupInfo(packagePrefix, lookup)); From 0dac633e3c5123ca177b461f39fbea47d2eb9103 Mon Sep 17 00:00:00 2001 From: Crosby <32882447+crosby-moe@users.noreply.github.com> Date: Tue, 18 Aug 2026 00:37:34 -0400 Subject: [PATCH 16/17] improve registration speed again for listener heavy classes by reusing lookup infos --- .../meteordevelopment/orbit/EventBus.java | 57 +++++++++++++++++-- .../orbit/listeners/LambdaListener.java | 53 ++--------------- 2 files changed, 57 insertions(+), 53 deletions(-) diff --git a/src/main/java/meteordevelopment/orbit/EventBus.java b/src/main/java/meteordevelopment/orbit/EventBus.java index cc5f3bd..61ee6fd 100644 --- a/src/main/java/meteordevelopment/orbit/EventBus.java +++ b/src/main/java/meteordevelopment/orbit/EventBus.java @@ -3,17 +3,24 @@ import meteordevelopment.orbit.listeners.IListener; import meteordevelopment.orbit.listeners.LambdaListener; +import java.lang.invoke.LambdaMetafactory; +import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.ref.WeakReference; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.function.Consumer; /** * Default implementation of {@link IEventBus}. */ public class EventBus implements IEventBus { + private static final Map> LAMBDA_FACTORY_CACHE = Collections.synchronizedMap(new WeakHashMap<>()); + private final Map> listenerCache = Collections.synchronizedMap(new IdentityHashMap<>()); private final Map, List> staticListenerCache = new ConcurrentHashMap<>(); @@ -84,12 +91,12 @@ public T post(T event) { @Override public void subscribe(Object object) { - subscribe(listenerCache.computeIfAbsent(object, o -> getListeners(o.getClass(), o))); + subscribe(listenerCache.computeIfAbsent(object, o -> createListeners(o.getClass(), o))); } @Override public void subscribe(Class klass) { - subscribe(staticListenerCache.computeIfAbsent(klass, k -> getListeners(k, null))); + subscribe(staticListenerCache.computeIfAbsent(klass, k -> createListeners(k, null))); } private void subscribe(List listeners) { @@ -134,13 +141,51 @@ public void unsubscribe(IListener listener) { if (l != null) l.remove(listener); } - private List getListeners(Class klass, Object object) { + private List createListeners(Class klass, Object object) { List listeners = new ArrayList<>(); while (klass != Object.class) { + MethodHandles.Lookup lookup = null; + for (Method method : klass.getDeclaredMethods()) { - if (isValid(method) && (object != null || Modifier.isStatic(method.getModifiers()))) { - listeners.add(new LambdaListener(getLambdaFactory(klass), klass, object, method)); + // skip invalid methods + boolean isStatic = Modifier.isStatic(method.getModifiers()); + if (!isValid(method) || (object == null && !isStatic)) { + continue; + } + + // get or create lambda factory + WeakReference ref = LAMBDA_FACTORY_CACHE.get(method); + MethodHandle lambdaFactory = ref != null ? ref.get() : null; + try { + if (lambdaFactory == null) { + // lazily search for lookup infos + if (lookup == null) { + lookup = getLookupInfo(klass).in(klass); + } + + lambdaFactory = LambdaMetafactory.metafactory( + lookup, "accept", + isStatic ? MethodType.methodType(Consumer.class) : MethodType.methodType(Consumer.class, klass), + MethodType.methodType(void.class, Object.class), + lookup.unreflect(method), + MethodType.methodType(void.class, method.getParameters()[0].getType()) + ).getTarget(); + + if (!isStatic) { + lambdaFactory = lambdaFactory.asType(MethodType.methodType(Consumer.class, Object.class)); + } + + LAMBDA_FACTORY_CACHE.put(method, new WeakReference<>(lambdaFactory)); + } + + listeners.add(new LambdaListener(lambdaFactory, object, method)); + } catch (Throwable throwable) { + String message = String.format( + "Could not create lambda listener for '%s.%s(%s)'.", + klass.getSimpleName(), method.getName(), method.getParameters()[0].getType().getSimpleName() + ); + throw new IllegalStateException(message, throwable); } } @@ -158,7 +203,7 @@ private boolean isValid(Method method) { return !method.getParameters()[0].getType().isPrimitive(); } - private MethodHandles.Lookup getLambdaFactory(Class klass) { + private MethodHandles.Lookup getLookupInfo(Class klass) { synchronized (lookupInfos) { for (LookupInfo info : lookupInfos) { if (klass.getName().startsWith(info.packagePrefix)) return info.lookup; diff --git a/src/main/java/meteordevelopment/orbit/listeners/LambdaListener.java b/src/main/java/meteordevelopment/orbit/listeners/LambdaListener.java index d60bc76..884b82f 100644 --- a/src/main/java/meteordevelopment/orbit/listeners/LambdaListener.java +++ b/src/main/java/meteordevelopment/orbit/listeners/LambdaListener.java @@ -2,24 +2,15 @@ import meteordevelopment.orbit.EventHandler; -import java.lang.invoke.LambdaMetafactory; import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; -import java.lang.ref.WeakReference; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.util.Collections; -import java.util.Map; -import java.util.WeakHashMap; import java.util.function.Consumer; /** * Default implementation of a {@link IListener} that creates a lambda at runtime to call the target method. */ public class LambdaListener implements IListener { - private static final Map> LAMBDA_FACTORY_CACHE = Collections.synchronizedMap(new WeakHashMap<>()); - private final Class target; private final boolean isStatic; private final int priority; @@ -28,50 +19,18 @@ public class LambdaListener implements IListener { /** * Creates a new lambda listener, can be used for both static and non-static methods. * - * @param klass Class of the object - * @param object Object, null if static - * @param method Method to create lambda for + * @param lambdaFactory The factory from which the lambda is created + * @param object Object, null if static + * @param method Method to create lambda for */ @SuppressWarnings("unchecked") - public LambdaListener(MethodHandles.Lookup lookup, Class klass, Object object, Method method) { + public LambdaListener(MethodHandle lambdaFactory, Object object, Method method) throws Throwable { this.target = method.getParameters()[0].getType(); this.isStatic = Modifier.isStatic(method.getModifiers()); this.priority = method.getAnnotation(EventHandler.class).priority(); - WeakReference lambdaFactoryRef = LAMBDA_FACTORY_CACHE.get(method); - MethodHandle lambdaFactory; - try { - if (lambdaFactoryRef != null && lambdaFactoryRef.get() != null) { - lambdaFactory = lambdaFactoryRef.get(); - } else { - MethodHandles.Lookup innerLookup = lookup.in(klass); - - lambdaFactory = LambdaMetafactory.metafactory( - innerLookup, "accept", - isStatic ? MethodType.methodType(Consumer.class) : MethodType.methodType(Consumer.class, klass), - MethodType.methodType(void.class, Object.class), - innerLookup.unreflect(method), - MethodType.methodType(void.class, method.getParameters()[0].getType()) - ).getTarget(); - - if (!isStatic) { - lambdaFactory = lambdaFactory.asType(MethodType.methodType(Consumer.class, Object.class)); - } - - LAMBDA_FACTORY_CACHE.put(method, new WeakReference<>(lambdaFactory)); - } - - assert lambdaFactory != null; - - if (isStatic) this.executor = (Consumer) lambdaFactory.invokeExact(); - else this.executor = (Consumer) lambdaFactory.invokeExact(object); - } catch (Throwable throwable) { - String message = String.format( - "Could not create lambda listener for '%s.%s(%s)'.", - klass.getSimpleName(), method.getName(), target.getSimpleName() - ); - throw new IllegalStateException(message, throwable); - } + if (isStatic) this.executor = (Consumer) lambdaFactory.invokeExact(); + else this.executor = (Consumer) lambdaFactory.invokeExact(object); } @Override From 735142ee44b0f02dbed1c8f38ef3fc98387d97d6 Mon Sep 17 00:00:00 2001 From: Crosby <32882447+crosby-moe@users.noreply.github.com> Date: Tue, 18 Aug 2026 00:39:14 -0400 Subject: [PATCH 17/17] bump version to 0.3.0 --- build.gradle | 2 +- src/main/java/meteordevelopment/orbit/IEventBus.java | 3 +++ .../java/meteordevelopment/orbit/listeners/LambdaListener.java | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 24d6c40..a5ac047 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ plugins { } group "meteordevelopment" -version "0.2.5" +version "0.3.0" sourceCompatibility = targetCompatibility = JavaVersion.VERSION_1_8 diff --git a/src/main/java/meteordevelopment/orbit/IEventBus.java b/src/main/java/meteordevelopment/orbit/IEventBus.java index 023ad2c..aaba7df 100644 --- a/src/main/java/meteordevelopment/orbit/IEventBus.java +++ b/src/main/java/meteordevelopment/orbit/IEventBus.java @@ -28,6 +28,7 @@ public interface IEventBus { * Returns whether the object is currently subscribed to the event bus. * @param object The object to query * @return whether the object is currently subscribed to the event bus + * @since 0.3.0 */ boolean isSubscribed(Object object); @@ -35,6 +36,7 @@ public interface IEventBus { * Returns whether the class is currently subscribed to the event bus. * @param klass The class to query * @return whether the class is currently subscribed to the event bus + * @since 0.3.0 */ boolean isSubscribed(Class klass); @@ -42,6 +44,7 @@ public interface IEventBus { * Returns whether the listener is currently subscribed to the event bus. * @param listener The listener to query * @return whether the listener is currently subscribed to the event bus + * @since 0.3.0 */ boolean isSubscribed(IListener listener); diff --git a/src/main/java/meteordevelopment/orbit/listeners/LambdaListener.java b/src/main/java/meteordevelopment/orbit/listeners/LambdaListener.java index 884b82f..2d57acf 100644 --- a/src/main/java/meteordevelopment/orbit/listeners/LambdaListener.java +++ b/src/main/java/meteordevelopment/orbit/listeners/LambdaListener.java @@ -48,6 +48,9 @@ public int getPriority() { return priority; } + /** + * @return Whether the method associated with this listener is static + */ public boolean isStatic() { return isStatic; }