Java ASM GeneratorAdapter variable naming

Viewed 587

I am generating a simple class and unable to inject a proper variable name. ASM version is 5.2.

Here is the code:

package com.test;

import org.objectweb.asm.*;
import org.objectweb.asm.commons.GeneratorAdapter;
import org.objectweb.asm.commons.Method;

import java.nio.file.Files;
import java.nio.file.Paths;

public class Main {

    public static void main(String[] args) throws Exception {
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
        String name = "com.test.Sub";
        cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, name.replace('.', '/'), null, "java/lang/Object", null);
        Method ctor = Method.getMethod("void <init>()");
        GeneratorAdapter mg = new GeneratorAdapter(Opcodes.ACC_PUBLIC, ctor, null, null, cw);
        mg.visitCode();
        mg.loadThis();
        mg.invokeConstructor(Type.getType(Object.class), ctor);
        int var = mg.newLocal(Type.INT_TYPE);
        mg.push(42.42);
        mg.storeLocal(var);
        Label varLabel = mg.mark();
        mg.returnValue();
        Label endLabel = mg.mark();
        mg.visitLocalVariable("x", "D", null, varLabel, endLabel, var);
        mg.endMethod();
        cw.visitEnd();
        byte[] bytes = cw.toByteArray();
        Files.write(Paths.get(name + ".class"), bytes);
    }

}

I am using a GeneratorAdapter to simplify code generation. Since GeneratorAdapter is inherited from LocalVariablesSorter, I assume that it is allowed to use newLocal(Type) method from it.

There is nothing wrong with the emitted bytecode except the name of the variable. When visitLocalVariable() method is called, instead of assigning a name to the variable it creates a new one in the bytecode.

Emitted bytecode:

// class version 52.0 (52)
// access flags 0x1
public class com/test/Sub {
  // access flags 0x1
  public <init>()V
    ALOAD 0
    INVOKESPECIAL java/lang/Object.<init> ()V
    LDC 42.42
    DSTORE 1
   L0
    RETURN
   L1
    LOCALVARIABLE x D L0 L1 3
    MAXSTACK = 2
    MAXLOCALS = 5
}

I am using the same variable index provided by newLocal() call in visitLocalVariable(). However in the bytecode mapped index is 3 instead of 1. If the variable has a "shorter" type such as int then the index would be 2 and still not 1 as it should.

From my observations this happens because of the following. LocalVariablesSorter maintains a mapping from old variable indices to new ones. It also overrides method visitLocalVariable and before delegating a call down a visitor chain it computes a newIndex from the mapping. The newIndex is calculated via another private method remap(). This method checks whether the mapping already exists for a given variable and if not then a new mapping is created. The problem as I see it is that newLocal() method does not add anything to the mapping.

Also I can see from ASM sources that storeInsn() in GeneratorAdapter delegates visitVarInsn() call down the chain instead of calling the implementation of LocalVariablesSorter. Because it is in LocalVariablesSorter implementation the remap() method is called for the variable index and mapping is updated.

Therefore my question is how to use GeneratorAdapter so variables are named properly in emitted bytecode or how to combine GeneratorAdapter with LocalVariablesSorter in a chain so they work properly together?

1 Answers
Related