I am trying to make something similar to Mixins (from the Minecraft mod loader Fabric) and what I made so far seems to work (doesn't throw any errors, I don't know how else to check), but I got stuck now that I need to write the bytecode back to the original method.
How do I do this?
My code;
/**
* @param method1 The base method
* @param method2 The code that should be added
* @param descriptor The descriptor for both of the methods
* @param access_level The access level for both of the methods (private, public, etc.)
* @param pos The position to inject to
* @param method1Class The base methods source class
* @param method The base method as a Method object
* @throws Exception when something went wrong
*/
public static void mixin(MethodNode method1, MethodNode method2, String descriptor, int access_level, String pos, Class<?> method1Class, Method method) throws Exception {
// Create a new method with merged characteristics
MethodNode mergedMethod = new MethodNode(
access_level,
"mergedMethod",
descriptor,
null,
null
);
// Merge the instructions from both methods into the merged method
switch (pos){
case "HEAD" -> {
mergedMethod.instructions.add(method2.instructions);
mergedMethod.instructions.add(method1.instructions);
}
case "TAIL" -> {
mergedMethod.instructions.add(method1.instructions);
mergedMethod.instructions.add(method2.instructions);
}
default -> {
throw new IllegalArgumentException("Invalid mixin point provided!");
}
}
// Recalculate the stack size and max locals
mergedMethod.maxStack = Math.max(method1.maxStack, method2.maxStack);
mergedMethod.maxLocals = Math.max(method1.maxLocals, method2.maxLocals);
// Generate the bytecode for the merged method
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
mergedMethod.accept(cw);
byte[] bytecode = cw.toByteArray();
method.setAccessible(true);
}
mergedMethod.maxStackandmergedMethod.maxLocalswhen you useCOMPUTE_MAXS. When this code contains branches, this will not be enough; you would have to useCOMPUTE_FRAMESthen (which still implies calculating max). Further, this approach won’t work with appending to the end, as you’re placing the code after the lastreturnorthrowwhich has no effect. While putting a complete method’s code at the beginning effectively replaces the entire code with the other method.