获取方法中当前解释的字节码指令的索引

时间:2015-08-05 19:16:16

标签: java bytecode java-bytecode-asm

我希望在访问此字节码时获取方法中字节码的索引号。例如,给定下面的字节码序列,invokevirtual的索引号是7(使用SKIP_DEBUG访问方法体)。

public calculate(IILjava/lang/String;J)J
   L0
    LINENUMBER 17 L0
    ICONST_3  //0 
    ISTORE 6  //1
   L1
    LINENUMBER 18 L1
    LDC 10.0  //2
    DSTORE 7  //3
   L2
    LINENUMBER 19 L2
    ALOAD 0  //4
    GETFIELD code/sxu/asm/Callee._call2 : Lcode/sxu/asm/Callee2; //5
    LDC "xushijie"  //6
    INVOKEVIRTUAL code/sxu/asm/Callee2.sayHello (Ljava/lang/String;)I  //7
    ISTORE 9

}

我的代码就像:

ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES|ClassWriter.COMPUTE_MAXS);

    cr.accept(new SomeMethodVisitor(api, owner, access, name,   desc, cw.visitMethod(access, name, desc, owner, null)), SKIP_DEBUG);

class SomeMethodVisitor extends MethodVisitor{
            @Override
            public void visitMethodInsn(final int opcode, final String owner,
                    final String name, final String desc, final boolean itf) {
                int index = ???  //Get the current  bytecode index number here.
                super.visitMethodInsn(opcode, owner, name, desc, itf);
            }
}

使用基于树的ASM API相对容易,我们可以使用:

 class MethodNode{
     public InsnList instructions;
}

但我在基于事件的模式中没有一个好的解决方案。此外,覆盖MethodVisitor的所有visitXXX方法并计算已经过去的所有字节码也不是一个好的解决方案。

1 个答案:

答案 0 :(得分:1)

ASM实际上非常棘手,因此使用Javassist代替这个功能几乎是值得的。

有一个名为MethodVisitor的{​​{1}}子类。如果你对它进行子类化,则可以获得字节码大小的运行总和(因此,字节码中的偏移量)。为什么"有点"?

某些字节码操作的大小可能不同。例如,如果要将整数常量压入堆栈,则可以使用一个,两个或三个字节的指令代码来执行此操作,具体取决于整数的大小。 ASM持有字节码的操作抽象。换句话说,对于该指令,它将维护一个节点,该节点表示"在值X的堆栈上推送一个整数。" CodeSizeEvaluator将决定如何在实际字节码中执行此操作。出于这个原因,Classwriter并不真正知道该指令是1,2或3个字节。这就是为什么它维持一个" min"或者"最大"

您可以通过查看X的值并确定将使用哪个实际指令并选择" actual"来扩充逻辑。尺寸。这是一些棘手的案例。这些是你有表跳转(即case语句)。这些被填充,因此它们对齐到4字节边界。知道对齐需要知道它们开始的字节码计数(你应该有)。更难的是跳跃和结果。 CodeSizeEvaluator通过插入带有长goto的代码来处理跳转偏移大于+/- 32K的情况。除非你做一些疯狂的事情,否则这几乎不会发生。尽管如此,Classwriter将允许分支最多7个字节。您可能需要假设一个典型的情况并计算3个字节。

我希望这会有所帮助。

P.S。这是来自记忆,所以我可能已经忘记了其他一些边缘情况。