Java – ASM – Uses the strange localVar index of newLocal from LocalVariableSorter

ASM – Uses the strange localVar index of newLocal from LocalVariableSorter… here is a solution to the problem.

ASM – Uses the strange localVar index of newLocal from LocalVariableSorter

I’m adding a new local variable via newLocal in LocalVariableSorter. The method I want to add the local variable to is an instance method with long parameters. I’m going to add two natives; One long and one thing. There are no other local variables in the sample code.

So I expect the following slots/indexes:

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`

However, the return values I get from newLocal are 3 and 7. Why is there such a big gap?

To make things weirder, when I add the xSTORE directive with these indexes and check the results using javap, it shows me:

LSTORE 5
ASTORE 8

Note: Not only is the value different from the one I passed to the xSTORE directive, but the gap between them is now 3 instead of the previous 4.

Although the generated code works. I just want to understand what’s going on here, magic and why.

Thanks

Solution

The LocalVariableSorter class has a design that is easy to misuse.

Local variables are experienced when a method defined by the MethodVisitor API is called renumbering as mentioned in class documentation

Therefore, when used with ClassReader, the old code that is accessed is converted. Since you don’t want the new code injected to go through this transformation, but want to use the newly defined variable, you must bypass the LocalVariableSorter and call the method MethodVisitor. on the underlying target

When you call visitVarInsn(LSTORE, 3) on LocalVariableSorter, it is processed like the old instruction referencing index 3, and because you injected a new variable that occupies indexes 3 and 4, The “old variable” at index 3 is remapped to the next free index, which is 5 (and 6). Then, when you define the next new variable, it gets index 7 and calls visitVarInsn(astore, 7) on LocalVariableSorter like dealing with the old variable that conflicts with the new variable, so it is remapped to 8.

This behavior exactly matches the first sentence of the class document:

LocalVariablesSorter

A MethodVisitor that renumbers local variables in their order of appearance.

Therefore, when you must call

newLocal on LocalVariableSorter to create a new variable that will not be remapped, you must call the visit... original method, wrapping MethodVisitor to use it. When you use the subclass GeneratorAdapter, you can use its newly defined methods (those that don’t start with visit...) to create new directives that won’t be converted, but for me this makes things worse, there are conversion directives and methods that create unconverted instructions on the same class, and you always need to remember visit.... The prefix will vary. For some methods, you still need to access the original method visitors, as described in this answer. It processes visitLocalVariable to create debug information for the created variable.

Related Problems and Solutions