use of org.lanternpowered.server.event.filter.delegate.ParameterFilterSourceDelegate in project LanternServer by LanternPowered.
the class FilterGenerator method generateClass.
byte[] generateClass(String name, Method method) {
name = name.replace('.', '/');
final Parameter[] params = method.getParameters();
final 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;
final List<FilterDelegate> additional = new ArrayList<>();
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) {
final 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
final int[] plocals = new int[params.length - 1];
for (int i = 1; i < params.length; i++) {
final Parameter param = params[i];
ParameterFilterSourceDelegate source = null;
final List<ParameterFilterDelegate> paramFilters = new ArrayList<>();
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() + ")");
}
final 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();
final byte[] data = cw.toByteArray();
if (FILTER_DEBUG) {
final Path outDir = Paths.get(".sponge.debug.out");
final Path outFile = outDir.resolve(name + ".class");
if (!Files.exists(outFile.getParent())) {
try {
Files.createDirectories(outFile.getParent());
} catch (IOException e) {
Lantern.getLogger().warn("Unable to create the filter debug directory.", e);
}
}
try (final OutputStream out = Files.newOutputStream(outFile)) {
out.write(data);
} catch (IOException e) {
Lantern.getLogger().warn("Unable to create the filter debug class.", e);
}
}
return data;
}
Aggregations