Search code examples
javabytecodejava-bytecode-asmjavap

Bad local StackMapTable


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.


Solution

  • 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.