ASM文档说标签代表一个基本 block ,它是控制图中的一个节点。因此,我在这个简单的示例中测试了 visitLabel
方法:
public static void main(String[] args) {
int x = 3, y = 4;
if (x < y) {
x++;
}
}
对于 visitLabel
方法,我使用 native API 来检测它:setID(int id)
,其中 id 是增量的。在此示例中,CFG 应具有 3 个节点:一个位于开头,一个用于 if 语句的每个分支。所以我预计 setID
将在 3 个位置被调用。然而,它被调用了5次,并且有很多nop
指令。有人能为我解释一下为什么吗?
这是上述程序的检测字节码。
public static void main(java.lang.String[]);
Code:
0: iconst_2
1: invokestatic #13 // Method setId:(I)V
4: iconst_3
5: istore_1
6: iconst_3
7: invokestatic #13 // Method setId:(I)V
10: iconst_4
11: istore_2
12: iconst_4
13: invokestatic #13 // Method setId:(I)V
16: iload_1
17: iload_2
18: if_icmpge 28
21: iconst_5
22: invokestatic #13 // Method setId:(I)V
25: iinc 1, 1
28: bipush 6
30: invokestatic #13 // Method setId:(I)V
33: return
34: nop
35: nop
36: nop
37: nop
38: athrow
我不明白的是为什么每个istore
指令之前有一个标签
。没有分支使其成为 CFG 中的新节点。
最佳答案
标签
的主要用途是表示字节码序列中的位置。由于这是分支目标所必需的,因此您可以使用它们来识别基本 block 。但您必须注意,当 LineNumberTable
时,它们也用于报告行号。属性存在并用于在 LocalVariableTable
时报告局部变量范围属性也存在,对于较新的类文件,它们的类型注释记录在 RuntimeVisibleTypeAnnotations
中。属性。此外,标签可以标记异常处理程序的 protected 区域。对于从 Java 源代码生成的代码,此 protected 区域与 try
block 匹配,因此它是一个基本 block ,但不需要保留其他字节码。
查看
由于局部变量的范围可能跨越最后一个 return
指令,因此有可能在最后一条指令之后遇到标签,这就是您的情况。您在 return
指令后注入(inject) bipush 7, invokestatic#13
,导致代码无法访问。
显然,您还使用COMPUTE_FRAMES
选项让ASM从头开始重新计算堆栈映射帧,但由于未知的初始堆栈状态,无法计算无法访问的代码的帧。 ASM 通过用 nop
指令替换不可访问的代码并后跟单个 athrow
语句来解决此问题。对于这个序列,可以指定一个有效的初始堆栈帧,并且它对执行没有影响(因为代码无法访问)。
如您所见,四个 nop
指令加上一个 athrow
指令跨度五个字节,这与替换的 bipush 7, invokestatic#13 的大小相同
序列有。
您可以通过指定 ClassReader.SKIP_DEBUG
来删除大部分报告的标签。到其accept
method 。然后,您的示例仅获得一个报告标签,即与 if
语句关联的分支目标。但你必须处理visitJumpInsn
识别条件代码的开始。
因此,要识别所有基本 block ,您必须处理所有分支指令,即通过 visitJumpInsn
、visitLookupSwitchInsn
和 visitTableSwitchInsn
,如下以及所有结束指令,即 athrow
和 return
的所有变体。此外,您需要处理所有 visitTryCatchBlock
调用。如果您需要在一次传递中识别分支指令的潜在目标,我会使用 visitFrame
而不是标签,因为对于类文件版本 51 (Java 7) 或更高。
顺便说一句,当您注入(inject)的只是加载常量和调用静态方法(在可到达的位置)的这些序列时,我会使用 COMPUTE_MAXS
而不是 COMPUTE_FRAMES
,因为当一般代码结构不改变时,不需要昂贵的重新计算。
关于java - ASM : visitLabel generates too many labels and nop instructions,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53232522/