错误的本地 StackMapTable

Bad local StackMapTable

我在一个已经存在的 class 中使用 ASM 生成了一个简单的 getter 方法。

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);

然后我生成了class。

ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
cn.accept(cw);

现在,我可以通过 cw.toByteArray() 访问 class。 问题是当我尝试加载 class 时出现错误,因为 StackMapTable 不正确(应该是,我正在使用 ClassWriter.COMPUTE_FRAMES ?)

错误

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   

在此之后我添加了一个 CheckClassAdapter 以查看问题所在。

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)));

输出如下。

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

如果我看一下我的方法 get_c() 我可以看到它有 ?在一些操作码中(可能是因为第一个操作码不被理解?)

00000 .  :  :     ALOAD 0
00001 ?   :     GETFIELD a.c : Ljava/lang/String;
00002 ?   :     ARETURN

我现在真的不明白那是什么意思,但该方法看起来并不像预期的那样。我编译了一个简单的 Getter class 方法是这样的。

00000 Getter  :  :     ALOAD 0
00001 Getter  : Getter  :     GETFIELD me/ffy00/Getter.a : Ljava/lang/String;
00002 Getter  : String  :     ARETURN

我已经尝试将 class 版本设置为 50,因为 StackMapTable 尚未实现,但它似乎没有任何改变。我只是将 ClassNode 的版本变量设置为 50。 像这样。

cn.version = 50;

class 在 javap 中看起来像这样。

public class a
  minor version: 0
  major version: 50
  flags: ACC_PUBLIC, ACC_SUPER

我也可以使用 javap 获取我的方法,包括堆栈大小和本地编号,但该方法没有 StackMapTable(是的,我使用的是 -v,StackMapTable 显示在其他方法中)。

  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

我的变量看起来像这样。

  private static final java.lang.String c;
    descriptor: Ljava/lang/String;
    flags: ACC_PRIVATE, ACC_STATIC, ACC_FINAL

能否指出我需要做些什么来修复该方法。我找不到任何有关如何使用 ASM 修复 StackMapTable 的有用信息。

正如dave_thompson_085所指出的,问题在于您不小心将方法设为静态,但您仍在访问this。或者用字节码术语来说,您正试图从插槽 0 加载一个对象,但在方法开始时插槽 0 中没有任何内容。

ASM 仅根据您提供的代码生成堆栈帧。它无法为无效代码神奇地生成有效堆栈帧。