Почему Java 7 Bytecode Verifier жалуется на этот стековый фрейм?

У меня есть метод, который я изменил в классе Java 7 (основная версия 51). С помощью javap Я посмотрел на байт-код и карту стека кадров. Все выглядит хорошо

public int addOne(int);
flags: ACC_PUBLIC
Code:
  stack=2, locals=2, args_size=2
     0: iload_1
     1: iconst_0
     2: invokestatic  #50                 // Method isSomething:(I)Z
     5: ifeq          12
     8: iconst_0
     9: goto          13
    12: iconst_1
    13: iadd
    14: ireturn

StackMapTable: number_of_entries = 2
     frame_type = 255 /* full_frame */
    offset_delta = 12
    locals = [ class test/Target, int ]
    stack = [ int ]
     frame_type = 255 /* full_frame */
    offset_delta = 0
    locals = [ class test/Target, int ]
    stack = [ int, int ]

Этот верификатор выдает это исключение:

java.lang.VerifyError: Expecting a stackmap frame at branch target 12
Exception Details:
  Location:
    test/Target.addOne(I)I @5: ifeq
  Reason:
    Expected stackmap frame at this location.
  Bytecode:
    0000000: 1b03 b800 3299 0007 03a7 0004 0460 ac

Что сводит меня с ума, так это то, что компилятор сгенерировал тот же код из исходного кода Java, и он выглядит так:

  public int addOne(int);
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=2
         0: iload_1
         1: iconst_0
         2: invokestatic  #16                 // Method isSomething:(I)Z
         5: ifeq          12
         8: iconst_0
         9: goto          13
        12: iconst_1
        13: iadd
        14: ireturn

      StackMapTable: number_of_entries = 2
           frame_type = 76 /* same_locals_1_stack_item */
          stack = [ int ]
           frame_type = 255 /* full_frame */
          offset_delta = 0
          locals = [ class test/Target, int ]
          stack = [ int, int ]

Обратите внимание, что единственная разница в карте стековых кадров состоит в том, что синтетическая карта имеет все полные кадры, но это не должно вызывать различий. Кто-нибудь знает, почему верфьеру может не понравиться моя синтетическая карта?

3 ответа

Решение

Я не могу воспроизвести эту проблему. Возможно, вы создаете стековые фреймы таким образом, что javap все равно будет читать, но на самом деле это не так? Потому что мой отредактированный класс имеет тот же вывод javap, но он проверяет просто отлично. Если вы опубликуете фактический файл класса, я смогу увидеть, смогу ли я найти проблему, потому что я не думаю, что есть что-то еще, что я могу сделать только с выводом JavaScript.

Источник:

public class ArrayTest {
    public int addOne(int x){
        return x + (isSomething(0) ? 0 : 1);
    }

    public static boolean isSomething(int z) {return true;}
}

Javap-вывод метода в оригинальном классе

  public int addOne(int);
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=2
         0: iload_1
         1: iconst_0
         2: invokestatic  #2                  // Method isSomething:(I)Z
         5: ifeq          12
         8: iconst_0
         9: goto          13
        12: iconst_1
        13: iadd
        14: ireturn
      StackMapTable: number_of_entries = 2
           frame_type = 76 /* same_locals_1_stack_item */
          stack = [ int ]
           frame_type = 255 /* full_frame */
          offset_delta = 0
          locals = [ class ArrayTest, int ]
          stack = [ int, int ]

Вывод Javap метода в редактируемый класс

  public int addOne(int);
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=2
         0: iload_1
         1: iconst_0
         2: invokestatic  #15                 // Method ArrayTest.isSomething:(
)Z
         5: ifeq          12
         8: iconst_0
         9: goto          13
        12: iconst_1
        13: iadd
        14: ireturn
      StackMapTable: number_of_entries = 2
           frame_type = 255 /* full_frame */
          offset_delta = 12
          locals = [ class ArrayTest2, int ]
          stack = [ int ]
           frame_type = 255 /* full_frame */
          offset_delta = 0
          locals = [ class ArrayTest2, int ]
          stack = [ int, int ]

Как видите, у меня тот же вывод Javap, но мой класс работает просто отлично.

Ответ в том, что javassist отстой, и я глубоко сожалею об этом.

StackMapTable атрибут получен из вызова CodeAttribute.getAttribute(String tag), Даже при том, как вы получаете к нему доступ, нет никакого API, чтобы добавить его обратно, если это не тип StackMapTable, Единственный API, который принимает ваниль AttributeInfo в качестве параметра на MethodInfo учебный класс.

В тех случаях, когда метод не нуждался (или не имел) фрейма стека, вы получаете null, Если вы создаете AttributeInfo структура для новой карты стека кадров, вы не должны добавлять MethodInfo (где addAttribute API есть), но CodeAttribute где это принадлежит.

Это то, что я делал:

MethodInfo mi ...
AttributeInfo attr ...

mi.addAttribute(attr);

Вот что мне нужно было сделать:

CodeAttribute ca ...
ca.getAttributes().add(attr);

(Конечно, ca.getAttributes()возвращает нетипизированный List потому что мы все скучаем по 2004 году.)

Я копался в методе, который позволяет добавить тип StackFramMap к CodeAttribute и разобрался с этой работой из-за отсутствия универсального API.

Результатом использования верхней конструкции является то, что javap создаст впечатление, что у вас есть надлежащий StackMapTable. Вы делаете, но он прикреплен к неправильному объекту, и вы не можете увидеть это из javap выход.

Я не использовал ASM для своего проекта, потому что навязчивое использование шаблона посетителя показалось мне раздражающим. Теперь я признаю, что это было плохое решение. Поскольку у javassist нет обновлений с 2012 года, мне интересно, если проект мертв. У меня, конечно, есть куча ревизий, которые я бы отправил. Это бардак.

РЕДАКТИРОВАТЬ О вау. Внутренний код Javassist предполагает, что любой StackMapTable приписать это свой внутренний StackMapTable типа (потому что, как еще вы бы добавить StackMapTable атрибуты). Я думаю, я мог бы создать свой собственный StackMapTable Например, за исключением того, что конструкторы SFM защищены пакетами без видимой причины. Становится все хуже и хуже...

http://www.csg.ci.i.u-tokyo.ac.jp/~chiba/javassist/html/javassist/bytecode/MethodInfo.html

Теперь вы можете позвонить:

mi.rebuildStackMapIf6(pool, cf);

Это восстановит карту стека, и не будет использовать или нуждаться -XX:-UseSplitVerifier

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