1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-08-30 17:17:55 +00:00

Replace ASM generation with MethodHandles

This is the second time I've rewritten our class generation in a little
over a month. Oh dear!

Back in d562a051c7 we started using method
handles inside our generated ASM, effectively replacing a direct call
with .invokeExact on a constant method handle.

This goes one step further and removes our ASM entirely, building up a
MethodHandle that checks arguments and then wraps the return value.
Rather than generating a class, we just return a new LuaFunction
instance that invokeExacts the method handle.

This is definitely slower than what we had before, but in the order of
8ns vs 12ns (in the worst case, sometimes they're much more comparable),
so I'm not too worried in practice.

However, generation of the actual method is now a bit faster. I've not
done any proper benchmarking, but it's about 20-30% faster.

This also gives us a bit more flexibility in the future, for instance
uisng bound MethodHandles in generation (e.g. for instance methods on
GenericSources). Not something I'm planning on doing right now, but is
an option.
This commit is contained in:
Jonathan Coates
2023-10-11 20:05:20 +01:00
parent bd327e37eb
commit 71669cf49c
8 changed files with 266 additions and 225 deletions

View File

@@ -19,7 +19,9 @@ import org.teavm.metaprogramming.ReflectClass;
import javax.annotation.Nullable;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
@@ -39,7 +41,7 @@ import static org.objectweb.asm.Opcodes.*;
*/
public final class StaticGenerator<T> {
private static final String METHOD_NAME = "apply";
private static final String[] EXCEPTIONS = new String[]{Type.getInternalName(LuaException.class)};
private static final String[] EXCEPTIONS = new String[]{ Type.getInternalName(LuaException.class) };
private static final String INTERNAL_METHOD_RESULT = Type.getInternalName(MethodResult.class);
private static final String DESC_METHOD_RESULT = Type.getDescriptor(MethodResult.class);
@@ -67,7 +69,7 @@ public final class StaticGenerator<T> {
this.context = context;
this.createClass = createClass;
interfaces = new String[]{Type.getInternalName(base)};
interfaces = new String[]{ Type.getInternalName(base) };
var methodDesc = new StringBuilder().append("(Ljava/lang/Object;");
for (var klass : context) methodDesc.append(Type.getDescriptor(klass));
@@ -245,7 +247,7 @@ public final class StaticGenerator<T> {
mw.visitTypeInsn(NEW, INTERNAL_COERCED);
mw.visitInsn(DUP);
mw.visitVarInsn(ALOAD, 2 + context.size());
Reflect.loadInt(mw, argIndex);
loadInt(mw, argIndex);
mw.visitMethodInsn(INVOKEINTERFACE, INTERNAL_ARGUMENTS, "getStringCoerced", "(I)Ljava/lang/String;", true);
mw.visitMethodInsn(INVOKESPECIAL, INTERNAL_COERCED, "<init>", "(Ljava/lang/Object;)V", false);
return true;
@@ -258,16 +260,16 @@ public final class StaticGenerator<T> {
if (Enum.class.isAssignableFrom(klass) && klass != Enum.class) {
mw.visitVarInsn(ALOAD, 2 + context.size());
Reflect.loadInt(mw, argIndex);
loadInt(mw, argIndex);
mw.visitLdcInsn(Type.getType(klass));
mw.visitMethodInsn(INVOKEINTERFACE, INTERNAL_ARGUMENTS, "optEnum", "(ILjava/lang/Class;)Ljava/util/Optional;", true);
return true;
}
var name = Reflect.getLuaName(Primitives.unwrap(klass), unsafe);
var name = getLuaName(Primitives.unwrap(klass), unsafe);
if (name != null) {
mw.visitVarInsn(ALOAD, 2 + context.size());
Reflect.loadInt(mw, argIndex);
loadInt(mw, argIndex);
mw.visitMethodInsn(INVOKEINTERFACE, INTERNAL_ARGUMENTS, "opt" + name, "(I)Ljava/util/Optional;", true);
return true;
}
@@ -275,19 +277,19 @@ public final class StaticGenerator<T> {
if (Enum.class.isAssignableFrom(arg) && arg != Enum.class) {
mw.visitVarInsn(ALOAD, 2 + context.size());
Reflect.loadInt(mw, argIndex);
loadInt(mw, argIndex);
mw.visitLdcInsn(Type.getType(arg));
mw.visitMethodInsn(INVOKEINTERFACE, INTERNAL_ARGUMENTS, "getEnum", "(ILjava/lang/Class;)Ljava/lang/Enum;", true);
mw.visitTypeInsn(CHECKCAST, Type.getInternalName(arg));
return true;
}
var name = arg == Object.class ? "" : Reflect.getLuaName(arg, unsafe);
var name = arg == Object.class ? "" : getLuaName(arg, unsafe);
if (name != null) {
if (Reflect.getRawType(method, genericArg, false) == null) return null;
mw.visitVarInsn(ALOAD, 2 + context.size());
Reflect.loadInt(mw, argIndex);
loadInt(mw, argIndex);
mw.visitMethodInsn(INVOKEINTERFACE, INTERNAL_ARGUMENTS, "get" + name, "(I)" + Type.getDescriptor(arg), true);
return true;
}
@@ -297,6 +299,31 @@ public final class StaticGenerator<T> {
return null;
}
@Nullable
private static String getLuaName(Class<?> klass, boolean unsafe) {
if (klass.isPrimitive()) {
if (klass == int.class) return "Int";
if (klass == boolean.class) return "Boolean";
if (klass == double.class) return "Double";
if (klass == long.class) return "Long";
} else {
if (klass == Map.class) return "Table";
if (klass == String.class) return "String";
if (klass == ByteBuffer.class) return "Bytes";
if (klass == LuaTable.class && unsafe) return "TableUnsafe";
}
return null;
}
private static void loadInt(MethodVisitor visitor, int value) {
if (value >= -1 && value <= 5) {
visitor.visitInsn(ICONST_0 + value);
} else {
visitor.visitLdcInsn(value);
}
}
@SuppressWarnings("Guava")
static <T, U> com.google.common.base.Function<T, U> catching(Function<T, U> function, U def) {
return x -> {