Search in sources :

Example 1 with SubtypeFilterDelegate

use of org.spongepowered.common.event.filter.delegate.SubtypeFilterDelegate in project SpongeCommon by SpongePowered.

the class FilterGenerator method generateClass.

public byte[] generateClass(String name, Method method) {
    name = name.replace('.', '/');
    Parameter[] params = method.getParameters();
    ClassWriter cw = new ClassWriter(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) });
    SubtypeFilterDelegate sfilter = null;
    List<FilterDelegate> additional = Lists.newArrayList();
    boolean cancellation = false;
    for (Annotation anno : method.getAnnotations()) {
        Object obj = filterFromAnnotation(anno.annotationType());
        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(anno);
        } else if (obj instanceof EventTypeFilter) {
            EventTypeFilter etf = (EventTypeFilter) obj;
            additional.add(etf.getDelegate(anno));
            if (etf == EventTypeFilter.CANCELLATION) {
                cancellation = true;
            }
        }
    }
    if (!cancellation && Cancellable.class.isAssignableFrom(method.getParameterTypes()[0])) {
        additional.add(new CancellationEventFilterDelegate(Tristate.FALSE));
    }
    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 (FilterDelegate eventFilter : additional) {
            local = eventFilter.write(name, cw, mv, method, local);
        }
        // local var indices of the parameters values
        int[] plocals = new int[params.length - 1];
        for (int i = 1; i < params.length; i++) {
            Parameter param = params[i];
            ParameterFilterSourceDelegate source = null;
            List<ParameterFilterDelegate> paramFilters = Lists.newArrayList();
            for (Annotation anno : param.getAnnotations()) {
                Object obj = filterFromAnnotation(anno.annotationType());
                if (obj == null) {
                    continue;
                }
                if (obj instanceof ParameterSource) {
                    if (source != null) {
                        throw new IllegalStateException("Cannot have multiple parameter filter source annotations (for " + param.getName() + ")");
                    }
                    source = ((ParameterSource) obj).getDelegate(anno);
                } else if (obj instanceof ParameterFilter) {
                    paramFilters.add(((ParameterFilter) obj).getDelegate(anno));
                }
            }
            if (source == null) {
                throw new IllegalStateException("Cannot have additional parameters filters without a source (for " + param.getName() + ")");
            }
            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.getName() + ")");
            }
            Tuple<Integer, Integer> localState = source.write(cw, mv, method, param, local);
            local = localState.getFirst();
            plocals[i - 1] = localState.getSecond();
            for (ParameterFilterDelegate paramFilter : paramFilters) {
                paramFilter.write(cw, mv, method, param, plocals[i - 1]);
            }
        }
        // create the return array
        if (params.length == 1) {
            mv.visitInsn(ICONST_1);
        } else {
            mv.visitIntInsn(BIPUSH, params.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 < params.length; i++) {
            mv.visitInsn(DUP);
            mv.visitIntInsn(BIPUSH, i);
            Type paramType = Type.getType(params[i].getType());
            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();
    byte[] data = cw.toByteArray();
    if (FILTER_DEBUG) {
        File outDir = new File(".sponge.debug.out");
        File outFile = new File(outDir, name + ".class");
        if (!outFile.getParentFile().exists()) {
            outFile.getParentFile().mkdirs();
        }
        try (FileOutputStream out = new FileOutputStream(outFile)) {
            out.write(data);
        } catch (IOException ignored) {
            ignored.printStackTrace();
        }
    }
    return data;
}
Also used : CancellationEventFilterDelegate(org.spongepowered.common.event.filter.delegate.CancellationEventFilterDelegate) Cancellable(org.spongepowered.api.event.Cancellable) ParameterFilterDelegate(org.spongepowered.common.event.filter.delegate.ParameterFilterDelegate) MethodVisitor(org.objectweb.asm.MethodVisitor) List(java.util.List) 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) ClassWriter(org.objectweb.asm.ClassWriter) Annotation(java.lang.annotation.Annotation) 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) Parameter(java.lang.reflect.Parameter) Event(org.spongepowered.api.event.Event) File(java.io.File) Tuple(org.spongepowered.api.util.Tuple)

Example 2 with SubtypeFilterDelegate

use of org.spongepowered.common.event.filter.delegate.SubtypeFilterDelegate 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)

Aggregations

File (java.io.File)2 FileOutputStream (java.io.FileOutputStream)2 IOException (java.io.IOException)2 Annotation (java.lang.annotation.Annotation)2 List (java.util.List)2 ClassWriter (org.objectweb.asm.ClassWriter)2 MethodVisitor (org.objectweb.asm.MethodVisitor)2 Type (org.objectweb.asm.Type)2 Cancellable (org.spongepowered.api.event.Cancellable)2 Event (org.spongepowered.api.event.Event)2 Tuple (org.spongepowered.api.util.Tuple)2 AllCauseFilterSourceDelegate (org.spongepowered.common.event.filter.delegate.AllCauseFilterSourceDelegate)2 CancellationEventFilterDelegate (org.spongepowered.common.event.filter.delegate.CancellationEventFilterDelegate)2 ExcludeSubtypeFilterDelegate (org.spongepowered.common.event.filter.delegate.ExcludeSubtypeFilterDelegate)2 FilterDelegate (org.spongepowered.common.event.filter.delegate.FilterDelegate)2 HasDataFilterDelegate (org.spongepowered.common.event.filter.delegate.HasDataFilterDelegate)2 IncludeSubtypeFilterDelegate (org.spongepowered.common.event.filter.delegate.IncludeSubtypeFilterDelegate)2 ParameterFilterDelegate (org.spongepowered.common.event.filter.delegate.ParameterFilterDelegate)2 ParameterFilterSourceDelegate (org.spongepowered.common.event.filter.delegate.ParameterFilterSourceDelegate)2 SubtypeFilterDelegate (org.spongepowered.common.event.filter.delegate.SubtypeFilterDelegate)2