mirror of
https://github.com/LuckPerms/LuckPerms.git
synced 2025-02-25 17:01:26 +01:00
Use a MethodHandle to call event class constructors
This commit is contained in:
parent
c3128dec0d
commit
6805574553
@ -44,7 +44,6 @@ import net.luckperms.api.event.util.Param;
|
|||||||
import java.lang.invoke.MethodHandle;
|
import java.lang.invoke.MethodHandle;
|
||||||
import java.lang.invoke.MethodHandles;
|
import java.lang.invoke.MethodHandles;
|
||||||
import java.lang.invoke.MethodType;
|
import java.lang.invoke.MethodType;
|
||||||
import java.lang.reflect.Constructor;
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
@ -60,23 +59,13 @@ import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
|
|||||||
*/
|
*/
|
||||||
public class GeneratedEventClass {
|
public class GeneratedEventClass {
|
||||||
|
|
||||||
/** The MethodHandles.lookup() method */
|
|
||||||
private static final Method METHOD_HANDLES_LOOKUP;
|
|
||||||
static {
|
|
||||||
try {
|
|
||||||
METHOD_HANDLES_LOOKUP = MethodHandles.class.getMethod("lookup");
|
|
||||||
} catch (NoSuchMethodException e) {
|
|
||||||
throw new ExceptionInInitializerError(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A loading cache of event types to {@link GeneratedEventClass}es.
|
* A loading cache of event types to {@link GeneratedEventClass}es.
|
||||||
*/
|
*/
|
||||||
private static final Map<Class<? extends LuckPermsEvent>, GeneratedEventClass> CACHE = LoadingMap.of(clazz -> {
|
private static final Map<Class<? extends LuckPermsEvent>, GeneratedEventClass> CACHE = LoadingMap.of(clazz -> {
|
||||||
try {
|
try {
|
||||||
return new GeneratedEventClass(clazz);
|
return new GeneratedEventClass(clazz);
|
||||||
} catch (ReflectiveOperationException e) {
|
} catch (Throwable e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -92,17 +81,16 @@ public class GeneratedEventClass {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The constructor used to create instances of the generated class
|
* A method handle for the constructor of the event class.
|
||||||
*/
|
*/
|
||||||
private final Constructor<? extends AbstractEvent> constructor;
|
private final MethodHandle constructor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An array of {@link MethodHandle}s, which can set values for each of the properties in the event class.
|
* An array of {@link MethodHandle}s, which can set values for each of the properties in the event class.
|
||||||
*/
|
*/
|
||||||
private final MethodHandle[] setters;
|
private final MethodHandle[] setters;
|
||||||
|
|
||||||
private GeneratedEventClass(Class<? extends LuckPermsEvent> eventClass) throws ReflectiveOperationException {
|
private GeneratedEventClass(Class<? extends LuckPermsEvent> eventClass) throws Throwable {
|
||||||
|
|
||||||
// get a TypeDescription for the event class
|
// get a TypeDescription for the event class
|
||||||
TypeDescription eventClassType = new TypeDescription.ForLoadedType(eventClass);
|
TypeDescription eventClassType = new TypeDescription.ForLoadedType(eventClass);
|
||||||
|
|
||||||
@ -125,7 +113,7 @@ public class GeneratedEventClass {
|
|||||||
.intercept(FixedValue.value(eventClassType))
|
.intercept(FixedValue.value(eventClassType))
|
||||||
// implement AbstractEvent#mh by calling & returning the value of MethodHandles.lookup()
|
// implement AbstractEvent#mh by calling & returning the value of MethodHandles.lookup()
|
||||||
.method(named("mhl").and(returns(MethodHandles.Lookup.class)).and(takesArguments(0)))
|
.method(named("mhl").and(returns(MethodHandles.Lookup.class)).and(takesArguments(0)))
|
||||||
.intercept(MethodCall.invoke(METHOD_HANDLES_LOOKUP))
|
.intercept(MethodCall.invoke(MethodHandles.class.getMethod("lookup")))
|
||||||
// implement a toString method
|
// implement a toString method
|
||||||
.withToString();
|
.withToString();
|
||||||
|
|
||||||
@ -135,29 +123,24 @@ public class GeneratedEventClass {
|
|||||||
.sorted(Comparator.comparingInt(o -> o.getAnnotation(Param.class).value()))
|
.sorted(Comparator.comparingInt(o -> o.getAnnotation(Param.class).value()))
|
||||||
.toArray(Method[]::new);
|
.toArray(Method[]::new);
|
||||||
|
|
||||||
// for each method on the event interface annotated with @Param
|
// for each property, define a field on the generated class to hold the value
|
||||||
for (Method method : properties) {
|
for (Method method : properties) {
|
||||||
if (!method.isAnnotationPresent(Param.class)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// define a field on the generated class to hold the value
|
|
||||||
builder = builder.defineField(method.getName(), method.getReturnType(), Visibility.PRIVATE);
|
builder = builder.defineField(method.getName(), method.getReturnType(), Visibility.PRIVATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// finish building, load the class, get a constructor
|
// finish building, load the class, get a constructor
|
||||||
Class<? extends AbstractEvent> generatedClass = builder.make().load(GeneratedEventClass.class.getClassLoader()).getLoaded();
|
Class<? extends AbstractEvent> generatedClass = builder.make().load(GeneratedEventClass.class.getClassLoader()).getLoaded();
|
||||||
this.constructor = generatedClass.getDeclaredConstructor(LuckPerms.class);
|
this.constructor = MethodHandles.publicLookup().in(generatedClass)
|
||||||
|
.findConstructor(generatedClass, MethodType.methodType(void.class, LuckPerms.class))
|
||||||
|
.asType(MethodType.methodType(AbstractEvent.class, LuckPerms.class));
|
||||||
|
|
||||||
// create a dummy instance of the generated class & get the method handle lookup instance
|
// create a dummy instance of the generated class & get the method handle lookup instance
|
||||||
MethodHandles.Lookup lookup = this.constructor.newInstance(new Object[]{null}).mhl();
|
MethodHandles.Lookup lookup = ((AbstractEvent) this.constructor.invoke((Object) null)).mhl();
|
||||||
|
|
||||||
// get 'setter' MethodHandles
|
// get 'setter' MethodHandles for each property
|
||||||
this.setters = new MethodHandle[properties.length];
|
this.setters = new MethodHandle[properties.length];
|
||||||
for (int i = 0; i < properties.length; i++) {
|
for (int i = 0; i < properties.length; i++) {
|
||||||
Method method = properties[i];
|
Method method = properties[i];
|
||||||
|
|
||||||
// obtain a setter MethodHandle for the property
|
|
||||||
this.setters[i] = lookup.findSetter(generatedClass, method.getName(), method.getReturnType())
|
this.setters[i] = lookup.findSetter(generatedClass, method.getName(), method.getReturnType())
|
||||||
.asType(MethodType.methodType(void.class, new Class[]{AbstractEvent.class, Object.class}));
|
.asType(MethodType.methodType(void.class, new Class[]{AbstractEvent.class, Object.class}));
|
||||||
}
|
}
|
||||||
@ -177,7 +160,7 @@ public class GeneratedEventClass {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// create a new instance of the event
|
// create a new instance of the event
|
||||||
final AbstractEvent event = this.constructor.newInstance(api);
|
final AbstractEvent event = (AbstractEvent) this.constructor.invokeExact(api);
|
||||||
|
|
||||||
// set the properties onto the event instance
|
// set the properties onto the event instance
|
||||||
for (int i = 0; i < this.setters.length; i++) {
|
for (int i = 0; i < this.setters.length; i++) {
|
||||||
|
Loading…
Reference in New Issue
Block a user