我正在通过LocalVariableSorter
newLocal
添加新的当地人。我添加局部变量的方法是一个带有长参数的实例方法。我正在添加两个当地人;一个长,一个对象。示例代码中没有其他本地变量。
因此,我本期望以下插槽/索引:
0 - this
1 - the long param
3 - my 1st local added via `newLocal` - using two slots as it is a long
5 - my 2nd local added via `newLocal`
不过,我从newLocal
得到的回报是 3 和 7。为什么差距这么大?
更奇怪的是,当我使用这些索引添加xSTORE
指令并使用javap检查结果时,它会显示:
LSTORE 5
ASTORE 8
注意:不仅这些值与我传递给 xSTORE 指令的值不同,而且它们之间的差距现在是 3,而不是以前的 4。
不过,生成的代码有效。我只是想了解这里发生了什么魔术以及为什么。
谢谢
LocalVariableSorter
类有一个设计,这使得它很容易被错误地使用。
当调用MethodVisitor
API 在其上定义的方法时,局部变量将经历类文档中提到的重新编号。
因此,当与ClassReader
一起使用时,访问的旧代码会被转换。由于您不希望注入的新代码进行此转换,而是要使用新定义的变量,因此您必须绕过LocalVariableSorter
并在基础目标MethodVisitor
上调用方法。
当你在LocalVariableSorter
上调用visitVarInsn(LSTORE, 3)
时,它被处理得像引用索引3
的旧指令一样,并且由于你注入了一个占用索引的新变量3
和4
,索引3
处的"旧变量"被重新映射到下一个自由索引,即5
(和6
(。然后,当您定义下一个新变量时,它会获得索引7
,并且在LocalVariableSorter
上调用visitVarInsn(ASTORE, 7)
就像处理与新变量冲突的旧变量一样,因此它会重新映射到8
。
此行为与类文档的第一句话完全匹配:
LocalVariablesSorter
一个 MethodVisiter,它按局部变量的出现顺序重新编号。
因此,虽然您必须在LocalVariableSorter
上调用newLocal
以创建一个不会被重新映射的新变量,但您必须在原始包装MethodVisitor
上调用visit…
方法才能使用它。当你使用子类GeneratorAdapter
时,你可以使用它新定义的方法(那些不以visit…
开头的方法(来创建不会被转换的新指令,但对我来说,这会让事情变得更糟,有方法转换指令和在同一类上创建未转换的指令,并且始终需要记住visit…
前缀会有所不同。对于某些方法,您仍然需要访问原始方法访问者,如本答案中所述,该答案涉及为创建的变量创建调试信息visitLocalVariable
。