I'm trying to compile an AST to a java classfile, using ASM to create the class itself. I ran into a problem when I try to add local variables to functions (functions are created correctly).
F.e., a simple function like this:
public void test() {
int a = 10;
int b = 11;
int c = 12;
int d = 1;
}
Becomes this, once instrumented (decompiled with JD-GUI):
public void test() {
int i = 10;
int j = 11;
int k = 12;
int m = 4;
}
The bytecode for this looks like this:
public test() { //()V
bipush 10
istore1
bipush 11
istore2
bipush 12
istore3
iconst_4
istore4
return
}
I'm creating the local variables like this:
methodVisitor.visitVarInsn(Opcodes.BIPUSH, symbol.value);
methodVisitor.visitVarInsn(Opcodes.ISTORE, position);
symbol
is the current local variable, and position
its index, 1-based. F.e. a
would be on position 1
and d
would be on position 4
.
My problem is the correct use of the Opcodes
for different values. I tried Opcodes
like Opcodes.LDC
, but then the generated class doesn't have any local variables in its methods, but all the globally defined variables and functions are in there twice.
What's the correct way of adding variables of arbitrary type and with an arbitrary value to a method?
edit:
As per the comments I tried to incorporate the other questions answer like so:
MethodVisitor methodVisitor = cw.visitMethod(
access,
methodName,
typeDescriptors,
null,
null
);
LocalVariablesSorter mv = new LocalVariablesSorter(access, typeDescriptors, methodVisitor);
I then use the LocalVariablesSorter
to add a local variable like this:
// 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);
which decompiles to:
/* Error */
public void test()
{
// Byte code:
// 0: istore_2
// 1: istore 4
// 3: istore 6
// 5: istore 8
// 7: return
}
resulting in this bytecode:
public test() { //()V
istore2
istore4
istore6
istore8
return
}
What am I missing here?
The ISTORE
instruction stores the value on top of the stack to a register. You have no values on the stack and this results in a validation error.
If you want to store the value 2
for example, you need to load the value onto the stack first:
net.bytebuddy.jar.asm.Type type = getType(symbol.type);
int id = mv.newLocal(type);
mv.visitInsn(Opcodes.ICONST_2);
mv.visitVarInsn(Opcodes.ISTORE, id);
For "higher" values, there are other instructions such as BIPUSH
or SIPUSH
. Otherwise, you can use LDC
to load a value from the constant pool. This is normally the most efficient way of storing values; ASM implicitly deduplicates values in it.