我使用ASM库生成字节码,方法的“最大堆栈大小”将自动计算。在运行期间,我发现此值(最大堆栈大小)不正确。
我的源代码是:
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); .... MethodType initType = MethodType.methodType(void.class, clsList); mv = cw.visitMethod(ACC_PUBLIC, "<init>", initType.toMethodDescriptorString(), null, null); mv.visitCode(); mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKESPECIAL, "java/lang/invoke/BaseTemplate", "<init>", "()V", false); for(int i=0; i< list.size(); i++){ mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 1+i); mv.visitFieldInsn(PUTFIELD, className, list.get(i).name(), Utils.getFieldDesc(list.get(i).type())); } mv.visitInsn(RETURN); //mv.visitMaxs(2, 4); //Verify succeeds if uncomment this line. mv.visitEnd(); .... //Verify generated code before class loading.. PrintWriter pw = new PrintWriter(System.out); CheckClassAdapter.verify(new ClassReader(cw.toByteArray()), true, pw); Class<?> expClass =defineClass(..);
上面的代码将生成字节码:
Classfile /C:/temp/TGWD.class Last modified Mar 11, 2015; size 403 bytes MD5 checksum f58b96ad4cb0bc9e62f2ae5e11e63e90 public class TGWD extends java.lang.invoke.BaseTemplate minor version: 0 major version: 51 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Utf8 TGWD #2 = Class #1 // TGWD #3 = Utf8 java/lang/invoke/BaseTemplate #4 = Class #3 // java/lang/invoke/BaseTemplate #5 = Utf8 guard #6 = Utf8 Ljava/lang/invoke/MethodHandle; #7 = Utf8 trueTarget #8 = Utf8 falseTarget #9 = Utf8 <init> #10 = Utf8 (Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;)V #11 = Utf8 ()V #12 = NameAndType #9:#11 // "<init>":()V #13 = Methodref #4.#12 // java/lang/invoke/BaseTemplate."<init>":()V #14 = NameAndType #5:#6 // guard:Ljava/lang/invoke/MethodHandle; #15 = Fieldref #2.#14 // TGWD.guard:Ljava/lang/invoke/MethodHandle; #16 = NameAndType #7:#6 // trueTarget:Ljava/lang/invoke/MethodHandle; #17 = Fieldref #2.#16 // TGWD.trueTarget:Ljava/lang/invoke/MethodHandle; #18 = NameAndType #8:#6 // falseTarget:Ljava/lang/invoke/MethodHandle; #19 = Fieldref #2.#18 // TGWD.falseTarget:Ljava/lang/invoke/MethodHandle; #20 = Utf8 eval #21 = Utf8 Code { final java.lang.invoke.MethodHandle guard; flags: ACC_FINAL final java.lang.invoke.MethodHandle trueTarget; flags: ACC_FINAL final java.lang.invoke.MethodHandle falseTarget; flags: ACC_FINAL public TGWD(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle); flags: ACC_PUBLIC Code: stack=0, locals=4, args_size=4 0: aload_0 1: invokespecial #13 // Method java/lang/invoke/BaseTemplate."<init>":()V 4: aload_0 5: aload_1 6: putfield #15 // Field guard:Ljava/lang/invoke/MethodHandle; 9: aload_0 10: aload_2 11: putfield #17 // Field trueTarget:Ljava/lang/invoke/MethodHandle; 14: aload_0 15: aload_3 16: putfield #19 // Field falseTarget:Ljava/lang/invoke/MethodHandle; 19: return public void eval(); flags: ACC_PUBLIC Code: stack=0, locals=1, args_size=1 0: return }
字节码报告错误:
org.objectweb.asm.tree.analysis.AnalyzerException: Error at instruction 0: Insufficient maximum stack size. at org.objectweb.asm.tree.analysis.Analyzer.analyze(Unknown Source) at org.objectweb.asm.util.CheckClassAdapter.verify(Unknown Source) at org.objectweb.asm.util.CheckClassAdapter.verify(Unknown Source)
因为构造方法:stack = 0,locals = 4,args_size = 4
正确的堆栈大小为2。
即使设置了ClassWriter(COMPUTE_MAX+COMPUTE_STACK),仍有另一个线程ASM(来自ObjectWeb)无法正确计算MaxStack,这表明如果其他地方的字节码无效,则可能会错误地计算最大堆栈大小。
所以对我来说,问题是:
您不能忽略对的呼叫visitMax。从以下文档中ClassWriter.COMPUTE_MAXS:
visitMax
ClassWriter.COMPUTE_MAXS
如果该标志被设置,则自变量visitMaxs所述的方法MethodVisitor中由返回visitMethod方法将被忽略,并且从签名和每个方法的字节码自动计算。
换句话说,当您指定标志时,您可以传入任何您想要的内容,例如,调用visitMax(-1,-1)以强调您没有提供实际值,但是您仍然必须调用该方法来触发正确值的计算。
visitMax(-1,-1)
顺便说一句,由于您创建的是版本的类文件,因此51您应该指定,COMPUTE_FRAMES因为我怀疑要StackMapTable手动创建属性。请注意,这COMPUTE_FRAMES暗示着COMPUTE_MAXS行为。
51
COMPUTE_FRAMES
StackMapTable
COMPUTE_MAXS