Подтвердить что ты не робот

Какова цель флажка доступа ACC_SUPER к файлам Java Class?

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

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

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

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

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

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

- Источник (ClassFile format)

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

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

Спасибо.

4b9b3361

Ответ 1

ACC_SUPER был введен для исправления проблемы с вызовом супер-методов. Флаг ACC_SUPER отмечает класс, скомпилированный для измененной семантики команды opcode 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 VM игнорирует ACC_SUPER и сразу же запускает Component.paint, что отлично, в то время как 1.1 VM найдет набор ACC_SUPER и, таким образом, сделает сам поиск, который заставит его вызывать Container.paint, хотя ссылка на метод байтового кода была Component.paint.

Об этом можно узнать в этот старый пост в блоге ikvm.net.