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;
}
Aggregations