0

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);
    }
3
  • I think this would be easier to do if you use the visitor API... Anyway, I don't get where you are stuck at. What do you want as output? Overwrite the old class file with the new one? Commented Jun 13, 2023 at 8:42
  • 2
    There’s no sense in trying to calculate mergedMethod.maxStack and mergedMethod.maxLocals when you use COMPUTE_MAXS. When this code contains branches, this will not be enough; you would have to use COMPUTE_FRAMES then (which still implies calculating max). Further, this approach won’t work with appending to the end, as you’re placing the code after the last return or throw which has no effect. While putting a complete method’s code at the beginning effectively replaces the entire code with the other method. Commented Jun 13, 2023 at 8:43
  • 2
    There is no way to “write the bytecode back to the original method” in the Reflection API. You can a) use the modified code to produce a new runtime class b) overwrite the persistent class files, to affect the next run, c) write a Java Agent using the Instrumentation API, or d) write a debugger using the debugging API. Commented Jun 13, 2023 at 8:47

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.