Java – ASM looks at the instructions before maxStack?

ASM looks at the instructions before maxStack?… here is a solution to the problem.

ASM looks at the instructions before maxStack?

I’m trying to convert bytecode to a different format using the ASM library, which can be done using MethodVisitor, just like 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 problem is that I can only see maxStack after the actual instructions – I’ve tested it, this is the order in which the methods are called – and it would be helpful to provide the maxStack value when translating the instructions

Is there any way to see maxStack first?

Solution

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

One solution is to traverse the class twice, storing the maximum value on the first traversal.

Another method is to temporarily store the current method information. ASM’s Tree API can help you in this regard. The MethodNode class implements MethodVisitor, stores all visited artifacts, and has an accept(MethodVisitor) to access 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 leave the original MethodVisitor unchanged, but return an adapter that implements the expected modifications. It is a subclass of MethodNode that logs all artifacts, but immediately reports visitMaxs to actualVisitor, and then at visitEnd it will accept (actualVisitor) access all recorded information.

So note that actualVisitor will encounter visitMaxs twice, once before all other elements, and then again in the standard sequence of events.

Related Problems and Solutions