I've generated a simple getter method with ASM in an already existent class.
mv = cn.visitMethod(access, // public method
"get_" + f.name, // name
"()Ljava/lang/String;", // descriptor
null, // signature (null means not generic)
null); // exceptions (array of strings
mv.visitCode();
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitFieldInsn(Opcodes.GETFIELD, cn.name, f.name, f.desc);
mv.visitInsn(Opcodes.ARETURN);
mv.visitMaxs(0, 0);
Then I generated the class.
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
cn.accept(cw);
Now, I can access the class via cw.toByteArray(). The problem is when I try the load the class I get an error because the StackMapTable isn't right (it should be, I'm using ClassWriter.COMPUTE_FRAMES ?)
The error
Exception in thread "main" java.lang.VerifyError: Bad local variable type
Exception Details:
Location:
a.get_c()Ljava/lang/String; @0: aload_0
Reason:
Type top (current frame, locals[0]) is not assignable to reference type
Current Frame:
bci: @0
flags: { }
locals: { }
stack: { }
Bytecode:
0x0000000: 2ab4 0012 b0
After this I added a CheckClassAdapter to see what is wrong.
CheckClassAdapter ca = new CheckClassAdapter(cw, false); //Check data flow
ClassReader cr = new ClassReader(cw.toByteArray());
ca.verify(cr, new GenericClassLoader(), true, new PrintWriter(new PrintStream(System.out)));
The output is the following.
org.objectweb.asm.tree.analysis.AnalyzerException: Error at instruction 0: Expected an object reference, but found .
at org.objectweb.asm.tree.analysis.Analyzer.analyze(Unknown Source)
at org.objectweb.asm.util.CheckClassAdapter.verify(Unknown Source)
at me.ffy00.ClassGenerator2.generateSkeleton(ClassGenerator2.java:85)
at me.ffy00.Test.main(Test.java:25)
Caused by: org.objectweb.asm.tree.analysis.AnalyzerException: Expected an object reference, but found .
at org.objectweb.asm.tree.analysis.BasicVerifier.copyOperation(Unknown Source)
at org.objectweb.asm.tree.analysis.BasicVerifier.copyOperation(Unknown Source)
at org.objectweb.asm.tree.analysis.Frame.execute(Unknown Source)
... 4 more
get_c()Ljava/lang/String;
00000 . : : ALOAD 0
00001 ? : GETFIELD a.c : Ljava/lang/String;
00002 ? : ARETURN
If I take a look to my method get_c() I can see it has ? in some Opcodes (maybe because the first Opcode isn't understood?)
00000 . : : ALOAD 0
00001 ? : GETFIELD a.c : Ljava/lang/String;
00002 ? : ARETURN
I don't really now what that means but the method doesn't look like it's supposed. I compiled a simple Getter class and the method looks like this.
00000 Getter : : ALOAD 0
00001 Getter : Getter : GETFIELD me/ffy00/Getter.a : Ljava/lang/String;
00002 Getter : String : ARETURN
I already tried to set the class version to 50 because the StackMapTable wasn't implemented yet, but it doesn't appear to change anything. I just set the version variable of the ClassNode to 50. Like this.
cn.version = 50;
And the class looks like this in javap.
public class a
minor version: 0
major version: 50
flags: ACC_PUBLIC, ACC_SUPER
Also using javap I can get my method, including the stack size and the local number but the method doesn't have a StackMapTable (yes I'm using -v, the StackMapTable shows in other methods).
static java.lang.String get_c();
descriptor: ()Ljava/lang/String;
flags: ACC_STATIC
Code:
stack=1, locals=1, args_size=0
0: aload_0
1: getfield #18 // Field c:Ljava/lang/String;
4: areturn
My variable looks like this.
private static final java.lang.String c;
descriptor: Ljava/lang/String;
flags: ACC_PRIVATE, ACC_STATIC, ACC_FINAL
Can some point me to what I need to do to fix the method. I couldn't find any helpful information about how to fix the StackMapTable with ASM.
As pointed out by dave_thompson_085, the problem is that you accidentally made the method static, but you are still accessing this
. Or in bytecode terms, you are trying to load an object out of slot 0, but there is nothing in slot 0 at the start of the method.
ASM only generates the stack frames based on the code you give it. It can't magically generate valid stackframes for invalid code.