我希望在访问此字节码时获取方法中字节码的索引号。例如,给定下面的字节码序列,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
方法并计算已经过去的所有字节码也不是一个好的解决方案。
答案 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。这是来自记忆,所以我可能已经忘记了其他一些边缘情况。