错误的本地 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 仅根据您提供的代码生成堆栈帧。它无法为无效代码神奇地生成有效堆栈帧。
我在一个已经存在的 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 仅根据您提供的代码生成堆栈帧。它无法为无效代码神奇地生成有效堆栈帧。