错误的本地StackMapTable

时间:2017-07-20 13:31:16

标签: java bytecode java-bytecode-asm javap

我已经在已经存在的类中使用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);

然后我生成了这个类。

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

现在,我可以通过cw.toByteArray()访问该类。 问题是,当我尝试加载类时,我得到一个错误,因为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类,方法如下所示。

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

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

cn.version = 50;

该课程在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的任何有用信息。

1 个答案:

答案 0 :(得分:0)

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

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