Search in sources :

Example 1 with AnnotationFormatException

use of io.leangen.geantyref.AnnotationFormatException 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

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 ClassWriter (org.objectweb.asm.ClassWriter)1 MethodVisitor (org.objectweb.asm.MethodVisitor)1 Type (org.objectweb.asm.Type)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 HasDataFilterDelegate (org.spongepowered.common.event.filter.delegate.HasDataFilterDelegate)1 IncludeSubtypeFilterDelegate (org.spongepowered.common.event.filter.delegate.IncludeSubtypeFilterDelegate)1 ParameterFilterDelegate (org.spongepowered.common.event.filter.delegate.ParameterFilterDelegate)1