错误的堆栈大小由ASM库计算

时间:2015-03-11 14:02:49

标签: java bytecode java-bytecode-asm bytecode-manipulation

我使用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.

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

所以对我来说,问题是:

  • 生成的文件中的无效字节码在哪里?
  • 我仍然希望避免调用visitMax()。因为有许多生成的字节码方法,并且这些值的手动计算并不是一件容易的事。

1 个答案:

答案 0 :(得分:9)

您无法忽略对visitMax的调用。来自documentation of ClassWriter.COMPUTE_MAXS

  

如果设置了此标志,那么visitMaxs方法返回的MethodVisitor visitMethod方法的参数将被忽略,并自动从签名和字节码中计算出来。每种方法。

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

顺便说一句,由于您创建了版本为51的类文件,因此我应该指定COMPUTE_FRAMES,因为我怀疑您是否要手动创建StackMapTable属性。请注意,COMPUTE_FRAMES隐含COMPUTE_MAXS行为。