ASM看maxStack之前的指令?

ASM look at maxStack before instructions?

我正在尝试使用 ASM 库将字节码转换为不同的格式,这可以使用 MethodVisitor 来完成,就像这个简单的测试代码一样:

    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);
        }
    };

一个问题是我只能在实际指令后才能看到 maxStack – 我已经测试过了,这就是调用方法的顺序 – 而在翻译时提供 maxStack 值会很有帮助说明。

有什么办法可以先看到 maxStack 吗?

为方法计算的最大堆栈取决于方法主体中的指令和路径。因此,max stack 只有在方法体被分析后才可用。请参阅第 3.3.1、3.3.2 和 3.5 节here

您可以在一个单独的通道中获得最大堆栈并存储它以供以后通道参考。但是,修改方法主体可能会使此类存储的堆栈信息无效。

ASM API 不支持在遍历指令之前访问此信息。

一个解决方案是遍历 class 两次,在第一次遍历时存储最大值。

另一种方法是暂时存储当前方法信息。 ASM 的树 API 可以在这里帮助你。 class MethodNode 实现 MethodVisitor,存储所有访问过的工件,并有一个 accept(MethodVisitor) 来访问所有存储的工件:

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);

所以在这里,我们保持原来的 MethodVisitor 不变,但 return 一个适配器实现了预期的修改。它是 MethodNode 的子 class 记录所有工件,但立即向 actualVisitor 报告 visitMaxs,然后在 visitEnd 它将 accept(actualVisitor) 访问所有记录的信息。

请注意,因此,actualVisitor 将遇到 visitMaxs 两次,一次在所有其他元素之前,然后在标准事件序列中再次遇到。