Search in sources :

Example 1 with LoaderClassWriter

use of org.spongepowered.common.event.gen.LoaderClassWriter in project SpongeCommon by SpongePowered.

the class ClassEventListenerFactory method generateClass.

private static byte[] generateClass(String name, final Class<?> handle, final ListenerClassVisitor.DiscoveredMethod method, final Class<?> eventClass, final Class<? extends EventFilter> filter) {
    name = name.replace('.', '/');
    final String handleName = Type.getInternalName(handle);
    final String handleDescriptor = Type.getDescriptor(handle);
    final String filterName = Type.getInternalName(filter);
    final String eventDescriptor = method.descriptor();
    final ClassWriter cw = new LoaderClassWriter(handle.getClassLoader(), ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
    MethodVisitor mv;
    final FieldVisitor fv;
    cw.visit(V1_6, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, name, null, ClassEventListenerFactory.BASE_HANDLER, null);
    {
        fv = cw.visitField(ACC_PRIVATE + ACC_STATIC, "FILTER", "L" + filterName + ";", null, null);
        fv.visitEnd();
    }
    {
        mv = cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);
        mv.visitCode();
        mv.visitTypeInsn(NEW, filterName);
        mv.visitInsn(DUP);
        mv.visitMethodInsn(INVOKESPECIAL, filterName, "<init>", "()V", false);
        mv.visitFieldInsn(PUTSTATIC, name, "FILTER", "L" + filterName + ";");
        mv.visitInsn(RETURN);
        mv.visitMaxs(0, 0);
        mv.visitEnd();
    }
    {
        mv = cw.visitMethod(ACC_PUBLIC, "<init>", '(' + handleDescriptor + ")V", null, null);
        mv.visitCode();
        mv.visitVarInsn(ALOAD, 0);
        mv.visitVarInsn(ALOAD, 1);
        mv.visitMethodInsn(INVOKESPECIAL, ClassEventListenerFactory.BASE_HANDLER, "<init>", "(Ljava/lang/Object;)V", false);
        mv.visitInsn(RETURN);
        mv.visitMaxs(0, 0);
        mv.visitEnd();
    }
    {
        mv = cw.visitMethod(ACC_PUBLIC, "handle", ClassEventListenerFactory.HANDLE_METHOD_DESCRIPTOR, null, new String[] { "java/lang/Exception" });
        mv.visitCode();
        mv.visitFieldInsn(GETSTATIC, name, "FILTER", "L" + filterName + ";");
        mv.visitVarInsn(ALOAD, 1);
        mv.visitMethodInsn(INVOKEINTERFACE, Type.getInternalName(EventFilter.class), "filter", ClassEventListenerFactory.FILTER_DESCRIPTOR, true);
        mv.visitVarInsn(ASTORE, 2);
        mv.visitVarInsn(ALOAD, 2);
        final Label l2 = new Label();
        mv.visitJumpInsn(IFNULL, l2);
        mv.visitVarInsn(ALOAD, 0);
        mv.visitFieldInsn(GETFIELD, name, "handle", "Ljava/lang/Object;");
        mv.visitTypeInsn(CHECKCAST, handleName);
        for (int i = 0; i < method.parameterTypes().length; i++) {
            mv.visitVarInsn(ALOAD, 2);
            mv.visitIntInsn(BIPUSH, i);
            mv.visitInsn(AALOAD);
            final Type paramType = method.parameterTypes()[i].type();
            GeneratorUtils.visitUnboxingMethod(mv, paramType);
        }
        mv.visitMethodInsn(INVOKEVIRTUAL, handleName, method.methodName(), eventDescriptor, false);
        mv.visitLabel(l2);
        mv.visitInsn(RETURN);
        mv.visitMaxs(0, 0);
        mv.visitEnd();
    }
    cw.visitEnd();
    return cw.toByteArray();
}
Also used : Type(org.objectweb.asm.Type) Label(org.objectweb.asm.Label) FieldVisitor(org.objectweb.asm.FieldVisitor) ClassWriter(org.objectweb.asm.ClassWriter) LoaderClassWriter(org.spongepowered.common.event.gen.LoaderClassWriter) LoaderClassWriter(org.spongepowered.common.event.gen.LoaderClassWriter) MethodVisitor(org.objectweb.asm.MethodVisitor)

Example 2 with LoaderClassWriter

