JVM规范中可验证局部变量类型冲突的调试信息表示疑问
核心矛盾:规范限制与合法字节码场景
一方面,Java类规范规定:
Code属性的属性表中,每个局部变量最多只能有一个LocalVariableTable属性。
但另一方面,JVM字节码验证层面允许方法不同区域的局部变量可以存在冲突类型。例如Leroy(2003)的研究指出:
寄存器(或栈槽)两种类型的最小上界可为
top,导致该寄存器在合并状态下的类型为top。这对应条件分支的两个分支中,寄存器持有不兼容类型(如一个分支为int,另一个为对象引用)的场景,合并点后该寄存器被视为未初始化(无法再从中加载数据)。
这就引出疑问:JVM的调试信息规范是否存在缺陷?这类同一局部变量复用为不同类型的场景,需要在LocalVariableTable中为不同类型范围添加多条记录,但规范似乎禁止这么做。
StackMapTables对该场景的影响
对于类版本>=50的情况,验证算法已从2003年论文所述的类型推断改为使用StackMapTables进行类型检查。那么后者是否禁止上述冲突类型的场景?如果是,是不是只有类版本<=49时才存在可验证与可调试(局部变量层面)的规范冲突?
补充说明:栈与局部变量的合并规则差异
(至少对提问者而言)容易混淆栈与局部变量的验证要求:即使JDK 1.0在合并时要求栈具有相同类型(引用类型除外),但局部变量并非如此。
最早的JVM规范(1996)提到:
合并两个局部变量状态时,比较对应的局部变量对。若两种类型不同,除非两者均包含引用值,否则验证器会记录该局部变量包含不可用值。若两者均包含引用值,则合并状态包含对这两种类型的第一个共同超类实例的引用。
该段落与JDK 11规范中的内容几乎一致:
合并两个局部变量数组状态时,比较对应的局部变量对。合并后的局部变量值按上述规则计算,但允许对应值为不同的基本类型。这种情况下,验证器会记录合并后的局部变量包含不可用值。
按规范,这种情况不会导致验证失败(与栈的类似情况不同)。因此Leroy的研究符合JVM规范,既然分支中同一局部变量使用不同类型是有效字节码(除非后续读取该局部变量),那疑问依然存在:这种场景是否无法通过LocalVariableTable进行调试信息表示?
内容的提问来源于stack exchange,提问作者Computer says 'no'--SOooooo

