Какова цель флага доступа ACC_SUPER в файлах классов Java? - PullRequest
27 голосов
/ 21 января 2012

Инструкция invokespecial JVM используется для вызова методов инициализации (<init>) при создании новых объектов.Описание инструкции предполагает (но не уточняет), что решение о том, вызывать ли конструктор суперкласса или конструктор текущего класса, зависит от состояния флага ACC_SUPER, установленного в файле class.

Из спецификации JVM от Sun:

Далее для вызова выбирается разрешенный метод, если не выполняются все следующие условия:

  • ACC_SUPERфлаг (см. Таблицу 4.1, «Доступ к классу и модификаторы свойств») устанавливается для текущего класса.

- Источник (invokespecial определение кода операции)

Установка флага ACC_SUPER указывает, какую из двух альтернативных семантик для его invokespecial инструкции должна выразить виртуальная машина Java;флаг ACC_SUPER существует для обратной совместимости для кода, скомпилированного более старыми компиляторами Sun для языка программирования Java.Все новые реализации виртуальной машины Java должны реализовывать семантику для invokespecial, документированную в этой спецификации.Все новые компиляторы в наборе команд виртуальной машины Java должны установить флаг ACC_SUPER.Старые компиляторы Sun генерировали флаги ClassFile с отключенным ACC_SUPER.Более ранние реализации Sun виртуальных машин Java игнорируют флаг, если он установлен.

- Источник (ClassFile формат)

В определении говорится, что флагдля обратной совместимости со старыми компиляторами.Однако это продолжает противоречить Sun's older Java virtual machine implementations ignore the flag if it is set.

. Флаг все еще используется с кодом операции invokespecial?Судя по тому, что я могу сказать, это кажется бесполезным, и я не могу найти ресурс, чтобы предположить, что он когда-либо делал.

Спасибо.

1 Ответ

36 голосов
/ 21 января 2012

ACC_SUPER был введен для исправления проблемы с вызовом супер методов.Флаг ACC_SUPER помечает класс как скомпилированный для измененной семантики инструкции кода операции 183.Его назначение аналогично назначению номера версии файла класса, поскольку позволяет JVM определять, был ли класс скомпилирован для более старой или более новой семантики этой инструкции.Java 1.0.2 не установила и проигнорировала ACC_SUPER, в то время как Java 1.1 и более поздние всегда устанавливают ACC_SUPER.

До Java 1.1 инструкция байтового кода с кодом операции 183, который теперь называется invokespecial, называлась invokenonvirtual и имелачастично другая спецификация.Это использовалось всякий раз, когда методы экземпляра должны были быть вызваны без поиска виртуального метода.Это имело место для частных методов, инициализаторов экземпляров (конструкторов) и для реализации вызовов методов на super.Но в последнем случае возникли проблемы с развивающимися библиотеками классов.

Ссылка на метод в байтовом коде (CONSTANT_Methodref_info) определяет не только имя и аргумент и возвращаемые типы метода, но также и класс, к которому он принадлежит,Код операции 183 получает такой ссылочный параметр метода и предназначен для непосредственного вызова ссылочного метода из указанного класса без дальнейших поисков.В случае вызовов на super ответственность за разрешение ближайшего суперкласса, реализующего этот метод, и генерацию ссылки на него в байт-коде лежит на компиляторах.

Начиная с Java 1.1 он был изменен, чтобы по существу игнорироватькласс, указанный в CONSTANT_Methodref_info, и вместо этого выполнить поиск ближайшего супер-метода с заданным именем метода и сигнатурой в JVM.Обычно это делается сейчас, когда класс загружается или непосредственно перед выполнением инструкции или когда JIT компилируется в первый раз.

Вот пример, почему это изменение было необходимым.В Java 1.0.2 классы AWT Container и Component были определены следующим образом:

class Component
{
    public void paint( Graphics g ) {}
}

class Container extends Component
{
    // inherits paint from Component but doesn't override it
}

В Java 1.1 класс Conatiner был изменен, чтобы иметь собственную реализацию paint:

class Container extends Component
{
    public void paint( Graphics g ) {/*...*/}
}

Теперь, когда у вас был прямой или косвенный подкласс Container, который сделал вызов на super.paint(g) и скомпилировал его для 1.0.2, он сгенерировал инструкцию invokenonvirtual для Component.paint, так как это был первый родительский объект, у которого был этот метод.Но если бы вы использовали этот скомпилированный класс на JVM, которая также имела Container.paint, он все равно вызвал бы Component.paint, что не соответствует ожиданиям.

С другой стороны, когда вы скомпилировали класс для 1.1и выполнил его на JVM 1.0.2, он выкинул бы AbstractMethodError или, что более вероятно, для виртуальных машин той эпохи просто рухнулЧтобы избежать сбоя, вы должны были написать ((Component)super).paint(g) и скомпилировать его с помощью компилятора 1.1, чтобы получить желаемое поведение в любой виртуальной машине.Это установит ACC_SUPER, но все равно сгенерирует инструкцию для вызова Component.paint.Виртуальная машина 1.0.2 игнорировала бы ACC_SUPER и пошла бы прямо, чтобы вызвать Component.paint, что хорошо, в то время как виртуальная машина 1.1 нашла бы установленный ACC_SUPER и, таким образом, сама выполнила поиск, который заставил бы ее вызвать Container.paint, даже если ссылка на метод байтового кода была Component.paint.

Подробнее об этом можно прочитать в этом старом посте в блоге ikvm.net .

...