ASM 库计算的堆栈大小错误

Wrong Stack Size calculated by ASM library

我使用 ASM 库生成字节码,'Max stack size' 方法留待自动计算。在运行时,我发现这个值(最大堆栈大小)不正确。

我的源代码是:

    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)

因为构造方法: 堆栈=0,本地=4,args_size=4

正确的堆栈大小是 2。

还有一个线程ASM (from ObjectWeb) not calculating MaxStack correctly even though ClassWriter( COMPUTE_MAX + COMPUTE_STACK ) is set,表示如果其他地方的字节码无效,可能会错误计算最大堆栈大小。

所以对我来说,问题是:

您不能省略对 visitMax 的调用。来自 documentation of ClassWriter.COMPUTE_MAXS:

If this flag is set, then the arguments of the visitMaxs method of the MethodVisitor returned by the visitMethod method will be ignored, and computed automatically from the signature and the bytecode of each method.

换句话说,当你指定标志时,你可以传入任何你想要的,例如调用 visitMax(-1,-1) 以强调您没有提供实际值,但您仍然必须调用该方法来触发正确值的计算。

顺便说一下,由于您创建了 class 版本 51 的文件,因此您应该指定 COMPUTE_FRAMES as I doubt that you want to create StackMapTable attributes manually. Note that COMPUTE_FRAMES implies COMPUTE_MAXS 行为。