Search code examples
javajvmjava-bytecode-asm

ASM look at maxStack before instructions?


I'm trying to use the ASM library to translate byte code into a different format, which can be done with a MethodVisitor, as with this simple test code:

    return new MethodVisitor(ASM7) {
        @Override
        public void visitInsn(int opcode) {
            System.out.println(String.format("%02x", opcode));
        }

        @Override
        public void visitMaxs(int maxStack, int maxLocals) {
            System.out.println(maxStack);
        }
    };

One issue is that I only get to see maxStack after the actual instructions – I've tested it, and that is the order the methods are called in – whereas it would be helpful to have the maxStack value available while translating the instructions.

Is there any way to get to see maxStack first?


Solution

  • The ASM API does not support accessing this information before traversing the instructions.

    One solution would be to traverse the class twice, storing the max values in the first pass.

    The alternative is to store the current method information temporarily. ASM’s Tree API can help you here. The class MethodNode implements MethodVisitor, storing all visited artifacts and has an accept(MethodVisitor) to visit all stored artifacts:

    classReader.accept(new ClassVisitor(Opcodes.ASM7) {
        @Override
        public MethodVisitor visitMethod(int access, String name, String descriptor,
                                         String signature, String[] exceptions) {
            MethodVisitor actualVisitor = new MethodVisitor(Opcodes.ASM7) {
                @Override
                public void visitInsn(int opcode) {
                    System.out.printf("%02x%n", opcode);
                }
    
                @Override
                public void visitMaxs(int maxStack, int maxLocals) {
                    System.out.println("max stack: "+maxStack);
                }
            };
            return new MethodNode(Opcodes.ASM7) {
                @Override
                public void visitMaxs(int maxStack, int maxLocals) {
                    actualVisitor.visitMaxs(maxStack, maxLocals);
                    super.visitMaxs(maxStack, maxLocals);
                }
                @Override
                public void visitEnd() {
                    accept(actualVisitor);
                }
            };
        }
    }, 0);
    

    So here, we keep the original MethodVisitor as-is but return an adapter implementing the intended modification. It’s a subclass of MethodNode recording all artifacts but reporting visitMaxs immediately to the actualVisitor, then at visitEnd it will accept(actualVisitor) to visit all recorded information.

    Note that therefore, actualVisitor will encounter visitMaxs twice, once before all other elements, then again within the standard sequence of events.