ASM - 使用LocalVariableSorter

时间:2018-05-02 17:28:47

标签: java bytecode java-bytecode-asm byte-buddy jvm-bytecode

我从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 在这里发生的原因。

由于

1 个答案:

答案 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…以创建已创建变量的调试信息。