use of org.spongepowered.common.event.gen.DefineableClassLoader in project SpongeCommon by SpongePowered.
the class EventManagerRegistrationTest method successfulRegistrationWithAsmDefinedClass.
@Test
public void successfulRegistrationWithAsmDefinedClass() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
final EventManager eventManager = new TestEventManager();
final PluginContainer mock = Mockito.mock(PluginContainer.class);
final ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
writer.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, "org/spongepowered/common/test/event/BombDummy", null, "java/lang/Object", null);
final MethodVisitor ctor = writer.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
ctor.visitCode();
ctor.visitVarInsn(Opcodes.ALOAD, 0);
ctor.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
ctor.visitInsn(Opcodes.RETURN);
ctor.visitMaxs(1, 1);
final MethodVisitor el = writer.visitMethod(Opcodes.ACC_PUBLIC, "onBlockChange", "(Lorg/spongepowered/api/event/block/ChangeBlockEvent;)V", null, null);
el.visitCode();
el.visitAnnotation(Type.getDescriptor(Listener.class), true);
el.visitInsn(Opcodes.RETURN);
final MethodVisitor bomb = writer.visitMethod(Opcodes.ACC_PUBLIC, "throwup", "(Lcom/example/doesnt/Exist;)V", null, null);
bomb.visitCode();
bomb.visitInsn(Opcodes.RETURN);
final DefineableClassLoader loader = new ReferencedDefinableClassLoader(this.getClass().getClassLoader());
final Class<?> clazz = loader.defineClass("org.spongepowered.common.test.event.BombDummy", writer.toByteArray());
final Object o = clazz.getConstructor().newInstance();
eventManager.registerListeners(mock, o);
}
use of org.spongepowered.common.event.gen.DefineableClassLoader in project SpongeCommon by SpongePowered.
the class SpongeEventManager method registerListener.
private void registerListener(final PluginContainer plugin, final Object listenerObject) {
Objects.requireNonNull(plugin, "plugin");
Objects.requireNonNull(listenerObject, "listener");
if (this.registeredListeners.contains(listenerObject)) {
SpongeCommon.logger().warn("Plugin {} attempted to register an already registered listener ({})", plugin.metadata().id(), listenerObject.getClass().getName());
Thread.dumpStack();
return;
}
final List<RegisteredListener<? extends Event>> handlers = new ArrayList<>();
final Map<ListenerClassVisitor.DiscoveredMethod, String> methodErrors = new HashMap<>();
final Class<?> handle = listenerObject.getClass();
final ClassLoader handleLoader = handle.getClassLoader();
AnnotatedEventListener.Factory handlerFactory = this.classLoaders.get(handleLoader);
if (handlerFactory == null) {
final DefineableClassLoader classLoader = new DefineableClassLoader(handleLoader);
handlerFactory = new ClassEventListenerFactory("org.spongepowered.common.event.listener", new FilterFactory("org.spongepowered.common.event.filters", classLoader), classLoader);
this.classLoaders.put(handleLoader, handlerFactory);
}
try {
final List<ListenerClassVisitor.DiscoveredMethod> methods = ListenerClassVisitor.getEventListenerMethods(handle);
for (final ListenerClassVisitor.DiscoveredMethod method : methods) {
final Listener listener = method.listener();
@Nullable final String error = SpongeEventManager.getHandlerErrorOrNull(method);
if (error == null) {
final Type eventType = method.parameterTypes()[0].genericType();
final AnnotatedEventListener handler;
try {
handler = handlerFactory.create(listenerObject, method);
} catch (final Exception e) {
SpongeCommon.logger().error("Failed to create handler for {} on {}", method, handle, e);
continue;
}
handlers.add(SpongeEventManager.createRegistration(plugin, eventType, listener.order(), listener.beforeModifications(), handler));
} else {
methodErrors.put(method, error);
}
}
} catch (final IOException ioe) {
SpongeCommon.logger().warn("Exception trying to register superclass listeners", ioe);
} catch (final NoSuchMethodException nsme) {
SpongeCommon.logger().warn("Discovered method listener somehow not found for class " + handle.getName(), nsme);
} catch (final ClassNotFoundException e) {
SpongeCommon.logger().warn("Somehow couldn't classload a class while trying to register event listeners for containing class: " + handle.getName(), e);
}
// about those.
for (Class<?> handleParent = handle; handleParent != Object.class; handleParent = handleParent.getSuperclass()) {
try {
final List<ListenerClassVisitor.DiscoveredMethod> methods = ListenerClassVisitor.getEventListenerMethods(handleParent);
for (final ListenerClassVisitor.DiscoveredMethod method : methods) {
if (!methodErrors.containsKey(method)) {
@Nullable final String error = SpongeEventManager.getHandlerErrorOrNull(method);
if (error != null) {
methodErrors.put(method, error);
}
}
}
} catch (final RuntimeException re) {
// This is the Forge classloader that checks for client sided classes
if (re.getMessage().startsWith("Attempted to load class")) {
continue;
}
SpongeCommon.logger().warn("Exception trying to register superclass listeners", re);
} catch (final Exception e) {
SpongeCommon.logger().warn("Attempted to register listeners but had an exception loading listeners for super classes", e);
}
}
for (final Map.Entry<ListenerClassVisitor.DiscoveredMethod, String> method : methodErrors.entrySet()) {
SpongeCommon.logger().warn("Invalid listener method {} in {}: {}", method.getKey(), method.getKey().declaringClass().getName(), method.getValue());
}
this.registeredListeners.add(listenerObject);
this.register(handlers);
}
use of org.spongepowered.common.event.gen.DefineableClassLoader in project SpongeCommon by SpongePowered.
the class ReflectionTest method testNonExistentMethodOnCustomClass.
@Test
public void testNonExistentMethodOnCustomClass() {
final ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
writer.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, "org/spongepowered/common/test/block/BombDummy", null, "net/minecraft/world/level/block/Block", null);
{
final MethodVisitor ctor = writer.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "(Lnet/minecraft/world/level/block/state/BlockBehaviour$Properties;)V", null, null);
ctor.visitCode();
ctor.visitVarInsn(Opcodes.ALOAD, 0);
ctor.visitVarInsn(Opcodes.ALOAD, 1);
ctor.visitMethodInsn(Opcodes.INVOKESPECIAL, "net/minecraft/world/level/block/Block", "<init>", "(Lnet/minecraft/world/level/block/state/BlockBehaviour$Properties;)V", false);
ctor.visitInsn(Opcodes.RETURN);
ctor.visitMaxs(0, 0);
}
{
final MethodVisitor el = writer.visitMethod(Opcodes.ACC_PUBLIC, "neighborChanged", "(Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/Block;Lnet/minecraft/core/BlockPos;Z)V", null, null);
el.visitCode();
el.visitInsn(Opcodes.RETURN);
el.visitMaxs(0, 0);
}
{
final MethodVisitor bomb = writer.visitMethod(Opcodes.ACC_PUBLIC, "throwup", "(Lcom/example/doesnt/Exist;)V", null, null);
bomb.visitCode();
bomb.visitInsn(Opcodes.RETURN);
bomb.visitMaxs(0, 0);
}
final DefineableClassLoader loader = new ReferencedDefinableClassLoader(this.getClass().getClassLoader());
final byte[] bombClazzBytes = writer.toByteArray();
final Class<?> clazz = loader.defineClass("org.spongepowered.common.test.block.BombDummy", bombClazzBytes);
final boolean neighborChanged = ReflectionUtil.isNeighborChangedDeclared(clazz);
Assertions.assertTrue(neighborChanged, "NeighborChanged should have been defined on BombDummy");
final boolean entityInside = ReflectionUtil.isEntityInsideDeclared(clazz);
Assertions.assertFalse(entityInside, "isEntityInsideDeclared is not defined on BombDummy");
NoClassDefFoundError e = null;
try {
getNeighborChanged(clazz);
} catch (final Exception ex) {
Assertions.fail("Expected a class not found exception for com/example/doesnt/Exist");
} catch (final NoClassDefFoundError ee) {
e = ee;
}
Assertions.assertNotNull(e, "Should have gotten a class exception");
}
Aggregations