Неправильный размер стека, рассчитанный библиотекой ASM

Я генерирую байт-коды, используя библиотеку 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 (из ObjectWeb), который неправильно вычисляет MaxStack, даже если установлен ClassWriter( COMPUTE_MAX + COMPUTE_STACK), что указывает на то, что максимальный размер стека может быть неверно рассчитан, если байт-код в другом месте недопустим.

Так что для меня вопросы:

  • Где аннулировать байт-коды в созданном файле?
  • Я все еще предпочитаю избегать вызова visitMax(). Поскольку существует ряд сгенерированных методов байт-кода, и вычисление этих значений вручную не является легкой задачей.

1 ответ

Решение

Вы не можете пропустить звонок visitMax, Из документации ClassWriter.COMPUTE_MAXS:

Если этот флаг установлен, то аргументы метода visitMaxs метода MethodVisitor, возвращаемого методом visitMethod, будут игнорироваться и вычисляться автоматически из сигнатуры и байт-кода каждого метода.

Другими словами, когда вы указываете флаг, вы можете передать все, что захотите, например, вызов visitMax(-1,-1) чтобы подчеркнуть, что вы не предоставляете фактические значения, но вам все равно нужно вызвать метод, чтобы инициировать вычисление правильных значений.

Кстати, так как вы создаете файл класса версии 51 Вы должны указать COMPUTE_FRAMES как я сомневаюсь, что вы хотите создать StackMapTable атрибуты вручную. Обратите внимание, что COMPUTE_FRAMES подразумевает COMPUTE_MAXS поведение.

Другие вопросы по тегам