Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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<AnnotationNode> 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(), "<init>",
method.desc.substring(0, method.desc.lastIndexOf(')') + 1) + "V", false);
method.visitInsn(ARETURN);
}

/**
* @return Whether any transformation was done to the targetClass
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -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
* <h2>Example:</h2>
*
* <pre>{@code
* @FactoryFromConstructor
* private native static Vec3i createVec3i(int x, int y, int z);
* }</pre>
*
* Will generate this method based on the stub signature.
*
* <pre>{@code
* private static Vec3i createVec3i(int x, int y, int z) {
* return new Vec3i(x, y, z);
* }
* }</pre>
*/
@Target({ METHOD })
public @interface FactoryFromConstructor {}
Original file line number Diff line number Diff line change
@@ -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;