Типы в байт-коде

Я работал в течение некоторого времени над (Java) Байт-код, однако, мне никогда не приходило в голову спрашивать, почему некоторые инструкции вводятся? Я понимаю, что в операции ADD, мы должны различать целочисленное дополнение и дополнение FP (вот почему, у нас есть IADD и FADD). Однако, почему мы должны различать ISTORE и FSTORE? Они оба включают ту же самую операцию, которая перемещает 32 бита от стека до положения локальной переменной?

Единственный ответ, о котором я могу думать, для безопасности типов, для предотвращения этого: (ILOAD, ILOAD, FADD). Однако я полагаю, что безопасность типов уже осуществляется на уровне языка Java. Хорошо, формат файла Класса не непосредственно вместе с Java, также - действительно ли это - способ осуществить безопасность типов для языков, которые не поддерживают его? Какая-либо мысль?Спасибо.

Править: следовать ответу Reedy. Я записал эту минимальную программу:

public static void main(String args[])
{
    int x = 1;
}

который скомпилированный в:

iconst_1
istore_1
return

с помощью редактора байт-кода я изменил вторую инструкцию:

iconst_1
fstore_1
return

и это возвратило java.lang. VerifyError: Ожидание найти плавание на стеке.

Интересно, если на стеке нет никакой информации о типе, просто биты, как сделал инструкцию FSTORE, знали, что это имело дело с интервалом и не плаванием?

Примечание: Я не мог найти лучший заголовок для этого вопроса. Не стесняйтесь улучшать его.

13
задан H-H 15 April 2010 в 07:39
поделиться

3 ответа

Эти инструкции набраны для обеспечения типовой безопасности программы. При загрузке класса виртуальная машина выполняет проверку байт-кодов, чтобы гарантировать, что, например, число с плавающей запятой не передается в качестве аргумента методу, ожидающему целое число. Эта статическая проверка требует, чтобы проверяющий мог определять типы и количество значений в стеке для любого заданного пути выполнения. Инструкции загрузки и сохранения нуждаются в теге типа, потому что локальные переменные в кадрах стека не типизированы (т.е. вы можете сохранить локальную переменную, а затем сохранить ее в той же позиции). Теги типа в инструкциях позволяют проверяющему узнать, какой тип значения хранится в каждой локальной переменной.

Верификатор просматривает каждый код операции в методе и отслеживает, какие типы будут в стеке и в локальных переменных после выполнения каждого из них. Вы правы, что это еще одна форма проверки типов, которая дублирует некоторые проверки, выполняемые компилятором java. Этап проверки предотвращает загрузку любого кода, который может привести к выполнению виртуальной машиной недопустимой инструкции, и обеспечивает свойства безопасности платформы Java без значительных потерь времени выполнения, связанных с проверкой типов перед каждой операцией. Проверка типа во время выполнения для каждого кода операции будет снижать производительность при каждом выполнении метода, но статическая проверка выполняется только один раз при загрузке класса.

Случай 1:

Instruction             Verification    Stack Types            Local Variable Types 
----------------------- --------------- ---------------------- ----------------------- 
<method entry>          OK              []                     1: none
iconst_1                OK              [int]                  1: none
istore_1                OK              []                     1: int
return                  OK              []                     1: int

Случай 2:

Instruction             Verification    Stack Types            Local Variable Types 
----------------------- --------------- ---------------------- ----------------------- 
<method entry>          OK              []                     1: none
iconst_1                OK              [int]                  1: none
fstore_1                Error: Expecting to find float on stack

Ошибка выдается, потому что проверяющий знает, что fstore_1 ожидает в стеке число с плавающей запятой, но результат выполнения предыдущих инструкций оставляет в стеке int.

Эта проверка выполняется без выполнения кодов операций, а выполняется путем просмотра типов инструкций, точно так же, как компилятор Java выдает ошибку, когда вы пишете (Integer) «abcd» . Компилятору не нужно запускать программу, чтобы знать, что «abcd» является строкой и не может быть преобразован в Integer .

18
ответ дан 1 December 2019 в 21:11
поделиться

Чтобы ответить на ваш первый вопрос с моим лучшим предположением: эти байт-коды разные, потому что они могут требовать разных реализаций. Например, конкретная архитектура может хранить целочисленные операнды в основном стеке, но операнды с плавающей запятой в аппаратных регистрах.

Чтобы ответить на ваш второй вопрос, VerifyError выдается при загрузке класса, а не при его выполнении. Процесс проверки описан здесь ; обратите внимание на пропуск №3.

3
ответ дан 1 December 2019 в 21:11
поделиться

Джефф Риди объяснил в своем ответе, что делает верификатор при загрузке класса. Я просто хочу добавить, что вы можете отключить верификатор с помощью параметра JVM. Это не рекомендуется!

Для вашего примера программы (с iconst и fstore) результатом работы с отключенной проверкой является ошибка виртуальной машины, которая останавливает JVM со следующим сообщением:

=============== DEBUG MESSAGE: illegal bytecode sequence - method not verified ================

#
# An unexpected error has been detected by HotSpot Virtual Machine:
#
#  EXCEPTION_PRIV_INSTRUCTION (0xc0000096) at pc=0x00a82571, pid=2496, tid=3408
#
# Java VM: Java HotSpot(TM) Client VM (1.5.0_15-b04 mixed mode, sharing)
# Problematic frame:
# j  BytecodeMismatch.main([Ljava/lang/String;)V+0
#
...
4
ответ дан 1 December 2019 в 21:11
поделиться
Другие вопросы по тегам:

Похожие вопросы: