一方面,Java 类规范说:
代码属性的属性表中每个局部变量可能有 不超过一个 LocalVariableTable 属性。
但另一方面,许多研究论文都思考这样一个问题:在 JVM 字节码验证(和执行)级别,方法的不同区域中的局部变量可能存在类型冲突。 例如乐华 (2003)
寄存器(或堆栈槽)的两种类型的最小上限可以是“top”,导致该寄存器 有类型 合并状态下的“顶部”。这对应于寄存器保存值的情况 条件条件的两个臂中的不兼容类型(例如,一个臂中的int
和一个对象引用 另一个),因此在之后被视为未初始化(不再从此寄存器加载) 合并点。
那么,这是否意味着调试信息的 JVM 规范已被部分破坏,因为它无法在这种[复杂]情况下表示调试信息,即局部变量在同一方法中以不同类型重用? (这些需要在 LocalVariableTable 中包含多个记录,每个记录对应具有不同类型的范围。但规范似乎禁止这样做。)
当然,验证算法已经发生了
变化(从[2003]论文中描述的类型推断)到使用 StackMapTables 对类版本 >= 50 进行类型检查。后者是否禁止第二个引用中的情况? (如果是这样,可验证的内容和可调试的内容之间的规范冲突是否仅限于类版本 <= 49?)
注意有时很容易将堆栈的要求与局部变量的要求混淆(至少对我来说!)。虽然在合并时,甚至 JDK 1.0 也要求堆栈具有相同的类型(引用除外),但局部变量的情况并非如此。从第一个要合并两个局部变量状态,相应的局部变量对是 比较的。如果两种类型不相同,则除非两者都包含引用 值,验证器记录局部变量包含不可用的值。如果两者都 局部变量对包含参考值,合并状态包含 对这两种类型的第一个公共超类的实例的引用。
:该句子/段落几乎相同
例如在 JDK 11 规范中
要合并两个局部变量数组状态,需要比较相应的局部变量对。合并的局部变量的值是使用上述规则计算的,只是相应的值允许是不同的原始类型。在这种情况下,验证器会记录合并的局部变量包含不可用的值。
因此,Leroy 编写的内容与 JVM 规范进行了检查。因此,由于分支上的相同本地变量具有不同类型是有效的字节码,除非您稍后从该本地读取,否则我的问题仍然有效:出于调试目的,这种情况是否无法在 LocalVariableTable 中表示?但是,至少在规范中,这不会导致验证失败(与堆栈的类似情况不同)。
句子的主语是“
LocalVariableTable
属性内的条目”。相当令人惊讶的信息是,一个
LocalVariableTable
属性允许有多个
Code
属性,这一点已经在前面的句子中暗示过了:如果
LocalVariableTable
属性的属性表中存在多个属性”的指定限制在实践中意义不大,因为
Code
属性,则它们可以按任意顺序出现。“每个局部变量最多有一个LocalVariableTable
one
LocalVariableTable
属性足以描述方法的 all局部变量。我在现实生活中从未在一个方法中见过多个
LocalVariableTable
属性,并且只是验证了即使像广泛使用的 ASM 库这样的库也没有准备好在一个方法中处理多个 LocalVariableTable
属性。然而,HotSpot JVM 确实在
LocalVariableTable
属性处接受多个 Code
属性(我刚刚检查了这一点),并验证所有这些属性中没有重复的条目。但“重复条目”在这里意味着“具有相同 start_pc、长度、名称和索引组合的条目”,仅更改其中一个值即可使 JVM 接受它,尽管这是矛盾的。另一方面,目前尚不清楚为什么 JVM 首先在可选的调试属性中强制执行规范中未明确提及的约束。