use of org.spongepowered.common.event.gen.LoaderClassWriter in project SpongeCommon by SpongePowered.

the class FilterGenerator method generateClass.

public byte[] generateClass(String name, final ListenerClassVisitor.DiscoveredMethod method) throws ClassNotFoundException {
    name = name.replace('.', '/');
    final ListenerClassVisitor.ListenerParameter[] parameters = method.parameterTypes();
    SubtypeFilterDelegate sfilter = null;
    final List<FilterDelegate> additional = new ArrayList<>();
    boolean cancellation = false;
    for (final ListenerClassVisitor.ListenerAnnotation anno : method.annotations()) {
        final Annotation annotation;
        try {
            annotation = anno.annotation();
        } catch (final AnnotationFormatException e) {
            throw new ClassNotFoundException("Failed to load annotation", e);
        }
        final Object obj = FilterGenerator.filterFromAnnotation(method.classByLoader(anno.type().getClassName()));
        if (obj == null) {
            continue;
        }
        if (obj instanceof SubtypeFilter) {
            if (sfilter != null) {
                throw new IllegalStateException("Cannot have both @Include and @Exclude annotations present at once");
            }
            sfilter = ((SubtypeFilter) obj).getDelegate(annotation);
        } else if (obj instanceof EventTypeFilter) {
            final EventTypeFilter etf = (EventTypeFilter) obj;
            additional.add(etf.getDelegate(annotation));
            if (etf == EventTypeFilter.CANCELLATION) {
                cancellation = true;
            }
        }
    }
    if (!cancellation && Cancellable.class.isAssignableFrom(parameters[0].clazz())) {
        additional.add(new CancellationEventFilterDelegate(Tristate.FALSE));
    }
    // we know there are no filters, skip generating a class
    if (additional.isEmpty() && sfilter == null && parameters.length == 1) {
        return null;
    }
    final ClassWriter cw = new LoaderClassWriter(method.declaringClass().getClassLoader(), ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
    MethodVisitor mv;
    cw.visit(V1_6, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, name, null, "java/lang/Object", new String[] { Type.getInternalName(EventFilter.class) });
    if (sfilter != null) {
        sfilter.createFields(cw);
    }
    {
        mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
        mv.visitCode();
        mv.visitVarInsn(ALOAD, 0);
        mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
        if (sfilter != null) {
            sfilter.writeCtor(name, cw, mv);
        }
        mv.visitInsn(RETURN);
        mv.visitMaxs(0, 0);
        mv.visitEnd();
    }
    {
        mv = cw.visitMethod(ACC_PUBLIC, "filter", "(" + Type.getDescriptor(Event.class) + ")[Ljava/lang/Object;", null, null);
        mv.visitCode();
        // index of the next available local variable
        int local = 2;
        if (sfilter != null) {
            local = sfilter.write(name, cw, mv, method, local);
        }
        for (final FilterDelegate eventFilter : additional) {
            local = eventFilter.write(name, cw, mv, method, local);
        }
        // local var indices of the parameters values
        final int[] plocals = new int[parameters.length - 1];
        for (int i = 1; i < parameters.length; i++) {
            final ListenerClassVisitor.ListenerParameter param = parameters[i];
            ParameterFilterSourceDelegate source = null;
            final List<ParameterFilterDelegate> paramFilters = new ArrayList<>();
            for (final ListenerClassVisitor.ListenerAnnotation anno : param.annotations()) {
                final Object obj = FilterGenerator.filterFromAnnotation(method.classByLoader(anno.type().getClassName()));
                if (obj == null) {
                    continue;
                }
                final Annotation annotation;
                try {
                    annotation = anno.annotation();
                } catch (AnnotationFormatException e) {
                    throw new ClassNotFoundException("Failed to load annotation", e);
                }
                if (obj instanceof ParameterSource) {
                    if (source != null) {
                        throw new IllegalStateException("Cannot have multiple parameter filter source annotations (for " + param.name() + ")");
                    }
                    source = ((ParameterSource) obj).getDelegate(annotation);
                } else if (obj instanceof ParameterFilter) {
                    paramFilters.add(((ParameterFilter) obj).getDelegate(annotation));
                }
            }
            if (source == null) {
                throw new IllegalStateException("Cannot have additional parameters filters without a source (for " + param.name() + ")");
            }
            if (source instanceof AllCauseFilterSourceDelegate && !paramFilters.isEmpty()) {
                // TODO until better handling for filtering arrays is added
                throw new IllegalStateException("Cannot have additional parameters filters without an array source (for " + param.name() + ")");
            }
            final Tuple<Integer, Integer> localState = source.write(cw, mv, method, i, local, plocals, parameters);
            local = localState.first();
            plocals[i - 1] = localState.second();
            for (final ParameterFilterDelegate paramFilter : paramFilters) {
                paramFilter.write(cw, mv, param, plocals[i - 1]);
            }
        }
        // create the return array
        if (parameters.length == 1) {
            mv.visitInsn(ICONST_1);
        } else {
            mv.visitIntInsn(BIPUSH, parameters.length);
        }
        mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
        // load the event into the array
        mv.visitInsn(DUP);
        mv.visitInsn(ICONST_0);
        mv.visitVarInsn(ALOAD, 1);
        mv.visitInsn(AASTORE);
        // load all the params into the array
        for (int i = 1; i < parameters.length; i++) {
            mv.visitInsn(DUP);
            mv.visitIntInsn(BIPUSH, i);
            final Type paramType = parameters[i].type();
            mv.visitVarInsn(paramType.getOpcode(ILOAD), plocals[i - 1]);
            GeneratorUtils.visitBoxingMethod(mv, paramType);
            mv.visitInsn(AASTORE);
        }
        mv.visitInsn(ARETURN);
        mv.visitMaxs(0, 0);
        mv.visitEnd();
    }
    cw.visitEnd();
    final byte[] data = cw.toByteArray();
    if (FilterGenerator.FILTER_DEBUG) {
        final File outDir = new File(".sponge.debug.out");
        final File outFile = new File(outDir, name + ".class");
        if (!outFile.getParentFile().exists()) {
            outFile.getParentFile().mkdirs();
        }
        try (final FileOutputStream out = new FileOutputStream(outFile)) {
            out.write(data);
        } catch (final IOException ignored) {
            ignored.printStackTrace();
        }
    }
    return data;
}
Also used : CancellationEventFilterDelegate(org.spongepowered.common.event.filter.delegate.CancellationEventFilterDelegate) Cancellable(org.spongepowered.api.event.Cancellable) ArrayList(java.util.ArrayList) ParameterFilterDelegate(org.spongepowered.common.event.filter.delegate.ParameterFilterDelegate) MethodVisitor(org.objectweb.asm.MethodVisitor) ListenerClassVisitor(org.spongepowered.common.event.manager.ListenerClassVisitor) AnnotationFormatException(io.leangen.geantyref.AnnotationFormatException) List(java.util.List) ArrayList(java.util.ArrayList) LoaderClassWriter(org.spongepowered.common.event.gen.LoaderClassWriter) SubtypeFilterDelegate(org.spongepowered.common.event.filter.delegate.SubtypeFilterDelegate) SupportsDataFilterDelegate(org.spongepowered.common.event.filter.delegate.SupportsDataFilterDelegate) IncludeSubtypeFilterDelegate(org.spongepowered.common.event.filter.delegate.IncludeSubtypeFilterDelegate) FilterDelegate(org.spongepowered.common.event.filter.delegate.FilterDelegate) ParameterFilterDelegate(org.spongepowered.common.event.filter.delegate.ParameterFilterDelegate) HasDataFilterDelegate(org.spongepowered.common.event.filter.delegate.HasDataFilterDelegate) ExcludeSubtypeFilterDelegate(org.spongepowered.common.event.filter.delegate.ExcludeSubtypeFilterDelegate) CancellationEventFilterDelegate(org.spongepowered.common.event.filter.delegate.CancellationEventFilterDelegate) ParameterFilterSourceDelegate(org.spongepowered.common.event.filter.delegate.ParameterFilterSourceDelegate) AllCauseFilterSourceDelegate(org.spongepowered.common.event.filter.delegate.AllCauseFilterSourceDelegate) IOException(java.io.IOException) Annotation(java.lang.annotation.Annotation) LoaderClassWriter(org.spongepowered.common.event.gen.LoaderClassWriter) ClassWriter(org.objectweb.asm.ClassWriter) SubtypeFilterDelegate(org.spongepowered.common.event.filter.delegate.SubtypeFilterDelegate) IncludeSubtypeFilterDelegate(org.spongepowered.common.event.filter.delegate.IncludeSubtypeFilterDelegate) ExcludeSubtypeFilterDelegate(org.spongepowered.common.event.filter.delegate.ExcludeSubtypeFilterDelegate) Type(org.objectweb.asm.Type) FileOutputStream(java.io.FileOutputStream) Event(org.spongepowered.api.event.Event) File(java.io.File) Tuple(org.spongepowered.api.util.Tuple)

Example 3 with LoaderClassWriter

use of org.spongepowered.common.event.gen.LoaderClassWriter in project SpongeCommon by SpongePowered.

the class ClassEventListenerFactory method generateClass.

private static byte[] generateClass(String name, final Class<?> handle, final ListenerClassVisitor.DiscoveredMethod method, final Class<?> eventClass) {
    name = name.replace('.', '/');
    final String handleName = Type.getInternalName(handle);
    final String handleDescriptor = Type.getDescriptor(handle);
    final String eventName = Type.getInternalName(eventClass);
    final ClassWriter cw = new LoaderClassWriter(handle.getClassLoader(), ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
    MethodVisitor mv;
    cw.visit(V1_6, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, name, null, ClassEventListenerFactory.BASE_HANDLER, null);
    {
        mv = cw.visitMethod(ACC_PUBLIC, "<init>", '(' + handleDescriptor + ")V", null, null);
        mv.visitCode();
        mv.visitVarInsn(ALOAD, 0);
        mv.visitVarInsn(ALOAD, 1);
        mv.visitMethodInsn(INVOKESPECIAL, ClassEventListenerFactory.BASE_HANDLER, "<init>", "(Ljava/lang/Object;)V", false);
        mv.visitInsn(RETURN);
        mv.visitMaxs(0, 0);
        mv.visitEnd();
    }
    {
        mv = cw.visitMethod(ACC_PUBLIC, "handle", ClassEventListenerFactory.HANDLE_METHOD_DESCRIPTOR, null, null);
        mv.visitCode();
        mv.visitVarInsn(ALOAD, 0);
        mv.visitFieldInsn(GETFIELD, name, "handle", "Ljava/lang/Object;");
        mv.visitTypeInsn(CHECKCAST, handleName);
        mv.visitVarInsn(ALOAD, 1);
        mv.visitTypeInsn(CHECKCAST, eventName);
        mv.visitMethodInsn(INVOKEVIRTUAL, handleName, method.methodName(), "(L" + eventName + ";)V", false);
        mv.visitInsn(RETURN);
        mv.visitMaxs(0, 0);
        mv.visitEnd();
    }
    cw.visitEnd();
    return cw.toByteArray();
}
Also used : ClassWriter(org.objectweb.asm.ClassWriter) LoaderClassWriter(org.spongepowered.common.event.gen.LoaderClassWriter) LoaderClassWriter(org.spongepowered.common.event.gen.LoaderClassWriter) MethodVisitor(org.objectweb.asm.MethodVisitor)

Aggregations

ClassWriter (org.objectweb.asm.ClassWriter)3 MethodVisitor (org.objectweb.asm.MethodVisitor)3 LoaderClassWriter (org.spongepowered.common.event.gen.LoaderClassWriter)3 Type (org.objectweb.asm.Type)2 AnnotationFormatException (io.leangen.geantyref.AnnotationFormatException)1 File (java.io.File)1 FileOutputStream (java.io.FileOutputStream)1 IOException (java.io.IOException)1 Annotation (java.lang.annotation.Annotation)1 ArrayList (java.util.ArrayList)1 List (java.util.List)1 FieldVisitor (org.objectweb.asm.FieldVisitor)1 Label (org.objectweb.asm.Label)1 Cancellable (org.spongepowered.api.event.Cancellable)1 Event (org.spongepowered.api.event.Event)1 Tuple (org.spongepowered.api.util.Tuple)1 AllCauseFilterSourceDelegate (org.spongepowered.common.event.filter.delegate.AllCauseFilterSourceDelegate)1 CancellationEventFilterDelegate (org.spongepowered.common.event.filter.delegate.CancellationEventFilterDelegate)1 ExcludeSubtypeFilterDelegate (org.spongepowered.common.event.filter.delegate.ExcludeSubtypeFilterDelegate)1 FilterDelegate (org.spongepowered.common.event.filter.delegate.FilterDelegate)1