Java ASM-如何在方法中创建局部变量

Java ASM-如何在方法中创建局部变量,java,java-bytecode-asm,Java,Java Bytecode Asm,我试图将AST编译成java类文件,使用ASM创建类本身。我在尝试向函数添加局部变量时遇到了一个问题(函数是正确创建的) 例如,一个简单的函数如下: public void test() { int a = 10; int b = 11; int c = 12; int d = 1; } public test() { //()V bipush 10 istore1 bipush 11 i

我试图将AST编译成java类文件,使用ASM创建类本身。我在尝试向函数添加局部变量时遇到了一个问题(函数是正确创建的)

例如,一个简单的函数如下:

public void test() {
    int a = 10;
    int b = 11;
    int c = 12;
    int d = 1;
}
 public test() { //()V
         bipush 10
         istore1
         bipush 11
         istore2
         bipush 12
         istore3
         iconst_4
         istore4
         return
 }
一旦插入指令(使用JD-GUI反编译),就会变成这样:

此操作的字节码如下所示:

public void test() {
    int a = 10;
    int b = 11;
    int c = 12;
    int d = 1;
}
 public test() { //()V
         bipush 10
         istore1
         bipush 11
         istore2
         bipush 12
         istore3
         iconst_4
         istore4
         return
 }
我正在创建如下局部变量:

methodVisitor.visitVarInsn(Opcodes.BIPUSH, symbol.value);
methodVisitor.visitVarInsn(Opcodes.ISTORE, position);
// type is net.bytebuddy.jar.asm.Type.INT_TYPE
net.bytebuddy.jar.asm.Type type = getType(symbol.type);
int id = mv.newLocal(type);
mv.visitVarInsn(Opcodes.ISTORE, id);
symbol
是当前局部变量,
position
是其索引,以1为基础。F.e.
a
将处于位置
1
d
将处于位置
4

我的问题是对不同的值正确使用操作码。我尝试了像
Opcodes.LDC
那样的
Opcodes
,但是生成的类在其方法中没有任何局部变量,但是所有全局定义的变量和函数都在其中两次

向方法中添加任意类型和任意值的变量的正确方法是什么


编辑:

根据我的评论,我试图将其他问题纳入答案中,如下所示:

MethodVisitor methodVisitor = cw.visitMethod(
        access,
        methodName,
        typeDescriptors,
        null,
        null
);

LocalVariablesSorter mv = new LocalVariablesSorter(access, typeDescriptors, methodVisitor);
然后我使用
LocalVariablesSorter
添加如下局部变量:

methodVisitor.visitVarInsn(Opcodes.BIPUSH, symbol.value);
methodVisitor.visitVarInsn(Opcodes.ISTORE, position);
// type is net.bytebuddy.jar.asm.Type.INT_TYPE
net.bytebuddy.jar.asm.Type type = getType(symbol.type);
int id = mv.newLocal(type);
mv.visitVarInsn(Opcodes.ISTORE, id);
哪些反编译到:

/* Error */
public void test()
{
    // Byte code:
    //   0: istore_2
    //   1: istore 4
    //   3: istore 6
    //   5: istore 8
    //   7: return
}
产生此字节码:

public test() { //()V
    istore2
    istore4
    istore6
    istore8
    return
}

这里缺少什么?

指令将堆栈顶部的值存储到寄存器中。堆栈上没有值,这将导致验证错误

例如,如果要存储值
2
,则需要首先将值加载到堆栈中:

net.bytebuddy.jar.asm.Type type = getType(symbol.type);
int id = mv.newLocal(type);
mv.visitInsn(Opcodes.ICONST_2);
mv.visitVarInsn(Opcodes.ISTORE, id);

对于“较高”值,还有其他指令,如
BIPUSH
SIPUSH
。否则,您可以使用
LDC
从常量池加载值。这通常是存储值的最有效方式;ASM隐式删除其中的重复数据。

的可能重复也提供了类似的答案。看到了这两个问题,但它们对我没有帮助。它们之间没有真正的联系,因为我的问题是关于动态创建局部变量(使用任意类型)。其中一个链接问题提到了,您不使用,但应该使用。根据指南,您仍然需要类似于
mv.visitVarInsn(LSTORE,time)的内容
在调用
newLocal
之后,这基本上就是我要问的->如果不使用开关检查刚刚添加的变量类型,如何确定是否需要
LSTORE
或其他内容?当库已经知道变量的类型时,这样做似乎很乏味。。。必须有更好的方法。好的,这解决了编辑中的问题。但是您如何决定使用
ICONST_2
BIPUSH
等。?你必须自己做,还是ASM可以帮你?F.e.然后我必须这样做:
if(val==2)返回操作码。ICONST_2
if(val>5)返回操作码。BIPUSH
,等等。对于小于5的值,可以使用
BIPUSH
,但最有效的是常量值。您可以创建某种形式的实用程序,为您推送价值
BIPUSH
只能推送一个字节大小的值,
SIPUSH
只能推送两个字节的值。所有较大的值都必须通过常量池推送。如果您将目标
MethodVisitor
包装为。然后您可以调用它,例如,
iconst(2)
,它将为该值选择最有效的指令。