diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/ASMConfigPlugin.java b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/ASMConfigPlugin.java index 0548b24d..a4d98e54 100644 --- a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/ASMConfigPlugin.java +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/ASMConfigPlugin.java @@ -38,6 +38,7 @@ import io.github.notstirred.dasm.util.Pair; import io.github.opencubicchunks.cc_core.annotation.Public; import io.github.opencubicchunks.cubicchunks.CubicChunks; +import io.github.opencubicchunks.cubicchunks.util.asm.FactoryFromConstructor; import net.minecraft.Util; import net.neoforged.fml.loading.FMLEnvironment; import org.apache.logging.log4j.LogManager; @@ -185,6 +186,7 @@ public ASMConfigPlugin() { @Override public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { doPublicAnnotation(targetClass); + doFactoryFromConstructorAnnotation(targetClass); // Apply POST_APPLY dasm transforms boolean wasTransformed; @@ -261,6 +263,47 @@ private void doPublicAnnotation(ClassNode targetClass) { } } + private void doFactoryFromConstructorAnnotation(ClassNode targetClass) { + // Generate constructor calls matching the signature of methods annotated with @FactoryFromConstructor + for (MethodNode method : targetClass.methods) { + List annotations = method.invisibleAnnotations; + if (annotations != null) { + if (annotations.stream().anyMatch( + annotationNode -> annotationNode.desc.equals("L" + FactoryFromConstructor.class.getName().replace('.', '/') + ";"))) { + if ((method.access & ACC_STATIC) == 0) { + throw new IllegalStateException("Tried to generate a factory method on a non-static dst"); + } + transformStubToFactory(method); + } + } + } + } + + private static void transformStubToFactory(MethodNode method) { + method.access &= ~ACC_NATIVE; + Type methodType = Type.getMethodType(method.desc); + Type returnType = methodType.getReturnType(); + Type[] argumentTypes = methodType.getArgumentTypes(); + + method.instructions.clear(); + method.visitTypeInsn(NEW, returnType.getInternalName()); + method.visitInsn(DUP); + for (int i = 0; i < argumentTypes.length; i++) { + Type argumentType = argumentTypes[i]; + switch (argumentType.getSort()) { + case Type.OBJECT, Type.ARRAY -> method.visitVarInsn(ALOAD, i); + case Type.LONG -> method.visitVarInsn(LLOAD, i); + case Type.INT, Type.SHORT, Type.BYTE, Type.BOOLEAN, Type.CHAR -> method.visitVarInsn(ILOAD, i); + case Type.DOUBLE -> method.visitVarInsn(DLOAD, i); + case Type.FLOAT -> method.visitVarInsn(FLOAD, i); + default -> throw new IllegalStateException("Unexpected sort: " + argumentType.getSort()); + } + } + method.visitMethodInsn(INVOKESPECIAL, returnType.getInternalName(), "", + method.desc.substring(0, method.desc.lastIndexOf(')') + 1) + "V", false); + method.visitInsn(ARETURN); + } + /** * @return Whether any transformation was done to the targetClass */ diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/util/asm/FactoryFromConstructor.java b/src/main/java/io/github/opencubicchunks/cubicchunks/util/asm/FactoryFromConstructor.java new file mode 100644 index 00000000..c30d32e1 --- /dev/null +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/util/asm/FactoryFromConstructor.java @@ -0,0 +1,26 @@ +package io.github.opencubicchunks.cubicchunks.util.asm; + +import static java.lang.annotation.ElementType.METHOD; + +import java.lang.annotation.Target; + +/** + * Can be placed on a native stub factory method within a mixin class, the generated method will call the constructor of the return type with the + * given arguments + *

Example:

+ * + *
{@code
+ * @FactoryFromConstructor
+ * private native static Vec3i createVec3i(int x, int y, int z);
+ * }
+ * + * Will generate this method based on the stub signature. + * + *
{@code
+ * private static Vec3i createVec3i(int x, int y, int z) {
+ *     return new Vec3i(x, y, z);
+ * }
+ * }
+ */ +@Target({ METHOD }) +public @interface FactoryFromConstructor {} diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/util/asm/package-info.java b/src/main/java/io/github/opencubicchunks/cubicchunks/util/asm/package-info.java new file mode 100644 index 00000000..4974db52 --- /dev/null +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/util/asm/package-info.java @@ -0,0 +1,7 @@ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +package io.github.opencubicchunks.cubicchunks.util.asm; + +import javax.annotation.ParametersAreNonnullByDefault; + +import io.github.opencubicchunks.cc_core.annotation.MethodsReturnNonnullByDefault;