Спецификация виртуальной машины Java (JVMS): ошибка в «5.4.5 переопределении метода»

28 сентября 2009 года я подал следующую ошибку: К сожалению, я до сих пор не получил никакого ответа, и окончательная версия спецификации по-прежнему неверна. Это действительно ошибка? Если нет, почему бы и нет? Если да, что мне делать?

The section that contains the bug is 5.4.5 (Method overriding): http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-5.html#jvms-5.4.5 in combination with the description of the INVOKEVIRTUAL opcode: http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.invokevirtual

Согласно 5.4.5 m1 может переопределить m2 , даже если m1 является закрытым. Это может произойти, если вы создаете файлы .class вручную или комбинируете .class из двух компиляций.

В моем примере у меня есть классы A и B с B extends A . Я скомпилировал эти классы, чтобы A содержал public метод с именем f и B содержит private , также называемый f (сначала объявив оба метода public , компилируя, скопировав A.class в безопасное место, удалив объявление f в A и переход на private в B , затем скомпилируйте B и используя сохраненную версию A.class ).

Когда это выполняется, мой текущий JVM Oracle выводит A (что означает метод f в A ). В соответствии со спецификацией, B должен быть результатом (что означает метод f в B ).

EDIT: На самом деле, B.f должен быть разрешен. Вызов может завершиться неудачно из-за проверки прав доступа для разрешенного метода, если вызывающий абонент не является B . Однако, я считаю, что часть разрешения метода неверна.

Я думаю, что определение в 5.4.5 должно проверять права доступа m1 , а не только m2 .

public class A {
  public void f();
    Code:
       0: getstatic     #2                 //Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #3                 //String A
       5: invokevirtual #4                 //Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: return
}

public class B extends A {
  private void f();
    Code:
       0: getstatic     #2                 //Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #3                 //String B
       5: invokevirtual #4                 //Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: return
}

Благодаря, Карстен

4
nl ja de
JVM не всегда точно соответствует спецификации. Наверное, это еще один пример.
добавлено автор Antimony, источник
Лично я просто попытался подражать JVM Hotspot, где это возможно, поскольку это стандарт дефакто, поэтому его эмуляция приведет к лучшей совместимости со сломанными программами.
добавлено автор Antimony, источник
Это неразрешимая проблема, и это становится еще хуже, если вы хотите рассмотреть отражение или JNI.
добавлено автор Antimony, источник
Кто вызывает метод? Если это класс, отличный от B , он не должен выводить B , а скорее бросать ошибку. Решение метода должно найти Bf() , но затем в соответствии с 5.4.3.3 : "В противном случае, если поиск метода будет успешным, но ссылочный метод недоступен (§5.4.4 ) до D, разрешение метода выбрасывает IllegalAccessError ", тогда как в §5.4.4 четко указано, что методы private доступны только для одного и того же класса. Поскольку вы вызываете метод private любым другим способом, чем команда invokespecial , я предпочел бы ожидать уточнения в более новой спецификации, чем изменение JVM.
добавлено автор Holger, источник
@ C-Otto: Я бы тоже этого не ожидал. Я просто имел в виду сценарий «если взять слово за словом». Теперь, когда спецификация Java 8 JVM содержит ожидаемое разъяснение, что линия мысли устарела.
добавлено автор Holger, источник
Итак, спецификация является «правильной» в том смысле, что следует использовать B.f ? Меня не волнуют другие JVM, потому что я реализовал свою собственную (абстрактную) JVM, но для этого мне нужна спецификация без ошибок.
добавлено автор C-Otto, источник
Ну, я работаю над инструментом, который доказывает (не) завершение. Выполнение неприятных трюков, таких как игнорирование спецификации, не очень хорошая идея :)
добавлено автор C-Otto, источник
Спасибо. Я не заметил, что за последние четыре года я преследовал свою кандидатуру.
добавлено автор C-Otto, источник
Хорошо, ты можешь быть прав. Тем не менее, я ожидаю, что разрешение метода не найдет B.f в первую очередь.
добавлено автор C-Otto, источник

1 ответы

Наконец, ваша проблема была решена. текущая версия спецификации Java 8 JVM содержит необходимые пояснения:

5.4.5 Overriding

An instance method mC declared in class C overrides another instance method mA declared in class A iff either mC is the same as mA, or all of the following are true:

  • C is a subclass of A.
  • mC has the same name and descriptor as mA.
  • mC is not marked ACC_PRIVATE.
  • One of the following is true:
    • mA is marked ACC_PUBLIC; or is marked ACC_PROTECTED; or is marked neither ACC_PUBLIC nor ACC_PROTECTED nor ACC_PRIVATE and A belongs to the same run-time package as C.
    • mC overrides a method m' (m' distinct from mC and mA) such that m' overrides mA.

В §4.10.1.5 есть еще одно дополнение: «Проверка типов абстрактных и родных методов»:

частные методы и статические методы ортогональны для отправки динамических методов,   поэтому они никогда не переопределяют другие методы (§5.4.5).

Менее пяти лет для исправления, это быстро по сравнению с некоторыми другими проблемами ...

1
добавлено