0

我在一个已经存在的类中使用 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的版本变量设置为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 的有用信息。

4

1 回答 1

0

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

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

于 2017-07-20T14:29:00.340 回答