Search in sources :

Example 1 with DefineableClassLoader

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);
}
Also used : TestEventManager(org.spongepowered.common.test.TestEventManager) DefineableClassLoader(org.spongepowered.common.event.gen.DefineableClassLoader) PluginContainer(org.spongepowered.plugin.PluginContainer) Listener(org.spongepowered.api.event.Listener) ReferencedDefinableClassLoader(org.spongepowered.common.util.ReferencedDefinableClassLoader) EventManager(org.spongepowered.api.event.EventManager) TestEventManager(org.spongepowered.common.test.TestEventManager) ClassWriter(org.objectweb.asm.ClassWriter) MethodVisitor(org.objectweb.asm.MethodVisitor) Test(org.junit.jupiter.api.Test)

Example 2 with DefineableClassLoader

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);
}
Also used : DefineableClassLoader(org.spongepowered.common.event.gen.DefineableClassLoader) EventListener(org.spongepowered.api.event.EventListener) Listener(org.spongepowered.api.event.Listener) IdentityHashMap(java.util.IdentityHashMap) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) FilterFactory(org.spongepowered.common.event.filter.FilterFactory) DefineableClassLoader(org.spongepowered.common.event.gen.DefineableClassLoader) IOException(java.io.IOException) IOException(java.io.IOException) Type(java.lang.reflect.Type) InteractContainerEvent(org.spongepowered.api.event.item.inventory.container.InteractContainerEvent) GenericEvent(org.spongepowered.api.event.GenericEvent) Event(org.spongepowered.api.event.Event) AbstractEvent(org.spongepowered.api.event.impl.AbstractEvent) Map(java.util.Map) IdentityHashMap(java.util.IdentityHashMap) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) HashMap(java.util.HashMap) Nullable(org.checkerframework.checker.nullness.qual.Nullable)

Example 3 with DefineableClassLoader

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");
}
Also used : DefineableClassLoader(org.spongepowered.common.event.gen.DefineableClassLoader) ClassWriter(org.objectweb.asm.ClassWriter) MethodVisitor(org.objectweb.asm.MethodVisitor) Test(org.junit.jupiter.api.Test)

Aggregations

DefineableClassLoader (org.spongepowered.common.event.gen.DefineableClassLoader)3 Test (org.junit.jupiter.api.Test)2 ClassWriter (org.objectweb.asm.ClassWriter)2 MethodVisitor (org.objectweb.asm.MethodVisitor)2 Listener (org.spongepowered.api.event.Listener)2 IOException (java.io.IOException)1 Type (java.lang.reflect.Type)1 ArrayList (java.util.ArrayList)1 HashMap (java.util.HashMap)1 IdentityHashMap (java.util.IdentityHashMap)1 Map (java.util.Map)1 ConcurrentHashMap (java.util.concurrent.ConcurrentHashMap)1 Nullable (org.checkerframework.checker.nullness.qual.Nullable)1 Event (org.spongepowered.api.event.Event)1 EventListener (org.spongepowered.api.event.EventListener)1 EventManager (org.spongepowered.api.event.EventManager)1 GenericEvent (org.spongepowered.api.event.GenericEvent)1 AbstractEvent (org.spongepowered.api.event.impl.AbstractEvent)1 InteractContainerEvent (org.spongepowered.api.event.item.inventory.container.InteractContainerEvent)1 FilterFactory (org.spongepowered.common.event.filter.FilterFactory)1