我已经在已经存在的类中使用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的任何有用信息。
答案 0 :(得分:0)
正如dave_thompson_085所指出的,问题是您不小心将方法设为静态,但仍在访问this
。或者在字节码术语中,您尝试从插槽0加载对象,但在方法开头的插槽0中没有任何内容。
ASM仅根据您提供的代码生成堆栈帧。它无法为无效代码神奇地生成有效的堆栈帧。