Use ASM for event executors.

Uses method handles for private or static methods.
This commit is contained in:
Techcable
2016-03-03 13:20:33 -07:00
parent 258a2ce7b8
commit 844bc6c46a
7 changed files with 319 additions and 0 deletions

View File

@@ -5,9 +5,75 @@ import org.bukkit.event.EventException;
import org.bukkit.event.Listener;
import org.jetbrains.annotations.NotNull;
// Paper start
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Function;
import com.destroystokyo.paper.event.executor.MethodHandleEventExecutor;
import com.destroystokyo.paper.event.executor.StaticMethodHandleEventExecutor;
import com.destroystokyo.paper.event.executor.asm.ASMEventExecutorGenerator;
import com.destroystokyo.paper.event.executor.asm.ClassDefiner;
import com.google.common.base.Preconditions;
// Paper end
/**
* Interface which defines the class for event call backs to plugins
*/
public interface EventExecutor {
public void execute(@NotNull Listener listener, @NotNull Event event) throws EventException;
// Paper start
ConcurrentMap<Method, Class<? extends EventExecutor>> eventExecutorMap = new ConcurrentHashMap<Method, Class<? extends EventExecutor>>() {
@NotNull
@Override
public Class<? extends EventExecutor> computeIfAbsent(@NotNull Method key, @NotNull Function<? super Method, ? extends Class<? extends EventExecutor>> mappingFunction) {
Class<? extends EventExecutor> executorClass = get(key);
if (executorClass != null)
return executorClass;
//noinspection SynchronizationOnLocalVariableOrMethodParameter
synchronized (key) {
executorClass = get(key);
if (executorClass != null)
return executorClass;
return super.computeIfAbsent(key, mappingFunction);
}
}
};
@NotNull
public static EventExecutor create(@NotNull Method m, @NotNull Class<? extends Event> eventClass) {
Preconditions.checkNotNull(m, "Null method");
Preconditions.checkArgument(m.getParameterCount() != 0, "Incorrect number of arguments %s", m.getParameterCount());
Preconditions.checkArgument(m.getParameterTypes()[0] == eventClass, "First parameter %s doesn't match event class %s", m.getParameterTypes()[0], eventClass);
ClassDefiner definer = ClassDefiner.getInstance();
if (Modifier.isStatic(m.getModifiers())) {
return new StaticMethodHandleEventExecutor(eventClass, m);
} else if (definer.isBypassAccessChecks() || Modifier.isPublic(m.getDeclaringClass().getModifiers()) && Modifier.isPublic(m.getModifiers())) {
// get the existing generated EventExecutor class for the Method or generate one
Class<? extends EventExecutor> executorClass = eventExecutorMap.computeIfAbsent(m, (__) -> {
String name = ASMEventExecutorGenerator.generateName();
byte[] classData = ASMEventExecutorGenerator.generateEventExecutor(m, name);
return definer.defineClass(m.getDeclaringClass().getClassLoader(), name, classData).asSubclass(EventExecutor.class);
});
try {
EventExecutor asmExecutor = executorClass.newInstance();
// Define a wrapper to conform to bukkit stupidity (passing in events that don't match and wrapper exception)
return (listener, event) -> {
if (!eventClass.isInstance(event)) return;
asmExecutor.execute(listener, event);
};
} catch (InstantiationException | IllegalAccessException e) {
throw new AssertionError("Unable to initialize generated event executor", e);
}
} else {
return new MethodHandleEventExecutor(eventClass, m);
}
}
// Paper end
}