我从newLocal
通过LocalVariableSorter
添加新的本地人。我添加locals的方法是具有long参数的实例方法。我添加了两个本地人;一个长,一个对象。示例代码中没有其他本地变量。
结果我原本期望以下插槽/索引:
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。
结果代码可以正常工作。我只想了解 magic 在这里发生的原因。
由于
答案 0 :(得分:2)
LocalVariableSorter
类有一个设计,这使得它很容易错误使用。
当调用MethodVisitor
API定义的方法时,它们会经历class documentation中提到的重新编号。
因此,当与ClassReader
一起使用时,访问过的旧代码会被转换。由于您不希望注入的新代码经历此转换,但要使用新定义的变量,您必须绕过LocalVariableSorter
并调用基础目标MethodVisitor
上的方法。
当您在visitVarInsn(LSTORE, 3)
上调用LocalVariableSorter
时,它会像处理索引3
的旧指令一样处理,因为您注入了一个占用索引3
和{的新变量{1}},索引4
处的“旧变量”会重新映射到下一个免费索引,即3
(和5
)。然后,当您定义下一个新变量时,它会获得索引6
,并且7
上的visitVarInsn(ASTORE, 7)
调用会像旧变量一样处理,与您的新变量冲突,因此会重新映射到LocalVariableSorter
。
此行为完全匹配类文档的第一句话:
LocalVariablesSorter一个MethodVisitor,按其外观顺序重新编号局部变量。
因此,当您必须在8
上调用newLocal
来创建一个无法重新映射的新变量时,您必须调用原始的LocalVariableSorter
方法,包裹{ {1}}使用它。当您使用子类visit…
时,您可以使用其新定义的方法(那些不以MethodVisitor
开头的方法)来创建不会被转换的新指令,但对我来说,这会让事情变得更糟,具有转换指令和在同一个类上创建未转换指令的方法,并始终需要记住GeneratorAdapter
前缀有所不同。对于某些方法,您仍然需要访问原始方法访问者,如this answer中所述,它处理visit…
以创建已创建变量的调试信息。