Invokespecial Verify Error: тип не может быть назначен - PullRequest
5 голосов
/ 11 марта 2019

Я изменил строку 15 байт-кода ниже и изменил ее форму invokevirtual на invokespecial (JAVA 8). К сожалению, я получаю ошибку проверки (неверный тип в стеке операндов)

Я знаю, что значение стека операндов должно быть подклассом класса, указанного в objectref, но в этом случае # 18 - это Type, а не Type $ ClassType, как предполагает ошибка. Или, говоря по-другому, не должен ли в стеке frameframe в строке 15 иметь тип, а не тип $ ClassType в стеке [0]? Что мне не хватает?

edit: стекикарты одинаковы до и после изменения. (в случае, если используемые мной ASM COMPUTE FRAMES изменили бы их)

Exception Details:
  Location:
    com/sun/tools/javac/code/Type$ClassType.toString()Ljava/lang/String; @15: invokespecial
  Reason:
    Type 'com/sun/tools/javac/code/Type' (current frame, stack[0]) is not assignable to 'com/sun/tools/javac/code/Type$ClassType'
  Current Frame:
    bci: @15
    flags: { }
    locals: { 'com/sun/tools/javac/code/Type$ClassType', 'java/lang/StringBuilder' }
    stack: { 'com/sun/tools/javac/code/Type', 'com/sun/tools/javac/code/TypeTag' }
  ...     
  Stackmap Table:
append_frame(@71,Object[#108])
same_frame(@85)
same_frame(@121)

Вот код. Тип $ ClassType является прямым подклассом Type, а com / sun / tools / javac / code / Type $ ClassType является текущим классом, который позволяет нам вызывать суперкласс (например, Type) с invokespecial

    public class com.sun.tools.javac.code.Type$ClassType extends com.sun.tools.javac.code.Type implements
 javax.lang.model.type.DeclaredType
    ....
    public java.lang.String toString();
        descriptor: ()Ljava/lang/String;
        flags: ACC_PUBLIC
        Code:
          stack=4, locals=2, args_size=1
             0: new           #108                // class java/lang/StringBuilder
             3: dup
             4: invokespecial #17                 // Method java/lang/StringBuilder."<init>":()V
             7: astore_1
             8: aload_0
             9: invokevirtual #13                 // Method com/sun/tools/javac/code/Type$ClassType.getEnclosingType:()Lcom/sun/tools/javac/code/Type;
            12: getstatic     #10                 // Field com/sun/tools/javac/code/TypeTag.CLASS:Lcom/sun/tools/javac/code/TypeTag;
            15: invokespecial #18                 // Method com/sun/tools/javac/code/Type.hasTag:(Lcom/sun/tools/javac/code/TypeTag;)Z
            18: ifeq          71
            .....
            StackMapTable: number_of_entries = 3
              frame_type = 252 /* append */
                offset_delta = 71
                locals = [ class java/lang/StringBuilder ]
              frame_type = 13 /* same */
              frame_type = 35 /* same */

Ответы [ 2 ]

4 голосов
/ 11 марта 2019

invokespecial используется для реализации любой из трех вещей

  1. Вызов конструктора
  2. Вызов метода private
  3. Выполнение вызова super. …

Хотя 1. здесь не применяется (поскольку имя целевого метода не <init>), в любом из других случаев требуется, чтобы тип получателя был текущего класса или его подкласса.Таким образом, даже когда класс объявления метода равен Type, ожидается, что фактический тип получателя будет назначаться текущему классу, Type$ClassType.

Ближайший эквивалент тому, что вы создали с помощью вашего изменения,super вызов, хотя в исходном коде Java вызов метода через super приводит к тому, что ссылка на приемник будет такой же, как this, которая присваивается текущему классу по своей природе.

В байт-кодена уровне, правила менее ограничительны, тем не менее, вызов метода, позволяющий обходить объявления методов в вашем текущем классе или его подклассах, не разрешается вызывать для ссылки на тип, которая может указывать на экземпляр совершенно не связанной иерархии подклассов, т.е.Type не является Type$ClassType.

Соответствующее правило JVMS было процитировано в ответе апангина уже.

3 голосов
/ 11 марта 2019

Вы пытаетесь выполнить invokespecial в экземпляре Type (возвращается invokevirtual @ 9), в то время как верификатор ожидает ссылку на текущий класс, то есть Type$ClassType.

См. JVMS §4.10.1.9 :

Можно корректно заменить типы, соответствующие текущему классу и типы аргументов, заданные в дескрипторе входящего стека операндов с тип возвращаемого значения в дескрипторе, возвращающий состояние исходящего типа.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...