From e2b2feb7c5546ba9dfae3a197dc9a6613b72ffcf Mon Sep 17 00:00:00 2001 From: Maddy Miller Date: Mon, 9 Aug 2021 10:06:00 +0100 Subject: [PATCH] Use MethodHandle for faster event bus (#1865) * Use MethodHandle for faster event bus * Implement hashCode/equals * Apply review comments * Bind to the object directly --- .../eventbus/AnnotatedSubscriberFinder.java | 11 ++- .../util/eventbus/MethodEventHandler.java | 7 +- .../eventbus/MethodHandleEventHandler.java | 82 +++++++++++++++++++ 3 files changed, 96 insertions(+), 4 deletions(-) create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/util/eventbus/MethodHandleEventHandler.java diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/eventbus/AnnotatedSubscriberFinder.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/eventbus/AnnotatedSubscriberFinder.java index e2e7e80c3..a6d17be49 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/eventbus/AnnotatedSubscriberFinder.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/eventbus/AnnotatedSubscriberFinder.java @@ -22,6 +22,8 @@ package com.sk89q.worldedit.util.eventbus; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; import java.lang.reflect.Method; /** @@ -59,7 +61,14 @@ class AnnotatedSubscriberFinder implements SubscriberFindingStrategy { ); } Class eventType = parameterTypes[0]; - EventHandler handler = new MethodEventHandler(annotation.priority(), listener, method); + MethodHandle handle; + try { + handle = MethodHandles.publicLookup().unreflect(method); + } catch (IllegalAccessException e) { + throw new IllegalArgumentException("Method " + method + " failed to unreflect.", e); + } + + EventHandler handler = new MethodHandleEventHandler(annotation.priority(), listener, handle, method.getName()); methodsInListener.put(eventType, handler); } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/eventbus/MethodEventHandler.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/eventbus/MethodEventHandler.java index c4114677e..0353103c3 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/eventbus/MethodEventHandler.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/eventbus/MethodEventHandler.java @@ -20,6 +20,7 @@ package com.sk89q.worldedit.util.eventbus; import java.lang.reflect.Method; +import java.util.Objects; import static com.google.common.base.Preconditions.checkNotNull; @@ -35,7 +36,7 @@ public class MethodEventHandler extends EventHandler { * Create a new event handler. * * @param priority the priority - * @param method the method + * @param method the method */ public MethodEventHandler(Priority priority, Object object, Method method) { super(priority); @@ -72,7 +73,8 @@ public class MethodEventHandler extends EventHandler { if (!method.equals(that.method)) { return false; } - return object != null ? object.equals(that.object) : that.object == null; + + return Objects.equals(object, that.object); } @Override @@ -81,5 +83,4 @@ public class MethodEventHandler extends EventHandler { result = 31 * result + method.hashCode(); return result; } - } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/eventbus/MethodHandleEventHandler.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/eventbus/MethodHandleEventHandler.java new file mode 100644 index 000000000..33ca1d3b2 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/eventbus/MethodHandleEventHandler.java @@ -0,0 +1,82 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.util.eventbus; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodType; +import java.util.Objects; + +public class MethodHandleEventHandler extends EventHandler { + + private final MethodHandle methodHandle; + private final String methodName; + private final Object object; + + /** + * Create a new event handler that uses MethodHandles to dispatch. + * + * @param priority the priority + * @param object The object to invoke it on + * @param methodHandle The handle to invoke + * @param methodName The name of the method (for equality checks) + */ + protected MethodHandleEventHandler(Priority priority, Object object, MethodHandle methodHandle, String methodName) { + super(priority); + + this.object = object; + this.methodHandle = methodHandle.bindTo(object).asType(MethodType.methodType(void.class, Object.class)); + this.methodName = methodName; + } + + @Override + public void dispatch(Object event) throws Exception { + try { + this.methodHandle.invokeExact(event); + } catch (Exception | Error e) { + throw e; + } catch (Throwable t) { + // If it's not an Exception or Error, throw it wrapped. + throw new Exception(t); + } + } + + @Override + public int hashCode() { + return Objects.hash(methodName, object); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + MethodHandleEventHandler that = (MethodHandleEventHandler) o; + + if (!methodName.equals(that.methodName)) { + return false; + } + + return Objects.equals(object, that.object); + } +}