Spec-Zone.ru › Java Virtual Machine Specification 7

Глава 4. Формат файла class

Оглавление

4.1. Структура ClassFile
4.2. Внутренняя форма имён
4.2.1. Бинарные имена классов и интерфейсов
4.2.2. Неопределённые имена
4.3. Дескрипторы и подписи
4.3.1. Нотация грамматики
4.3.2. Дескрипторы полей
4.3.3. Дескрипторы методов
4.3.4. Подписи
4.4. Пул констант
4.4.1. Структура CONSTANT_Class_info
4.4.2. Структуры CONSTANT_Fieldref_info, CONSTANT_Methodref_info и CONSTANT_InterfaceMethodref_info
4.4.3. Структура CONSTANT_String_info
4.4.4. Структуры CONSTANT_Integer_info и CONSTANT_Float_info
4.4.5. Структуры CONSTANT_Long_info и CONSTANT_Double_info
4.4.6. Структура CONSTANT_NameAndType_info
4.4.7. Структура CONSTANT_Utf8_info
4.4.8. Структура CONSTANT_MethodHandle_info
4.4.9. Структура CONSTANT_MethodType_info
4.4.10. Структура CONSTANT_InvokeDynamic_info
4.5. Поля
4.6. Методы
4.7. Атрибуты
4.7.1. Определение и именование новых атрибутов
4.7.2. Атрибут ConstantValue
4.7.3. Атрибут Code
4.7.4. Атрибут StackMapTable
4.7.5. Атрибут Exceptions
4.7.6. Атрибут InnerClasses
4.7.7. Атрибут EnclosingMethod
4.7.8. Атрибут Synthetic
4.7.9. Атрибут Signature
4.7.10. Атрибут SourceFile
4.7.11. Атрибут SourceDebugExtension
4.7.12. Атрибут LineNumberTable
4.7.13. Атрибут LocalVariableTable
4.7.14. Атрибут LocalVariableTypeTable
4.7.15. Атрибут Deprecated
4.7.16. Атрибут RuntimeVisibleAnnotations
4.7.16.1. Структура element_value
4.7.17. Атрибут RuntimeInvisibleAnnotations
4.7.18. Атрибут RuntimeVisibleParameterAnnotations
4.7.19. Атрибут RuntimeInvisibleParameterAnnotations
4.7.20. Атрибут AnnotationDefault
4.7.21. Атрибут BootstrapMethods
4.8. Проверка формата
4.9. Ограничения на код виртуальной машины Java
4.9.1. Статические ограничения
4.9.2. Структурные ограничения
4.10. Верификация файлов class
4.10.1. Верификация с помощью проверки типов
4.10.1.1. Доступы к артефактам виртуальной машины Java
4.10.1.2. Система проверки типов
4.10.1.3. Представление инструкций
4.10.1.4. Представление фрейма карты стека
4.10.1.5. Проверка типов абстрактных и нативных методов
4.10.1.6. Проверка типов методов с кодом
4.10.1.7. Проверка типов инструкций загрузки и сохранения
4.10.1.8. Проверка типов членов protected
4.10.1.9. Проверка типов инструкций
END_OF_DOCUMENT_MARKER
aaload
aastore
aconst_null
aload
aload_
anewarray
areturn
arraylength
astore
astore_
athrow
baload
bastore
bipush
caload
castore
checkcast
d2f
d2i
d2l
dadd
daload
dastore
dcmp<op>
dconst_
ddiv
dload
dload_
dmul
dneg
drem
dreturn
dstore
dstore_
dsub
dup
dup_x1
dup_x2
dup2
dup2_x1
dup2_x2
f2d
f2i
f2l
fadd
faload
fastore
fcmp<op>
fconst_
fdiv
fload
fload_
fmul
fneg
frem
freturn
fstore
fstore_
fsub
getfield
getstatic
goto
goto_w
i2b
i2c
i2d
i2f
i2l
i2s
iadd
iaload
iand
iastore
if_acmp<cond>
if_icmp<cond>
if<cond>
ifnonnull
ifnull
iinc
iload
iload_
imul
ineg
instanceof
invokedynamic
invokeinterface
invokespecial
invokestatic
invokevirtual
ior
irem
ireturn
ishl
ishr
istore
istore_
isub
iushr
ixor
l2d
l2f
l2i
ladd
laload
land
lastore
lcmp
lconst_
ldc
ldc_w
ldc2_w
ldiv
lload
lload_
lmul
lneg
lookupswitch
lor
lrem
lreturn
lshl
lshr
lstore
lstore_
lsub
lushr
lxor
monitorenter
monitorexit
multianewarray
new
newarray
nop
pop
pop2
putfield
putstatic
return
saload
sastore
sipush
swap
tableswitch
wide
4.10.2. Проверка с помощью вывода типов
4.10.2.1. Процесс проверки с помощью вывода типов
4.10.2.2. Верификатор байткода
4.10.2.3. Значения типов long и double
4.10.2.4. Методы инициализации экземпляров и вновь созданные объекты
4.10.2.5. Исключения и finally
4.11. Ограничения виртуальной машины Java

В данном разделе описывается формат файла виртуальной машины Java class. Каждый файл class содержит определение одного класса или интерфейса. Хотя классу или интерфейсу не обязательно иметь внешнее представление, буквально содержащееся в файле (например, потому что класс сгенерирован загрузчиком классов), мы будем условно ссылаться на любое допустимое представление класса или интерфейса как на находящееся в формате файла class.

Файл class состоит из потока байтов по 8 бит. Все 16-битные, 32-битные и 64-битные величины формируются путём считывания двух, четырёх и восьми последовательных байтов по 8 бит соответственно. Многобайтовые данные всегда хранятся в порядке big-endian, где старшие байты идут первыми. В платформе Java SE этот формат поддерживается интерфейсами java.io.DataInput и java.io.DataOutput и классами, такими как java.io.DataInputStream и java.io.DataOutputStream.

В данном разделе определены собственные типы данных, представляющие данные файла class: типы u1, u2 и u4 соответственно представляют беззнаковое целое число из одного, двух или четырёх байтов. В платформе Java SE эти типы могут быть считаны методами, такими как readUnsignedByte, readUnsignedShort и readInt интерфейса java.io.DataInput.

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

Таблицы, состоящие из нуля или более элементов переменной длины, используются в нескольких структурах файлов class. Хотя для обозначения элементов таблицы мы используем синтаксис массива C-подобного вида, тот факт, что таблицы являются потоком структур переменной длины, означает, что невозможно напрямую преобразовать индекс таблицы в байтовый смещение в самой таблице.

Если мы ссылаемся на структуру данных как на массив, она состоит из нуля или более смежных элементов фиксированной длины и может быть индексирована как массив.

Ссылка на символ ASCII в этом разделе должна интерпретироваться как соответствующее кодовое значение Unicode для символа ASCII.

END_OF_DOCUMENT_MARKER

4.1. Структура файла ClassFile

Файл ClassFile состоит из одной структуры ClassFile:

ClassFile {
    u4             magic;
    u2             minor_version;
    u2             major_version;
    u2             constant_pool_count;
    cp_info        constant_pool[constant_pool_count-1];
    u2             access_flags;
    u2             this_class;
    u2             super_class;
    u2             interfaces_count;
    u2             interfaces[interfaces_count];
    u2             fields_count;
    field_info     fields[fields_count];
    u2             methods_count;
    method_info    methods[methods_count];
    u2             attributes_count;
    attribute_info attributes[attributes_count];
}

Элементы структуры ClassFile следующие:

magic

Элемент magic содержит магическое число, идентифицирующее формат файла ClassFile; его значение равно 0xCAFEBABE.

minor_version, major_version

Значения элементов minor_version и major_version — это номера версии, малой и большой, соответственно, файла ClassFile. Вместе они определяют версию формата файла ClassFile. Если файл ClassFile имеет номер малой версии m и номер большой версии M, то версия формата файла ClassFile обозначается как M.m. Таким образом, версии формата файла ClassFile могут быть упорядочены лексикографически, например, 1.5 < 2.0 < 2.1.

Реализация Java Virtual Machine может поддерживать формат файла ClassFile версии v тогда и только тогда, когда v лежит в некотором непрерывном диапазоне Mi.0 ≤ v ≤ Mj.m. Границы диапазона определяются уровнем выпуска Java SE платформы, которому соответствует реализация Java Virtual Machine.

Реализация Java Virtual Machine Oracle в выпуске JDK 1.0.2 поддерживает версии формата файла ClassFile от 45.0 до 45.3 включительно. Выпуски JDK 1.1.* поддерживают версии формата файла ClassFile в диапазоне от 45.0 до 45.65535 включительно. Для k ≥ 2 выпуск JDK 1.k поддерживает версии формата файла ClassFile в диапазоне от 45.0 до 44+k.0 включительно.

constant_pool_count

Значение элемента constant_pool_count равно количеству записей в таблице constant_pool плюс единица. Индекс в таблице constant_pool считается допустимым, если он больше нуля и меньше constant_pool_count, за исключением констант типа long и double, указанных в §4.4.5.

constant_pool[]

constant_pool — это таблица структур (§4.4), представляющих различные строковые константы, имена классов и интерфейсов, имена полей и другие константы, на которые ссылается структура ClassFile и её подструктуры. Формат каждой записи таблицы constant_pool определяется её первым байтом «тега».

Таблица constant_pool индексируется с 1 до constant_pool_count-1.

access_flags

Значение элемента access_flags представляет собой маску флагов, используемых для обозначения разрешений доступа и свойств этого класса или интерфейса. Интерпретация каждого флага, при его установке, приведена в таблице 4.1.

Таблица 4.1. Модификаторы доступа к классам и свойствам

Имя флага Значение Интерпретация
ACC_PUBLIC 0x0001 Объявлен public; может быть доступен извне пакета.
ACC_FINAL 0x0010 Объявлен final; не допускается наследование.
ACC_SUPER 0x0020 Обрабатывает методы суперкласса особым образом при вызове инструкцией invokespecial.
ACC_INTERFACE 0x0200 Представляет собой интерфейс, а не класс.
ACC_ABSTRACT 0x0400 Объявлен abstract; не должен быть инстанцирован.
ACC_SYNTHETIC 0x1000 Объявлен синтетическим; отсутствует в исходном коде.
ACC_ANNOTATION 0x2000 Объявлен как тип аннотации.
ACC_ENUM 0x4000 Объявлен как тип enum.

Класс может быть помечен флагом ACC_SYNTHETIC, чтобы указать, что он был сгенерирован компилятором и не отображается в исходном коде.

Флаг ACC_ENUM указывает, что этот класс или его суперкласс объявлен как перечислимый тип.

Интерфейс определяется установкой флага ACC_INTERFACE. Если флаг ACC_INTERFACE не установлен, этот файл ClassFile определяет класс, а не интерфейс.

Если установлен флаг ACC_INTERFACE этого файла ClassFile, должен быть установлен и флаг ACC_ABSTRACT (JLS §9.1.1.1). Такой файл ClassFile не должен иметь установленных флагов ACC_FINAL, ACC_SUPER или ACC_ENUM.

Тип аннотации должен иметь установленный флаг ACC_ANNOTATION. Если установлен флаг ACC_ANNOTATION, должен быть установлен и флаг ACC_INTERFACE. Если флаг ACC_INTERFACE этого файла ClassFile не установлен, он может иметь установленные любые другие флаги из таблицы 4.1, за исключением флага ACC_ANNOTATION. Однако такой файл ClassFile не может иметь установленные одновременно флаги ACC_FINAL и ACC_ABSTRACT (JLS §8.1.1.2).

Флаг ACC_SUPER указывает, какая из двух альтернативных семантик должна быть выражена инструкцией invokespecial (§invokespecial), если она встречается в этом классе. Компиляторы для набора команд Java Virtual Machine должны устанавливать флаг ACC_SUPER.

Флаг ACC_SUPER существует для обратной совместимости с кодом, скомпилированным более старыми компиляторами языка программирования Java. В выпусках Oracle's JDK до версии 1.0.2 компилятор генерировал ClassFile access_flags, в которых флагу, теперь представляющему ACC_SUPER, не было назначено никакого значения, и реализация Java Virtual Machine Oracle игнорировала флаг, если он был установлен.

Все биты элемента access_flags, не назначенные в таблице 4.1, зарезервированы для будущего использования. Они должны быть установлены в ноль в сгенерированных файлах ClassFile и должны игнорироваться реализациями Java Virtual Machine.

this_class

Значение элемента this_class должно быть допустимым индексом в таблице constant_pool. Запись constant_pool по этому индексу должна быть структурой CONSTANT_Class_info (§4.4.1), представляющей класс или интерфейс, определённый этим файлом ClassFile.

super_class

Для класса значение элемента super_class либо должно быть равно нулю, либо должно быть допустимым индексом в таблице constant_pool. Если значение элемента super_class отлично от нуля, запись constant_pool по этому индексу должна быть структурой CONSTANT_Class_info (§4.4.1), представляющей непосредственный суперкласс класса, определённого этим файлом ClassFile. Ни непосредственный суперкласс, ни любой из его суперклассов не может иметь установленного флага ACC_FINAL в элементе access_flags своей структуры ClassFile.

Если значение элемента super_class равно нулю, то этот файл ClassFile должен представлять класс Object, единственный класс или интерфейс без непосредственного суперкласса.

Для интерфейса значение элемента super_class всегда должно быть допустимым индексом в таблице constant_pool. Запись constant_pool по этому индексу должна быть структурой CONSTANT_Class_info, представляющей класс Object.

interfaces_count

Значение элемента interfaces_count даёт количество непосредственных суперинтерфейсов этого класса или интерфейса.

interfaces[]

Каждое значение в массиве interfaces должно быть допустимым индексом в таблице constant_pool. Запись constant_pool по каждому значению interfaces[i], где 0 ≤ i < interfaces_count, должна быть структурой CONSTANT_Class_info (§4.4.1), представляющей интерфейс, являющийся непосредственным суперинтерфейсом этого класса или интерфейса в порядке следования, заданном в источнике для типа.

fields_count

Значение элемента fields_count даёт количество структур Field в таблице fields. Структуры Field (§4.5) представляют все поля, как переменные класса, так и переменные экземпляра, объявленные этим классом или интерфейсом.

fields[]

Каждая запись в таблице fields должна быть структурой Field (§4.5), дающей полное описание поля в этом классе или интерфейсе. Таблица fields включает только те поля, которые объявлены этим классом или интерфейсом. Она не включает записи, представляющие поля, унаследованные от суперклассов или суперинтерфейсов.

methods_count

Значение элемента methods_count даёт количество структур Method в таблице methods.

methods[]

Каждое значение в таблице methods должно быть структурой method_info (§4.6), дающей полное описание метода в этом классе или интерфейсе. Если ни один из флагов ACC_NATIVE и ACC_ABSTRACT не установлен в элементе access_flags структуры method_info, то инструкции Java Virtual Machine, реализующие метод, также предоставляются.

Структуры method_info представляют все методы, объявленные этим типом класса или интерфейса, включая методы экземпляра, методы класса, методы инициализации экземпляра (§2.9) и любой метод инициализации класса или интерфейса (§2.9). Таблица methods не включает элементы, представляющие методы, унаследованные от суперклассов или суперинтерфейсов.

attributes_count

Значение элемента attributes_count указывает количество атрибутов (§4.7) в таблице attributes этого класса.

attributes[]

Каждое значение таблицы attributes должно быть структурой attribute_info (§4.7).

Атрибуты, определённые в данном стандарте, присутствующие в таблице attributes структуры ClassFile, — это атрибуты InnerClasses (§4.7.6), EnclosingMethod (§4.7.7), Synthetic (§4.7.8), Signature (§4.7.9), SourceFile (§4.7.10), SourceDebugExtension (§4.7.11), Deprecated (§4.7.15), RuntimeVisibleAnnotations (§4.7.16), RuntimeInvisibleAnnotations (§4.7.17) и BootstrapMethods (§4.7.21) атрибуты.

Если реализация Java Virtual Machine распознаёт файлы версии 49.0 или выше, она должна распознавать и корректно считывать атрибуты Signature (§4.7.9), RuntimeVisibleAnnotations (§4.7.16) и RuntimeInvisibleAnnotations (§4.7.17), найденные в таблице attributes структуры ClassFile файла class версии 49.0 или выше.

Если реализация Java Virtual Machine распознаёт файлы версии 51.0 или выше, она должна распознавать и корректно считывать атрибуты BootstrapMethods (§4.7.21), найденные в таблице attributes структуры ClassFile файла class версии 51.0 или выше.

Реализация Java Virtual Machine обязана игнорировать любые или все атрибуты в таблице attributes структуры ClassFile, которые она не распознаёт. Атрибуты, не определённые в данном стандарте, не должны влиять на семантику файла class, а лишь предоставлять дополнительную описательную информацию (§4.7.1).

4.2. Внутренняя форма имён

4.2.1. Бинарные имена классов и интерфейсов

Имена классов и интерфейсов, которые появляются в структурах файлов class, всегда представлены в полностью квалифицированной форме, известной как бинарные имена (JLS §13.1). Такие имена всегда представлены как структуры CONSTANT_Utf8_info (§4.4.7) и, следовательно, могут быть взяты, если не ограничены, из всего кодировочного пространства Unicode. Имена классов и интерфейсов ссылаются на эти структуры CONSTANT_NameAndType_info (§4.4.6), которые имеют такие имена в качестве части своего дескриптора (§4.3), и со всех структур CONSTANT_Class_info (§4.4.1).

По историческим причинам синтаксис бинарных имён, которые появляются в структурах файлов class, отличается от синтаксиса бинарных имён, описанных в JLS §13.1. В этой внутренней форме ASCII точки (.), которые обычно отделяют идентификаторы, образующие бинарное имя, заменены ASCII слешами (/). Сами идентификаторы должны быть неквалифицированными именами (§4.2.2).

Например, обычное бинарное имя класса Thread — java.lang.Thread. Во внутренней форме, используемой в дескрипторах в формате файла class, ссылка на имя класса Thread реализуется с помощью структуры CONSTANT_Utf8_info, представляющей строку java/lang/Thread.

4.2.2. Неквалифицированные имена

Имена методов, полей и локальных переменных хранятся как неквалифицированные имена. Неквалифицированное имя не должно содержать ни одного из ASCII символов . ; [ / (то есть точки или точки с запятой или левой квадратной скобки или слэша).

Имена методов дополнительно ограничены тем, что, за исключением специальных имён методов <init> и <clinit> (§2.9), они не должны содержать ASCII символов < или > (то есть левой угловой скобки или правой угловой скобки).

Обратите внимание, что имя поля или имя метода интерфейса может быть <init> или <clinit>, но ни одна инструкция вызова метода не может ссылаться на <clinit>, и только инструкция invokespecial (§invokespecial) может ссылаться на <init>.

4.3. Дескрипторы и сигнатуры

A дескриптор — это строка, представляющая тип поля или метода. Дескрипторы представлены в формате файла class с использованием модифицированных строк UTF-8 (§4.4.7) и, следовательно, могут быть взяты, если не ограничены, из всего пространства кодов Юникода.

A сигнатура — это строка, представляющая обобщённый тип поля или метода или информацию об обобщённом типе для объявления класса.

4.3.1. Нотация грамматики

Дескрипторы и сигнатуры задаются с помощью грамматики. Эта грамматика — это набор правил, описывающих, как последовательности символов могут образовывать синтаксически правильные дескрипторы различных типов. Терминальные символы грамматики показаны шрифтом bold fixed-width. Нетерминальные символы показаны шрифтом курсив. Определение нетерминального символа вводится именем определяемого нетерминального символа, за которым следует двоеточие. Затем следуют одна или несколько альтернативных правых частей для нетерминального символа на последующих строках. Например, правило:


FieldType:
BaseType
ObjectType
ArrayType

указывает, что FieldType может представлять либо BaseType, либо ObjectType, либо ArrayType.

Нетерминальный символ в правой части правила, за которым следует звёздочка (*), представляет нуль или более различных значений, полученных от этого нетерминального символа, присоединённых без каких-либо пробелов. Аналогично, нетерминальный символ в правой части правила, за которым следует знак плюс (+), представляет одно или более различных значений, полученных от этого нетерминального символа, присоединённых без каких-либо пробелов. Правило:


MethodDescriptor:
( ParameterDescriptor* ) ReturnDescriptor

указывает, что MethodDescriptor представляет левую круглую скобку, за которой следуют нуль или более значений ParameterDescriptor, за которой следует правая круглая скобка, за которой следует ReturnDescriptor.

4.3.2. Дескрипторы полей

A дескриптор поля представляет тип класса, экземпляра или локальной переменной. Это последовательность символов, сгенерированных грамматикой:


FieldDescriptor:
FieldType

FieldType:
BaseType
ObjectType
ArrayType

BaseType:
B
C
D
F
I
J
S
Z

ObjectType:
L ClassName ;

ArrayType:
[ ComponentType

ComponentType:
FieldType

Символы BaseType, L и ; ObjectType и [ ArrayType — это все символы ASCII.

The ClassName represents a binary class or interface name encoded in internal form (§4.2.1).

Интерпретация дескрипторов полей как типов показана в таблице 4.2.

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

Таблица 4.2. Интерпретация символов FieldType

BaseType Символ Тип Интерпретация
B byte знаковое байтовое значение
C char код точки кода Юникода в базовой многоязычной плоскости, закодированный с помощью UTF-16
D double значение с плавающей запятой двойной точности
F float значение с плавающей запятой одинарной точности
I int целое число
J long длинное целое число
L ClassName ; reference экземпляр класса ClassName
S short знаковое короткое значение
Z boolean true или false
[ reference одно измерение массива

Дескриптор поля переменной экземпляра типа int — это просто I.

Дескриптор поля переменной экземпляра типа Object — это Ljava/lang/Object;. Обратите внимание, что используется внутренняя форма двоичного имени для класса Object.

Дескриптор поля переменной экземпляра, которая является многомерным double массивом, double d[][][], — это [[[D.

4.3.3. Дескрипторы методов

A дескриптор метода представляет параметры, которые принимает метод, и значение, которое он возвращает:


MethodDescriptor:
( ParameterDescriptor* ) ReturnDescriptor

A дескриптор параметра представляет параметр, передаваемый методу:


ParameterDescriptor:
FieldType

A дескриптор возврата представляет тип значения, возвращаемого методом. Это последовательность символов, сгенерированная грамматикой:


ReturnDescriptor:
FieldType
VoidDescriptor

VoidDescriptor:
V

Символ V указывает, что метод не возвращает значения (его тип возврата — void).

Метод дескриптор является допустимым только в том случае, если он представляет параметры метода с общей длиной 255 или меньше, где эта длина включает вклад для this в случае вызовов методов экземпляров или интерфейсов. Общая длина вычисляется путем суммирования вкладов отдельных параметров, где параметр типа long или double вносит два единицы в длину, а параметр любого другого типа вносит одну единицу.

Дескриптор метода для метода:

Object m(int i, double d, Thread t) {..}

равен (IDLjava/lang/Thread;)Ljava/lang/Object;. Обратите внимание, что используются внутренние формы двоичных имен Thread и Object.

Дескриптор метода для m одинаков, независимо от того, является ли m методом класса или методом экземпляра. Хотя методу экземпляра передаётся this, ссылка на текущий экземпляр класса, помимо его предполагаемых параметров, этот факт не отражается в дескрипторе метода. Ссылка на this передаётся неявно инструкциями вызова методов виртуальной машины Java, вызывающими методы экземпляров (§2.6.1). Ссылка на this не передаётся методу класса.

4.3.4. Подписи

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

Этот вид информации о типах необходим для поддержки рефлексии, отладки и компилятора Java.

В дальнейшем терминальный символ Идентификатор используется для обозначения имени типа, поля, локальной переменной, параметра, метода или параметра типа, как сгенерировано компилятором Java. Такое имя не должно содержать ни одного из ASCII-символов . ; [ / < > : (то есть символов, запрещённых в именах методов (§4.2.2), а также двоеточия), но может содержать символы, которые не должны появляться в идентификаторе в языке программирования Java (JLS §3.8).

Подпись класса, определяемая продукцией ClassSignature, используется для кодирования информации о типах объявления класса. Она описывает все формальные параметры типа, которые может иметь класс, и перечисляет его (возможно, параметризованный) прямой суперкласс и прямые суперинтерфейсы, если таковые имеются.


ClassSignature:
FormalTypeParametersopt SuperclassSignature SuperinterfaceSignature*

Формальный параметр типа описывается его именем, за которым следуют его классовые и интерфейсные ограничения. Если ограничение класса не указывает тип, то оно считается Object.


FormalTypeParameters:
< FormalTypeParameter+ >

FormalTypeParameter:
Identifier ClassBound InterfaceBound*

ClassBound:
: FieldTypeSignatureopt

InterfaceBound:
: FieldTypeSignature

SuperclassSignature:
ClassTypeSignature

SuperinterfaceSignature:
ClassTypeSignature

Подпись типа поля, определённая продукцией FieldTypeSignature, кодирует (возможно, параметризованный) тип для поля, параметра или локальной переменной.


FieldTypeSignature:
ClassTypeSignature
ArrayTypeSignature
TypeVariableSignature

Подпись типа класса даёт полную информацию о типе класса или интерфейса. Подпись типа класса должна быть сформулирована таким образом, чтобы её можно было надёжно сопоставить с бинарным именем класса, который она обозначает, стерев все аргументы типов и преобразовав каждый . символ в подписи в $ символ.


ClassTypeSignature:
L PackageSpecifieropt SimpleClassTypeSignature ClassTypeSignatureSuffix* ;

PackageSpecifier:
Identifier / PackageSpecifier*

SimpleClassTypeSignature:
Identifier TypeArgumentsopt

ClassTypeSignatureSuffix:
. SimpleClassTypeSignature

TypeVariableSignature:
T Identifier ;

TypeArguments:
< TypeArgument+ >

TypeArgument:
WildcardIndicatoropt FieldTypeSignature
*

WildcardIndicator:
+
-

ArrayTypeSignature:
[ TypeSignature

TypeSignature:
FieldTypeSignature
BaseType

Подпись метода, определённая продукцией MethodTypeSignature, кодирует (возможно, параметризованные) типы формальных аргументов метода и исключений, объявленных в его throws-описании, его (возможно, параметризованный) возвращаемый тип и любые формальные параметры типа в объявлении метода.


MethodTypeSignature:
FormalTypeParametersopt (TypeSignature*) ReturnType ThrowsSignature*

ReturnType:
TypeSignature
VoidDescriptor

ThrowsSignature:
^ ClassTypeSignature
^ TypeVariableSignature

Если throws-описание метода или конструктора не включает переменные типа, ThowsSignature может быть опущено из MethodTypeSignature.

Компилятор Java должен выводить информацию о дженерик-подписях для любого класса, интерфейса, конструктора или члена, чья дженерик-подпись на языке программирования Java включала бы ссылки на переменные типа или параметризованные типы.

Подпись и дескриптор (§4.3.3) данного метода или конструктора могут не совпадать точно из-за артефактов, сгенерированных компилятором. В частности, количество TypeSignature которые кодируют формальные аргументы в MethodTypeSignature может быть меньше, чем количество ParameterDescriptors в MethodDescriptor.

Реализация виртуальной машины Java от Oracle не проверяет корректность подписей, описанных в этом подразделе, во время загрузки или линковки. Вместо этого эти проверки откладываются до использования подписей рефлексивными методами, как указано в API Class и членов java.lang.reflect. Будущие версии реализации виртуальной машины Java могут быть обязаны выполнить некоторые или все эти проверки во время загрузки или линковки.

4.4. Пул констант

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

Все записи таблицы constant_pool имеют следующий общий формат:

cp_info {
    u1 tag;
    u1 info[];
}

Каждый элемент таблицы constant_pool должен начинаться с 1-байтового тега, указывающего тип записи cp_info. Содержимое массива info зависит от значения tag. Действительные теги и их значения перечислены в таблице 4.3. Каждый байт тега должен быть последован двумя или более байтами, предоставляющими информацию о конкретной константе. Формат дополнительной информации зависит от значения тега.

Таблица 4.3. Теги пула констант

Тип константы Значение
CONSTANT_Class 7
CONSTANT_Fieldref 9
CONSTANT_Methodref 10
CONSTANT_InterfaceMethodref 11
CONSTANT_String 8
CONSTANT_Integer 3
CONSTANT_Float 4
CONSTANT_Long 5
CONSTANT_Double 6
CONSTANT_NameAndType 12
CONSTANT_Utf8 1
CONSTANT_MethodHandle 15
CONSTANT_MethodType 16
CONSTANT_InvokeDynamic 18

4.4.1. Структура CONSTANT_Class_info

Структура CONSTANT_Class_info используется для представления класса или интерфейса:

CONSTANT_Class_info {
    u1 tag;
    u2 name_index;
}

Элементы структуры CONSTANT_Class_info следующие:

tag

Элемент tag имеет значение CONSTANT_Class (7).

name_index

Значение элемента name_index должно быть допустимым индексом в таблице constant_pool. Запись constant_pool в этом индексе должна быть структурой CONSTANT_Utf8_info (§4.4.7) — допустимым именем класса или интерфейса, закодированным во внутреннем формате (§4.2.1).

Поскольку массивы являются объектами, операторы anewarray и multianewarray могут ссылаться на "классы" массивов через структуры CONSTANT_Class_info в таблице constant_pool. Для таких классов массивов имя класса является описанием типа массива.

Например, имя класса, представляющее двухмерный массив int,

int[][]

равно

[[I

Имя класса, представляющее массив типа класса Thread,

Thread[]

равно

[Ljava/lang/Thread;

Описание типа массива является допустимым только в том случае, если оно представляет 255 или меньше измерений.

4.4.2. Структуры CONSTANT_Fieldref_info, CONSTANT_Methodref_info и CONSTANT_InterfaceMethodref_info

Поля, методы и методы интерфейсов представлены похожими структурами:

CONSTANT_Fieldref_info {
    u1 tag;
    u2 class_index;
    u2 name_and_type_index;
}

CONSTANT_Methodref_info {
    u1 tag;
    u2 class_index;
    u2 name_and_type_index;
}

CONSTANT_InterfaceMethodref_info {
    u1 tag;
    u2 class_index;
    u2 name_and_type_index;
}

Элементы этих структур следующие:

tag

Элемент tag структуры CONSTANT_Fieldref_info имеет значение CONSTANT_Fieldref (9).

Элемент tag структуры CONSTANT_Methodref_info имеет значение CONSTANT_Methodref (10).

Элемент tag структуры CONSTANT_InterfaceMethodref_info имеет значение CONSTANT_InterfaceMethodref (11).

class_index

Значение элемента class_index должно быть допустимым индексом в таблице constant_pool. Запись constant_pool в этом индексе должна быть структурой CONSTANT_Class_info (§4.4.1) — типом класса или интерфейса, который имеет поле или метод в качестве члена.

Элемент class_index структуры CONSTANT_Methodref_info должен быть типом класса, а не типом интерфейса.

Элемент class_index структуры CONSTANT_InterfaceMethodref_info должен быть типом интерфейса.

Элемент class_index структуры CONSTANT_Fieldref_info может быть типом класса или типом интерфейса.

name_and_type_index

Значение элемента name_and_type_index должно быть допустимым индексом в таблице constant_pool. Запись constant_pool в этом индексе должна быть структурой CONSTANT_NameAndType_info (§4.4.6). Эта запись указывает имя и описание поля или метода.

В структуре CONSTANT_Fieldref_info указанное описание должно быть описанием поля (§4.3.2). В противном случае указанное описание должно быть описанием метода (§4.3.3).

Если имя метода структуры CONSTANT_Methodref_info начинается с '<' ('\u003c'), то имя должно быть специальным именем <init>, представляющим метод инициализации экземпляра (§2.9). Тип возвращаемого значения такого метода должен быть void.

4.4.3. Структура CONSTANT_String_info

Структура CONSTANT_String_info используется для представления константных объектов типа String:

CONSTANT_String_info {
    u1 tag;
    u2 string_index;
}

Элементы структуры CONSTANT_String_info следующие:

tag

Элемент tag структуры CONSTANT_String_info имеет значение CONSTANT_String (8).

string_index

Значение элемента string_index должно быть допустимым индексом в таблице constant_pool. Запись constant_pool в этом индексе должна быть структурой CONSTANT_Utf8_info (§4.4.7) — последовательностью кодов Юникода, к которой должен быть инициализирован объект String.

4.4.4. Структуры CONSTANT_Integer_info и CONSTANT_Float_info

Структуры CONSTANT_Integer_info и CONSTANT_Float_info представляют числовые константы 4 байта (int и float):

CONSTANT_Integer_info {
    u1 tag;
    u4 bytes;
}

CONSTANT_Float_info {
    u1 tag;
    u4 bytes;
}

Элементы этих структур следующие:

тег

Элемент tag структуры CONSTANT_Integer_info имеет значение CONSTANT_Integer (3).

Элемент tag структуры CONSTANT_Float_info имеет значение CONSTANT_Float (4).

байты

Элемент bytes структуры CONSTANT_Integer_info представляет значение целочисленной константы. Байты значения хранятся в формате big-endian (старший байт первым).

Элемент bytes структуры CONSTANT_Float_info представляет значение константы с плавающей точкой в формате single IEEE 754 (§2.3.2). Байты представления в формате single хранятся в формате big-endian (старший байт первым).

Значение, представленное структурой CONSTANT_Float_info, определяется следующим образом. Байты значения сначала преобразуются в целочисленную константу bits. Затем:

  • Если bits равно 0x7f800000, значение float будет положительной бесконечностью.

  • Если bits равно 0xff800000, значение float будет отрицательной бесконечностью.

  • Если bits находится в диапазоне от 0x7f800001 до 0x7fffffff или от 0xff800001 до 0xffffffff, значение float будет NaN.

  • Во всех остальных случаях, пусть s, e и m — три значения, которые могут быть вычислены из bits:

    int s = ((bits >> 31) == 0) ? 1 : -1;
    int e = ((bits >> 23) & 0xff);
    int m = (e == 0) ?
              (bits & 0x7fffff) << 1 :
              (bits & 0x7fffff) | 0x800000;
    	  

Тогда значение float равно результату математического выражения s · m · 2e-150.

4.4.5. Структуры CONSTANT_Long_info и CONSTANT_Double_info

Структуры CONSTANT_Long_info и CONSTANT_Double_info представляют числовые константы 8 байт (long и double):

CONSTANT_Long_info {
    u1 tag;
    u4 high_bytes;
    u4 low_bytes;
}

CONSTANT_Double_info {
    u1 tag;
    u4 high_bytes;
    u4 low_bytes;
}

Все константы 8 байт занимают две записи в таблице constant_pool файла class. Если структура CONSTANT_Long_info или CONSTANT_Double_info является элементом таблицы constant_pool с индексом n, то следующий пригодный элемент в пуле находится по индексу n+2. Индекс constant_pool n+1 должен быть корректным, но считается непригодным.

В ретроспективе, использование двух записей для констант 8 байт — неудачное решение.

Элементы этих структур следующие:

тег

Элемент tag структуры CONSTANT_Long_info имеет значение CONSTANT_Long (5).

Элемент tag структуры CONSTANT_Double_info имеет значение CONSTANT_Double (6).

high_bytes, low_bytes

Беззнаковые элементы high_bytes и low_bytes структуры CONSTANT_Long_info вместе представляют значение целочисленной константы

((long) high_bytes << 32) + low_bytes
      

где байты каждого из high_bytes и low_bytes хранятся в формате big-endian (старший байт первым).

Элементы high_bytes и low_bytes структуры CONSTANT_Double_info вместе представляют значение константы с плавающей точкой в формате double IEEE 754 (§2.3.2). Байты каждого элемента хранятся в формате big-endian (старший байт первым).

Значение, представленное структурой CONSTANT_Double_info, определяется следующим образом. Элементы high_bytes и low_bytes преобразуются в целочисленную константу bits, которая равна

((long) high_bytes << 32) + low_bytes
      

Затем:

  • Если bits равно 0x7ff0000000000000L, значение double будет положительной бесконечностью.

  • Если bits равно 0xfff0000000000000L, значение double будет отрицательной бесконечностью.

  • Если bits находится в диапазоне от 0x7ff0000000000001L до 0x7fffffffffffffffL или от 0xfff0000000000001L до 0xffffffffffffffffL, значение double будет NaN.

  • Во всех остальных случаях, пусть s, e и m — три значения, которые могут быть вычислены из bits:

    int s = ((bits >> 63) == 0) ? 1 : -1;
    int e = (int)((bits >> 52) & 0x7ffL);
    long m = (e == 0) ?
               (bits & 0xfffffffffffffL) << 1 :
               (bits & 0xfffffffffffffL) | 0x10000000000000L;
    	  

Затем значение с плавающей точкой равно результату математического выражения s · m · 2e-1075.

4.4.6. Структура CONSTANT_NameAndType_info

Структура CONSTANT_NameAndType_info используется для представления поля или метода без указания класса или типа интерфейса, к которому оно принадлежит:

CONSTANT_NameAndType_info {
    u1 tag;
    u2 name_index;
    u2 descriptor_index;
}

Элементы структуры CONSTANT_NameAndType_info следующие:

тег

Элемент tag структуры CONSTANT_NameAndType_info имеет значение CONSTANT_NameAndType (12).

name_index

Значение элемента name_index должно быть корректным индексом в таблице constant_pool. Элемент constant_pool по этому индексу должен быть структурой CONSTANT_Utf8_info (§4.4.7) представляющей либо специальное имя метода <init> (§2.9), либо корректное неопределённое имя (§4.2.2) поля или метода.

descriptor_index

Значение элемента descriptor_index должно быть корректным индексом в таблице constant_pool. Элемент constant_pool по этому индексу должен быть структурой CONSTANT_Utf8_info (§4.4.7) представляющей корректный дескриптор поля (§4.3.2) или метода (§4.3.3).

4.4.7. Структура CONSTANT_Utf8_info

Структура CONSTANT_Utf8_info используется для представления константных строковых значений:

CONSTANT_Utf8_info {
    u1 tag;
    u2 length;
    u1 bytes[length];
}

Элементы структуры CONSTANT_Utf8_info следующие:

tag

Элемент tag структуры CONSTANT_Utf8_info имеет значение CONSTANT_Utf8 (1).

length

Значение элемента length задаёт количество байтов в массиве bytes (а не длину результирующей строки). Строки в структуре CONSTANT_Utf8_info не завершаются нулём.

bytes[]

Массив bytes содержит байты строки. Ни один байт не может иметь значение (byte)0 или находиться в диапазоне (byte)0xf0 - (byte)0xff.

Содержание строки закодировано в модифицированной кодировке UTF-8. Модифицированные строки UTF-8 закодированы так, что последовательности кодовых точек, содержащие только не нулевые ASCII символы, могут быть представлены с использованием только 1 байта на кодовую точку, но все кодовые точки в кодовой таблице Юникода могут быть представлены.

  • Кодовые точки в диапазоне '\u0001' до '\u007F' представлены одним байтом:

    Таблица 4.4.

    0 биты 6-0

    7 бит данных в байте дают значение кодовой точки, которая представлена.

  • Нулевая кодовая точка ('\u0000') и кодовые точки в диапазоне '\u0080' до '\u07FF' представлены парой байтов x и y:

    Таблица 4.5.

    x:

    Таблица 4.6.

    1 1 0 биты 10-6

    y:

    Таблица 4.7.

    1 0 биты 5-0


    Байты представляют кодовую точку со значением:

    ((x & 0x1f) << 6) + (y & 0x3f)
        
  • Кодовые точки в диапазоне '\u0800' до '\uFFFF' представлены 3 байтами x, y и z:

    Таблица 4.8.

    x:

    Таблица 4.9.

    1 1 1 0 биты 15-12

    y:

    Таблица 4.10.

    1 0 биты 11-6

    z:

    Таблица 4.11.

    1 0 биты 5-0


    Три байта представляют кодовую точку со значением:

    ((x & 0xf) << 12) + ((y & 0x3f) << 6) + (z & 0x3f)
        
  • Символы с кодовыми точками выше U+FFFF (так называемые дополнительные символы) представляются путём отдельного кодирования двух суррогатных кодовых единиц их представления в UTF-16. Каждая из суррогатных кодовых единиц представляется тремя байтами. Это означает, что дополнительные символы представляются шестью байтами, u, v, w, x, y и z :

    Таблица 4.12.

    u:

    Таблица 4.13.

    1 1 1 0 1 1 0 1

    v:

    Таблица 4.14.

    1 0 1 0 (биты 20-16)-1

    w:

    Таблица 4.15.

    1 0 биты 15-10

    x:

    Таблица 4.16.

    1 1 1 0 1 1 0 1

    y:

    Таблица 4.17.

    1 0 1 1 биты 9-6

    z:

    Таблица 4.18.

    1 0 биты 5-0


    Эти шесть байтов представляют кодовую точку со значением:

    0x10000 + ((v & 0x0f) << 16) + ((w & 0x3f) << 10) +
    ((y & 0x0f) << 6) + (z & 0x3f)
        

Байты многобайтовых символов хранятся в файле class в формате big-endian (старший байт первым).

Есть два отличия между этим форматом и стандартным форматом UTF-8. Во-первых, нулевой символ (char)0 кодируется с помощью 2-байтного формата, а не 1-байтного, чтобы изменённые строки UTF-8 никогда не содержали вложенных нулей. Во-вторых, используются только 1-байтный, 2-байтный и 3-байтный форматы стандартного UTF-8. Виртуальная машина Java не распознаёт 4-байтный формат стандартного UTF-8; вместо этого она использует свой собственный формат два-три-байта.

Дополнительную информацию о стандартном формате UTF-8 см. в разделе 3.9 Формы кодирования Юникод в Стандарте Юникод, версия 6.0.0.

4.4.8. Структура CONSTANT_MethodHandle_info

Структура CONSTANT_MethodHandle_info используется для представления дескриптора метода:

CONSTANT_MethodHandle_info {
    u1 tag;
    u1 reference_kind;
    u2 reference_index;
}

Элементы структуры CONSTANT_MethodHandle_info следующие:

tag

Элемент tag структуры CONSTANT_MethodHandle_info имеет значение CONSTANT_MethodHandle (15).

reference_kind

Значение элемента reference_kind должно быть в диапазоне от 1 до 9. Это значение определяет тип дескриптора метода, который характеризует его поведение в байткоде (§5.4.3.5).

reference_index

Значение элемента reference_index должно быть допустимым индексом в таблице constant_pool.

Если значение элемента reference_kind равно 1 (REF_getField), 2 (REF_getStatic), 3 (REF_putField) или 4 (REF_putStatic), то запись constant_pool в этом индексе должна быть структурой CONSTANT_Fieldref_info (§4.4.2), представляющей поле, для которого должен быть создан дескриптор метода.

Если значение элемента reference_kind равно 5 (REF_invokeVirtual), 6 (REF_invokeStatic), 7 (REF_invokeSpecial) или 8 (REF_newInvokeSpecial), то запись constant_pool в этом индексе должна быть структурой CONSTANT_Methodref_info (§4.4.2) представляющей метод класса или конструктор (§2.9), для которого должен быть создан дескриптор метода.

Если значение элемента reference_kind равно 9 (REF_invokeInterface), то запись constant_pool в этом индексе должна быть структурой CONSTANT_InterfaceMethodref_info (§4.4.2) представляющей метод интерфейса, для которого должен быть создан дескриптор метода.

Если значение элемента reference_kind равно 5 (REF_invokeVirtual), 6 (REF_invokeStatic), 7 (REF_invokeSpecial) или 9 (REF_invokeInterface), то имя метода, представленного структурой CONSTANT_Methodref_info, не должно быть <init> или <clinit>.

Если значение равно 8 (REF_newInvokeSpecial), то имя метода, представленного структурой CONSTANT_Methodref_info, должно быть <init>.

4.4.9. Структура CONSTANT_MethodType_info

Структура CONSTANT_MethodType_info используется для представления типа метода:

CONSTANT_MethodType_info {
    u1 tag;
    u2 descriptor_index;
}

Элементы структуры CONSTANT_MethodType_info следующие:

tag

Элемент tag структуры CONSTANT_MethodType_info имеет значение CONSTANT_MethodType (16).

descriptor_index

Значение элемента descriptor_index должно быть допустимым индексом в таблице constant_pool. Запись constant_pool в этом индексе должна быть структурой CONSTANT_Utf8_info (§4.4.7), представляющей дескриптор метода (§4.3.3).

4.4.10. Структура CONSTANT_InvokeDynamic_info

Структура CONSTANT_InvokeDynamic_info используется инструкцией invokedynamic (§invokedynamic) для указания целевого метода, динамического имени вызова, типов аргументов и возвращаемого значения вызова, а также, необязательно, последовательности дополнительных констант, называемых статическими аргументами целевого метода.

CONSTANT_InvokeDynamic_info {
    u1 tag;
    u2 bootstrap_method_attr_index;
    u2 name_and_type_index;
}

Элементы структуры CONSTANT_InvokeDynamic_info следующие:

tag

Элемент tag структуры CONSTANT_InvokeDynamic_info имеет значение CONSTANT_InvokeDynamic (18).

bootstrap_method_attr_index

Значение элемента bootstrap_method_attr_index должно быть допустимым индексом в массиве bootstrap_methods таблицы bootstrap-методов (§4.7.21) этого файла class.

name_and_type_index

Значение элемента name_and_type_index должно быть допустимым индексом в таблице constant_pool. Запись constant_pool в этом индексе должна быть структурой CONSTANT_NameAndType_info (§4.4.6), представляющей имя и дескриптор метода (§4.3.3).

4.5. Поля

Каждое поле описывается структурой field_info. Два поля в одном файле class не могут иметь одинакового имени и дескриптора (§4.3.2).

Структура имеет следующий формат:

field_info {
    u2             access_flags;
    u2             name_index;
    u2             descriptor_index;
    u2             attributes_count;
    attribute_info attributes[attributes_count];
}

Элементы структуры field_info следующие:

access_flags

Значение элемента access_flags представляет собой маску флагов, используемых для обозначения разрешений доступа к этому полю и его свойств. Интерпретация каждого флага, при установке, приведена в Таблице 4.4.

Таблица 4.4. Флаги доступа к полям и их свойства

Имя флага Значение Интерпретация
ACC_PUBLIC 0x0001 Объявлено public; может быть обращено извне пакета.
ACC_PRIVATE 0x0002 Объявлено private; доступно только внутри определяемого класса.
ACC_PROTECTED 0x0004 Объявлено protected; может быть обращено из подклассов.
ACC_STATIC 0x0008 Объявлено static.
ACC_FINAL 0x0010 Объявлено final; никогда не присваивается напрямую после создания объекта (JLS §17.5).
ACC_VOLATILE 0x0040 Объявлено volatile; не может быть кэшировано.
ACC_TRANSIENT 0x0080 Объявлено transient; не записывается и не считывается менеджером персистентных объектов.
ACC_SYNTHETIC 0x1000 Объявлено синтетическим; не присутствует в исходном коде.
ACC_ENUM 0x4000 Объявлено элементом enum.

Поле может быть помечено флагом ACC_SYNTHETIC, чтобы указать, что оно было сгенерировано компилятором и не отображается в исходном коде.

Флаг ACC_ENUM указывает, что это поле используется для хранения элемента перечислимого типа.

Поля классов могут устанавливать любые флаги в Таблице 4.4. Однако конкретное поле класса может иметь не более одного из флагов ACC_PRIVATE, ACC_PROTECTED и ACC_PUBLIC (JLS §8.3.1) и не должно иметь одновременно флаги ACC_FINAL и ACC_VOLATILE (JLS §8.3.1.4).

Все поля интерфейсов должны иметь установленные флаги ACC_PUBLIC, ACC_STATIC и ACC_FINAL; они могут иметь установленный флаг ACC_SYNTHETIC и не должны иметь установленных других флагов в Таблице 4.4 (JLS §9.3).

Все биты элемента access_flags, не назначенные в Таблице 4.4, зарезервированы для будущего использования. Они должны быть установлены в ноль в сгенерированных файлах class и должны игнорироваться реализациями виртуальной машины Java.

name_index

Значение элемента name_index должно быть допустимым индексом в таблице constant_pool. Запись constant_pool в этом индексе должна быть структурой CONSTANT_Utf8_info (§4.4.7), которая должна представлять действительное неоквалифицированное имя (§4.2.2), обозначающее поле.

descriptor_index

Значение элемента descriptor_index должно быть допустимым индексом в таблице constant_pool. Запись constant_pool в этом индексе должна быть структурой CONSTANT_Utf8_info (§4.4.7), представляющей действительный дескриптор поля (§4.3.2).

attributes_count

Значение элемента attributes_count указывает количество дополнительных атрибутов (§4.7) данного поля.

attributes[]

Каждое значение в таблице attributes должно быть структурой атрибута (§4.7). Поле может иметь любое количество связанных с ним атрибутов.

Атрибуты, определённые в этом спецификации, присутствующие в таблице attributes структуры field_info, являются ConstantValue (§4.7.2), Synthetic (§4.7.8), Signature (§4.7.9), Deprecated (§4.7.15), RuntimeVisibleAnnotations (§4.7.16) и RuntimeInvisibleAnnotations (§4.7.17).

Реализация виртуальной машины Java должна распознавать и корректно считывать атрибуты ConstantValue (§4.7.2), встречающиеся в таблице attributes структуры field_info. Если реализация виртуальной машины Java распознаёт файлы class с номером версии 49.0 или выше, она должна распознавать и корректно считывать атрибуты Signature (§4.7.9), RuntimeVisibleAnnotations (§4.7.16) и RuntimeInvisibleAnnotations (§4.7.17), встречающиеся в таблице attributes структуры field_info файла class с номером версии 49.0 или выше.

Реализация виртуальной машины Java должна игнорировать все нераспознанные атрибуты в таблице attributes структуры field_info. Атрибуты, не определённые в этом документе, не должны влиять на семантику файла class, но только предоставлять дополнительную описательную информацию (§4.7.1).

4.6. Методы

Каждый метод, включая каждый метод инициализации экземпляра (§2.9) и метод инициализации класса или интерфейса (§2.9), описывается структурой method_info. В одном файле class не должно быть двух методов с одинаковым именем и дескриптором (§4.3.3).

Структура имеет следующий формат:

method_info {
    u2             access_flags;
    u2             name_index;
    u2             descriptor_index;
    u2             attributes_count;
    attribute_info attributes[attributes_count];
}

Элементы структуры method_info следующие:

access_flags

Значение элемента access_flags представляет собой маску флагов, используемых для обозначения разрешений доступа и свойств этого метода. Интерпретация каждого флага, при установке, показана в таблице 4.5.

Таблица 4.5. Флаги доступа и свойств метода

Имя флага Значение Интерпретация
ACC_PUBLIC 0x0001 Объявлен public; может быть доступен извне своего пакета.
ACC_PRIVATE 0x0002 Объявлен private; доступен только внутри определяемого класса.
ACC_PROTECTED 0x0004 Объявлен protected; может быть доступен внутри подклассов.
ACC_STATIC 0x0008 Объявлен static.
ACC_FINAL 0x0010 Объявлен final; не должен переопределяться (§5.4.5).
ACC_SYNCHRONIZED 0x0020 Объявлен synchronized; вызов обернут использованием монитора.
ACC_BRIDGE 0x0040 Мостовой метод, сгенерированный компилятором.
ACC_VARARGS 0x0080 Объявлен с переменным числом аргументов.
ACC_NATIVE 0x0100 Объявлен native; реализован на языке, отличном от Java.
ACC_ABSTRACT 0x0400 Объявлен abstract; реализация не предоставлена.
ACC_STRICT 0x0800 Объявлен strictfp; режим с плавающей точкой — FP-строгий.
ACC_SYNTHETIC 0x1000 Объявлен синтетическим; отсутствует в исходном коде.

Флаг ACC_VARARGS указывает, что этот метод принимает переменное число аргументов на уровне исходного кода. Метод, объявленный с переменным числом аргументов, должен быть скомпилирован с установленным флагом ACC_VARARGS в 1. Все остальные методы должны быть скомпилированы с установленным флагом ACC_VARARGS в 0.

Флаг ACC_BRIDGE используется для обозначения мостового метода, сгенерированного Java-компилятором.

Метод может быть помечен флагом ACC_SYNTHETIC, чтобы указать, что он был сгенерирован компилятором и не отображается в исходном коде, если это не один из методов, перечисленных в §4.7.8.

Методы классов могут устанавливать любые флаги в таблице 4.5. Однако у конкретного метода класса может быть установлен не более одного из флагов ACC_PRIVATE, ACC_PROTECTED и ACC_PUBLIC (JLS §8.4.3). Если для конкретного метода установлен флаг ACC_ABSTRACT, то не должен быть установлен ни один из флагов ACC_FINAL, ACC_NATIVE, ACC_PRIVATE, ACC_STATIC, ACC_STRICT или ACC_SYNCHRONIZED (JLS §8.4.3.1, JLS §8.4.3.3, JLS §8.4.3.4).

Все методы интерфейсов должны иметь установленные флаги ACC_ABSTRACT и ACC_PUBLIC; они могут иметь установленные флаги ACC_VARARGS, ACC_BRIDGE и ACC_SYNTHETIC и не должны иметь установленных других флагов в таблице 4.5 (JLS §9.4).

Конкретный метод инициализации экземпляра (§2.9) может иметь не более одного из флагов ACC_PRIVATE, ACC_PROTECTED и ACC_PUBLIC, а также флаги ACC_STRICT, ACC_VARARGS и ACC_SYNTHETIC, но не должен иметь установленных других флагов в таблице 4.5.

Методы инициализации класса и интерфейса (§2.9) вызываются неявно виртуальной машиной Java. Значение их элемента access_flags игнорируется, за исключением установки флага ACC_STRICT.

Все биты элемента access_flags, не присвоенные в таблице 4.5, зарезервированы для будущих применений. Они должны быть установлены в ноль в сгенерированных файлах class и должны игнорироваться реализациями виртуальной машины Java.

name_index

Значение элемента name_index должно быть допустимым индексом в таблице constant_pool. Элемент constant_pool в этом индексе должен быть структурой CONSTANT_Utf8_info (§4.4.7), представляющей одно из специальных имён методов (§2.9) <init> или <clinit>, или допустимое неопределённое имя (§4.2.2), обозначающее метод.

descriptor_index

Значение элемента descriptor_index должно быть допустимым индексом в таблице constant_pool. Элемент constant_pool в этом индексе должен быть структурой CONSTANT_Utf8_info (§4.4.7), представляющей допустимый дескриптор метода (§4.3.3).

В будущей версии этого спецификации может потребоваться, чтобы последний параметр дескриптора метода был типом массива, если флаг ACC_VARARGS установлен в элементе access_flags.

attributes_count

Значение элемента attributes_count указывает количество дополнительных атрибутов (§4.7) этого метода.

attributes[]

Каждое значение в таблице attributes должно быть структурой атрибута (§4.7). Метод может иметь любое количество необязательных атрибутов.

Атрибуты, определённые в этой спецификации, как присутствующие в таблице attributes структуры method_info — это атрибуты Code (§4.7.3), Exceptions (§4.7.5), Synthetic (§4.7.8), Signature (§4.7.9), Deprecated (§4.7.15), RuntimeVisibleAnnotations (§4.7.16), RuntimeInvisibleAnnotations (§4.7.17), RuntimeVisibleParameterAnnotations (§4.7.18), RuntimeInvisibleParameterAnnotations (§4.7.19) и AnnotationDefault (§4.7.20) атрибуты.

Реализация виртуальной машины Java должна распознавать и правильно читать атрибуты Code (§4.7.3) и Exceptions (§4.7.5), найденные в таблице attributes структуры method_info. Если реализация виртуальной машины Java распознает файлы class с номером версии 49.0 или выше, она должна распознавать и правильно читать атрибуты Signature (§4.7.9), RuntimeVisibleAnnotations (§4.7.16), RuntimeInvisibleAnnotations (§4.7.17), RuntimeVisibleParameterAnnotations (§4.7.18), RuntimeInvisibleParameterAnnotations (§4.7.19) и AnnotationDefault (§4.7.20), найденные в таблице attributes структуры method_info файла class с номером версии 49.0 или выше.

Реализация виртуальной машины Java обязана игнорировать без ошибок любые или все атрибуты в таблице attributes структуры method_info, которые она не распознаёт. Атрибуты, не определённые в этой спецификации, не могут влиять на семантику файла class, а только предоставляют дополнительную описательную информацию (§4.7.1).

4.7. Атрибуты

Атрибуты используются в структурах ClassFile, field_info, method_info и Code_attribute (§4.1, §4.5, §4.6, §4.7.3) формата файла class. Все атрибуты имеют следующий общий формат:

attribute_info {
    u2 attribute_name_index;
    u4 attribute_length;
    u1 info[attribute_length];
}

Для всех атрибутов attribute_name_index должен быть допустимым беззнаковым 16-битным индексом в константном пуле класса. Элемент константного пула constant_pool в позиции attribute_name_index должен быть структурой CONSTANT_Utf8_info (§4.4.7), представляющей имя атрибута. Значение поля attribute_length указывает длину последующей информации в байтах. Длина не включает начальные шесть байтов, содержащих элементы attribute_name_index и attribute_length.

Некоторые атрибуты предопределены в спецификации файла class. Они перечислены в Таблице 4.6, вместе с версией платформы Java SE и версией формата файла class, в которой они впервые появились. В контексте их использования в этой спецификации, то есть в таблицах attributes структур файлов class, в которых они появляются, имена этих предопределённых атрибутов зарезервированы. Из предопределённых атрибутов:

  • Атрибуты ConstantValue, Code и Exceptions должны распознаваться и правильно считываться читателем файла class для корректного интерпретирования файла class реализацией Java Virtual Machine.

  • Атрибуты InnerClasses, EnclosingMethod и Synthetic должны распознаваться и правильно считываться читателем файла class для правильной реализации библиотек классов платформы Java SE (§2.12).

  • Атрибуты RuntimeVisibleAnnotations, RuntimeInvisibleAnnotations, RuntimeVisibleParameterAnnotations, RuntimeInvisibleParameterAnnotations и AnnotationDefault должны распознаваться и правильно считываться читателем файла class для правильной реализации библиотек классов платформы Java SE (§2.12), если номер версии файла class равен или больше 49.0, и реализация Java Virtual Machine распознаёт файлы class с номером версии 49.0 или выше.

  • Атрибут Signature должен распознаваться и правильно считываться читателем файла class, если номер версии файла class равен или больше 49.0, и реализация Java Virtual Machine распознаёт файлы class с номером версии 49.0 или выше.

  • Атрибут StackMapTable должен распознаваться и правильно считываться читателем файла class, если номер версии файла class равен или больше 50.0, и реализация Java Virtual Machine распознаёт файлы class с номером версии 50.0 или выше.

  • Атрибут BootstrapMethods должен распознаваться и правильно считываться читателем файла class, если номер версии файла class равен или больше 51.0, и реализация Java Virtual Machine распознаёт файлы class с номером версии 51.0 или выше.

Использование оставшихся предопределённых атрибутов является необязательным; читатель файла class может использовать содержащуюся в них информацию или иначе должен проигнорировать эти атрибуты.

Таблица 4.6. Предопределённые атрибуты файлов class

Атрибут Раздел Java SE Файл class
ConstantValue §4.7.2 1.0.2 45.3
Code §4.7.3 1.0.2 45.3
StackMapTable §4.7.4 6 50.0
Exceptions §4.7.5 1.0.2 45.3
InnerClasses §4.7.6 1.1 45.3
EnclosingMethod §4.7.7 5.0 49.0
Synthetic §4.7.8 1.1 45.3
Signature §4.7.9 5.0 49.0
SourceFile §4.7.10 1.0.2 45.3
SourceDebugExtension §4.7.11 5.0 49.0
LineNumberTable §4.7.12 1.0.2 45.3
LocalVariableTable §4.7.13 1.0.2 45.3
LocalVariableTypeTable §4.7.14 5.0 49.0
Deprecated §4.7.15 1.1 45.3
RuntimeVisibleAnnotations §4.7.16 5.0 49.0
RuntimeInvisibleAnnotations §4.7.17 5.0 49.0
RuntimeVisibleParameterAnnotations §4.7.18 5.0 49.0
RuntimeInvisibleParameterAnnotations §4.7.19 5.0 49.0
AnnotationDefault §4.7.20 5.0 49.0
BootstrapMethods §4.7.21 7 51.0

4.7.1. Определение и именование новых атрибутов

Компиляторы могут определять и генерировать файлы class, содержащие новые атрибуты в таблицах attributes структур файлов class. Реализации Java Virtual Machine могут распознавать и использовать новые атрибуты, найденные в таблицах attributes структур файлов class. Однако любой атрибут, не определённый в этой спецификации Java Virtual Machine, не должен влиять на семантику типов классов или интерфейсов. Реализации Java Virtual Machine обязаны игнорировать атрибуты, которые они не распознают.

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

Реализации Java Virtual Machine запрещается явно выбрасывать исключение или иным образом отказываться от использования файлов class только из-за наличия нового атрибута. Конечно, инструменты, работающие с файлами class, могут работать неправильно, если им предоставляются файлы class, которые не содержат все необходимые атрибуты.

Два атрибута, предназначенные для разного назначения, но использующие одно и то же имя атрибута и имеющие одинаковую длину, будут конфликтовать в реализациях, распознающих любой из них. Атрибуты, определённые иначе, чем в этой спецификации, должны иметь имена, выбранные в соответствии с соглашениями об именовании пакетов, описанных в Спецификации языка Java, Java SE 7 Edition (JLS §6.1).

Будущие версии этой спецификации могут определить дополнительные атрибуты.

4.7.2. Атрибут ConstantValue

Атрибут ConstantValue — атрибут фиксированной длины в таблице attributes структуры field_info (§4.5). Атрибут ConstantValue представляет значение постоянного поля. В таблице attributes заданной структуры field_info может быть не более одного атрибута ConstantValue. Если поле является статическим (то есть, установлен флаг ACC_STATIC (Таблица 4.4) в элементе access_flags структуры field_info), то постоянное поле, представленное структурой field_info, присваивается значение, указанное атрибутом ConstantValue, как часть инициализации класса или интерфейса, объявляющего постоянное поле (§5.5). Это происходит до вызова метода инициализации класса или интерфейса (§2.9) этого класса или интерфейса.

Если структура field_info, представляющая нестатическое поле, имеет атрибут ConstantValue, то этот атрибут должен быть проигнорирован. Каждая реализация Java Virtual Machine должна распознавать атрибуты ConstantValue.

Атрибут ConstantValue имеет следующий формат:

ConstantValue_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 constantvalue_index;
}

Элементы структуры ConstantValue_attribute следующие:

attribute_name_index

Значение элемента attribute_name_index должно быть допустимым индексом в таблице constant_pool. Запись constant_pool в этом индексе должна быть структурой CONSTANT_Utf8_info (§4.4.7), представляющей строку "ConstantValue".

attribute_length

Значение элемента attribute_length структуры ConstantValue_attribute должно быть равно 2.

constantvalue_index

Значение элемента constantvalue_index должно быть допустимым индексом в таблице constant_pool. Запись constant_pool в этом индексе предоставляет постоянное значение, представленное этим атрибутом. Запись constant_pool должна быть соответствующего типа к полю, как показано в таблице 4.7.

Таблица 4.7. Типы атрибутов постоянного значения

Тип поля Тип записи
long CONSTANT_Long
float CONSTANT_Float
double CONSTANT_Double
int, short, char, byte, boolean CONSTANT_Integer
String CONSTANT_String

4.7.3. Атрибут Code

Атрибут Code — это атрибут переменной длины в таблице attributes структуры method_info (§4.6). Атрибут Code содержит инструкции виртуальной машины Java и вспомогательную информацию для одного метода, метода инициализации экземпляра (§2.9), или метода инициализации класса или интерфейса (§2.9). Каждая реализация виртуальной машины Java должна распознавать атрибуты Code. Если метод является либо native, либо abstract, его структура method_info не должна содержать атрибут Code. В противном случае, его структура method_info должна содержать ровно один атрибут Code.

Атрибут Code имеет следующий формат:

Code_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 max_stack;
    u2 max_locals;
    u4 code_length;
    u1 code[code_length];
    u2 exception_table_length;
    {   u2 start_pc;
        u2 end_pc;
        u2 handler_pc;
        u2 catch_type;
    } exception_table[exception_table_length];
    u2 attributes_count;
    attribute_info attributes[attributes_count];
}

Элементы структуры Code_attribute следующие:

attribute_name_index

Значение элемента attribute_name_index должно быть допустимым индексом в таблице constant_pool. Запись constant_pool в этом индексе должна быть структурой CONSTANT_Utf8_info (§4.4.7) представляющей строку "Code".

attribute_length

Значение элемента attribute_length указывает длину атрибута, за исключением начальных шести байтов.

max_stack

Значение элемента max_stack задает максимальную глубину стека операндов этого метода (§2.6.2) в любой момент во время выполнения метода.

max_locals

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

Наибольший индекс локальной переменной для значения типа long или double равен max_locals - 2. Наибольший индекс локальной переменной для значения любого другого типа равен max_locals - 1.

code_length

Значение элемента code_length задаёт количество байтов в массиве code для данного метода. Значение code_length должно быть больше нуля; массив code не должен быть пустым.

code[]

Массив code даёт фактические байты кода виртуальной машины Java, реализующие метод.

При чтении массива code в память на машине с байтовой адресацией, если первый байт массива выровнен на границе 4 байтов, 32-битные смещения tableswitch и lookupswitch будут выровнены на границе 4 байтов. (См. описания этих инструкций для получения дополнительной информации о последствиях выравнивания массива code.)

Подробные ограничения на содержимое массива code обширны и приведены в отдельном разделе (§4.9).

exception_table_length

Значение элемента exception_table_length задаёт количество записей в таблице exception_table.

exception_table[]

Каждая запись в массиве exception_table описывает один обработчик исключений в массиве code. Порядок обработчиков в массиве exception_table важен (§2.10).

Каждая запись exception_table содержит следующие четыре элемента:

start_pc, end_pc

Значения двух элементов start_pc и end_pc указывают диапазоны в массиве code, в которых активен обработчик исключений. Значение start_pc должно быть допустимым индексом в массиве code для кода инструкции. Значение end_pc либо должно быть допустимым индексом в массиве code для кода инструкции, либо должно быть равно code_length, длине массива code. Значение start_pc должно быть меньше значения end_pc.

start_pc — включительно, а end_pc — исключающее; то есть обработчик исключений должен быть активен, пока счётчик команд находится в интервале [start_pc, end_pc).

Тот факт, что end_pc является исключающим, — это историческая ошибка в проектировании виртуальной машины Java: если код виртуальной машины Java для метода имеет ровно 65535 байтов и заканчивается инструкцией длиной 1 байт, то эта инструкция не может быть защищена обработчиком исключений. Разработчик компилятора может обойти эту ошибку, ограничив максимальный размер сгенерированного кода виртуальной машины Java для любого метода, метода инициализации экземпляра или статического инициализатора (размер любого массива кода) до 65534 байтов.

handler_pc

Значение элемента handler_pc указывает начало обработчика исключений. Значение элемента должно быть допустимым индексом в массиве code и должно быть индексом кода инструкции.

catch_type

Если значение элемента catch_type ненулевое, оно должно быть допустимым индексом в таблице constant_pool. Запись constant_pool в этом индексе должна быть структурой CONSTANT_Class_info (§4.4.1), представляющей класс исключений, который должен быть пойман этим обработчиком исключений. Обработчик исключений будет вызван только в том случае, если брошенное исключение является экземпляром данного класса или одного из его подклассов.

Если значение элемента catch_type равно нулю, этот обработчик исключений вызывается для всех исключений. Это используется для реализации finally (§3.13).

attributes_count

Значение элемента attributes_count указывает количество атрибутов атрибута Code.

attributes[]

Каждое значение в таблице attributes должно быть структурой атрибута (§4.7). Атрибут Code может иметь любое количество дополнительных атрибутов.

Единственные атрибуты, определённые в этом спецификации как появляющиеся в таблице attributes атрибута Code — это атрибуты LineNumberTable (§4.7.12), LocalVariableTable (§4.7.13), LocalVariableTypeTable (§4.7.14) и StackMapTable (§4.7.4).

Если реализация виртуальной машины Java распознаёт файлы версии 50.0 или выше, она должна распознавать и правильно читать атрибуты StackMapTable (§4.7.4), найденные в таблице attributes атрибута Code файла class версии 50.0 или выше.

Реализация виртуальной машины Java обязана игнорировать любые или все атрибуты в таблице attributes атрибута Code, которые она не распознаёт. Атрибуты, не определённые в этой спецификации, не могут влиять на семантику файла class, а только предоставлять дополнительную описательную информацию (§4.7.1).

4.7.4. Атрибут StackMapTable

Атрибут StackMapTable — это атрибут переменной длины в таблице attributes атрибута Code (§4.7.3). Этот атрибут используется во время процесса проверки с помощью проверки типов (§4.10.1). Атрибут Code метода может содержать не более одного атрибута StackMapTable.

Атрибут StackMapTable состоит из нуля или более кадров карты стека. Каждый кадр карты стека задаёт (явно или неявно) смещение байткода, типы проверки (§4.10.1.2) для локальных переменных и типы проверки для стека операндов.

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

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

В файле class, номер версии которого не меньше 50.0, если атрибут Code метода не имеет атрибута StackMapTable, у него есть неявный атрибут карты стека. Этот неявный атрибут карты стека эквивалентен атрибуту StackMapTable с number_of_entries, равным нулю.

Атрибут StackMapTable имеет следующий формат:

StackMapTable_attribute {
    u2              attribute_name_index;
    u4              attribute_length;
    u2              number_of_entries;
    stack_map_frame entries[number_of_entries];
}

Элементы структуры StackMapTable_attribute таковы:

attribute_name_index

Значение элемента attribute_name_index должно быть допустимым индексом в таблице constant_pool. Элемент constant_pool в этой позиции должен быть структурой CONSTANT_Utf8_info (§4.4.7) представляющей строку "StackMapTable".

attribute_length

Значение элемента attribute_length указывает длину атрибута, за исключением начальных шести байтов.

number_of_entries

Значение элемента number_of_entries даёт количество записей stack_map_frame в таблице entries.

entries

Массив entries даёт структуры stack_map_frame метода.

Каждая структура stack_map_frame задаёт состояние типа в определённом смещении байткода. Каждый тип кадра задаёт (явно или неявно) значение, offset_delta, используемое для вычисления фактического смещения байткода, в котором применяется кадр. Смещение байткода, в котором применяется кадр, вычисляется путём добавления offset_delta + 1 к смещению байткода предыдущего кадра, если только предыдущий кадр не является начальным кадром метода, в этом случае смещение байткода равно offset_delta.

Использование разницы смещений, а не фактического смещения байткода, гарантирует, по определению, правильный отсортированный порядок кадров карты стека. Кроме того, последовательное использование формулы offset_delta + 1 для всех явных кадров гарантирует отсутствие дубликатов.

Мы говорим, что инструкция в байткоде имеет соответствующий кадр карты стека, если инструкция начинается со смещения i в массиве code атрибута Code, и атрибут Code имеет атрибут StackMapTable, чей массив записей содержит структуру stack_map_frame, которая применяется в смещении байткода i.

Структура stack_map_frame состоит из однобайтового тега, за которым следуют нуль или более байтов, предоставляющих дополнительную информацию, в зависимости от тега.

Кадр карты стека может принадлежать одному из нескольких типов кадров:

union stack_map_frame {
    same_frame;
    same_locals_1_stack_item_frame;
    same_locals_1_stack_item_frame_extended;
    chop_frame;
    same_frame_extended;
    append_frame;
    full_frame;
}

Все типы кадров, даже full_frame, полагаются на предыдущий кадр для части своей семантики. Это ставит вопрос о том, какой является первый кадр? Начальный кадр неявный и вычисляется из описания метода. (См. предикат Prolog methodInitialStackFrame (§4.10.1.6).)

  • Тип кадра same_frame представлен тегами в диапазоне [0-63]. Если тип кадра равен same_frame, это означает, что кадр имеет ровно те же локальные переменные, что и предыдущий кадр карты стека, и что количество элементов стека равно нулю. Значение offset_delta для кадра — значение элемента тега frame_type.

    same_frame {
        u1 frame_type = SAME; /* 0-63 */
    }
        
  • Тип кадра same_locals_1_stack_item_frame представлен тегами в диапазоне [64, 127]. Если тип кадра same_locals_1_stack_item_frame, это означает, что кадр имеет ровно те же локальные переменные, что и предыдущий кадр карты стека, и что количество элементов стека равно 1. Значение offset_delta для кадра — значение (frame_type - 64). После frame_type следует verification_type_info для одного элемента стека.

    same_locals_1_stack_item_frame {
        u1 frame_type = SAME_LOCALS_1_STACK_ITEM; /* 64-127 */
        verification_type_info stack[1];
    }
    

    Теги в диапазоне [128-246] зарезервированы для будущего использования.

  • Тип кадра same_locals_1_stack_item_frame_extended представлен тегом 247. Тип кадра same_locals_1_stack_item_frame_extended указывает, что кадр имеет ровно те же локальные переменные, что и предыдущий кадр карты стека, и что количество элементов стека равно 1. Значение offset_delta для кадра задано явно. После frame_type следует verification_type_info для одного элемента стека.

    same_locals_1_stack_item_frame_extended {
        u1 frame_type = SAME_LOCALS_1_STACK_ITEM_EXTENDED; /* 247 */
        u2 offset_delta;
        verification_type_info stack[1];
    }
        
  • Тип кадра chop_frame представлен тегами в диапазоне [248-250]. Если frame_type равен chop_frame, это означает, что стек операндов пуст, а текущие локальные переменные совпадают с локальными переменными в предыдущем кадре, за исключением последних k локальных переменных. Значение k задаётся формулой 251 - frame_type.

    chop_frame {
        u1 frame_type = CHOP; /* 248-250 */
        u2 offset_delta;
    }
        
  • Тип кадра same_frame_extended представлен значением тега 251. Если тип кадра same_frame_extended, это означает, что кадр имеет ровно те же локальные переменные, что и предыдущий кадр карты стека, и что количество элементов стека равно нулю.

    same_frame_extended {
        u1 frame_type = SAME_FRAME_EXTENDED; /* 251 */
        u2 offset_delta;
    }
       
  • Тип кадра append_frame представлен тегами в диапазоне [252-254]. Если frame_type равно append_frame, это означает, что стек операндов пуст, а текущие локальные переменные совпадают с локальными переменными в предыдущем кадре, за исключением того, что определены дополнительные k локальных переменных. Значение k задаётся формулой frame_type - 251.

    append_frame {
        u1 frame_type = APPEND; /* 252-254 */
        u2 offset_delta;
        verification_type_info locals[frame_type - 251];
    }
        

    Нулевой элемент в locals представляет тип первой дополнительной локальной переменной. Если locals[M] представляет локальную переменную N, то locals[M+1] представляет локальную переменную N+1, если locals[M] является одним из:

    • Top_variable_info

    • Integer_variable_info

    • Float_variable_info

    • Null_variable_info

    • UninitializedThis_variable_info

    • Object_variable_info

    • Uninitialized_variable_info

    В противном случае locals[M+1] представляет локальную переменную N+2.

    Ошибка, если для любого индекса i, locals[i] представляет локальную переменную, индекс которой больше максимального количества локальных переменных для метода.

  • Тип кадра full_frame представлен значением тега 255.

    full_frame {
        u1 frame_type = FULL_FRAME; /* 255 */
        u2 offset_delta;
        u2 number_of_locals;
        verification_type_info locals[number_of_locals];
        u2 number_of_stack_items;
        verification_type_info stack[number_of_stack_items];
    }
        

    Нулевой элемент в locals представляет тип локальной переменной 0. Если locals[M] представляет локальную переменную N, то locals[M+1] представляет локальную переменную N+1, если locals[M] является одним из:

    • Top_variable_info

    • Integer_variable_info

    • Float_variable_info

    • Null_variable_info

    • UninitializedThis_variable_info

    • Object_variable_info

    • Uninitialized_variable_info

    В противном случае locals[M+1] представляет локальную переменную N+2.

    Ошибка, если для любого индекса i, locals[i] представляет локальную переменную, индекс которой больше максимального количества локальных переменных для метода.

    Нулевой элемент в stack представляет тип элемента стека внизу, а последующие элементы — типы элементов стека ближе к вершине стека операндов. Мы будем называть нижний элемент стека элементом стека 0, а последующие элементы — элементом стека 1, 2 и т. д. Если stack[M] представляет элемент стека N, то stack[M+1] представляет элемент стека N+1, если stack[M] является одним из:

    • Top_variable_info

    • Integer_variable_info

    • Float_variable_info

    • Null_variable_info

    • UninitializedThis_variable_info

    • Object_variable_info

    • Uninitialized_variable_info

    В противном случае stack[M+1] представляет элемент стека N+2.

    Ошибка, если для любого индекса i, stack[i] представляет элемент стека, индекс которого больше максимального размера стека операндов для метода.

Структура verification_type_info состоит из однобайтового тега, за которым следуют нуль или более байтов, предоставляющих дополнительную информацию о теге. Каждая структура verification_type_info указывает тип проверки одного или двух расположений.

union verification_type_info {
    Top_variable_info;
    Integer_variable_info;
    Float_variable_info;
    Long_variable_info;
    Double_variable_info;
    Null_variable_info;
    UninitializedThis_variable_info;
    Object_variable_info;
    Uninitialized_variable_info;
}
  • Тип Top_variable_info указывает, что локальная переменная имеет тип проверки top.

    Top_variable_info {
        u1 tag = ITEM_Top; /* 0 */
    }
        
  • Тип Integer_variable_info указывает, что расположение содержит тип проверки int.

    Integer_variable_info {
        u1 tag = ITEM_Integer; /* 1 */
    }
        
  • Тип Float_variable_info указывает, что расположение содержит тип проверки float.

    Float_variable_info {
        u1 tag = ITEM_Float; /* 2 */
    }
        
  • Тип Long_variable_info указывает, что расположение содержит тип проверки long.

    Long_variable_info {
        u1 tag = ITEM_Long; /* 4 */
    }
        

    Эта структура содержит содержимое двух расположений в стеке операндов или в массиве локальных переменных.

    Если расположение является локальной переменной, то:

    • Это не должна быть локальная переменная с наибольшим индексом.

    • Следующая локальная переменная с более высоким номером содержит тип проверки top.

    Если расположение является элементом стека операндов, то:

    • Текущее расположение не должно быть самым верхним расположением стека операндов.

    • Следующее расположение, более близкое к вершине стека операндов, содержит тип проверки top.

  • Тип Double_variable_info указывает, что расположение содержит тип проверки double.

    Double_variable_info {
        u1 tag = ITEM_Double; /* 3 */
    }
        

    Эта структура содержит содержимое двух расположений в стеке операндов или в массиве локальных переменных.

    Если расположение является локальной переменной, то:

    • Это не должна быть локальная переменная с наибольшим индексом.

    • Следующая локальная переменная с более высоким номером содержит тип проверки top.

    Если расположение является элементом стека операндов, то:

    • Текущее расположение не должно быть самым верхним расположением стека операндов.

    • Следующее расположение, более близкое к вершине стека операндов, содержит тип проверки top.

  • Тип Null_variable_info указывает, что расположение содержит тип проверки null.

    Null_variable_info {
        u1 tag = ITEM_Null; /* 5 */
    }
        
  • Тип UninitializedThis_variable_info указывает, что расположение содержит тип проверки uninitializedThis.

    UninitializedThis_variable_info {
        u1 tag = ITEM_UninitializedThis; /* 6 */
    }
        
  • Тип Object_variable_info указывает, что расположение содержит экземпляр класса, представленного структурой CONSTANT_Class_info (§4.4.1), найденной в таблице constant_pool по индексу cpool_index.

    Object_variable_info {
        u1 tag = ITEM_Object; /* 7 */
        u2 cpool_index;
    }
        
  • Тип Uninitialized_variable_info указывает, что расположение содержит тип проверки uninitialized(offset). Элемент offset указывает смещение в массиве code атрибута Code (§4.7.3), содержащего этот атрибут StackMapTable, инструкции new (§new), которая создала объект, хранящийся в расположении.

    Uninitialized_variable_info {
        u1 tag = ITEM_Uninitialized /* 8 */
        u2 offset;
    }
        

4.7.5. Атрибут Exceptions

Атрибут Exceptions — атрибут переменной длины в таблице attributes структуры method_info (§4.6). Атрибут Exceptions указывает, какие исключения с проверкой может вызвать метод. В каждой структуре method_info может быть не более одного атрибута Exceptions.

Атрибут Exceptions имеет следующий формат:

Exceptions_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 number_of_exceptions;
    u2 exception_index_table[number_of_exceptions];
}

Элементы структуры Exceptions_attribute следующие:

attribute_name_index

Значение элемента attribute_name_index должно быть допустимым индексом в таблице constant_pool. Элемент constant_pool по этому индексу должен быть структурой CONSTANT_Utf8_info (§4.4.7), представляющей строку "Exceptions".

attribute_length

Значение элемента attribute_length указывает длину атрибута, за исключением начальных шести байтов.

number_of_exceptions

Значение элемента number_of_exceptions указывает количество записей в exception_index_table.

exception_index_table[]

Каждое значение в массиве exception_index_table должно быть допустимым индексом в таблице constant_pool. Элемент constant_pool, на который ссылается каждый элемент таблицы, должен быть структурой CONSTANT_Class_info (§4.4.1), представляющей тип класса, который должен быть брошен этим методом.

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

  • Исключение является экземпляром RuntimeException или одного из его подклассов.

  • Исключение является экземпляром Error или одного из его подклассов.

  • Исключение является экземпляром одного из классов исключений, описанных в exception_index_table, или одного из их подклассов.

Эти требования не проверяются в виртуальной машине Java; они проверяются только на этапе компиляции.

4.7.6. Атрибут InnerClasses

Атрибут InnerClasses — атрибут переменной длины в таблице attributes структуры ClassFile (§4.1). Если константный пул класса или интерфейса C содержит запись CONSTANT_Class_info, представляющую класс или интерфейс, который не является членом пакета, то структура ClassFile класса C должна содержать ровно один атрибут InnerClasses в своей таблице attributes.

Атрибут InnerClasses имеет следующий формат:

InnerClasses_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 number_of_classes;
    {   u2 inner_class_info_index;
        u2 outer_class_info_index;
        u2 inner_name_index;
        u2 inner_class_access_flags;
    } classes[number_of_classes];
}

Элементы структуры InnerClasses_attribute следующие:

attribute_name_index

Значение элемента attribute_name_index должно быть допустимым индексом в таблице constant_pool. Запись constant_pool в этой таблице должна быть структурой CONSTANT_Utf8_info (§4.4.7), представляющей строку "InnerClasses".

attribute_length

Значение элемента attribute_length указывает длину атрибута, не включая начальные шесть байтов.

number_of_classes

Значение элемента number_of_classes указывает количество записей в массиве classes.

classes[]

Каждая запись CONSTANT_Class_info в таблице constant_pool, представляющая класс или интерфейс C, который не является членом пакета, должна иметь ровно одну соответствующую запись в массиве classes.

Если класс имеет члены, которые являются классами или интерфейсами, его таблица constant_pool (и, следовательно, атрибут InnerClasses) должен ссылаться на каждого такого члена, даже если этот член не упоминается в классе иным образом. Эти правила подразумевают, что вложенный класс или интерфейсный член будет иметь информацию InnerClasses для каждого окружающего класса и для каждого непосредственного члена.

Каждая запись массива classes содержит следующие четыре элемента:

inner_class_info_index

Значение элемента inner_class_info_index должно быть допустимым индексом в таблице constant_pool. Запись constant_pool в этой таблице должна быть структурой CONSTANT_Class_info (§4.4.1), представляющей C. Остальные элементы записи в массиве classes содержат информацию о C.

outer_class_info_index

Если C не является членом класса или интерфейса (то есть, если C — это класс или интерфейс верхнего уровня (JLS §7.6) или локальный класс (JLS §14.3) или анонимный класс (JLS §15.9.5)), значение элемента outer_class_info_index должно быть равно нулю.

В противном случае, значение элемента outer_class_info_index должно быть допустимым индексом в таблице constant_pool, и запись в этой таблице должна быть структурой CONSTANT_Class_info (§4.4.1), представляющей класс или интерфейс, членом которого является C.

inner_name_index

Если C — анонимный (JLS §15.9.5), значение элемента inner_name_index должно быть равно нулю.

В противном случае, значение элемента inner_name_index должно быть допустимым индексом в таблице constant_pool, и запись в этой таблице должна быть структурой CONSTANT_Utf8_info (§4.4.7), представляющей исходное простое имя C, как оно указано в исходном коде, из которого был скомпилирован этот файл class.

inner_class_access_flags

Значение элемента inner_class_access_flags — маска флагов, используемых для обозначения разрешений доступа и свойств класса или интерфейса C, как они объявлены в исходном коде, из которого был скомпилирован этот файл class. Оно используется компилятором для восстановления исходной информации, когда исходный код недоступен. Флаги показаны в Таблице 4.8.

Таблица 4.8. Флаги доступа и свойств вложенных классов

Имя флага Значение Интерпретация
ACC_PUBLIC 0x0001 Помеченный или неявно public в исходном коде.
ACC_PRIVATE 0x0002 Помеченный private в исходном коде.
ACC_PROTECTED 0x0004 Помеченный protected в исходном коде.
ACC_STATIC 0x0008 Помеченный или неявно static в исходном коде.
ACC_FINAL 0x0010 Помеченный final в исходном коде.
ACC_INTERFACE 0x0200 Был interface в исходном коде.
ACC_ABSTRACT 0x0400 Помеченный или неявно abstract в исходном коде.
ACC_SYNTHETIC 0x1000 Объявлен синтетическим; отсутствует в исходном коде.
ACC_ANNOTATION 0x2000 Объявлен как тип аннотации.
ACC_ENUM 0x4000 Объявлен как тип enum.

Все биты элемента inner_class_access_flags, не назначенные в Таблице 4.8, зарезервированы для будущего использования. Они должны быть установлены в ноль в сгенерированных файлах class и должны игнорироваться реализациями Java Virtual Machine.

Если файл class имеет номер версии не меньше 51.0 и содержит атрибут InnerClasses в своей таблице attributes, то для всех записей в массиве classes атрибута InnerClasses, значение элемента outer_class_info_index должно быть равно нулю, если значение элемента inner_name_index равно нулю.

Реализация Java Virtual Machine от Oracle не проверяет согласованность атрибута InnerClasses с файлом class, представляющим класс или интерфейс, на который ссылается атрибут.

4.7.7. Атрибут EnclosingMethod

Атрибут EnclosingMethod — необязательный атрибут фиксированной длины в таблице attributes структуры ClassFile (§4.1). Класс должен иметь атрибут EnclosingMethod только в том случае, если он является локальным классом или анонимным классом. Класс может иметь не более одного атрибута EnclosingMethod.

Атрибут EnclosingMethod имеет следующий формат:

EnclosingMethod_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 class_index;
    u2 method_index;
}

Элементы структуры EnclosingMethod_attribute следующие:

attribute_name_index

Значение элемента attribute_name_index должно быть допустимым индексом в таблице constant_pool. Запись constant_pool в этой таблице должна быть структурой CONSTANT_Utf8_info (§4.4.7), представляющей строку "EnclosingMethod".

attribute_length

Значение элемента attribute_length равно четырём.

class_index

Значение элемента class_index должно быть допустимым индексом в таблице constant_pool. Запись constant_pool в этой таблице должна быть структурой CONSTANT_Class_info (§4.4.1), представляющей самый внутренний класс, который включает объявление текущего класса.

method_index

Если текущий класс не заключён непосредственно методом или конструктором, значение элемента method_index должно быть равно нулю.

В противном случае, значение элемента method_index должно быть допустимым индексом в таблице constant_pool. Запись constant_pool в этой таблице должна быть структурой CONSTANT_NameAndType_info (§4.4.6), представляющей имя и тип метода в классе, на который ссылается атрибут class_index выше.

Компилятор Java несёт ответственность за то, чтобы метод, идентифицированный через method_index, действительно был самым ближайшим лексически окружающим методом класса, который содержит этот атрибут EnclosingMethod.

4.7.8. Атрибут Synthetic

Атрибут Synthetic — это атрибут фиксированной длины в таблице attributes структуры ClassFile, field_info или method_info (§4.1, §4.5, §4.6). Член класса, который не появляется в исходном коде, должен быть помечен атрибутом Synthetic или иметь установленный флаг ACC_SYNTHETIC. Исключениями из этого требования являются методы, сгенерированные компилятором, которые не считаются артефактами реализации, а именно метод инициализации экземпляра, представляющий собой конструктор по умолчанию языка программирования Java (§2.9), метод инициализации класса (§2.9), и методы Enum.values() и Enum.valueOf().

Атрибут Synthetic был введен в релизе JDK 1.1 для поддержки вложенных классов и интерфейсов.

Атрибут Synthetic имеет следующий формат:

Synthetic_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
}

Элементы структуры Synthetic_attribute следующие:

attribute_name_index

Значение элемента attribute_name_index должно быть допустимым индексом в таблице constant_pool. Элемент constant_pool в этом индексе должен быть структурой CONSTANT_Utf8_info (§4.4.7), представляющей строку "Synthetic".

attribute_length

Значение элемента attribute_length равно нулю.

4.7.9. Атрибут Signature

Атрибут Signature — это необязательный атрибут фиксированной длины в таблице attributes структуры ClassFile, field_info или method_info (§4.1, §4.5, §4.6). Атрибут Signature записывает информацию о генерических сигнатурах для любого класса, интерфейса, конструктора или члена, чья генерическая сигнатура на языке программирования Java включала бы ссылки на переменные типов или параметризованные типы.

Атрибут Signature имеет следующий формат:

Signature_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 signature_index;
}

Элементы структуры Signature_attribute следующие:

attribute_name_index

Значение элемента attribute_name_index должно быть допустимым индексом в таблице constant_pool. Элемент constant_pool в этом индексе должен быть структурой CONSTANT_Utf8_info (§4.4.7), представляющей строку "Signature".

attribute_length

Значение элемента attribute_length структуры Signature_attribute должно быть 2.

signature_index

Значение элемента signature_index должно быть допустимым индексом в таблице constant_pool. Элемент константного пула в этом индексе должен быть структурой CONSTANT_Utf8_info (§4.4.7), представляющей сигнатуру класса (§4.3.4), если этот атрибут Signature — атрибут структуры ClassFile; сигнатуру метода, если этот атрибут Signature — атрибут структуры method_info; или сигнатуру типа поля в противном случае.

4.7.10. Атрибут SourceFile

Атрибут SourceFile — это необязательный атрибут фиксированной длины в таблице attributes структуры ClassFile (§4.1). В таблице attributes данной структуры ClassFile может быть не более одного атрибута SourceFile.

Атрибут SourceFile имеет следующий формат:

SourceFile_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 sourcefile_index;
}

Элементы структуры SourceFile_attribute следующие:

attribute_name_index

Значение элемента attribute_name_index должно быть допустимым индексом в таблице constant_pool. Элемент constant_pool в этом индексе должен быть структурой CONSTANT_Utf8_info (§4.4.7), представляющей строку "SourceFile".

attribute_length

Значение элемента attribute_length структуры SourceFile_attribute должно быть 2.

sourcefile_index

Значение элемента sourcefile_index должно быть допустимым индексом в таблице constant_pool. Элемент константного пула в этом индексе должен быть структурой CONSTANT_Utf8_info (§4.4.7), представляющей строку.

Строка, на которую ссылается элемент sourcefile_index, будет интерпретирована как указывающая имя исходного файла, из которого был скомпилирован данный файл class. Она не будет интерпретирована как указывающая на имя каталога, содержащего файл, или абсолютный путь к файлу; такая платформа-специфичная дополнительная информация должна быть предоставлена интерпретатором во время выполнения или инструментом разработки в момент фактического использования имени файла.

4.7.11. Атрибут SourceDebugExtension

Атрибут SourceDebugExtension — это необязательный атрибут в таблице attributes структуры ClassFile (§4.1). В таблице attributes данной структуры ClassFile может быть не более одного атрибута SourceDebugExtension.

Атрибут SourceDebugExtension имеет следующий формат:

SourceDebugExtension_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u1 debug_extension[attribute_length];
}

Элементы структуры SourceDebugExtension_attribute следующие:

attribute_name_index

Значение элемента attribute_name_index должно быть допустимым индексом в таблице constant_pool. Элемент constant_pool в этом индексе должен быть структурой CONSTANT_Utf8_info (§4.4.7), представляющей строку "SourceDebugExtension".

attribute_length

Значение элемента attribute_length указывает длину атрибута, за исключением начальных шести байт.

Значение элемента attribute_length — это число байт в элементе debug_extension[].

debug_extension[]

Массив debug_extension хранит расширенную отладочную информацию, которая не имеет семантического влияния на виртуальную машину Java. Информация представлена с помощью модифицированной строки UTF-8 (§4.4.7) без завершающего нулевого байта.

Обратите внимание, что массив debug_extension может обозначать строку, длина которой больше, чем может быть представлена экземпляром класса String.

4.7.12. Атрибут LineNumberTable

Атрибут LineNumberTable — это необязательный атрибут переменной длины в таблице attributes атрибута Code (§4.7.3). Он может использоваться отладчиками для определения, какой фрагмент массива code виртуальной машины Java соответствует заданной строке исходного файла.

Если атрибуты LineNumberTable присутствуют в таблице attributes данного атрибута Code, то они могут быть расположены в любом порядке. Кроме того, несколько атрибутов LineNumberTable могут совместно представлять одну строку исходного файла; то есть, атрибуты LineNumberTable не обязательно соответствуют одной строке исходного кода.

Атрибут LineNumberTable имеет следующий формат:

LineNumberTable_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 line_number_table_length;
    {   u2 start_pc;
        u2 line_number;	
    } line_number_table[line_number_table_length];
}

Элементы структуры LineNumberTable_attribute следующие:

attribute_name_index

Значение элемента attribute_name_index должно быть корректным индексом в таблице constant_pool. Элемент таблицы constant_pool с этим индексом должен быть структурой CONSTANT_Utf8_info (§4.4.7) представляющей строку "LineNumberTable".

attribute_length

Значение элемента attribute_length указывает длину атрибута, за исключением начальных шести байтов.

line_number_table_length

Значение элемента line_number_table_length указывает количество элементов в массиве line_number_table.

line_number_table[]

Каждый элемент массива line_number_table указывает, что номер строки в исходном файле меняется в заданной точке массива code. Каждый элемент line_number_table должен содержать следующие два элемента:

start_pc

Значение элемента start_pc должно указывать индекс в массиве code, в котором начинается код новой строки в исходном файле.

Значение start_pc должно быть меньше значения элемента code_length атрибута Code, к которому данный атрибут LineNumberTable относится.

line_number

Значение элемента line_number должно указывать соответствующий номер строки в исходном файле.

4.7.13. Атрибут LocalVariableTable

Атрибут LocalVariableTable — это необязательный атрибут переменной длины в таблице attributes атрибута Code (§4.7.3). Он может использоваться отладчиками для определения значения заданной локальной переменной во время выполнения метода.

Если атрибуты LocalVariableTable присутствуют в таблице attributes данного атрибута Code, то они могут быть расположены в любом порядке. Для каждой локальной переменной в атрибуте Code может быть не более одного атрибута LocalVariableTable.

Атрибут LocalVariableTable имеет следующий формат:

LocalVariableTable_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 local_variable_table_length;
    {   u2 start_pc;
        u2 length;
        u2 name_index;
        u2 descriptor_index;
        u2 index;
    } local_variable_table[local_variable_table_length];
}

Элементы структуры LocalVariableTable_attribute следующие:

attribute_name_index

Значение элемента attribute_name_index должно быть корректным индексом в таблице constant_pool. Элемент таблицы constant_pool с этим индексом должен быть структурой CONSTANT_Utf8_info (§4.4.7) представляющей строку "LocalVariableTable".

attribute_length

Значение элемента attribute_length указывает длину атрибута, за исключением начальных шести байтов.

local_variable_table_length

Значение элемента local_variable_table_length указывает количество элементов в массиве local_variable_table.

local_variable_table[]

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

start_pc, length

Данная локальная переменная должна иметь значение в индексах массива code в интервале [start_pc, start_pc + length), то есть между start_pc включительно и start_pc + length исключающее.

Значение start_pc должно быть корректным индексом в массиве code данного атрибута Code и должно быть индексом оператора инструкции.

Значение start_pc + length должно быть либо корректным индексом в массиве code данного атрибута Code и индексом оператора инструкции, либо первым индексом за пределами массива code.

name_index

Значение элемента name_index должно быть корректным индексом в таблице constant_pool. Элемент таблицы constant_pool с этим индексом должен содержать структуру CONSTANT_Utf8_info (§4.4.7) представляющую корректное неоквалифицированное имя (§4.2.2) локальной переменной.

descriptor_index

Значение элемента descriptor_index должно быть корректным индексом в таблице constant_pool. Элемент таблицы constant_pool с этим индексом должен содержать структуру CONSTANT_Utf8_info (§4.4.7) представляющую дескриптор поля (§4.3.2) кодирующий тип локальной переменной в исходной программе.

index

Данная локальная переменная должна находиться по индексу index в массиве локальных переменных текущей рамки.

Если локальная переменная по индексу index имеет тип double или long, она занимает позиции index и index + 1.

4.7.14. Атрибут LocalVariableTypeTable

Атрибут LocalVariableTypeTable — это необязательный атрибут переменной длины в таблице attributes атрибута Code (§4.7.3). Он может использоваться отладчиками для определения значения заданной локальной переменной во время выполнения метода.

Если атрибуты LocalVariableTypeTable присутствуют в таблице attributes заданного атрибута Code, то они могут появляться в произвольном порядке. Не может быть более одного атрибута LocalVariableTypeTable на одну локальную переменную в атрибуте Code.

Атрибут LocalVariableTypeTable отличается от атрибута LocalVariableTable тем, что он предоставляет информацию о сигнатуре, а не о дескрипторе. Это различие имеет значение только для переменных, тип которых является обобщенным ссылочным типом. Такие переменные будут отображаться в обеих таблицах, в то время как переменные других типов будут отображаться только в таблице LocalVariableTable.

Атрибут LocalVariableTypeTable имеет следующий формат:

LocalVariableTypeTable_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 local_variable_type_table_length;
    {   u2 start_pc;
        u2 length;
        u2 name_index;
        u2 signature_index;
        u2 index;
    } local_variable_type_table[local_variable_type_table_length];
}

Элементы структуры LocalVariableTypeTable следующие:

attribute_name_index

Значение элемента attribute_name_index должно быть допустимым индексом в таблице constant_pool. Элемент constant_pool в этом индексе должен быть структурой CONSTANT_Utf8_info (§4.4.7), представляющей строку "LocalVariableTypeTable".

attribute_length

Значение элемента attribute_length указывает длину атрибута, за исключением начальных шести байтов.

local_variable_type_table_length

Значение элемента local_variable_type_table_length указывает количество элементов в массиве local_variable_type_table.

local_variable_type_table[]

Каждый элемент массива local_variable_type_table указывает диапазон смещений массива code, в пределах которого локальная переменная имеет значение. Он также указывает индекс в массиве локальных переменных текущей фрейма, в котором находится эта локальная переменная. Каждый элемент должен содержать следующие пять элементов:

start_pc, length

Указанная локальная переменная должна иметь значение в индексах массива кода в интервале [start_pc, start_pc + length), то есть между start_pc включительно и start_pc + length не включительно.

Значение start_pc должно быть допустимым индексом в массиве code этого атрибута Code и должно быть индексом кода инструкции.

Значение start_pc + length должно быть либо допустимым индексом в массиве code этого атрибута Code и быть индексом кода инструкции, либо должно быть первым индексом за пределами конца этого массива code.

name_index

Значение элемента name_index должно быть допустимым индексом в таблице constant_pool. Элемент constant_pool в этом индексе должен содержать структуру CONSTANT_Utf8_info (§4.4.7) представляющую допустимое неопределенное имя (§4.2.2), обозначающее локальную переменную.

signature_index

Значение элемента signature_index должно быть допустимым индексом в таблице constant_pool. Элемент constant_pool в этом индексе должен содержать структуру CONSTANT_Utf8_info (§4.4.7) представляющую сигнатуру типа поля (§4.3.4), кодирующую тип локальной переменной в исходной программе.

index

Указанная локальная переменная должна находиться в позиции index в массиве локальных переменных текущей фрейма.

Если локальная переменная в позиции index имеет тип double или long, она занимает позиции в обоих массивах: index и index + 1.

4.7.15. Атрибут Deprecated

Атрибут Deprecated — это необязательный атрибут фиксированной длины в таблице attributes атрибута ClassFile, field_info или структуры method_info (§4.1, §4.5, §4.6). Класс, интерфейс, метод или поле могут быть помечены атрибутом Deprecated, чтобы указать, что класс, интерфейс, метод или поле устарели.

Интерпретатор или инструмент, считывающий формат файла class, такие как компилятор, могут использовать эту маркировку, чтобы проинформировать пользователя о том, что используется устаревший класс, интерфейс, метод или поле. Наличие атрибута Deprecated не изменяет семантику класса или интерфейса.

Атрибут Deprecated имеет следующий формат:

Deprecated_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
}

Элементы структуры атрибута Deprecated следующие:

attribute_name_index

Значение элемента attribute_name_index должно быть допустимым индексом в таблице constant_pool. Элемент constant_pool в этом индексе должен быть структурой CONSTANT_Utf8_info (§4.4.7), представляющей строку "Deprecated".

attribute_length

Значение элемента attribute_length равно нулю.

4.7.16. Атрибут RuntimeVisibleAnnotations

Атрибут RuntimeVisibleAnnotations представляет собой атрибут переменной длины в таблице attributes структуры ClassFile, field_info или method_info (§4.1, §4.5, §4.6). Атрибут RuntimeVisibleAnnotations записывает видимые во время выполнения аннотации языка программирования Java для соответствующего класса, поля или метода.

Каждая структура ClassFile, field_info и method_info может содержать не более одного атрибута RuntimeVisibleAnnotations, который записывает все видимые во время выполнения аннотации языка программирования Java для соответствующего элемента программы. Виртуальная машина Java должна сделать эти аннотации доступными, чтобы их можно было вернуть с помощью соответствующих рефлексивных API.

Атрибут RuntimeVisibleAnnotations имеет следующий формат:

RuntimeVisibleAnnotations_attribute {
    u2         attribute_name_index;
    u4         attribute_length;
    u2         num_annotations;
    annotation annotations[num_annotations];
}

Элементы структуры RuntimeVisibleAnnotations_attribute следующие:

attribute_name_index

Значение элемента attribute_name_index должно быть допустимым индексом в таблице constant_pool. Запись constant_pool в этом индексе должна быть структурой CONSTANT_Utf8_info (§4.4.7) представляющей строку "RuntimeVisibleAnnotations".

attribute_length

Значение элемента attribute_length указывает длину атрибута, исключая начальные шесть байтов.

Значение элемента attribute_length зависит от количества аннотаций, видимых во время выполнения, представленных структурой, и их значений.

num_annotations

Значение элемента num_annotations указывает количество аннотаций, видимых во время выполнения, представленных структурой.

Обратите внимание, что к элементу программы может быть непосредственно прикреплено не более 65535 аннотаций языка программирования Java, видимых во время выполнения.

annotations

Каждое значение таблицы annotations представляет собой одну аннотацию, видимую во время выполнения, для элемента программы. Структура аннотации имеет следующий формат:

annotation {
    u2 type_index;
    u2 num_element_value_pairs;
    {   u2            element_name_index;
        element_value value;
    } element_value_pairs[num_element_value_pairs];
}
      

Элементы структуры annotation следующие:

type_index

Значение элемента type_index должно быть допустимым индексом в таблице constant_pool. Запись constant_pool в этом индексе должна быть структурой CONSTANT_Utf8_info (§4.4.7) представляющей описатель поля, представляющий тип аннотации, соответствующий аннотации, представленной этой структурой annotation.

num_element_value_pairs

Значение элемента num_element_value_pairs указывает количество пар имя-значение элемента аннотации, представленной этой структурой annotation.

Обратите внимание, что в одной аннотации может содержаться не более 65535 пар имя-значение элемента.

element_value_pairs

Каждое значение таблицы element_value_pairs представляет собой одну пару имя-значение элемента в аннотации, представленной этой структурой annotation. Каждая запись element_value_pairs содержит следующие два элемента:

element_name_index

Значение элемента element_name_index должно быть допустимым индексом в таблице constant_pool. Запись constant_pool в этом индексе должна быть структурой CONSTANT_Utf8_info (§4.4.7) представляющей допустимый описатель поля (§4.3.2), обозначающий имя элемента типа аннотации, представленного этой записью element_value_pairs.

value

Значение элемента value представляет собой значение пары имя-значение элемента, представленной этой записью element_value_pairs.

4.7.16.1. Структура element_value

Структура element_value — это различающееся объединение, представляющее значение пары «элемент-значение». Она используется для представления значений элементов во всех атрибутах, описывающих аннотации (RuntimeVisibleAnnotations, RuntimeInvisibleAnnotations, RuntimeVisibleParameterAnnotations и RuntimeInvisibleParameterAnnotations).

Структура element_value имеет следующий формат:

element_value {
    u1 tag;
    union {
        u2 const_value_index;

        {   u2 type_name_index;
            u2 const_name_index;
        } enum_const_value;

        u2 class_info_index;

        annotation annotation_value;

        {   u2            num_values;
            element_value values[num_values];
        } array_value;
    } value;
}

Элементы структуры element_value следующие:

tag

Элемент tag указывает тип этой пары «элемент-значение» аннотации.

Буквы B, C, D, F, I, J, S и Z обозначают примитивный тип. Эти буквы интерпретируются так, как если бы они были дескрипторами полей (§4.3.2).

Другие допустимые значения для tag с их интерпретациями перечислены в таблице 4.9.

Таблица 4.9. Интерпретация дополнительных значений тега

tag Значение Тип элемента
s String
e постоянная перечисления
c класс
@ тип аннотации
[ массив

value

Элемент value представляет значение этого элемента аннотации. Этот элемент является объединением. Элемент tag выше определяет, какой элемент объединения следует использовать:

const_value_index

Элемент const_value_index используется, если элемент tag равен одному из B, C, D, F, I, J, S, Z или s.

Значение элемента const_value_index должно быть допустимым индексом в таблице constant_pool. Элемент constant_pool в этой позиции должен иметь правильный тип для типа поля, обозначенного элементом tag, как указано в таблице 4.9.

enum_const_value

Элемент enum_const_value используется, если элемент tag равен e.

Элемент enum_const_value состоит из следующих двух элементов:

type_name_index

Значение элемента type_name_index должно быть допустимым индексом в таблице constant_pool. Элемент constant_pool в этой позиции должен быть структурой CONSTANT_Utf8_info (§4.4.7), представляющей допустимый дескриптор поля (§4.3.2), который обозначает внутреннюю форму двоичного имени (§4.2.1) типа константы перечисления, представленной этой структурой element_value.

const_name_index

Значение элемента const_name_index должно быть допустимым индексом в таблице constant_pool. Элемент constant_pool в этой позиции должен быть структурой CONSTANT_Utf8_info (§4.4.7), представляющей простое имя константы перечисления, представленной этой структурой element_value.

class_info_index

Элемент class_info_index используется, если элемент tag равен c.

Элемент class_info_index должен быть допустимым индексом в таблице constant_pool. Элемент constant_pool в этой позиции должен быть структурой CONSTANT_Utf8_info (§4.4.7), представляющей дескриптор возвращаемого значения (§4.3.3) типа, представленного классом, обозначенным этой структурой element_value.

Например, V для Void.class, Ljava/lang/Object; для Object и т.д.

annotation_value

Элемент annotation_value используется, если элемент tag равен @.

Структура element_value представляет «вложенную» аннотацию.

array_value

Элемент array_value используется, если элемент tag равен [.

Элемент array_value состоит из следующих двух элементов:

num_values

Значение элемента num_values указывает количество элементов в массиве, представленном этой структурой element_value.

Обратите внимание, что в массиве допускается не более 65535 элементов.

values

Каждый элемент таблицы values дает значение элемента массива, представленного этой структурой element_value.

4.7.17. Атрибут RuntimeInvisibleAnnotations

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

Атрибут RuntimeInvisibleAnnotations — это атрибут переменной длины в таблице attributes структуры ClassFile, field_info или method_info (§4.1, §4.5, §4.6). Атрибут RuntimeInvisibleAnnotations записывает невидимые во время выполнения аннотации языка программирования Java для соответствующего класса, метода или поля.

Каждая структура ClassFile, field_info и method_info может содержать не более одного атрибута RuntimeInvisibleAnnotations, который записывает все невидимые во время выполнения аннотации языка программирования Java для соответствующего элемента программы.

Атрибут RuntimeInvisibleAnnotations имеет следующий формат:

RuntimeInvisibleAnnotations_attribute {
    u2         attribute_name_index;
    u4         attribute_length;
    u2         num_annotations;
    annotation annotations[num_annotations];
}

Элементы структуры RuntimeInvisibleAnnotations_attribute следующие:

attribute_name_index

Значение элемента attribute_name_index должно быть допустимым индексом в таблице constant_pool. Элемент constant_pool в этой позиции должен быть структурой CONSTANT_Utf8_info (§4.4.7), представляющей строку "RuntimeInvisibleAnnotations".

attribute_length

Значение элемента attribute_length указывает длину атрибута, исключая начальные шесть байтов.

Значение элемента attribute_length зависит от количества представленных аннотаций и их значений.

num_annotations

Значение элемента num_annotations указывает количество невидимых во время выполнения аннотаций, представленных структурой.

Обратите внимание, что к элементу программы может быть непосредственно прикреплено не более 65535 аннотаций языка программирования Java, невидимых во время выполнения.

annotations

Каждый элемент таблицы annotations представляет отдельную невидимую во время выполнения аннотацию элемента программы.

4.7.18. Атрибут RuntimeVisibleParameterAnnotations

Атрибут RuntimeVisibleParameterAnnotations — это атрибут переменной длины в таблице attributes структуры method_info (§4.6). Атрибут RuntimeVisibleParameterAnnotations записывает видимые во время выполнения аннотации языка программирования Java для параметров соответствующего метода.

Каждая структура method_info может содержать не более одного атрибута RuntimeVisibleParameterAnnotations, который записывает все видимые во время выполнения аннотации языка программирования Java для параметров соответствующего метода. Виртуальная машина Java должна предоставлять эти аннотации для возврата соответствующими рефлексивными API.

Атрибут RuntimeVisibleParameterAnnotations имеет следующий формат:

RuntimeVisibleParameterAnnotations_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u1 num_parameters;
    {   u2         num_annotations;
        annotation annotations[num_annotations];
    } parameter_annotations[num_parameters];
}

Элементы структуры RuntimeVisibleParameterAnnotations_attribute следующие:

attribute_name_index

Значение элемента attribute_name_index должно быть корректным индексом в таблице constant_pool. Элемент constant_pool в этом индексе должен быть структурой CONSTANT_Utf8_info (§4.4.7), представляющей строку "RuntimeVisibleParameterAnnotations".

attribute_length

Значение элемента attribute_length указывает длину атрибута, исключая начальные шесть байт.

Значение элемента attribute_length зависит от количества параметров, количества видимых во время выполнения аннотаций для каждого параметра и их значений.

num_parameters

Значение элемента num_parameters задаёт количество параметров метода, представленного структурой method_info, к которому относится аннотация. (Это дублирует информацию, которую можно извлечь из описания метода (§4.3.3).)

parameter_annotations

Каждое значение в таблице parameter_annotations представляет все видимые во время выполнения аннотации для одного параметра. Последовательность значений в таблице соответствует последовательности параметров в описании метода. Каждый элемент parameter_annotations содержит следующие два элемента:

num_annotations

Значение элемента num_annotations указывает количество видимых во время выполнения аннотаций для параметра, соответствующего порядковому номеру этого элемента parameter_annotations.

annotations

Каждое значение в таблице annotations представляет отдельную видимую во время выполнения аннотацию для параметра, соответствующего порядковому номеру этого элемента parameter_annotations.

4.7.19. Атрибут RuntimeInvisibleParameterAnnotations

Атрибут RuntimeInvisibleParameterAnnotations аналогичен атрибуту RuntimeVisibleParameterAnnotations, за исключением того, что аннотации, представленные атрибутом RuntimeInvisibleParameterAnnotations, не должны предоставляться для возврата рефлексивными API, если виртуальная машина Java не получила явных инструкций по сохранению этих аннотаций через какой-либо механизм, специфичный для реализации, такой как флаг командной строки. При отсутствии таких инструкций виртуальная машина Java игнорирует этот атрибут.

Атрибут RuntimeInvisibleParameterAnnotations — это атрибут переменной длины в таблице attributes структуры method_info (§4.6). Атрибут RuntimeInvisibleParameterAnnotations записывает невидимые во время выполнения аннотации языка программирования Java для параметров соответствующего метода.

Каждая структура method_info может содержать не более одного атрибута RuntimeInvisibleParameterAnnotations, который записывает все невидимые во время выполнения аннотации языка программирования Java для параметров соответствующего метода.

Атрибут RuntimeInvisibleParameterAnnotations имеет следующий формат:

RuntimeInvisibleParameterAnnotations_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u1 num_parameters;
    {   u2         num_annotations;
        annotation annotations[num_annotations];
    } parameter_annotations[num_parameters];
}

Элементы структуры RuntimeInvisibleParameterAnnotations_attribute следующие:

attribute_name_index

Значение элемента attribute_name_index должно быть корректным индексом в таблице constant_pool. Элемент constant_pool в этом индексе должен быть структурой CONSTANT_Utf8_info (§4.4.7), представляющей строку "RuntimeInvisibleParameterAnnotations".

attribute_length

Значение элемента attribute_length указывает длину атрибута, исключая начальные шесть байт.

Значение элемента attribute_length зависит от количества параметров, количества невидимых во время выполнения аннотаций для каждого параметра и их значений.

num_parameters

Значение элемента num_parameters задаёт количество параметров метода, представленного структурой method_info, к которому относится аннотация. (Это дублирует информацию, которую можно извлечь из описания метода (§4.3.3).)

parameter_annotations

Каждое значение в таблице parameter_annotations представляет все невидимые во время выполнения аннотации для одного параметра. Последовательность значений в таблице соответствует последовательности параметров в описании метода. Каждый элемент parameter_annotations содержит следующие два элемента:

num_annotations

Значение элемента num_annotations указывает количество невидимых во время выполнения аннотаций для параметра, соответствующего порядковому номеру этого элемента parameter_annotations.

annotations

Каждое значение в таблице annotations представляет отдельную невидимую во время выполнения аннотацию для параметра, соответствующего порядковому номеру этого элемента parameter_annotations.

4.7.20. Атрибут AnnotationDefault

Атрибут AnnotationDefault — это атрибут переменной длины в таблице attributes определённых структур method_info (§4.6), а именно тех, которые представляют элементы типов аннотаций. Атрибут AnnotationDefault записывает значение по умолчанию для элемента, представленного структурой method_info.

Каждая структура method_info, представляющая элемент типа аннотации, может содержать не более одного атрибута AnnotationDefault. Виртуальная машина Java должна предоставлять это значение по умолчанию для применения соответствующими рефлексивными API.

Атрибут AnnotationDefault имеет следующий формат:

AnnotationDefault_attribute {
    u2            attribute_name_index;
    u4            attribute_length;
    element_value default_value;
}

Элементы структуры AnnotationDefault_attribute следующие:

attribute_name_index

Значение элемента attribute_name_index должно быть корректным индексом в таблице constant_pool. Элемент constant_pool в этом индексе должен быть структурой CONSTANT_Utf8_info (§4.4.7), представляющей строку "AnnotationDefault".

attribute_length

Значение элемента attribute_length указывает длину атрибута, исключая начальные шесть байт.

Значение элемента attribute_length зависит от значения по умолчанию.

default_value

Элемент default_value представляет значение по умолчанию для элемента типа аннотации, значение по умолчанию которого представлено этим атрибутом AnnotationDefault.

4.7.21. Атрибут BootstrapMethods

Атрибут BootstrapMethods — это атрибут переменной длины в таблице attributes структуры ClassFile (§4.1). Атрибут BootstrapMethods записывает спецификаторы методов загрузки, на которые ссылаются инструкции invokedynamic (§invokedynamic).

В таблице attributes заданной структуры ClassFile должен быть ровно один атрибут BootstrapMethods, если таблица ClassFile этой структуры содержит по крайней мере одну запись CONSTANT_InvokeDynamic_info (§4.4.10). В таблице attributes заданной структуры ClassFile не может быть более одного атрибута BootstrapMethods.

Атрибут BootstrapMethods имеет следующий формат:

BootstrapMethods_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 num_bootstrap_methods;
    {   u2 bootstrap_method_ref;
        u2 num_bootstrap_arguments;
        u2 bootstrap_arguments[num_bootstrap_arguments];
    } bootstrap_methods[num_bootstrap_methods];
}

Элементы структуры BootstrapMethods_attribute следующие:

attribute_name_index

Значение элемента attribute_name_index должно быть корректным индексом в таблице constant_pool. Запись constant_pool в этой таблице по этому индексу должна быть структурой CONSTANT_Utf8_info (§4.4.7) представляющей строку "BootstrapMethods".

attribute_length

Значение элемента attribute_length указывает длину атрибута, за исключением начальных шести байтов.

Значение элемента attribute_length зависит от количества инструкций invokedynamic в этой структуре ClassFile.

num_bootstrap_methods

Значение элемента num_bootstrap_methods определяет количество спецификаторов методов загрузки в массиве bootstrap_methods.

bootstrap_methods[]

Каждая запись в массиве bootstrap_methods содержит индекс структуры CONSTANT_MethodHandle_info (§4.4.8), которая определяет метод загрузки, и последовательность (возможно, пустую) индексов статических аргументов для метода загрузки.

Каждая запись bootstrap_methods должна содержать следующие три элемента:

bootstrap_method_ref

Значение элемента bootstrap_method_ref должно быть корректным индексом в таблице constant_pool. Запись constant_pool по этому индексу должна быть структурой CONSTANT_MethodHandle_info (§4.4.8).

Элемент reference_kind структуры CONSTANT_MethodHandle_info должен иметь значение 6 (REF_invokeStatic) или 8 (REF_newInvokeSpecial) (§5.4.3.5), иначе вызов дескриптора метода загрузки во время разрешения спецификатора места вызова для инструкции invokedynamic завершится неожиданно.

num_bootstrap_arguments

Значение элемента num_bootstrap_arguments задаёт количество элементов в массиве bootstrap_arguments.

bootstrap_arguments

Каждая запись в массиве bootstrap_arguments должна быть корректным индексом в таблице constant_pool. Запись constant_pool по этому индексу должна быть структурой CONSTANT_String_info, CONSTANT_Class_info, CONSTANT_Integer_info, CONSTANT_Long_info, CONSTANT_Float_info, CONSTANT_Double_info, CONSTANT_MethodHandle_info или CONSTANT_MethodType_info (§4.4.3, §4.4.1, §4.4.4, §4.4.5), §4.4.8, §4.4.9).

4.8. Проверка формата

При загрузке потенциального файла class (§5.3) Java Virtual Machine сначала проверяет, соответствует ли файл основному формату файла class (§4.1). Этот процесс называется проверкой формата. Первые четыре байта должны содержать правильное магическое число. Все распознаваемые атрибуты должны иметь правильную длину. Файл class не должен быть усечён или иметь лишние байты в конце. Пул констант не должен содержать какой-либо непознаваемой информации.

Эта проверка целостности файла class необходима для интерпретации содержимого файла class.

Проверка формата отличается от проверки байткода. Оба являются частью процесса проверки. Исторически проверка формата была смешана с проверкой байткода, поскольку оба являются формой проверки целостности.

4.9. Ограничения на код Java Virtual Machine

Код Java Virtual Machine для метода, метода инициализации экземпляра или метода инициализации класса или интерфейса (§2.9) хранится в массиве code атрибута Code структуры method_info файла class (§4.6, §4.7.3). В этом разделе описываются ограничения, связанные с содержимым структуры Code_attribute.

4.9.1. Статические ограничения

Статические ограничения на файл class определяют корректность файла. За исключением статических ограничений на код Java Virtual Machine файла class, эти ограничения были приведены в предыдущих разделах. Статические ограничения на код Java Virtual Machine в файле class определяют, как инструкции Java Virtual Machine должны быть расположены в массиве code и какими должны быть операнды отдельных инструкций.

Статические ограничения на инструкции в массиве code следующие:

  • Массив code не должен быть пустым, поэтому значение элемента code_length не может быть 0.

  • Значение элемента code_length должно быть меньше 65536.

  • Код первой инструкции в массиве code начинается с индекса 0.

  • В массиве code могут присутствовать только инструкции, описанные в §6.5. Инструкции, использующие зарезервированные коды операций (§6.2) или любые коды операций, не документированные в этом спецификации, не должны присутствовать в массиве code.

  • Если версия файла class — 51.0 или выше, то ни jsr код операции, ни jsr_w код операции не должны присутствовать в массиве code.

  • Для каждой инструкции в массиве code, кроме последней, индекс кода операции следующей инструкции равен индексу кода операции текущей инструкции плюс длина этой инструкции, включая все её операнды.

    Инструкция wide обрабатывается как любая другая инструкция в этих целях; код операции, указывающий операцию, которую инструкция wide должна изменить, обрабатывается как один из операндов этой инструкции wide. Этот код операции никогда не должен быть напрямую достижим в ходе вычислений.

  • Последний байт последней инструкции в массиве code должен быть байтом с индексом code_length - 1.

Статические ограничения на операнды инструкций в массиве code следующие:

END_OF_DOCUMENT_MARKER
  • Целью каждой инструкции перехода и ветвления (jsr, jsr_w, goto, goto_w, ifeq, ifne, ifle, iflt, ifge, ifgt, ifnull, ifnonnull, if_icmpeq, if_icmpne, if_icmple, if_icmplt, if_icmpge, if_icmpgt, if_acmpeq, if_acmpne) должна быть операция в пределах данного метода.

    Целью инструкции перехода или ветвления никогда не может быть операция, используемая для указания операции, подлежащей модификации инструкцией wide; целью перехода или ветвления может быть сама инструкция wide.

  • Каждая цель, включая по умолчанию, каждой инструкции tableswitch должна быть операцией в рамках данного метода.

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

    Ни одна цель инструкции tableswitch не может быть операцией, используемой для указания операции, подлежащей модификации инструкцией wide; целью tableswitch может быть сама инструкция wide.

  • Каждая цель, включая по умолчанию, каждой инструкции lookupswitch должна быть операцией в пределах данного метода.

    Инструкция lookupswitch должна иметь количество пар «сопоставление-смещение», соответствующее значению ее операнда npairs. Пары «сопоставление-смещение» должны быть отсортированы по возрастанию знака значения сопоставления.

    Ни одна цель инструкции lookupswitch не может быть операцией, используемой для указания операции, подлежащей модификации инструкцией wide; целью lookupswitch может быть сама инструкция wide.

  • Операнд каждой инструкции ldc и каждой инструкции ldc_w должен быть допустимым индексом в таблицу constant_pool. Запись константного пула, на которую ссылается этот индекс, должна быть типа:

    • CONSTANT_Integer, CONSTANT_Float или CONSTANT_String, если номер версии файла class меньше 49.0.

    • CONSTANT_Integer, CONSTANT_Float, CONSTANT_String или CONSTANT_Class, если номер версии файла class равен 49.0 или 50.0.

    • CONSTANT_Integer, CONSTANT_Float, CONSTANT_String, CONSTANT_Class, CONSTANT_MethodType или CONSTANT_MethodHandle, если номер версии файла class равен 51.0.

  • Операторы каждой инструкции ldc2_w должны представлять собой допустимый индекс в таблице constant_pool. Запись константного пула, на которую ссылается этот индекс, должна быть типа CONSTANT_Long или CONSTANT_Double.

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

  • Операторы каждой инструкции getfield, putfield, getstatic и putstatic должны представлять собой допустимый индекс в таблице constant_pool. Запись константного пула, на которую ссылается этот индекс, должна быть типа CONSTANT_Fieldref.

  • Операнды indexbyte каждой инструкции invokevirtual, invokespecial и invokestatic должны представлять собой допустимый индекс в таблице constant_pool. Запись константного пула, на которую ссылается этот индекс, должна быть типа CONSTANT_Methodref.

  • Операнды indexbyte каждой инструкции invokedynamic должны представлять собой допустимый индекс в таблице constant_pool. Запись константного пула, на которую ссылается этот индекс, должна быть типа CONSTANT_InvokeDynamic.

    Третий и четвертый байты операнда каждой инструкции invokedynamic должны иметь значение ноль.

  • Только инструкция invokespecial разрешена для вызова метода инициализации экземпляра (§2.9).

    Ни один другой метод, имя которого начинается с символа '<' ('\u003c'), не может быть вызван инструкциями вызова метода. В частности, метод инициализации класса или интерфейса, специально названный <clinit>, никогда не вызывается явно инструкциями виртуальной машины Java, а только неявно самой виртуальной машиной Java.

  • Операнды indexbyte каждой инструкции invokeinterface должны представлять собой допустимый индекс в таблице constant_pool. Запись константного пула, на которую ссылается этот индекс, должна быть типа CONSTANT_InterfaceMethodref.

    Значение операнда count каждой инструкции invokeinterface должно отражать количество локальных переменных, необходимых для хранения аргументов, которые необходимо передать в метод интерфейса, как подразумевается описателем структуры CONSTANT_NameAndType_info, на которую ссылается запись константного пула CONSTANT_InterfaceMethodref.

    Четвертый байт операнда каждой инструкции invokeinterface должен иметь значение ноль.

  • Операторы каждой инструкции instanceof, checkcast, new и anewarray и операнды indexbyte каждой инструкции multianewarray должны представлять собой допустимый индекс в таблице constant_pool. Запись константного пула, на которую ссылается этот индекс, должна быть типа CONSTANT_Class.

  • Инструкция anewarray не может использоваться для создания массива более чем 255 размерностей.

  • Инструкция new не может ссылаться на запись таблицы CONSTANT_Class constant_pool, представляющую класс массива. Инструкция new не может использоваться для создания массива.

  • Инструкция multianewarray должна использоваться только для создания массива типа, размерность которого не меньше значения ее операнда dimensions. То есть, хотя инструкция multianewarray не обязана создавать все размерности типа массива, на который ссылаются ее операнды indexbyte, она не должна пытаться создать больше размерностей, чем есть в типе массива.

    Операнд dimensions каждой инструкции multianewarray не должен быть нулевым.

  • Операнд atype каждой инструкции newarray должен принимать одно из значений T_BOOLEAN (4), T_CHAR (5), T_FLOAT (6), T_DOUBLE (7), T_BYTE (8), T_SHORT (9), T_INT (10) или T_LONG (11).

  • Операнд index каждой инструкции iload, fload, aload, istore, fstore, astore, iinc и ret должен быть неотрицательным целым числом, не превышающим max_locals - 1.

  • Неявный индекс каждой инструкции iload_<n>, fload_<n>, aload_<n>, istore_<n>, fstore_<n> и astore_<n> не должен превышать значение max_locals - 1.

  • Операнд index каждой инструкции lload, dload, lstore и dstore должен быть не больше значения max_locals - 2.

  • Неявный индекс каждой инструкции lload_<n>, dload_<n>, lstore_<n> и dstore_<n> не должен превышать значение max_locals - 2.

  • Операнды indexbyte каждой инструкции wide, изменяющей инструкцию iload, fload, aload, istore, fstore, astore, ret или iinc, должны представлять неотрицательное целое число, не превышающее max_locals - 1.

    Операнды indexbyte каждой инструкции wide, изменяющей инструкцию lload, dload, lstore или dstore, должны представлять неотрицательное целое число, не превышающее max_locals - 2.

4.9.2. Структурные ограничения

Структурные ограничения на массив code задают ограничения на взаимосвязи инструкций виртуальной машины Java. Структурные ограничения следующие:

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

    Инструкция, работающая со значениями типа int, также может работать со значениями типа boolean, byte, char и short. (Как указано в §2.3.4 и §2.11.1, виртуальная машина Java внутренне преобразует значения типов boolean, byte, char и short в тип int.)

  • Если инструкция может выполняться по нескольким различным путям выполнения, стек операндов должен иметь одинаковую глубину (§2.6.2) перед выполнением инструкции, независимо от выбранного пути.

  • Ни в какой момент выполнения нельзя изменять порядок локальных переменных, хранящих значение типа long или double, или разделять пару.

    В любой момент нельзя обрабатывать локальные переменные такой пары по отдельности.

  • К локальной переменной (или паре локальных переменных в случае значения типа long или double) нельзя получить доступ, прежде чем ей будет присвоено значение.

  • В любой момент выполнения стек операндов не может увеличиться до глубины (§2.6.2), превышающей ту, которая подразумевается элементом max_stack.

  • В любой момент выполнения из стека операндов нельзя извлечь больше значений, чем в нём содержится.

  • Каждая инструкция invokespecial должна указывать метод инициализации экземпляра (§2.9), метод в текущем классе или метод в суперклассе текущего класса.

    Если инструкция invokespecial указывает метод инициализации экземпляра из класса, который не является текущим классом или суперклассом, и целевая ссылка в стеке операндов является экземпляром класса, созданным предыдущей инструкцией new, то invokespecial должна указывать метод инициализации экземпляра из класса этого экземпляра класса.

  • При вызове метода инициализации экземпляра (§2.9) неинициализированный экземпляр класса должен находиться в соответствующем положении в стеке операндов.

    Метод инициализации экземпляра никогда не должен вызываться для инициализированного экземпляра класса.

  • При вызове любого метода экземпляра или при обращении к любому полю экземпляра экземпляр класса, содержащий метод экземпляра или поле экземпляра, должен быть уже инициализирован.

  • Никогда не должно быть неинициализированного экземпляра класса в стеке операндов или в локальной переменной в целевой точке обратной ветви, если специальный тип неинициализированного экземпляра класса в инструкции ветвления не сливается с самим собой в целевой точке ветвления (§4.10.2.4).

  • В локальной переменной никогда не должно быть неинициализированного экземпляра класса в коде, защищённом обработчиком исключений (§4.10.2.4).

  • Неинициализированный экземпляр класса никогда не должен находиться в стеке операндов или в локальной переменной при выполнении инструкции jsr или jsr_w.

  • Каждый метод инициализации экземпляра (§2.9), за исключением метода инициализации экземпляра, полученного из конструктора класса Object, должен вызвать либо другой метод инициализации экземпляра класса this или метод инициализации экземпляра его непосредственного суперкласса super перед доступом к его полям экземпляра.

    Однако, поля экземпляра класса this, которые объявлены в текущем классе, могут быть присвоены до вызова любого метода инициализации экземпляра.

  • Аргументы каждого вызова метода должны быть совместимы с вызовом метода (JLS §5.3) с описателем метода (§4.3.3).

  • Тип каждого экземпляра класса, являющегося объектом вызова инструкции вызова метода, должен быть совместим с присваиванием (JLS §5.2) с типом класса или интерфейса, указанным в инструкции.

    Кроме того, тип объекта вызова инструкции invokespecial должен быть совместим с присваиванием с текущим классом, если не вызывается метод инициализации экземпляра.

  • Каждая инструкция возврата должна соответствовать типу возврата её метода:

    • Если метод возвращает boolean, byte, char, short или int, может быть использована только инструкция ireturn.

    • Если метод возвращает float, long или double, соответственно, может быть использована только инструкция freturn, lreturn или dreturn.

    • Если метод возвращает тип reference, он должен делать это с помощью инструкции areturn, и тип возвращаемого значения должен быть совместим с присваиванием (JLS §5.2) с описателем возврата (§4.3.3) метода.

    • Все методы инициализации экземпляров, методы инициализации класса или интерфейса и методы, объявленные для возврата void, должны использовать только инструкцию return.

  • Если используется getfield или putfield для доступа к полю protected, объявленному в суперклассе, которое является членом другой среды выполнения, чем текущий класс, то тип экземпляра класса, к которому осуществляется доступ, должен быть таким же, как или являться подклассом текущего класса.

    Если используется invokevirtual или invokespecial для доступа к методу protected, объявленному в суперклассе, который является членом другой среды выполнения, чем текущий класс, то тип экземпляра класса, к которому осуществляется доступ, должен быть таким же, как или являться подклассом текущего класса.

  • Тип каждого экземпляра класса, к которому осуществляется доступ с помощью инструкции getfield, или модифицированного инструкцией putfield, должен быть совместим с присваиванием (JLS §5.2) с типом класса, указанным в инструкции.

  • Тип каждого значения, хранящегося инструкцией putfield или putstatic, должен быть совместим с описателем поля (§4.3.2) экземпляра класса или класса, в который оно сохраняется:

    • Если тип описателя - boolean, byte, char, short или int, то значение должно быть int.

    • Если тип описателя - float, long или double, то значение должно быть float, long или double соответственно.

    • Если тип описателя - тип reference, то значение должно быть типа, совместимого с присваиванием (JLS §5.2) с типом описателя.

  • Тип каждого значения, хранящегося в массиве инструкцией aastore, должен быть типа reference.

    Компонентный тип массива, в который сохраняется инструкция aastore, также должен быть типа reference.

  • Каждая инструкция athrow должна выбрасывать только значения, являющиеся экземплярами класса Throwable или подклассов Throwable.

    Каждый класс, упомянутый в элементе catch_type таблицы исключений метода, должен быть Throwable или подклассом Throwable.

  • Выполнение никогда не выходит за пределы массива code.

  • Адрес возврата (значение типа returnAddress) не может быть загружен из локальной переменной.

  • Инструкции, следующие за инструкцией jsr или jsr_w, могут быть возвращены только одной инструкцией ret.

  • Инструкция jsr или jsr_w, к которой возвращаются, не может использоваться для рекурсивного вызова подпрограммы, если эта подпрограмма уже присутствует в цепочке вызовов подпрограмм. (Подпрограммы могут быть вложены при использовании конструкций try-finally из блока finally.)

  • Каждый экземпляр типа returnAddress может быть возвращен не более одного раза.

    Если инструкция ret возвращает в точку в цепочке вызовов подпрограмм выше инструкции ret, соответствующей данному экземпляру типа returnAddress, то этот экземпляр никогда не может быть использован как адрес возврата.

4.10. Проверка файлов class

Несмотря на то, что компилятор языка программирования Java должен генерировать только файлы class, удовлетворяющие всем статическим и структурным ограничениям в предыдущих разделах, виртуальная машина Java не гарантирует, что любой файл, который ей требуется загрузить, был сгенерирован этим компилятором или имеет правильную структуру. Приложения, такие как веб-браузеры, не загружают исходный код, который они затем компилируют; эти приложения загружают уже скомпилированные файлы class. Браузеру необходимо определить, был ли файл class создан надёжным компилятором или злоумышленником, пытающимся использовать виртуальную машину Java.

Дополнительной проблемой с проверкой на этапе компиляции является различие версий. Пользователь может успешно скомпилировать класс, скажем, PurchaseStockOptions, в качестве подкласса класса TradingClass. Но определение класса TradingClass может измениться с момента компиляции класса таким образом, что это несовместимо с существующими двоичными файлами. Методы могут быть удалены или изменены их возвращаемые типы или модификаторы. Поля могут изменить тип или измениться с экземпляра на статические переменные. Модификаторы доступа к методу или переменной могут измениться с public на private. Обсуждение этих вопросов см. в главе 13 «Двоичная совместимость» в Спецификации языка Java, Java SE 7 издание.

Из-за этих потенциальных проблем виртуальная машина Java должна самостоятельно проверять, что требуемые ограничения удовлетворяются файлами class, которые она пытается включить. Реализация виртуальной машины Java проверяет, что каждый файл class удовлетворяет необходимым ограничениям во время компоновки (§5.4).

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

  • Нет переполнений или недополнений стека операндов.

  • Все обращения и сохранения локальных переменных корректны.

  • Аргументы всех инструкций виртуальной машины Java имеют допустимые типы.

Верификатор также выполняет проверку, которую можно выполнить, не обращаясь к массиву code атрибута Code (§4.7.3). Проверки включают следующее:

  • Убедитесь, что классы final не являются подклассами и что методы final не переопределяются (§5.4.5).

  • Проверка, что у каждого класса (кроме Object) есть непосредственный суперкласс.

  • Проверка того, что пул констант удовлетворяет задокументированным статическим ограничениям; например, что каждая структура CONSTANT_Class_info в пуле констант содержит в своем элементе name_index действительный индекс пула констант для структуры CONSTANT_Utf8_info.

  • Проверка того, что все ссылки на поля и методы в пуле констант имеют допустимые имена, допустимые классы и допустимый описатель типа.

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

Существует две стратегии, которые могут использовать реализации виртуальной машины Java для проверки:

  • Проверка с помощью проверки типа должна использоваться для проверки файлов class, номер версии которых больше или равен 50.0.

  • Проверка с помощью вывода типа должна поддерживаться всеми реализациями виртуальных машин Java, кроме тех, которые соответствуют профилям Java ME CLDC и Java Card, для проверки файлов class, номер версии которых меньше 50.0.

    Проверка в реализациях виртуальных машин Java, поддерживающих профили Java ME CLDC и Java Card, регулируется соответствующими спецификациями.

4.10.1. Проверка по типу

Файл класса class, номер версии которого больше или равен 50.0 (§4.1), должен быть проверен с использованием правил проверки типа, указанных в этом разделе.

Если и только если номер версии файла class равен 50.0, то при неудачной проверке по типу реализация виртуальной машины Java может попытаться выполнить проверку по типу вывода (§4.10.2).

Это прагматическая корректировка, разработанная для облегчения перехода к новой дисциплине проверки. Многие инструменты, манипулирующие файлами class, могут изменять байткоды метода таким образом, что требует корректировки кадров карты стека метода. Если инструмент не выполняет необходимые корректировки кадров карты стека, проверка по типу может завершиться неудачей, даже если байткод принципиально корректен (и, следовательно, был бы проверен по старой схеме вывода типов). Чтобы предоставить разработчикам время для адаптации своих инструментов, реализации виртуальных машин Java могут перейти к старой дисциплине проверки, но только на ограниченное время.

В случаях, когда проверка по типу завершается неудачей, но вызов проверки по типу вывода успешен, ожидается определенная потеря производительности. Такая потеря неизбежна. Она также должна служить сигналом для поставщиков инструментов о том, что их вывод необходимо скорректировать, и предоставляет поставщикам дополнительный стимул для внесения этих корректировок.

В целом, переход к проверке по типу вывода поддерживает как постепенное добавление кадров карты стека в платформу Java SE (если они отсутствуют в файле класса версии 50.0 class, переход разрешён), так и постепенное удаление инструкций jsr и jsr_w из платформы Java SE (если они присутствуют в файле класса версии 50.0 class, переход разрешён).

Если реализация виртуальной машины Java когда-либо пытается выполнить проверку по типу вывода для файлов класса версии 50.0, она должна сделать это во всех случаях, когда проверка по типу завершается неудачей.

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

Проверяющий тип применяет правила типов, которые определяются с помощью предложений Prolog. Текст на английском языке используется для описания правил типов неформальным способом, в то время как предложения Prolog предоставляют формальное описание.

Проверяющий тип требует список кадров карты стека для каждого метода с атрибутом Code (§4.7.3). Список кадров карты стека задаётся атрибутом StackMapTable (§4.7.4) атрибута Code. Цель состоит в том, чтобы кадр карты стека появлялся в начале каждого базового блока в методе. Кадр карты стека указывает тип проверки каждого элемента стека операндов и каждой локальной переменной в начале каждого базового блока. Проверяющий тип читает кадры карты стека для каждого метода с атрибутом Code и использует эти карты для генерации доказательства типа безопасности инструкций в атрибуте Code.

Класс является безопасным с точки зрения типа, если все его методы безопасны с точки зрения типа, и он не наследуется от класса final.

classIsTypeSafe(Class) :-
    classClassName(Class, Name), 
    classDefiningLoader(Class, L),
    superclassChain(Name, L, Chain),
    Chain \= [],
    classSuperClassName(Class, SuperclassName),
    loadedClass(SuperclassName, L, Superclass),
    classIsNotFinal(Superclass),	 
    classMethods(Class, Methods), 
    checklist(methodIsTypeSafe(Class), Methods).
classIsTypeSafe(Class) :-
    classClassName(Class, 'java/lang/Object'),
    classDefiningLoader(Class, L),
    isBootstrapLoader(L),
    classMethods(Class, Methods), 
    checklist(methodIsTypeSafe(Class), Methods).

Предикат Prolog classIsTypeSafe предполагает, что Class является термином Prolog, представляющим бинарный класс, который был успешно распарсен и загружен. Данная спецификация не предписывает точную структуру этого термина, но требует определения определенных предикатов на нём.

Например, мы предполагаем предикат classMethods(Class, Methods), который, получив в качестве первого аргумента термин, представляющий класс, как описано выше, связывает свой второй аргумент со списком всех методов класса, представленных в удобной форме, описанной позже.

Если предикат classIsTypeSafe неверен, проверяющий тип должен выбросить исключение VerifyError, чтобы указать, что файл class имеет неправильный формат. В противном случае файл class успешно прошёл проверку по типу, и проверка байткода успешно завершена.

Остальная часть этого раздела подробно описывает процесс проверки по типу:

  • Сначала мы даём предикат Prolog для основных артефактов виртуальной машины Java, таких как классы и методы (§4.10.1.1).

  • Во-вторых, мы описываем систему типов, известную проверяющему типу (§4.10.1.2).

  • В-третьих, мы описываем представление инструкций и кадров карты стека в Prolog (§4.10.1.3, §4.10.1.4).

  • В-четвёртых, мы описываем, как метод проверяется по типу, для методов без кода (§4.10.1.5) и методов с кодом (§4.10.1.6).

  • В-пятых, мы обсудим вопросы проверки по типу, общие для всех инструкций загрузки и сохранения (§4.10.1.7), а также вопросы доступа к членам protected (§4.10.1.8).

  • Наконец, мы укажем правила проверки каждого из инструкций (§4.10.1.9).

4.10.1.1. Доступ к артефактам виртуальной машины Java

Мы постулируем существование 22 предикат Prolog («аксессоров»), которые обладают определённым ожидаемым поведением, но формальные определения которых не приводятся в данном спецификации.

classClassName(Class, ClassName)

Извлекает имя, ClassName, класса Class.

classIsInterface(Class)

Истинно тогда и только тогда, когда класс, Class, является интерфейсом.

classIsNotFinal(Class)

Истинно тогда и только тогда, когда класс, Class, не является классом final.

classSuperClassName(Class, SuperClassName)

Извлекает имя, SuperClassName, суперкласса класса Class.

classInterfaces(Class, Interfaces)

Извлекает список, Interfaces, непосредственных суперинтерфейсов класса Class.

classMethods(Class, Methods)

Извлекает список, Methods, методов, объявленных в классе Class.

classAttributes(Class, Attributes)

Извлекает список, Attributes, атрибутов класса Class.

Каждый атрибут представлен применением конструктора вида attribute(AttributeName, AttributeContents), где AttributeName — имя атрибута. Формат содержимого атрибута не определён.

classDefiningLoader(Class, Loader)

Извлекает определяющий загрузчик классов, Loader, класса Class.

isBootstrapLoader(Loader)

Истинно тогда и только тогда, когда загрузчик классов Loader является загрузчиком Bootstrap.

loadedClass(Name, InitiatingLoader, ClassDefinition)

Истинно тогда и только тогда, когда существует класс с именем Name, чьё представление (в соответствии с этой спецификацией) при загрузке загрузчиком классов InitiatingLoader равно ClassDefinition.

methodName(Method, Name)

Извлекает имя, Name, метода Method.

methodAccessFlags(Method, AccessFlags)

Извлекает флаги доступа, AccessFlags, метода Method.

methodDescriptor(Method, Descriptor)

Извлекает дескриптор, Descriptor, метода Method.

methodAttributes(Method, Attributes)

Извлекает список, Attributes, атрибутов метода Method.

isNotFinal(Method, Class)

Истинно тогда и только тогда, когда Method в классе Class не является final.

isProtected(MemberClass, MemberName, MemberDescriptor)

Истинно тогда и только тогда, когда существует член с именем MemberName и дескриптором MemberDescriptor в классе MemberClass, и он является protected.

isNotProtected(MemberClass, MemberName, MemberDescriptor)

Истинно тогда и только тогда, когда существует член с именем MemberName и дескриптором MemberDescriptor в классе MemberClass, и он не является protected.

parseFieldDescriptor(Descriptor, Type)

Преобразует дескриптор поля, Descriptor, в соответствующий тип проверки Type (§4.10.1.2).

parseMethodDescriptor(Descriptor, ArgTypeList, ReturnType)

Преобразует дескриптор метода, Descriptor, в список типов проверки, ArgTypeList, соответствующих типам аргументов метода, и тип проверки, ReturnType, соответствующий типу возвращаемого значения.

parseCodeAttribute(Class, Method, FrameSize, MaxStack, ParsedCode, Handlers, StackMap)

Извлекает поток инструкций, ParsedCode, метода Method в Class, а также максимальный размер стека операндов, MaxStack, максимальное количество локальных переменных, FrameSize, обработчики исключений, Handlers, и карту стека StackMap.

Представление потока инструкций и атрибута карты стека должно соответствовать спецификации в §4.10.1.3 и §4.10.1.4.

samePackageName(Class1, Class2)

Истинно тогда и только тогда, когда имена пакетов Class1 и Class2 совпадают.

differentPackageName(Class1, Class2)

Истинно тогда и только тогда, когда имена пакетов Class1 и Class2 различны.

При проверке типа тела метода удобно обращаться к информации о методе. Для этой цели мы определяем среду — шестёрку, состоящую из:

  • класса

  • метода

  • объявленного типа возвращаемого значения метода

  • инструкций в методе

  • максимального размера стека операндов

  • списка обработчиков исключений

Мы определяем аксессоры для извлечения информации из среды.

allInstructions(Environment, Instructions) :-
    Environment = environment(_Class, _Method, _ReturnType,
                              Instructions, _, _).

exceptionHandlers(Environment, Handlers) :-
    Environment = environment(_Class, _Method, _ReturnType,
                              _Instructions, _, Handlers).

maxOperandStackLength(Environment, MaxStack) :-
    Environment = environment(_Class, _Method, _ReturnType,
                              _Instructions, MaxStack, _Handlers).

thisClass(Environment, class(ClassName, L)) :-
    Environment = environment(Class, _Method, _ReturnType,
                              _Instructions, _, _),
    classDefiningLoader(Class, L),
    classClassName(Class, ClassName).

thisMethodReturnType(Environment, ReturnType) :-
    Environment = environment(_Class, _Method, ReturnType,
                              _Instructions, _, _).

Мы определяем дополнительные предикаты для извлечения информации более высокого уровня из среды.

offsetStackFrame(Environment, Offset, StackFrame) :-
    allInstructions(Environment, Instructions),
    member(stackMap(Offset, StackFrame), Instructions).

currentClassLoader(Environment, Loader) :-
    thisClass(Environment, class(_, Loader)).

Наконец, мы определяем общий предикат, используемый во всех правилах типов:

notMember(_, []).
notMember(X, [A | More]) :- X \= A, notMember(X, More).

Принцип, определяющий, какие аксессоры постулируются, а какие полностью специфицируются, заключается в том, чтобы не переопределять представление файла class. Предоставление конкретных аксессоров для термина Class или Method заставило бы нас полностью определить формат термина Prolog, представляющего файл class.

4.10.1.2. Система типов проверки

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

Большинство типов проверки имеют непосредственное соответствие с типами, представленными дескрипторами полей (§4.3.2) в таблице 4.2. Исключение составляют дескрипторы полей B, C, S и Z, все из которых соответствуют типу проверки int.

Verification type hierarchy:

                             top
                 ____________/\____________
                /                          \
               /                            \
            oneWord                       twoWord
           /   |   \                     /       \
          /    |    \                   /         \
        int  float  reference        long        double
                     /     \
                    /       \____________
                   /                     \
                  /                       \
           uninitialized                Object
            /         \                     \
           /           \                     \
uninitializedThis  uninitialized(offset)     +------------------+
                                             |  Java reference  |
                                             |  type hierarchy  |
                                             +------------------+
                                                      |
                                                      |
                                                     null

Подтипизация рефлексивна.

isAssignable(X, X).

Типы проверки, которые не являются ссылочными типами в языке программирования Java, имеют правила подтипизации следующего вида:

isAssignable(v, X) :- isAssignable(the_direct_supertype_of_v, X).

То есть, v является подтипом X, если непосредственный супертип v является подтипом X. Правила следующие:

isAssignable(oneWord, top).
isAssignable(twoWord, top).

isAssignable(int, X)    :- isAssignable(oneWord, X).
isAssignable(float, X)  :- isAssignable(oneWord, X).
isAssignable(long, X)   :- isAssignable(twoWord, X).
isAssignable(double, X) :- isAssignable(twoWord, X).

isAssignable(reference, X)   :- isAssignable(oneWord, X).
isAssignable(class(_, _), X) :- isAssignable(reference, X).
isAssignable(arrayOf(_), X)  :- isAssignable(reference, X).

isAssignable(uninitialized, X)     :- isAssignable(reference, X).
isAssignable(uninitializedThis, X) :- isAssignable(uninitialized, X).
isAssignable(uninitialized(_), X)  :- isAssignable(uninitialized, X).

isAssignable(null, class(_, _)).
isAssignable(null, arrayOf(_)).
isAssignable(null, X) :- isAssignable(class('java/lang/Object', BL), X),
                         isBootstrapLoader(BL).

Эти правила подтипизации не обязательно являются наиболее очевидной формулировкой подтипизации. Существует явное разделение между правилами подтипизации для ссылочных типов в языке программирования Java и правилами для остальных типов проверки. Разделение позволяет нам устанавливать общие отношения подтипизации между ссылочными типами языка программирования Java и другими типами проверки. Эти отношения сохраняются независимо от положения ссылочного типа Java в иерархии типов и помогают предотвратить избыточную загрузку классов реализацией виртуальной машины Java. Например, мы не хотим начинать подниматься по иерархии суперклассов Java в ответ на запрос вида class(foo, L) <: twoWord.

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

Правила подтипизации для ссылочных типов в языке программирования Java определяются рекурсивно с помощью isJavaAssignable.

isAssignable(class(X, Lx), class(Y, Ly)) :-
    isJavaAssignable(class(X, Lx), class(Y, Ly)).

isAssignable(arrayOf(X), class(Y, L)) :-
    isJavaAssignable(arrayOf(X), class(Y, L)).

isAssignable(arrayOf(X), arrayOf(Y)) :-
    isJavaAssignable(arrayOf(X), arrayOf(Y)).

При присваивании интерфейсы обрабатываются как Object.

isJavaAssignable(class(_, _), class(To, L)) :-
    loadedClass(To, L, ToClass),
    classIsInterface(ToClass).

isJavaAssignable(From, To) :-
    isJavaSubclassOf(From, To).

Типы массивов являются подтипами Object. Цель также состоит в том, чтобы типы массивов являлись подтипами Cloneable и java.io.Serializable.

isJavaAssignable(arrayOf(_), class('java/lang/Object', BL)) :-
    isBootstrapLoader(BL).

isJavaAssignable(arrayOf(_), X) :-
    isArrayInterface(X).

isArrayInterface(class('java/lang/Cloneable', BL)) :-
    isBootstrapLoader(BL).

isArrayInterface(class('java/io/Serializable', BL)) :-
    isBootstrapLoader(BL).

Подтипизация между массивами примитивных типов — это отношение тождества.

isJavaAssignable(arrayOf(X), arrayOf(Y)) :-
    atom(X),
    atom(Y),
    X = Y.

Подтипизация между массивами ссылочных типов ковариантна.

isJavaAssignable(arrayOf(X), arrayOf(Y)) :-
    compound(X), compound(Y), isJavaAssignable(X, Y).

Наследование рефлексивно.

isJavaSubclassOf(class(SubclassName, L), class(SubclassName, L)).
isJavaSubclassOf(class(SubclassName, LSub), class(SuperclassName, LSuper)) :-
    superclassChain(SubclassName, LSub, Chain),
    member(class(SuperclassName, L), Chain),
    loadedClass(SuperclassName, L, Sup),
    loadedClass(SuperclassName, LSuper, Sup).

superclassChain(ClassName, L, [class(SuperclassName, Ls) | Rest]) :-
    loadedClass(ClassName, L, Class),
    classSuperClassName(Class, SuperclassName),
    classDefiningLoader(Class, Ls),
    superclassChain(SuperclassName, Ls, Rest).

superclassChain('java/lang/Object', L, []) :-
    loadedClass('java/lang/Object', L, Class),
    classDefiningLoader(Class, BL),
    isBootstrapLoader(BL).

4.10.1.3. Представление инструкций

Индивидуальные инструкции байткода представляются в Прологе в виде термов, функтор которых является именем инструкции, а аргументы — её распарсенными операндами.

Например, инструкция aload представлена в виде терма aload(N), который включает индекс N, являющийся операндом инструкции.

Инструкции в целом представляются в виде списка термов следующего формата:

instruction(Offset, AnInstruction)

Например, instruction(21, aload(1)).

Порядок инструкций в этом списке должен совпадать с порядком в файле class.

Несколько инструкций имеют операнды, являющиеся записями пула констант, представляющими поля, методы и динамические места вызова. В пуле констант поле представлено структурой CONSTANT_Fieldref_info, метод — структурой CONSTANT_InterfaceMethodref_info (для метода интерфейса) или структурой CONSTANT_Methodref_info (для метода класса), а динамическое место вызова — структурой CONSTANT_InvokeDynamic_info (§4.4.2, §4.4.10). Такие структуры представляются как применения функтора в виде:

  • field(FieldClassName, FieldName, FieldDescriptor) для поля, где FieldClassName — имя класса, на который ссылается элемент class_index в структуре CONSTANT_Fieldref_info, а FieldName и FieldDescriptor соответствуют имени и описателю поля, на которые ссылается элемент name_and_type_index структуры CONSTANT_Fieldref_info.

  • imethod(MethodIntfName, MethodName, MethodDescriptor) для метода интерфейса, где MethodIntfName — имя интерфейса, на который ссылается элемент class_index структуры CONSTANT_InterfaceMethodref_info, а MethodName и MethodDescriptor соответствуют имени и описателю метода, на которые ссылается элемент name_and_type_index структуры CONSTANT_InterfaceMethodref_info;

  • method(MethodClassName, MethodName, MethodDescriptor) для метода класса, где MethodClassName — имя класса, на который ссылается элемент class_index структуры CONSTANT_Methodref_info, а MethodName и MethodDescriptor соответствуют имени и описателю метода, на которые ссылается элемент name_and_type_index структуры CONSTANT_Methodref_info; и

  • dmethod(CallSiteName, MethodDescriptor) для динамического места вызова, где CallSiteName и MethodDescriptor соответствуют имени и описателю метода, на которые ссылается элемент name_and_type_index структуры CONSTANT_InvokeDynamic_info.

Для ясности мы предполагаем, что описатели полей и методов (§4.3.2) отображаются на более читаемые имена: начальные L и конечные ; удаляются из имён классов, а символы BaseType, используемые для примитивных типов, отображаются на имена этих типов.

Например, инструкция getfield, операндом которой был индекс в пуле констант, ссылающийся на поле foo типа F в классе Bar, будет представлена как getfield(field('Bar', 'foo', 'F')).

Записи пула констант, которые ссылаются на константные значения, такие как CONSTANT_String, CONSTANT_Integer, CONSTANT_Float, CONSTANT_Long, CONSTANT_Double и CONSTANT_Class, кодируются с помощью функторов с именами string, int, float, long, double и classConstant соответственно.

Например, инструкция ldc для загрузки целого числа 91 будет закодирована как ldc(int(91)).

4.10.1.4. Представление кадра карты стека

Кадры карты стека представляются в Прологе в виде списка терминов следующего вида:

stackMap(Offset, TypeState)

где Offset — целое число, указывающее смещение инструкции, к которой относится кадр карты, а TypeState — ожидаемое состояние входного типа (§4.7.4) для этой инструкции.

Порядок кадров карты стека в этом списке должен совпадать с порядком в файле class.

TypeState имеет вид:

frame(Locals, OperandStack, Flags)

где:

  • Locals — список типов проверки, такой что N-й элемент списка (с индексированием с нуля) представляет тип локальной переменной N.

    Если у какой-либо локальной переменной в Locals тип uninitializedThis, то Flags содержит единственный элемент flagThisUninit, в противном случае это пустой список.

  • OperandStack — список типов, где первый элемент представляет тип вершины стека операндов, а элементы ниже вершины следуют в соответствующем порядке.

    Типы размером 2 (long и double) представлены двумя записями, где первая запись — top, а вторая — сам тип.

    Например, стек с double, int и long будет представлен как [top, double, int, top, long].

    Типы ссылок, отличные от типов массивов, представляются с помощью предикатов class. class(N, L) представляет класс с бинарным именем N, загруженный загрузчиком L. Обратите внимание, что L — это инициализирующий загрузчик (§5.3) класса, представленного class(N, L), и может, или не может, быть определяющим загрузчиком класса.

    Типы массивов представляются применением предикатов arrayOf к аргументу, обозначающему тип компоненты массива.

    Тип проверки uninitialized(offset) представлен применением предикатов uninitialized к аргументу, представляющему числовое значение offset.

    Другие типы проверки представлены в Прологе как атомы, имена которых обозначают тип проверки.

    Класс Object был бы представлен как class('java/lang/Object', BL), где BL — загрузчик инициализации.

    Типы int[] и Object[] были бы представлены arrayOf(int) и arrayOf(class('java/lang/Object', BL)) соответственно.

  • Flags — это список, который может быть пустым или содержать единственный элемент flagThisUninit.

    Этот флаг используется в конструкторах для маркировки состояний типов, где инициализация ещё не завершена. В таких состояниях типов недопустимо возвращаться из метода.

Подтипирование (§4.10.1.2) распространяется точечно на состояния типов.

Массив локальных переменных метода имеет фиксированную длину по построению (см. methodInitialStackFrame в §4.10.1.6), в то время как стек операндов увеличивается и уменьшается. Поэтому нам нужна явная проверка длины стеков операндов, применимость которых требуется.

frameIsAssignable(frame(Locals1, StackMap1, Flags1),
                  frame(Locals2, StackMap2, Flags2)) :-
    length(StackMap1, StackMapLength),
    length(StackMap2, StackMapLength),
    maplist(isAssignable, Locals1, Locals2),
    maplist(isAssignable, StackMap1, StackMap2),
    subset(Flags1, Flags2).

Длина стека операндов не должна превышать объявленную максимальную длину стека.

operandStackHasLegalLength(Environment, OperandStack) :-
    length(OperandStack, Length),
    maxOperandStackLength(Environment, MaxStack),
    Length =< MaxStack.

Некоторые инструкции для массивов (§aaload, §arraylength, §baload, §bastore) проверяют типы значений в стеке операндов на предмет того, являются ли они типами массивов. Следующее предложение обращается к I-му элементу стека операндов из состояния типа.

nth1OperandStackIs(I, frame(_Locals, OperandStack, _Flags), Element) :-
    nth1(I, OperandStack, Element).

Обработка стека операндов инструкциями загрузки и сохранения (§4.10.1.7) усложняется тем, что некоторые типы занимают две ячейки в стеке. Приведённые ниже предикаты учитывают это, позволяя остальной части спецификации абстрагироваться от этой проблемы.

Извлечь из стека список типов.

canPop(frame(Locals, OperandStack, Flags), Types,
       frame(Locals, PoppedOperandStack, Flags)) :-
    popMatchingList(OperandStack, Types, PoppedOperandStack).

popMatchingList(OperandStack, [], OperandStack).
popMatchingList(OperandStack, [P | Rest], NewOperandStack) :-
    popMatchingType(OperandStack, P, TempOperandStack, _ActualType),
    popMatchingList(TempOperandStack, Rest, NewOperandStack).

Извлечь из стека отдельный тип. Точнее, если логическая вершина стека — это подтип указанного типа, Type, то извлечь его. Если тип занимает две ячейки стека, то логическая вершина стека — это действительно тип, расположенный непосредственно под вершиной, а вершина стека — это непригодный тип top.

popMatchingType([ActualType | OperandStack],
                Type, OperandStack, ActualType) :-
    sizeOf(Type, 1),
    isAssignable(ActualType, Type).

popMatchingType([top, ActualType | OperandStack],
                Type, OperandStack, ActualType) :-
    sizeOf(Type, 2),
    isAssignable(ActualType, Type).

sizeOf(X, 2) :- isAssignable(X, twoWord).
sizeOf(X, 1) :- isAssignable(X, oneWord).
sizeOf(top, 1).

Поместить логический тип на стек. Точное поведение зависит от размера типа. Если помещаемый тип имеет размер 1, мы просто помещаем его на стек. Если помещаемый тип имеет размер 2, мы помещаем его, а затем помещаем top.

pushOperandStack(OperandStack, 'void', OperandStack).
pushOperandStack(OperandStack, Type, [Type | OperandStack]) :-
    sizeOf(Type, 1).
pushOperandStack(OperandStack, Type, [top, Type | OperandStack]) :-
    sizeOf(Type, 2).

Поместить список типов на стек, если есть место.

canSafelyPush(Environment, InputOperandStack, Type, OutputOperandStack) :-
    pushOperandStack(InputOperandStack, Type, OutputOperandStack),
    operandStackHasLegalLength(Environment, OutputOperandStack).

canSafelyPushList(Environment, InputOperandStack, Types,
                  OutputOperandStack) :-
    canPushList(InputOperandStack, Types, OutputOperandStack),
    operandStackHasLegalLength(Environment, OutputOperandStack).

canPushList(InputOperandStack, [], InputOperandStack).
canPushList(InputOperandStack, [Type | Rest], OutputOperandStack) :-
    pushOperandStack(InputOperandStack, Type, InterimOperandStack),
    canPushList(InterimOperandStack, Rest, OutputOperandStack).

Обработка стека операндов инструкциями dup полностью определяется категорией типов для значений на стеке (§2.11.1).

Типы категории 1 занимают одну ячейку стека. Извлечение логического типа категории 1, Type, из стека возможно, если вершина стека — Type, и Type не равно top (иначе это может обозначать верхнюю половину типа категории 2). Результатом является входной стек, с извлечённой верхней ячейкой.

popCategory1([Type | Rest], Type, Rest) :-
    Type \= top,
    sizeOf(Type, 1).

Типы категории 2 занимают две ячейки стека. Извлечение логического типа категории 2, Type, из стека возможно, если вершина стека — тип top, а ячейка непосредственно под ней — Type. Результатом является входной стек, с извлечёнными двумя верхними ячейками.

popCategory2([top, Type | Rest], Type, Rest) :-
    sizeOf(Type, 2).

Большинство правил типов для отдельных инструкций (§4.10.1.9) зависят от понятия допустимого перехода типов. Переход типов допустим, если можно извлечь список ожидаемых типов из стека операндов состояния входного типа и заменить их ожидаемым типом результата, получив новое допустимое состояние типа. В частности, размер стека операндов в новом состоянии типа не должен превышать его максимального объявленного размера.

validTypeTransition(Environment, ExpectedTypesOnStack, ResultType,
                    frame(Locals, InputOperandStack, Flags),
                    frame(Locals, NextOperandStack, Flags)) :-
    popMatchingList(InputOperandStack, ExpectedTypesOnStack,
                    InterimOperandStack),
    pushOperandStack(InterimOperandStack, ResultType, NextOperandStack),
    operandStackHasLegalLength(Environment, NextOperandStack).

4.10.1.5. Проверка типов абстрактных и нативных методов

Абстрактные и нативные методы считаются типобезопасными, если они не переопределяют финальный метод.

methodIsTypeSafe(Class, Method) :-
    doesNotOverrideFinalMethod(Class, Method),
    methodAccessFlags(Method, AccessFlags),
    member(abstract, AccessFlags).

methodIsTypeSafe(Class, Method) :-
    doesNotOverrideFinalMethod(Class, Method),
    methodAccessFlags(Method, AccessFlags),
    member(native, AccessFlags).

doesNotOverrideFinalMethod(class('java/lang/Object', L), Method) :-
    isBootstrapLoader(L).

doesNotOverrideFinalMethod(Class, Method) :-
    classSuperClassName(Class, SuperclassName),
    classDefiningLoader(Class, L),
    loadedClass(SuperclassName, L, Superclass),
    classMethods(Superclass, MethodList),
    finalMethodNotOverridden(Method, Superclass, MethodList).

finalMethodNotOverridden(Method, Superclass, MethodList) :-
    methodName(Method, Name),
    methodDescriptor(Method, Descriptor),
    member(method(_, Name, Descriptor), MethodList),
    isNotFinal(Method, Superclass).

finalMethodNotOverridden(Method, Superclass, MethodList) :-
    methodName(Method, Name),
    methodDescriptor(Method, Descriptor),
    notMember(method(_, Name, Descriptor), MethodList),
    doesNotOverrideFinalMethod(Superclass, Method).

4.10.1.6. Методы проверки типов с кодом

Неабстрактные, неродные методы являются корректными с точки зрения типов, если они имеют код, и этот код корректен с точки зрения типов.

methodIsTypeSafe(Class, Method) :-
    doesNotOverrideFinalMethod(Class, Method),
    methodAccessFlags(Method, AccessFlags),
    methodAttributes(Method, Attributes),
    notMember(native, AccessFlags),
    notMember(abstract, AccessFlags),
    member(attribute('Code', _), Attributes),
    methodWithCodeIsTypeSafe(Class, Method).

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

methodWithCodeIsTypeSafe(Class, Method) :-
    parseCodeAttribute(Class, Method, FrameSize, MaxStack,
                       ParsedCode, Handlers, StackMap),
    mergeStackMapAndCode(StackMap, ParsedCode, MergedCode),
    methodInitialStackFrame(Class, Method, FrameSize, StackFrame, ReturnType),
    Environment = environment(Class, Method, ReturnType, MergedCode,
                              MaxStack, Handlers),
    handlersAreLegal(Environment),
    mergedCodeIsTypeSafe(Environment, MergedCode, StackFrame).

Рассмотрим сначала обработчики исключений.

Обработчик исключений представлен применением функтора в форме:

handler(Start, End, Target, ClassName)

где аргументы, соответственно, представляют собой начало и конец диапазона инструкций, покрываемого обработчиком, первую инструкцию кода обработчика и имя класса исключения, для обработки которого предназначен данный обработчик.

Обработчик исключений является допустимым, если его начало (Start) меньше его конца (End), существует инструкция, смещение которой равно Start, существует инструкция, смещение которой равно End, и класс исключения обработчика может быть присвоен классу Throwable. Класс исключения обработчика является Throwable, если запись класса обработчика равна 0, в противном случае это класс, указанный в обработчике.

handlersAreLegal(Environment) :-
    exceptionHandlers(Environment, Handlers),
    checklist(handlerIsLegal(Environment), Handlers).

handlerIsLegal(Environment, Handler) :-
    Handler = handler(Start, End, Target, _),
    Start < End,
    allInstructions(Environment, Instructions),
    member(instruction(Start, _), Instructions),
    offsetStackFrame(Environment, Target, _),
    instructionsIncludeEnd(Instructions, End),
    currentClassLoader(Environment, CurrentLoader),
    handlerExceptionClass(Handler, ExceptionClass, CurrentLoader), 
    isBootstrapLoader(BL),
    isAssignable(ExceptionClass, class('java/lang/Throwable', BL)).

instructionsIncludeEnd(Instructions, End) :-
    member(instruction(End, _), Instructions).
instructionsIncludeEnd(Instructions, End) :-
    member(endOfCode(End), Instructions).

handlerExceptionClass(handler(_, _, _, 0),
                      class('java/lang/Throwable', BL), _) :-
    isBootstrapLoader(BL).

handlerExceptionClass(handler(_, _, _, Name),
                      class(Name, L), L) :-
    Name \= 0.

Теперь перейдем к потоку инструкций и кадрам карты стека.

Объединение инструкций и кадров карты стека в один поток включает четыре случая:

  • Объединение пустого StackMap и списка инструкций дает исходный список инструкций.

    mergeStackMapAndCode([], CodeList, CodeList).
    
  • Учитывая список кадров карты стека, начинающийся с состояния типа для инструкции по адресу Offset, и список инструкций, начинающийся по адресу Offset, объединенный список состоит из головы списка кадров карты стека, за которой следует голова списка инструкций, за которой следует объединение хвостов обоих списков.

    mergeStackMapAndCode([stackMap(Offset, Map) | RestMap],
                         [instruction(Offset, Parse) | RestCode],
                         [stackMap(Offset, Map),
                           instruction(Offset, Parse) | RestMerge]) :-
        mergeStackMapAndCode(RestMap, RestCode, RestMerge).
    
  • В противном случае, учитывая список кадров стека, начинающийся с состояния типа для инструкции по адресу OffsetM, и список инструкций, начинающийся по адресу OffsetP, если OffsetP < OffsetM, объединенный список состоит из головы списка инструкций, за которой следует объединение списка кадров карты стека и хвоста списка инструкций.

    mergeStackMapAndCode([stackMap(OffsetM, Map) | RestMap],
                         [instruction(OffsetP, Parse) | RestCode],
                         [instruction(OffsetP, Parse) | RestMerge]) :-
        OffsetP < OffsetM,
        mergeStackMapAndCode([stackMap(OffsetM, Map) | RestMap],
                             RestCode, RestMerge).
    
  • В противном случае объединение двух списков не определено. Поскольку список инструкций имеет монотонно возрастающие смещения, объединение двух списков не определено, если смещение каждого кадра карты стека не имеет соответствующего смещения инструкции, и кадры карты стека упорядочены монотонно возрастающим порядком.

Чтобы определить, является ли объединенный поток для метода корректным с точки зрения типов, мы сначала выводим начальное состояние типа метода.

Начальное состояние типа метода состоит из пустого стека операндов и типов локальных переменных, полученных из типа this и аргументов, а также соответствующего флага, в зависимости от того, является ли это <init> методом.

methodInitialStackFrame(Class, Method, FrameSize, frame(Locals, [], Flags),
                        ReturnType):-
    methodDescriptor(Method, Descriptor),
    parseMethodDescriptor(Descriptor, RawArgs, ReturnType),
    expandTypeList(RawArgs, Args),
    methodInitialThisType(Class, Method, ThisList),
    flags(ThisList, Flags),
    append(ThisList, Args, ThisArgs),
    expandToLength(ThisArgs, FrameSize, top, Locals).

Учитывая список типов, следующее предложение создает список, где каждый тип размера 2 заменяется двумя записями: одной для самого типа и одной top записью. Результат затем соответствует представлению списка в виде 32-битовых слов в виртуальной машине Java.

expandTypeList([], []).
expandTypeList([Item | List], [Item | Result]) :-
    sizeOf(Item, 1),
    expandTypeList(List, Result).
expandTypeList([Item | List], [Item, top | Result]) :-
    sizeOf(Item, 2),
    expandTypeList(List, Result).
flags([uninitializedThis], [flagThisUninit]).
flags(X, []) :- X \= [uninitializedThis].

expandToLength(List, Size, _Filler, List) :-
    length(List, Size).
expandToLength(List, Size, Filler, Result) :-
    length(List, ListLength),
    ListLength < Size,
    Delta is Size - ListLength,
    length(Extra, Delta),
    checklist(=(Filler), Extra),
    append(List, Extra, Result).

Для начального состояния типа метода экземпляра мы вычисляем тип this и помещаем его в список. Тип this в методе <init> класса Object равен Object; в других <init> методах тип this равен uninitializedThis; в противном случае, тип this в методе экземпляра равен class(N, L), где N — имя класса, содержащего метод, а L — его определяющий загрузчик классов.

Для начального состояния типа статического метода this не имеет значения, поэтому список пуст.

methodInitialThisType(_Class, Method, []) :-
    methodAccessFlags(Method, AccessFlags),
    member(static, AccessFlags),
    methodName(Method, MethodName),
    MethodName \= '<init>'.

methodInitialThisType(Class, Method, [This]) :-
    methodAccessFlags(Method, AccessFlags),\
    notMember(static, AccessFlags),\
    instanceMethodInitialThisType(Class, Method, This).

instanceMethodInitialThisType(Class, Method, class('java/lang/Object', L)) :-
    methodName(Method, '<init>'), 
    classDefiningLoader(Class, L),
    isBootstrapLoader(L),
    classClassName(Class, 'java/lang/Object').

instanceMethodInitialThisType(Class, Method, uninitializedThis) :-
    methodName(Method, '<init>'), 
    classClassName(Class, ClassName),
    classDefiningLoader(Class, CurrentLoader),
    superclassChain(ClassName, CurrentLoader, Chain),
    Chain \= [].

instanceMethodInitialThisType(Class, Method, class(ClassName, L)) :-
    methodName(Method, MethodName),
    MethodName \= '<init>',
    classDefiningLoader(Class, L),
    classClassName(Class, ClassName).

Теперь мы вычислим, является ли объединенный поток для метода корректным с точки зрения типов, используя начальное состояние типа метода:

  • Если у нас есть кадр карты стека и входное состояние типа, то состояние типа должно быть присваиваемо состоянию типа в кадре карты стека. Затем мы можем перейти к проверке типа остальной части потока с состоянием типа, указанным в кадре карты стека.

    mergedCodeIsTypeSafe(Environment, [stackMap(Offset, MapFrame) | MoreCode],
                         frame(Locals, OperandStack, Flags)) :-
        frameIsAssignable(frame(Locals, OperandStack, Flags), MapFrame),
        mergedCodeIsTypeSafe(Environment, MoreCode, MapFrame).
    
  • Объединенный поток кода является типобезопасным относительно входного состояния типа T, если он начинается с инструкции I, которая является типобезопасной относительно T, и I удовлетворяет своим обработчикам исключений (см. ниже), и хвост потока является типобезопасным, учитывая состояние типа, следующее за этим выполнением I.

    NextStackFrame указывает, что выполнение переходит к следующей инструкции. Для инструкции безусловного перехода она будет иметь специальное значение afterGoto. ExceptionStackFrame указывает, что передается обработчикам исключений.

    mergedCodeIsTypeSafe(Environment, [instruction(Offset, Parse) | MoreCode],
                         frame(Locals, OperandStack, Flags)) :-
        instructionIsTypeSafe(Parse, Environment, Offset,
                              frame(Locals, OperandStack, Flags),
                              NextStackFrame, ExceptionStackFrame),
        instructionSatisfiesHandlers(Environment, Offset, ExceptionStackFrame),
        mergedCodeIsTypeSafe(Environment, MoreCode, NextStackFrame).
    
  • После безусловного перехода (указанного входным состоянием типа afterGoto), если у нас есть кадр карты стека, указывающий состояние типа для следующих инструкций, мы можем продолжить и проверить их тип, используя состояние типа, предоставленное кадром карты стека.

    mergedCodeIsTypeSafe(Environment, [stackMap(Offset, MapFrame) | MoreCode],
                         afterGoto) :-
        mergedCodeIsTypeSafe(Environment, MoreCode, MapFrame).
    
  • Неправомерно иметь код после безусловного перехода без предоставления кадра карты стека для него.

    mergedCodeIsTypeSafe(_Environment, [instruction(_, _) | _MoreCode],
                         afterGoto) :-
        write_ln('No stack frame after unconditional branch'),
        fail.
    
  • Если у нас есть безусловный переход в конце кода, остановитесь.

    mergedCodeIsTypeSafe(_Environment, [endOfCode(Offset)],
                         afterGoto).
    

Переход к целевому адресу является безопасным с точки зрения типов, если у целевого адреса есть связанный кадр карты стека, Frame, и текущий кадр карты стека, StackFrame, может быть присвоен Frame.

targetIsTypeSafe(Environment, StackFrame, Target) :-
    offsetStackFrame(Environment, Target, Frame),
    frameIsAssignable(StackFrame, Frame).

Инструкция удовлетворяет своим обработчикам исключений, если она удовлетворяет каждому применимому обработчику исключений.

instructionSatisfiesHandlers(Environment, Offset, ExceptionStackFrame) :-
    exceptionHandlers(Environment, Handlers),
    sublist(isApplicableHandler(Offset), Handlers, ApplicableHandlers),
    checklist(instructionSatisfiesHandler(Environment, ExceptionStackFrame),
              ApplicableHandlers).

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

isApplicableHandler(Offset, handler(Start, End, _Target, _ClassName)) :-
    Offset >= Start,
    Offset < End.

Инструкция удовлетворяет обработчику исключений, если ее входное состояние типа является StackFrame, и целевой адрес обработчика (начальная инструкция кода обработчика) является типобезопасной, предполагая входное состояние типа T. Состояние типа T получено из StackFrame путем замены стека операндов стеком, единственным элементом которого является класс исключения обработчика.

instructionSatisfiesHandler(Environment, StackFrame, Handler) :-
    Handler = handler(_, _, Target, _),
    currentClassLoader(Environment, CurrentLoader),
    handlerExceptionClass(Handler, ExceptionClass, CurrentLoader), 
    /* The stack consists of just the exception. */
    StackFrame = frame(Locals, _, Flags),
    ExcStackFrame = frame(Locals, [ ExceptionClass ], Flags),
    operandStackHasLegalLength(Environment, ExcStackFrame),
    targetIsTypeSafe(Environment, ExcStackFrame, Target).

4.10.1.7. Проверка типов инструкций Load и Store

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

Загрузка значения типа Type из локальной переменной Index безопасна по типу, если тип этой локальной переменной является ActualType, ActualType присваиваемо Type, и помещение ActualType на входной стек операндов — это допустимый переход типов (§4.10.1.4), который приводит к новому состоянию типа NextStackFrame. После выполнения инструкции загрузки состояние типа будет NextStackFrame.

loadIsTypeSafe(Environment, Index, Type, StackFrame, NextStackFrame) :-
    StackFrame = frame(Locals, _OperandStack, _Flags),
    nth0(Index, Locals, ActualType),
    isAssignable(ActualType, Type),
    validTypeTransition(Environment, [], ActualType, StackFrame,
                        NextStackFrame).

Все инструкции сохранения являются вариациями общей схемы, отличаясь типом значения, которое сохраняет инструкция.

В общем случае инструкция сохранения безопасна по типу, если локальная переменная, на которую она ссылается, имеет тип, являющийся супертипом Type, а верхний элемент стека операндов — подтипом Type, где Type — тип, который инструкция предназначена для сохранения.

Более точно, сохранение безопасно по типу, если можно извлечь тип ActualType, который «соответствует» Type (то есть является подтипом Type), со стека операндов (§4.10.1.4), а затем корректно присвоить этот тип локальной переменной LIndex.

storeIsTypeSafe(_Environment, Index, Type,
                frame(Locals, OperandStack, Flags),
                frame(NextLocals, NextOperandStack, Flags)) :-
    popMatchingType(OperandStack, Type, NextOperandStack, ActualType),
    modifyLocalVariable(Index, ActualType, Locals, NextLocals).

Учитывая локальные переменные Locals, изменение типа Index на Type приводит к списку локальных переменных NewLocals. Изменения несколько сложны, поскольку некоторые значения (и соответствующие типы) занимают две локальные переменные. Следовательно, изменение LN может потребовать изменения LN+1 (поскольку тип будет занимать как слот N, так и N+1) или LN-1 (поскольку локальная переменная N ранее была верхней половиной значения/типа из двух слов, начиная с локальной переменной N-1, и поэтому локальная переменная N-1 должна быть аннулирована), или обоих. Это описано ниже. Мы начинаем с L0 и считаем вверх.

modifyLocalVariable(Index, Type, Locals, NewLocals) :-
    modifyLocalVariable(0, Index, Type, Locals, NewLocals).

Учитывая LocalsRest, суффикс списка локальных переменных, начиная с индекса I, изменение локальной переменной Index на тип Type приводит к суффиксу списка локальных переменных NextLocalsRest.

Если I < Index-1, просто скопируйте вход в выход и рекурсивно обработайте следующий элемент. Если I = Index-1, тип локальной переменной I может измениться. Это может произойти, если LI имеет тип размером 2. После того как мы установим LI+1 в новый тип (и соответствующее значение), тип/значение LI будет аннулировано, поскольку его верхняя половина будет уничтожена. Затем мы рекурсивно обрабатываем следующий элемент.

modifyLocalVariable(I, Index, Type,
                    [Locals1 | LocalsRest],
                    [Locals1 | NextLocalsRest] ) :-
    I < Index - 1, 
    I1 is I + 1,
    modifyLocalVariable(I1, Index, Type, LocalsRest, NextLocalsRest).

modifyLocalVariable(I, Index, Type,
                    [Locals1 | LocalsRest],
                    [NextLocals1 | NextLocalsRest] ) :-
    I =:= Index - 1,
    modifyPreIndexVariable(Locals1, NextLocals1),
    modifyLocalVariable(Index, Index, Type, LocalsRest, NextLocalsRest).

Когда мы находим переменную, которая занимает только одно слово, мы изменяем ее на Type, и всё. Когда мы находим переменную, которая занимает два слова, мы изменяем ее тип на Type, а тип следующего слова на top.

modifyLocalVariable(Index, Index, Type,
                    [_ | LocalsRest], [Type | LocalsRest]) :-
    sizeOf(Type, 1).

modifyLocalVariable(Index, Index, Type,
                    [_, _ | LocalsRest], [Type, top | LocalsRest]) :-
    sizeOf(Type, 2).

Мы ссылаемся на локальную переменную, индекс которой непосредственно предшествует локальной переменной, тип которой будет изменён, как на переменную с индексом перед. Будущий тип переменной с индексом перед, имеющей тип InputType, — это Result. Если тип локальной переменной с индексом перед, Type, имеет размер 1, он не изменяется. Если тип локальной переменной с индексом перед, Type, имеет размер 2, мы должны пометить нижнюю половину её значения из двух слов как недоступную, установив её тип на top.

modifyPreIndexVariable(Type, Type) :- sizeOf(Type, 1).
modifyPreIndexVariable(Type, top) :- sizeOf(Type, 2).

4.10.1.8. Проверка типов для protected членов

Все инструкции, которые обращаются к членам, должны учитывать правила, касающиеся protected членов. Этот раздел описывает проверку protected, соответствующую JLS §6.6.2.1.

Проверка protected применяется только к protected членам суперклассов текущего класса. protected члены в других классах будут обнаружены проверкой доступа при разрешении (§5.4.4). Существует четыре случая:

  • Если имя класса не является именем какого-либо суперкласса, то это не суперкласс, и его можно безопасно проигнорировать.

    passesProtectedCheck(Environment, MemberClassName, MemberName,
                         MemberDescriptor, StackFrame) :-
        thisClass(Environment, class(CurrentClassName, CurrentLoader)),
        superclassChain(CurrentClassName, CurrentLoader, Chain),
        notMember(class(MemberClassName, _), Chain).
    
  • Если MemberClassName совпадает с именем суперкласса, класс, который разрешается, может быть суперклассом. В этом случае, если ни один суперкласс с именем MemberClassName в другом пакете выполнения не имеет protected члена с именем MemberName и описанием MemberDescriptor, проверка protected не применяется.

    Это происходит потому, что фактический разрешаемый класс будет либо одним из этих суперклассов, в этом случае мы знаем, что он либо находится в том же пакете выполнения, и доступ разрешён; либо член, о котором идёт речь, не является protected, и проверка не применяется; либо это подкласс, в этом случае проверка всё равно пройдёт; либо это какой-то другой класс в том же пакете выполнения, в этом случае доступ разрешён, и проверка не нужна; либо верификатор не должен отмечать это как проблему, так как это всё равно будет обнаружено, потому что разрешение неизбежно потерпит неудачу.

    passesProtectedCheck(Environment, MemberClassName, MemberName,
                         MemberDescriptor, StackFrame) :-
        thisClass(Environment, class(CurrentClassName, CurrentLoader)),
        superclassChain(CurrentClassName, CurrentLoader, Chain),
        member(class(MemberClassName, _), Chain),
        classesInOtherPkgWithProtectedMember(
          class(CurrentClassName, CurrentLoader),
          MemberName, MemberDescriptor, MemberClassName, Chain, []).
    
  • Если существует protected член суперкласса в другом пакете выполнения, загрузите MemberClassName; если член, о котором идёт речь, не является protected, проверка не применяется. (Использование члена суперкласса, который не является protected, тривиально верно.)

    passesProtectedCheck(Environment, MemberClassName, MemberName,
                         MemberDescriptor,
                         frame(_Locals, [Target | Rest], _Flags)) :-
        thisClass(Environment, class(CurrentClassName, CurrentLoader)),
        superclassChain(CurrentClassName, CurrentLoader, Chain),
        member(class(MemberClassName, _), Chain),
        classesInOtherPkgWithProtectedMember(
          class(CurrentClassName, CurrentLoader),
          MemberName, MemberDescriptor, MemberClassName, Chain, List),
        List /= [],
        loadedClass(MemberClassName, CurrentLoader, ReferencedClass),
        isNotProtected(ReferencedClass, MemberName, MemberDescriptor).
    
  • В противном случае использование члена объекта типа Target требует, чтобы Target можно было присвоить типу текущего класса.

    passesProtectedCheck(Environment, MemberClassName, MemberName,
                         MemberDescriptor,
                         frame(_Locals, [Target | Rest], _Flags)) :-
        thisClass(Environment, class(CurrentClassName, CurrentLoader)),
        superclassChain(CurrentClassName, CurrentLoader, Chain),
        member(class(MemberClassName, _), Chain),
        classesInOtherPkgWithProtectedMember(
          class(CurrentClassName, CurrentLoader),
          MemberName, MemberDescriptor, MemberClassName, Chain, List),
        List /= [],
        loadedClass(MemberClassName, CurrentLoader, ReferencedClass),
        isProtected(ReferencedClass, MemberName, MemberDescriptor),
        isAssignable(Target, class(CurrentClassName, CurrentLoader)).
    

Предикату classesInOtherPkgWithProtectedMember(Class, MemberName, MemberDescriptor, MemberClassName, Chain, List) истинно, если List — это множество классов в Chain с именем MemberClassName, которые находятся в другом пакете выполнения, чем Class, и имеют protected член с именем MemberName и описанием MemberDescriptor.

classesInOtherPkgWithProtectedMember(_, _, _, _, [], []).

classesInOtherPkgWithProtectedMember(Class, MemberName,
                                     MemberDescriptor, MemberClassName,
                                     [class(MemberClassName, L) | Tail],
                                     [class(MemberClassName, L) | T]) :-
    differentRuntimePackage(Class, class(MemberClassName, L)),
    loadedClass(MemberClassName, L, Super),
    isProtected(Super, MemberName, MemberDescriptor),
    classesInOtherPkgWithProtectedMember(
      Class, MemberName, MemberDescriptor, MemberClassName, Tail, T).

classesInOtherPkgWithProtectedMember(Class, MemberName,
                                     MemberDescriptor, MemberClassName,
                                     [class(MemberClassName, L) | Tail],
                                     T) :-
    differentRuntimePackage(Class, class(MemberClassName, L)),
    loadedClass(MemberClassName, L, Super),
    isNotProtected(Super, MemberName, MemberDescriptor),
    classesInOtherPkgWithProtectedMember(
      Class, MemberName, MemberDescriptor, MemberClassName, Tail, T).

classesInOtherPkgWithProtectedMember(Class, MemberName,
                                     MemberDescriptor, MemberClassName,
                                     [class(MemberClassName, L) | Tail],
                                     T] :-
    sameRuntimePackage(Class, class(MemberClassName, L)),
    classesInOtherPkgWithProtectedMember(
      Class, MemberName, MemberDescriptor, MemberClassName, Tail, T).
sameRuntimePackage(Class1, Class2) :-
    classDefiningLoader(Class1, L),
    classDefiningLoader(Class2, L),
    samePackageName(Class1, Class2).

differentRuntimePackage(Class1, Class2) :-
    classDefiningLoader(Class1, L1),
    classDefiningLoader(Class2, L2),
    L1 \= L2.

differentRuntimePackage(Class1, Class2) :-
    differentPackageName(Class1, Class2).

4.10.1.9. Инструкции по проверке типов

В общем случае правило типа для инструкции задаётся относительно среды Environment, которая определяет класс и метод, в которых происходит данная инструкция (§4.10.1.1), и смещение Offset внутри метода, в котором происходит эта инструкция. Правило гласит, что если состояние входного типа StackFrame удовлетворяет определённым требованиям, то:

  • Инструкция является типобезопасной.

  • Доказуемо, что состояние типа после нормального завершения инструкции имеет определённую форму, заданную NextStackFrame, а состояние типа после внезапного завершения инструкции задаётся ExceptionStackFrame.

    Состояние типа после внезапного завершения инструкции такое же, как и состояние входного типа, за исключением того, что стек операндов пуст.

    exceptionStackFrame(StackFrame, ExceptionStackFrame) :-
        StackFrame = frame(Locals, _OperandStack, Flags),
        ExceptionStackFrame = frame(Locals, [], Flags).
        

Многие инструкции имеют правила типов, которые полностью изоморфны правилам для других инструкций. Если инструкция b1 изоморфна другой инструкции b2, то правило типа для b1 такое же, как и для b2.

instructionIsTypeSafe(Instruction, Environment, Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :-
    instructionHasEquivalentTypeRule(Instruction, IsomorphicInstruction),
    instructionIsTypeSafe(IsomorphicInstruction, Environment, Offset,
                          StackFrame, NextStackFrame,
                          ExceptionStackFrame).

Описание каждого правила на английском языке предназначено для читаемости, интуитивности и краткости. В связи с этим описание избегает повторения всех контекстных предположений, указанных выше. В частности:

  • Описание не упоминает явно среду.

  • Когда в дальнейшем описании говорится о стеке операндов или локальных переменных, имеется в виду стек операндов и компоненты локальных переменных состояния типа: либо входное состояние типа, либо выходное.

  • Состояние типа после внезапного завершения инструкции почти всегда идентично состоянию входного типа. Описание рассматривает состояние типа после внезапного завершения инструкции только в том случае, когда это не так.

  • Описание говорит о помещении и извлечении типов в стек операндов и не рассматривает явно вопросы об обрыве или переполнении стека. Описание предполагает, что эти операции могут быть выполнены успешно, но предписания Prolog для управления стеком операндов гарантируют, что необходимые проверки проводятся.

  • Описание рассматривает только манипуляции с логическими типами. На практике некоторые типы занимают более одного слова. Описание абстрагируется от этих деталей представления, но предписания Prolog, которые манипулируют данными, этого не делают.

Любые неоднозначности можно разрешить, обратившись к формальным предписаниям Prolog.

aaload

Инструкция aaload является типобезопасной тогда и только тогда, когда можно корректно заменить типы, соответствующие int, и тип массива с типом компонента ComponentType, где ComponentType является подтипом Object, с помощью ComponentType, получая выходное состояние типа.

instructionIsTypeSafe(aaload, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    nth1OperandStackIs(2, StackFrame, ArrayType),
    arrayComponentType(ArrayType, ComponentType),
    isBootstrapLoader(BL),
    validTypeTransition(Environment,
                        [int, arrayOf(class('java/lang/Object', BL))],
                        ComponentType, StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).

Тип компонента массива типа X равен X. Мы определяем тип компонента null как null.

arrayComponentType(arrayOf(X), X).
arrayComponentType(null, null).
aastore

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

instructionIsTypeSafe(aastore, _Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    isBootstrapLoader(BL),
    canPop(StackFrame,
           [class('java/lang/Object', BL),
            int,
            arrayOf(class('java/lang/Object', BL))],
           NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
aconst_null

Инструкция aconst_null является типобезопасной, если можно корректно поместить тип null в входной стек операндов, получая выходное состояние типа.

instructionIsTypeSafe(aconst_null, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    validTypeTransition(Environment, [], null, StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
aload

Инструкция aload с операндом Index является типобезопасной и приводит к выходному состоянию типа NextStackFrame, если инструкция загрузки с операндом Index и типом reference является типобезопасной и приводит к выходному состоянию типа NextStackFrame.

instructionIsTypeSafe(aload(Index), Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    loadIsTypeSafe(Environment, Index, reference, StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
aload_<n>

Инструкции aload_<n>, для 0 ≤ n ≤ 3, являются типобезопасными тогда и только тогда, когда эквивалентная инструкция aload является типобезопасной.

instructionHasEquivalentTypeRule(aload_0, aload(0)).
instructionHasEquivalentTypeRule(aload_1, aload(1)).
instructionHasEquivalentTypeRule(aload_2, aload(2)).
instructionHasEquivalentTypeRule(aload_3, aload(3)).
anewarray

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

instructionIsTypeSafe(anewarray(CP), Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    (CP = class(_, _) ; CP = arrayOf(_)),
    validTypeTransition(Environment, [int], arrayOf(CP),
                        StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
areturn

Инструкция areturn является типобезопасной тогда и только тогда, когда объявленный тип возврата окружающего метода, ReturnType, является типом reference, и можно корректно извлечь тип, соответствующий ReturnType, из входного стека операндов.

instructionIsTypeSafe(areturn, Environment, _Offset, StackFrame,
                      afterGoto, ExceptionStackFrame) :- 
    thisMethodReturnType(Environment, ReturnType),
    isAssignable(ReturnType, reference),
    canPop(StackFrame, [ReturnType], _PoppedStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
arraylength

Инструкция arraylength является типобезопасной тогда и только тогда, когда можно корректно заменить тип массива на входном стеке операндов на тип int, получая выходное состояние типа.

instructionIsTypeSafe(arraylength, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    nth1OperandStackIs(1, StackFrame, ArrayType),
    arrayComponentType(ArrayType, _),
    validTypeTransition(Environment, [top], int, StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
astore

Инструкция astore с операндом Index является типобезопасной и приводит к выходному состоянию типа NextStackFrame, если инструкция сохранения с операндом Index и типом reference является типобезопасной и приводит к выходному состоянию типа NextStackFrame.

instructionIsTypeSafe(astore(Index), Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    storeIsTypeSafe(Environment, Index, reference, StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
astore_<n>

Инструкции astore_<n>, для 0 ≤ n ≤ 3, являются типобезопасными тогда и только тогда, когда эквивалентная инструкция astore является типобезопасной.

instructionHasEquivalentTypeRule(astore_0, astore(0)).
instructionHasEquivalentTypeRule(astore_1, astore(1)).
instructionHasEquivalentTypeRule(astore_2, astore(2)).
instructionHasEquivalentTypeRule(astore_3, astore(3)).
athrow

Инструкция athrow является типобезопасной тогда и только тогда, когда вершина стека операндов соответствует Throwable.

instructionIsTypeSafe(athrow, _Environment, _Offset, StackFrame,
                      afterGoto, ExceptionStackFrame) :- 
    isBootstrapLoader(BL),
    canPop(StackFrame, [class('java/lang/Throwable', BL)], _PoppedStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
baload

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

instructionIsTypeSafe(baload, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :
    nth1OperandStackIs(2, StackFrame, ArrayType),
    isSmallArray(ArrayType),
    validTypeTransition(Environment, [int, top], int,
                        StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).

Тип массива является типом малого массива, если это массив типа byte, массив типа boolean или подтип этого (null).

isSmallArray(arrayOf(byte)).
isSmallArray(arrayOf(boolean)).
isSmallArray(null).
bastore

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

instructionIsTypeSafe(bastore, _Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    nth1OperandStackIs(3, StackFrame, ArrayType),
    isSmallArray(ArrayType),
    canPop(StackFrame, [int, int, top], NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
bipush

Инструкция bipush является типобезопасной тогда и только тогда, когда эквивалентная инструкция sipush является типобезопасной.

instructionHasEquivalentTypeRule(bipush(Value), sipush(Value)).
caload

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

instructionIsTypeSafe(caload, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    validTypeTransition(Environment, [int, arrayOf(char)], int,
                        StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
castore

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

instructionIsTypeSafe(castore, _Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    canPop(StackFrame, [int, int, arrayOf(char)], NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
checkcast

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

instructionIsTypeSafe(checkcast(CP), Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    (CP = class(_, _) ; CP = arrayOf(_)),
    isBootstrapLoader(BL),
    validTypeTransition(Environment, [class('java/lang/Object', BL)], CP,
                        StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
d2f

Инструкция d2f является типобезопасной, если можно корректно извлечь double со стека входных операндов и заменить его на float, получив состояние выходного типа.

instructionIsTypeSafe(d2f, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    validTypeTransition(Environment, [double], float,
                        StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
d2i

Инструкция d2i является типобезопасной, если можно корректно извлечь double со стека входных операндов и заменить его на int, получив состояние выходного типа.

instructionIsTypeSafe(d2i, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    validTypeTransition(Environment, [double], int,
                        StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
d2l

Инструкция d2l является типобезопасной, если можно корректно извлечь double со стека входных операндов и заменить его на long, получив состояние выходного типа.

instructionIsTypeSafe(d2l, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    validTypeTransition(Environment, [double], long,
                        StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
dadd

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

instructionIsTypeSafe(dadd, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :-
    validTypeTransition(Environment, [double, double], double,
                        StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
daload

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

instructionIsTypeSafe(daload, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    validTypeTransition(Environment, [int, arrayOf(double)], double,
                        StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
dastore

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

instructionIsTypeSafe(dastore, _Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    canPop(StackFrame, [double, int, arrayOf(double)], NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
dcmp<op>

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

instructionIsTypeSafe(dcmpg, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    validTypeTransition(Environment, [double, double], int,
                        StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).

Инструкция dcmpl является типобезопасной тогда и только тогда, когда эквивалентная инструкция dcmpg является типобезопасной.

instructionHasEquivalentTypeRule(dcmpl, dcmpg).
dconst_<d>

Инструкция dconst_0 является типобезопасной, если можно корректно поместить тип double на стек входных операндов, получив состояние выходного типа.

instructionIsTypeSafe(dconst_0, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    validTypeTransition(Environment, [], double, StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).

Инструкция dconst_1 является типобезопасной тогда и только тогда, когда эквивалентная инструкция dconst_0 является типобезопасной.

instructionHasEquivalentTypeRule(dconst_1, dconst_0).
ddiv

Инструкция ddiv является типобезопасной тогда и только тогда, когда эквивалентная инструкция dadd является типобезопасной.

instructionHasEquivalentTypeRule(ddiv, dadd).
dload

Инструкция dload с операндом Index является типобезопасной и генерирует состояние выходного типа NextStackFrame, если инструкция загрузки с операндом Index и типом double является типобезопасной и генерирует состояние выходного типа NextStackFrame.

instructionIsTypeSafe(dload(Index), Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    loadIsTypeSafe(Environment, Index, double, StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
dload_<n>

Инструкции dload_<n> для 0 ≤ n ≤ 3 являются типобезопасными тогда и только тогда, когда эквивалентная инструкция dload является типобезопасной.

instructionHasEquivalentTypeRule(dload_0, dload(0)).
instructionHasEquivalentTypeRule(dload_1, dload(1)).
instructionHasEquivalentTypeRule(dload_2, dload(2)).
instructionHasEquivalentTypeRule(dload_3, dload(3)).
dmul

Инструкция dmul является типобезопасной тогда и только тогда, когда эквивалентная инструкция dadd является типобезопасной.

instructionHasEquivalentTypeRule(dmul, dadd).
dneg

Инструкция dneg является типобезопасной тогда и только тогда, когда на стеке входных операндов существует тип, соответствующий double. Инструкция dneg не изменяет состояние типа.

instructionIsTypeSafe(dneg, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    validTypeTransition(Environment, [double], double,
                        StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
drem

Инструкция drem является типобезопасной тогда и только тогда, когда эквивалентная инструкция dadd является типобезопасной.

instructionHasEquivalentTypeRule(drem, dadd).
dreturn

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

instructionIsTypeSafe(dreturn, Environment, _Offset, StackFrame,
                      afterGoto, ExceptionStackFrame) :- 
    thisMethodReturnType(Environment, double),
    canPop(StackFrame, [double], _PoppedStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
dstore

Инструкция dstore с операндом Index является типобезопасной и генерирует состояние выходного типа NextStackFrame, если инструкция сохранения с операндом Index и типом double является типобезопасной и генерирует состояние выходного типа NextStackFrame.

instructionIsTypeSafe(dstore(Index), Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    storeIsTypeSafe(Environment, Index, double, StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
dstore_<n>

Инструкции dstore_<n> для 0 ≤ n ≤ 3 являются типобезопасными тогда и только тогда, когда эквивалентная инструкция dstore является типобезопасной.

instructionHasEquivalentTypeRule(dstore_0, dstore(0)).
instructionHasEquivalentTypeRule(dstore_1, dstore(1)).
instructionHasEquivalentTypeRule(dstore_2, dstore(2)).
instructionHasEquivalentTypeRule(dstore_3, dstore(3)).
dsub

Инструкция dsub является типобезопасной тогда и только тогда, когда эквивалентная инструкция dadd является типобезопасной.

instructionHasEquivalentTypeRule(dsub, dadd).
dup

Инструкция dup является типобезопасной тогда и только тогда, когда можно корректно заменить тип категории 1, Type, на типы Type и Type, получив состояние выходного типа.

instructionIsTypeSafe(dup, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :-
    StackFrame = frame(Locals, InputOperandStack, Flags),
    popCategory1(InputOperandStack, Type, _),
    canSafelyPush(Environment, InputOperandStack, Type, OutputOperandStack),
    NextStackFrame = frame(Locals, OutputOperandStack, Flags),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
dup_x1

Инструкция dup_x1 является типобезопасной тогда и только тогда, когда можно корректно заменить два типа категории 1, Type1 и Type2 на стеке входных операндов на типы Type1, Type2 и Type1, получив состояние выходного типа.

instructionIsTypeSafe(dup_x1, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    StackFrame = frame(Locals, InputOperandStack, Flags),
    popCategory1(InputOperandStack, Type1, Stack1),
    popCategory1(Stack1, Type2, Rest),
    canSafelyPushList(Environment, Rest, [Type1, Type2, Type1],
                      OutputOperandStack),
    NextStackFrame = frame(Locals, OutputOperandStack, Flags),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
dup_x2

Инструкция dup_x2 безопасна по типу, если она является безопасной по типу формой инструкции dup_x2.

instructionIsTypeSafe(dup_x2, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    StackFrame = frame(Locals, InputOperandStack, Flags),
    dup_x2SomeFormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack),
    NextStackFrame = frame(Locals, OutputOperandStack, Flags),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).

Инструкция dup_x2 является безопасной по типу формой инструкции dup_x2, если она является инструкцией безопасной по типу формы 1 dup_x2 или инструкцией безопасной по типу формы 2 dup_x2.

dup_x2SomeFormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
    dup_x2Form1IsTypeSafe(Environment, InputOperandStack, OutputOperandStack).

dup_x2SomeFormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
    dup_x2Form2IsTypeSafe(Environment, InputOperandStack, OutputOperandStack).

Инструкция dup_x2 является инструкцией безопасной по типу формы 1 dup_x2, если можно корректно заменить три типа категории 1, Type1, Type2, Type3 в стеке операндов на входе на типы Type1, Type2, Type3, Type1, что приводит к состоянию типа на выходе.

dup_x2Form1IsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
    popCategory1(InputOperandStack, Type1, Stack1),
    popCategory1(Stack1, Type2, Stack2),
    popCategory1(Stack2, Type3, Rest),
    canSafelyPushList(Environment, Rest, [Type1, Type3, Type2, Type1],
                      OutputOperandStack).

Инструкция dup_x2 является инструкцией безопасной по типу формы 2 dup_x2, если можно корректно заменить тип категории 1, Type1, и тип категории 2, Type2, в стеке операндов на входе на типы Type1, Type2, Type1, что приводит к состоянию типа на выходе.

dup_x2Form2IsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
    popCategory1(InputOperandStack, Type1, Stack1),
    popCategory2(Stack1, Type2, Rest),
    canSafelyPushList(Environment, Rest, [Type1, Type2, Type1],
                      OutputOperandStack).
dup2

Инструкция dup2 безопасна по типу, если она является безопасной по типу формой инструкции dup2.

instructionIsTypeSafe(dup2, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :-
    StackFrame = frame(Locals, InputOperandStack, Flags),
    dup2SomeFormIsTypeSafe(Environment,InputOperandStack, OutputOperandStack),
    NextStackFrame = frame(Locals, OutputOperandStack, Flags),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).

Инструкция dup2 является безопасной по типу формой инструкции dup2, если она является инструкцией безопасной по типу формы 1 dup2 или инструкцией безопасной по типу формы 2 dup2.

dup2SomeFormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
    dup2Form1IsTypeSafe(Environment,InputOperandStack, OutputOperandStack).

dup2SomeFormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
    dup2Form2IsTypeSafe(Environment,InputOperandStack, OutputOperandStack).

Инструкция dup2 является инструкцией безопасной по типу формы 1 dup2, если можно корректно заменить два типа категории 1, Type1 и Type2, в стеке операндов на входе на типы Type1, Type2, Type1, Type2, что приводит к состоянию типа на выходе.

dup2Form1IsTypeSafe(Environment, InputOperandStack, OutputOperandStack):-
    popCategory1(InputOperandStack, Type1, TempStack),
    popCategory1(TempStack, Type2, _),
    canSafelyPushList(Environment, InputOperandStack, [Type1, Type2],
                      OutputOperandStack).

Инструкция dup2 является инструкцией безопасной по типу формы 2 dup2, если можно корректно заменить тип категории 2, Type, в стеке операндов на входе на типы Type, Type, что приводит к состоянию типа на выходе.

dup2Form2IsTypeSafe(Environment, InputOperandStack, OutputOperandStack):-
    popCategory2(InputOperandStack, Type, _),
    canSafelyPush(Environment, InputOperandStack, Type, OutputOperandStack).
dup2_x1

Инструкция dup2_x1 безопасна по типу, если она является безопасной по типу формой инструкции dup2_x1.

instructionIsTypeSafe(dup2_x1, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    StackFrame = frame(Locals, InputOperandStack, Flags),
    dup2_x1SomeFormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack),
    NextStackFrame = frame(Locals, OutputOperandStack, Flags),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).

Инструкция dup2_x1 является безопасной по типу формой инструкции dup2_x1, если она является инструкцией безопасной по типу формы 1 dup2_x1 или инструкцией безопасной по типу формы 2 dup_x2.

dup2_x1SomeFormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
    dup2_x1Form1IsTypeSafe(Environment, InputOperandStack, OutputOperandStack).

dup2_x1SomeFormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
    dup2_x1Form2IsTypeSafe(Environment, InputOperandStack, OutputOperandStack).

Инструкция dup2_x1 является инструкцией безопасной по типу формы 1 dup2_x1, если можно корректно заменить три типа категории 1, Type1, Type2, Type3, в стеке операндов на входе на типы Type1, Type2, Type3, Type1, Type2, что приводит к состоянию типа на выходе.

dup2_x1Form1IsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
    popCategory1(InputOperandStack, Type1, Stack1),
    popCategory1(Stack1, Type2, Stack2),
    popCategory1(Stack2, Type3, Rest),
    canSafelyPushList(Environment, Rest, [Type2, Type1, Type3, Type2, Type1],
                      OutputOperandStack).

Инструкция dup2_x1 является инструкцией безопасной по типу формы 2 dup2_x1, если можно корректно заменить тип категории 2, Type1, и тип категории 1, Type2, в стеке операндов на входе на типы Type1, Type2, Type1, что приводит к состоянию типа на выходе.

dup2_x1Form2IsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
    popCategory2(InputOperandStack, Type1, Stack1),
    popCategory1(Stack1, Type2, Rest),
    canSafelyPushList(Environment, Rest, [Type1, Type2, Type1],
                      OutputOperandStack).
dup2_x2

Инструкция dup2_x2 безопасна по типу, если она является безопасной по типу формой инструкции dup2_x2.

instructionIsTypeSafe(dup2_x2, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    StackFrame = frame(Locals, InputOperandStack, Flags),
    dup2_x2SomeFormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack),
    NextStackFrame = frame(Locals, OutputOperandStack, Flags),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).

Инструкция dup2_x2 является безопасной по типу формой инструкции dup2_x2, если выполняется одно из следующих условий:

  • это инструкция безопасной по типу формы 1 dup2_x2.

  • это инструкция безопасной по типу формы 2 dup2_x2.

  • это инструкция безопасной по типу формы 3 dup2_x2.

  • это инструкция безопасной по типу формы 4 dup2_x2.

dup2_x2SomeFormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
    dup2_x2Form1IsTypeSafe(Environment, InputOperandStack, OutputOperandStack).

dup2_x2SomeFormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
    dup2_x2Form2IsTypeSafe(Environment, InputOperandStack, OutputOperandStack).

dup2_x2SomeFormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
    dup2_x2Form3IsTypeSafe(Environment, InputOperandStack, OutputOperandStack).

dup2_x2SomeFormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
    dup2_x2Form4IsTypeSafe(Environment, InputOperandStack, OutputOperandStack).

Инструкция dup2_x2 является инструкцией безопасной по типу формы 1 dup2_x2, если можно корректно заменить четыре типа категории 1, Type1, Type2, Type3, Type4, в стеке операндов на входе на типы Type1, Type2, Type3, Type4, Type1, Type2, что приводит к состоянию типа на выходе.

dup2_x2Form1IsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
    popCategory1(InputOperandStack, Type1, Stack1),
    popCategory1(Stack1, Type2, Stack2),
    popCategory1(Stack2, Type3, Stack3),
    popCategory1(Stack3, Type4, Rest),
    canSafelyPushList(Environment, Rest,
                      [Type2, Type1, Type4, Type3, Type2, Type1],
                      OutputOperandStack).

Инструкция dup2_x2 является инструкцией безопасной по типу формы 2 dup2_x2, если можно корректно заменить тип категории 2, Type1, и два типа категории 1, Type2, Type3, в стеке операндов на входе на типы Type1, Type2, Type3, Type1, что приводит к состоянию типа на выходе.

dup2_x2Form2IsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
    popCategory2(InputOperandStack, Type1, Stack1),
    popCategory1(Stack1, Type2, Stack2),
    popCategory1(Stack2, Type3, Rest),
    canSafelyPushList(Environment, Rest,
                      [Type1, Type3, Type2, Type1],
                      OutputOperandStack).

Инструкция dup2_x2 является инструкцией безопасной по типу формы 3 dup2_x2, если можно корректно заменить два типа категории 1, Type1, Type2, и тип категории 2, Type3, в стеке операндов на входе на типы Type1, Type2, Type3, Type1, Type2, что приводит к состоянию типа на выходе.

dup2_x2Form3IsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
    popCategory1(InputOperandStack, Type1, Stack1),
    popCategory1(Stack1, Type2, Stack2),
    popCategory2(Stack2, Type3, Rest),
    canSafelyPushList(Environment, Rest,
                      [Type2, Type1, Type3, Type2, Type1],
                      OutputOperandStack).

Инструкция dup2_x2 является инструкцией безопасной по типу формы 4 dup2_x2, если можно корректно заменить два типа категории 2, Type1, Type2, в стеке операндов на входе на типы Type1, Type2, Type1, что приводит к состоянию типа на выходе.

dup2_x2Form4IsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
    popCategory2(InputOperandStack, Type1, Stack1),
    popCategory2(Stack1, Type2, Rest),
    canSafelyPushList(Environment, Rest, [Type1, Type2, Type1],
                      OutputOperandStack).
f2d

Инструкция f2d безопасна по типу, если можно корректно извлечь float из стека операндов на входе и заменить его на double, что приводит к состоянию типа на выходе.

instructionIsTypeSafe(f2d, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    validTypeTransition(Environment, [float], double,
                        StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
f2i

Инструкция f2i безопасна по типу, если можно корректно извлечь float из стека операндов на входе и заменить его на int, что приводит к состоянию типа на выходе.

instructionIsTypeSafe(f2i, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    validTypeTransition(Environment, [float], int,
                        StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
f2l

Инструкция f2l безопасна по типу, если можно корректно извлечь float из стека операндов на входе и заменить его на long, что приводит к состоянию типа на выходе.

instructionIsTypeSafe(f2l, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    validTypeTransition(Environment, [float], long,
                        StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
fadd

Инструкция fadd безопасна по типу, если можно корректно заменить типы, соответствующие float и float, в стеке операндов на входе на float, что приводит к состоянию типа на выходе.

instructionIsTypeSafe(fadd, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    validTypeTransition(Environment, [float, float], float,
                        StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
faload

Инструкция faload безопасна по типу тогда и только тогда, когда можно корректно заменить типы, соответствующие int, и массив float в стеке входных операндов на тип float, получая состояние выходного типа.

instructionIsTypeSafe(faload, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    validTypeTransition(Environment, [int, arrayOf(float)], float,
                        StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
fastore

Инструкция fastore безопасна по типу тогда и только тогда, когда можно корректно извлечь типы, соответствующие float, int и массив float из стека входных операндов, получая состояние выходного типа.

instructionIsTypeSafe(fastore, _Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    canPop(StackFrame, [float, int, arrayOf(float)], NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
fcmp<op>

Инструкция fcmpg безопасна по типу тогда и только тогда, когда можно корректно заменить типы, соответствующие float и float в стеке входных операндов на тип int, получая состояние выходного типа.

instructionIsTypeSafe(fcmpg, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    validTypeTransition(Environment, [float, float], int,
                        StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).

Инструкция fcmpl безопасна по типу тогда и только тогда, когда эквивалентная инструкция fcmpg безопасна по типу.

instructionHasEquivalentTypeRule(fcmpl, fcmpg).
fconst_<f>

Инструкция fconst_0 безопасна по типу, если можно корректно поместить тип float в стек входных операндов, получая состояние выходного типа.

instructionIsTypeSafe(fconst_0, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    validTypeTransition(Environment, [], float, StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).

Правила для других вариантов fconst эквивалентны.

instructionHasEquivalentTypeRule(fconst_1, fconst_0).
instructionHasEquivalentTypeRule(fconst_2, fconst_0).
fdiv

Инструкция fdiv безопасна по типу тогда и только тогда, когда эквивалентная инструкция fadd безопасна по типу.

instructionHasEquivalentTypeRule(fdiv, fadd).
fload

Инструкция fload с операндом Index безопасна по типу и приводит к состоянию выходного типа NextStackFrame, если инструкция загрузки с операндом Index и типом float безопасна по типу и приводит к состоянию выходного типа NextStackFrame.

instructionIsTypeSafe(fload(Index), Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    loadIsTypeSafe(Environment, Index, float, StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
fload_<n>

Инструкции fload_<n> для 0 ≤ n ≤ 3 безопасны по типу тогда и только тогда, когда эквивалентная инструкция fload безопасна по типу.

instructionHasEquivalentTypeRule(fload_0, fload(0)).
instructionHasEquivalentTypeRule(fload_1, fload(1)).
instructionHasEquivalentTypeRule(fload_2, fload(2)).
instructionHasEquivalentTypeRule(fload_3, fload(3)).
fmul

Инструкция fmul безопасна по типу тогда и только тогда, когда эквивалентная инструкция fadd безопасна по типу.

instructionHasEquivalentTypeRule(fmul, fadd).
fneg

Инструкция fneg безопасна по типу тогда и только тогда, когда в стеке входных операндов есть тип, соответствующий float. Инструкция fneg не изменяет состояние типа.

instructionIsTypeSafe(fneg, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    validTypeTransition(Environment, [float], float,
                        StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
frem

Инструкция frem безопасна по типу тогда и только тогда, когда эквивалентная инструкция fadd безопасна по типу.

instructionHasEquivalentTypeRule(frem, fadd).
freturn

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

instructionIsTypeSafe(freturn, Environment, _Offset, StackFrame,
                      afterGoto, ExceptionStackFrame) :- 
    thisMethodReturnType(Environment, float),
    canPop(StackFrame, [float], _PoppedStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
fstore

Инструкция fstore с операндом Index безопасна по типу и приводит к состоянию выходного типа NextStackFrame, если инструкция сохранения с операндом Index и типом float безопасна по типу и приводит к состоянию выходного типа NextStackFrame.

instructionIsTypeSafe(fstore(Index), Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    storeIsTypeSafe(Environment, Index, float, StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
fstore_<n>

Инструкции fstore_<n> для 0 ≤ n ≤ 3 безопасны по типу тогда и только тогда, когда эквивалентная инструкция fstore безопасна по типу.

instructionHasEquivalentTypeRule(fstore_0, fstore(0)).
instructionHasEquivalentTypeRule(fstore_1, fstore(1)).
instructionHasEquivalentTypeRule(fstore_2, fstore(2)).
instructionHasEquivalentTypeRule(fstore_3, fstore(3)).
fsub

Инструкция fsub безопасна по типу тогда и только тогда, когда эквивалентная инструкция fadd безопасна по типу.

instructionHasEquivalentTypeRule(fsub, fadd).
getfield

Инструкция getfield с операндом CP безопасна по типу тогда и только тогда, когда CP ссылается на запись константного пула, обозначающую поле, объявленное с типом FieldType, в классе FieldClass, и можно корректно заменить тип, соответствующий FieldClass, на тип FieldType в стеке входных операндов, получая состояние выходного типа. FieldClass не должен быть типом массива. Поля protected подвергаются дополнительным проверкам (§4.10.1.8).

instructionIsTypeSafe(getfield(CP), Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    CP = field(FieldClass, FieldName, FieldDescriptor),
    parseFieldDescriptor(FieldDescriptor, FieldType),
    passesProtectedCheck(Environment, FieldClass, FieldName,
                         FieldDescriptor, StackFrame),
    validTypeTransition(Environment, [class(FieldClass)], FieldType,
                        StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
getstatic

Инструкция getstatic с операндом CP безопасна по типу тогда и только тогда, когда CP ссылается на запись константного пула, обозначающую поле, объявленное с типом FieldType, и можно корректно поместить FieldType в стек входных операндов, получая состояние выходного типа.

instructionIsTypeSafe(getstatic(CP), Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    CP = field(_FieldClass, _FieldName, FieldDescriptor),
    parseFieldDescriptor(FieldDescriptor, FieldType),
    validTypeTransition(Environment, [], FieldType,
                        StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
goto

Инструкция goto безопасна по типу тогда и только тогда, когда её целевой операнд является допустимой целью перехода.

instructionIsTypeSafe(goto(Target), Environment, _Offset, StackFrame,
                      afterGoto, ExceptionStackFrame) :-
    targetIsTypeSafe(Environment, StackFrame, Target),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
goto_w

Инструкция goto_w безопасна по типу тогда и только тогда, когда эквивалентная инструкция goto безопасна по типу.

instructionHasEquivalentTypeRule(goto_w(Target), goto(Target)).
i2b

Инструкция i2b безопасна по типу тогда и только тогда, когда эквивалентная инструкция ineg безопасна по типу.

instructionHasEquivalentTypeRule(i2b, ineg).
i2c

Инструкция i2c безопасна по типу тогда и только тогда, когда эквивалентная инструкция ineg безопасна по типу.

instructionHasEquivalentTypeRule(i2c, ineg).
i2d

Инструкция i2d безопасна по типу, если можно корректно извлечь int из стека входных операндов и заменить его на double, получая состояние выходного типа.

instructionIsTypeSafe(i2d, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    validTypeTransition(Environment, [int], double,
                        StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
i2f

Инструкция i2f безопасна по типу, если можно корректно извлечь int из стека входных операндов и заменить его на float, получая состояние выходного типа.

instructionIsTypeSafe(i2f, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    validTypeTransition(Environment, [int], float,
                        StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
i2l

Инструкция i2l является безопасной по типу, если можно корректно извлечь int из стека операндов и заменить его на long, получая выходное состояние типа.

instructionIsTypeSafe(i2l, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    validTypeTransition(Environment, [int], long,
                        StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
i2s

Инструкция i2s безопасна по типу тогда и только тогда, когда эквивалентная инструкция ineg безопасна по типу.

instructionHasEquivalentTypeRule(i2s, ineg).
iadd

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

instructionIsTypeSafe(iadd, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    validTypeTransition(Environment, [int, int], int,
                        StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
iaload

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

instructionIsTypeSafe(iaload, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    validTypeTransition(Environment, [int, arrayOf(int)], int,
                        StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
iand

Инструкция iand безопасна по типу тогда и только тогда, когда эквивалентная инструкция iadd безопасна по типу.

instructionHasEquivalentTypeRule(iand, iadd).
iastore

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

instructionIsTypeSafe(iastore, _Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    canPop(StackFrame, [int, int, arrayOf(int)], NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
if_acmp<cond>

Инструкция if_acmpeq безопасна по типу тогда и только тогда, когда можно корректно извлечь типы, соответствующие reference и reference из стека операндов, получая выходное состояние типа NextStackFrame, и операнд инструкции, Target, является допустимой целью ветвления, предполагая входное состояние типа NextStackFrame.

instructionIsTypeSafe(if_acmpeq(Target), Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    canPop(StackFrame, [reference, reference], NextStackFrame),
    targetIsTypeSafe(Environment, NextStackFrame, Target),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).

Правило для if_acmpne идентично.

instructionHasEquivalentTypeRule(if_acmpne(Target), if_acmpeq(Target)).
if_icmp<cond>

Инструкция if_icmpeq безопасна по типу тогда и только тогда, когда можно корректно извлечь типы, соответствующие int и int из стека операндов, получая выходное состояние типа NextStackFrame, и операнд инструкции, Target, является допустимой целью ветвления, предполагая входное состояние типа NextStackFrame.

instructionIsTypeSafe(if_icmpeq(Target), Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    canPop(StackFrame, [int, int], NextStackFrame),
    targetIsTypeSafe(Environment, NextStackFrame, Target),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).

Правила для всех остальных вариантов инструкции if_icmp<cond> идентичны.

instructionHasEquivalentTypeRule(if_icmpge(Target), if_icmpeq(Target)).
instructionHasEquivalentTypeRule(if_icmpgt(Target), if_icmpeq(Target)).
instructionHasEquivalentTypeRule(if_icmple(Target), if_icmpeq(Target)).
instructionHasEquivalentTypeRule(if_icmplt(Target), if_icmpeq(Target)).
instructionHasEquivalentTypeRule(if_icmpne(Target), if_icmpeq(Target)).
if<cond>

Инструкция ifeq безопасна по типу тогда и только тогда, когда можно корректно извлечь тип, соответствующий int из стека операндов, получая выходное состояние типа NextStackFrame, и операнд инструкции, Target, является допустимой целью ветвления, предполагая входное состояние типа NextStackFrame.

instructionIsTypeSafe(ifeq(Target), Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :-
    canPop(StackFrame, [int], NextStackFrame), 
    targetIsTypeSafe(Environment, NextStackFrame, Target),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).

Правила для всех других вариантов инструкции if<cond> идентичны.

instructionHasEquivalentTypeRule(ifge(Target), ifeq(Target)).
instructionHasEquivalentTypeRule(ifgt(Target), ifeq(Target)).
instructionHasEquivalentTypeRule(ifle(Target), ifeq(Target)).
instructionHasEquivalentTypeRule(iflt(Target), ifeq(Target)).
instructionHasEquivalentTypeRule(ifne(Target), ifeq(Target)).
ifnonnull

Инструкция ifnonnull безопасна по типу тогда и только тогда, когда можно корректно извлечь тип, соответствующий reference из стека операндов, получая выходное состояние типа NextStackFrame, и операнд инструкции, Target, является допустимой целью ветвления, предполагая входное состояние типа NextStackFrame.

instructionIsTypeSafe(ifnonnull(Target), Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    canPop(StackFrame, [reference], NextStackFrame),
    targetIsTypeSafe(Environment, NextStackFrame, Target),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
ifnull

Инструкция ifnull безопасна по типу тогда и только тогда, когда эквивалентная инструкция ifnonnull безопасна по типу.

instructionHasEquivalentTypeRule(ifnull(Target), ifnonnull(Target)).
iinc

Инструкция iinc с первым операндом Index безопасна по типу тогда и только тогда, когда LIndex имеет тип int. Инструкция iinc не изменяет состояние типа.

instructionIsTypeSafe(iinc(Index, _Value), _Environment, _Offset,
                      StackFrame, StackFrame, ExceptionStackFrame) :-
    StackFrame = frame(Locals, _OperandStack, _Flags),
    nth0(Index, Locals, int),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
iload

Инструкция iload с операндом Index безопасна по типу и возвращает выходное состояние типа NextStackFrame, если инструкция загрузки с операндом Index и типом int безопасна по типу и возвращает выходное состояние типа NextStackFrame.

instructionIsTypeSafe(iload(Index), Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    loadIsTypeSafe(Environment, Index, int, StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
iload_<n>

Инструкции iload_<n>, для 0 ≤ n ≤ 3, безопасны по типу тогда и только тогда, когда эквивалентная инструкция iload безопасна по типу.

instructionHasEquivalentTypeRule(iload_0, iload(0)).
instructionHasEquivalentTypeRule(iload_1, iload(1)).
instructionHasEquivalentTypeRule(iload_2, iload(2)).
instructionHasEquivalentTypeRule(iload_3, iload(3)).
imul

Инструкция imul безопасна по типу тогда и только тогда, когда эквивалентная инструкция iadd безопасна по типу.

instructionHasEquivalentTypeRule(imul, iadd).
ineg

Инструкция ineg безопасна по типу тогда и только тогда, когда существует тип, соответствующий int в стеке операндов. Инструкция ineg не изменяет состояние типа.

instructionIsTypeSafe(ineg, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    validTypeTransition(Environment, [int], int, StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
instanceof

Инструкция instanceof с операндом CP безопасна по типу тогда и только тогда, когда CP ссылается на запись константного пула, обозначающую класс или массив, и можно корректно заменить тип Object на вершине стека операндов типом int, получая выходное состояние типа.

instructionIsTypeSafe(instanceof(CP), Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    (CP = class(_, _) ; CP = arrayOf(_)),
    isBootstrapLoader(BL),
    validTypeTransition(Environment, [class('java/lang/Object'), BL], int,
                        StackFrame,NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
invokedynamic

Инструкция invokedynamic безопасна по типу тогда и только тогда, когда выполняются все следующие условия:

  • Её первый операнд, CP, ссылается на запись константного пула, обозначающую динамическую точку вызова с именем CallSiteName и дескриптором Descriptor.

  • CallSiteName не является <init>.

  • CallSiteName не является <clinit>.

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

instructionIsTypeSafe(invokedynamic(CP,0,0), Environment, _Offset,
                      StackFrame, NextStackFrame, ExceptionStackFrame) :- 
    CP = dmethod(CallSiteName, Descriptor),
    CallSiteName \= '<init>',
    CallSiteName \= '<clinit>',
    parseMethodDescriptor(Descriptor, OperandArgList, ReturnType),
    reverse(OperandArgList, StackArgList),
    validTypeTransition(Environment, StackArgList, ReturnType,
                        StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
invokeinterface

Инструкция invokeinterface безопасна с точки зрения типов, если выполняются все следующие условия:

  • Её первый операнд, CP, ссылается на запись в константном пуле, обозначающую метод интерфейса, названный MethodName, с дескриптором Descriptor, являющимся членом интерфейса MethodIntfName.

  • MethodName не является <init>.

  • MethodName не является <clinit>.

  • Её второй операнд, Count, является допустимым операндом-счётчиком (см. ниже).

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

instructionIsTypeSafe(invokeinterface(CP, Count, 0), Environment, _Offset,
                      StackFrame, NextStackFrame, ExceptionStackFrame) :- 
    CP = imethod(MethodIntfName, MethodName, Descriptor),
    MethodName \= '<init>',
    MethodName \= '<clinit>',
    parseMethodDescriptor(Descriptor, OperandArgList, ReturnType),
    currentClassLoader(Environment, L),
    reverse([class(MethodIntfName, L) | OperandArgList], StackArgList),
    canPop(StackFrame, StackArgList, TempFrame),
    validTypeTransition(Environment, [], ReturnType, TempFrame, NextStackFrame),
    countIsValid(Count, StackFrame, TempFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).

Операнд Count инструкции invokeinterface является допустимым, если он равен размеру аргументов инструкции. Это равно разнице между размером InputFrame и OutputFrame.

countIsValid(Count, InputFrame, OutputFrame) :-
    InputFrame = frame(_Locals1, OperandStack1, _Flags1),
    OutputFrame = frame(_Locals2, OperandStack2, _Flags2),
    length(OperandStack1, Length1),
    length(OperandStack2, Length2),
    Count =:= Length1 - Length2.
invokespecial

Инструкция invokespecial безопасна с точки зрения типов, если выполняются все следующие условия:

  • Её первый операнд, CP, ссылается на запись в константном пуле, обозначающую метод, названный MethodName, с дескриптором Descriptor, являющийся членом класса MethodClassName.

  • Или:

    • MethodName не является <init>.

    • MethodName не является <clinit>.

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

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

instructionIsTypeSafe(invokespecial(CP), Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    CP = method(MethodClassName, MethodName, Descriptor),
    MethodName \= '<init>',
    MethodName \= '<clinit>',
    parseMethodDescriptor(Descriptor, OperandArgList, ReturnType),
    thisClass(Environment, CurrentClass), 
    reverse([CurrentClass | OperandArgList], StackArgList),
    validTypeTransition(Environment, StackArgList, ReturnType,
                        StackFrame, NextStackFrame),
    currentClassLoader(Environment, L),
    reverse([class(MethodClassName, L) | OperandArgList], StackArgList2),
    validTypeTransition(Environment, StackArgList2, ReturnType,
                        StackFrame, _ResultStackFrame),
    isAssignable(class(CurrentClassName, L), class(MethodClassName, L)).
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
  • Или:

    • Имя метода является <init>.

    • Descriptor указывает на тип возвращаемого значения void.

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

    • Состояние исходящих типов выводится из состояния входящих типов путём сначала замены входящего стека операндов на OperandStack, а затем заменой всех экземпляров UninitializedArg на тип инициализируемого экземпляра.

instructionIsTypeSafe(invokespecial(CP), Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :-
    CP = method(MethodClassName, '<init>', Descriptor),
    parseMethodDescriptor(Descriptor, OperandArgList, void), 
    reverse(OperandArgList, StackArgList),
    canPop(StackFrame, StackArgList, TempFrame),
    TempFrame = frame(Locals, FullOperandStack, Flags),
    FullOperandStack = [UninitializedArg | OperandStack],
    currentClassLoader(Environment, CurrentLoader),
    rewrittenUninitializedType(UninitializedArg, Environment,
                               class(MethodClassName, CurrentLoader), This), 
    rewrittenInitializationFlags(UninitializedArg, Flags, NextFlags), 
    substitute(UninitializedArg, This, OperandStack, NextOperandStack),
    substitute(UninitializedArg, This, Locals, NextLocals),
    NextStackFrame = frame(NextLocals, NextOperandStack, NextFlags),
    ExceptionStackFrame = frame(NextLocals, [], Flags),
    passesProtectedCheck(Environment, MethodClassName, '<init>',
                         Descriptor, NextStackFrame).

rewrittenUninitializedType(uninitializedThis, Environment,
                           _MethodClass, This) :-
    thisClass(Environment, This).

rewrittenUninitializedType(uninitialized(Address), Environment,
                           MethodClass, MethodClass) :-
    allInstructions(Environment, Instructions),
    member(instruction(Address, new(MethodClass)), Instructions).

substitute(_Old, _New, [], []).
substitute(Old, New, [Old | FromRest], [New | ToRest]) :-
    substitute(Old, New, FromRest, ToRest).
substitute(Old, New, [From1 | FromRest], [From1 | ToRest]) :-
    From1 \= Old,
    substitute(Old, New, FromRest, ToRest).

Для вычисления типа, к которому необходимо переписать тип неинициализированного аргумента, существуют два случая:

  • Если мы инициализируем объект внутри его конструктора, его тип изначально uninitializedThis. Этот тип будет переписан на тип класса метода <init>.

  • Второй случай возникает при инициализации объекта, созданного инструкцией new. Неинициализированный тип аргумента переписывается на MethodClass, тип держателя метода <init>. Мы проверяем, есть ли на самом деле инструкция new по адресу Address.

rewrittenInitializationFlags(uninitializedThis, _Flags, []).
rewrittenInitializationFlags(uninitialized(_), Flags, Flags).

Правило для invokespecial метода <init> является единственным стимулом для возврата отдельной структуры стека исключений. Вопрос в том, что invokespecial может вызвать метод суперкласса <init>, и это вызов может завершиться неудачей, оставив this неинициализированным. Такая ситуация не может быть создана с помощью исходного кода на языке программирования Java, но может быть создана путём программирования непосредственно в байткоде.

Исходная структура стека содержит неинициализированный объект в локальной переменной и имеет флаг uninitializedThis. Нормальное завершение invokespecial инициализирует неинициализированный объект и выключает флаг uninitializedThis. Но если вызов метода <init> генерирует исключение, неинициализированный объект может остаться в частично инициализированном состоянии и должен быть сделан постоянно непригодным для использования. Это представлено структурой стека исключений, содержащей повреждённый объект (новое значение локальной переменной) и флаг uninitializedThis (старый флаг). Нет способа перейти от объекта, который, по-видимому, инициализирован и имеет флаг uninitializedThis, к должным образом инициализированному объекту, поэтому объект постоянно непригоден для использования. Если бы не этот случай, структура стека исключений могла бы быть такой же, как и входящая структура стека.

invokestatic

Инструкция invokestatic безопасна с точки зрения типов, если выполняются все следующие условия:

  • Её первый операнд, CP, ссылается на запись в константном пуле, обозначающую метод, названный MethodName, с дескриптором Descriptor.

  • MethodName не является <init>.

  • MethodName не является <clinit>.

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

instructionIsTypeSafe(invokestatic(CP), Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    CP = method(_MethodClassName, MethodName, Descriptor),
    MethodName \= '<init>',
    MethodName \= '<clinit>',
    parseMethodDescriptor(Descriptor, OperandArgList, ReturnType), 
    reverse(OperandArgList, StackArgList),
    validTypeTransition(Environment, StackArgList, ReturnType,
                        StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
invokevirtual

Инструкция invokevirtual безопасна с точки зрения типов, если выполняются все следующие условия:

  • Её первый операнд, CP, ссылается на запись в константном пуле, обозначающую метод, названный MethodName, с дескриптором Descriptor, являющийся членом класса MethodClassName.

  • MethodName не является <init>.

  • MethodName не является <clinit>.

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

  • Если метод является protected, использование соответствует особым правилам доступа к членам protected (§4.10.1.8).

instructionIsTypeSafe(invokevirtual(CP), Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    CP = method(MethodClassName, MethodName, Descriptor),
    MethodName \= '<init>',
    MethodName \= '<clinit>',
    parseMethodDescriptor(Descriptor, OperandArgList, ReturnType), 
    reverse(OperandArgList, ArgList),
    currentClassLoader(Environment, L),
    reverse([class(MethodClassName, L) | OperandArgList], StackArgList),
    validTypeTransition(Environment, StackArgList, ReturnType,
                        StackFrame, NextStackFrame),
    canPop(StackFrame, ArgList, PoppedFrame),
    passesProtectedCheck(Environment, MethodClassName, MethodName,
                         Descriptor, PoppedFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
ior

Инструкция ior безопасна с точки зрения типов, если эквивалентная инструкция iadd безопасна с точки зрения типов.

instructionHasEquivalentTypeRule(ior, iadd).
irem

Инструкция irem безопасна с точки зрения типов, если эквивалентная инструкция iadd безопасна с точки зрения типов.

instructionHasEquivalentTypeRule(irem, iadd).
ireturn

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

instructionIsTypeSafe(ireturn, Environment, _Offset, StackFrame,
                      afterGoto, ExceptionStackFrame) :- 
    thisMethodReturnType(Environment, int),
    canPop(StackFrame, [int], _PoppedStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
ishl

Инструкция ishl безопасна с точки зрения типов, если эквивалентная инструкция iadd безопасна с точки зрения типов.

instructionHasEquivalentTypeRule(ishl, iadd).
ishr

Инструкция ishr безопасна с точки зрения типов, если эквивалентная инструкция iadd безопасна с точки зрения типов.

instructionHasEquivalentTypeRule(ishr, iadd).
istore

Инструкция istore с операндом Index безопасна по типу и приводит к состоянию выходного типа NextStackFrame, если инструкция сохранения с операндом Index и типом int безопасна по типу и приводит к состоянию выходного типа NextStackFrame.

instructionIsTypeSafe(istore(Index), Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    storeIsTypeSafe(Environment, Index, int, StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
istore_<n>

Инструкции istore_<n>, для 0 ≤ n ≤ 3, безопасны по типу тогда и только тогда, когда эквивалентная инструкция istore безопасна по типу.

instructionHasEquivalentTypeRule(istore_0, istore(0)).
instructionHasEquivalentTypeRule(istore_1, istore(1)).
instructionHasEquivalentTypeRule(istore_2, istore(2)).
instructionHasEquivalentTypeRule(istore_3, istore(3)).
isub

Инструкция isub безопасна по типу тогда и только тогда, когда эквивалентная инструкция iadd безопасна по типу.

instructionHasEquivalentTypeRule(isub, iadd).
iushr

Инструкция iushr безопасна по типу тогда и только тогда, когда эквивалентная инструкция iadd безопасна по типу.

instructionHasEquivalentTypeRule(iushr, iadd).
ixor

Инструкция ixor безопасна по типу тогда и только тогда, когда эквивалентная инструкция iadd безопасна по типу.

instructionHasEquivalentTypeRule(ixor, iadd).
l2d

Инструкция l2d безопасна по типу, если можно корректно извлечь long из входного стека операндов и заменить его на double, при этом получив состояние выходного типа.

instructionIsTypeSafe(l2d, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    validTypeTransition(Environment, [long], double,
                        StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
l2f

Инструкция l2f безопасна по типу, если можно корректно извлечь long из входного стека операндов и заменить его на float, при этом получив состояние выходного типа.

instructionIsTypeSafe(l2f, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    validTypeTransition(Environment, [long], float,
                        StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
l2i

Инструкция l2i безопасна по типу, если можно корректно извлечь long из входного стека операндов и заменить его на int, при этом получив состояние выходного типа.

instructionIsTypeSafe(l2i, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    validTypeTransition(Environment, [long], int,
                        StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
ladd

Инструкция ladd безопасна по типу тогда и только тогда, когда можно корректно заменить типы, соответствующие long и long на входном стеке операндов, на long, получив состояние выходного типа.

instructionIsTypeSafe(ladd, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    validTypeTransition(Environment, [long, long], long,
                        StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
laload

Инструкция laload безопасна по типу тогда и только тогда, когда можно корректно заменить типы, соответствующие int и массиву long на входном стеке операндов, на long, получив состояние выходного типа.

instructionIsTypeSafe(laload, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    validTypeTransition(Environment, [int, arrayOf(long)], long,
                        StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
land

Инструкция land безопасна по типу тогда и только тогда, когда эквивалентная инструкция ladd безопасна по типу.

instructionHasEquivalentTypeRule(land, ladd).
lastore

Инструкция lastore безопасна по типу тогда и только тогда, когда можно корректно извлечь типы, соответствующие long, int и массиву long из входного стека операндов, получив состояние выходного типа.

instructionIsTypeSafe(lastore, _Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    canPop(StackFrame, [long, int, arrayOf(long)], NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
lcmp

Инструкция lcmp безопасна по типу тогда и только тогда, когда можно корректно заменить типы, соответствующие long и long на входном стеке операндов, на int, получив состояние выходного типа.

instructionIsTypeSafe(lcmp, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    validTypeTransition(Environment, [long, long], int,
                        StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
lconst_<l>

Инструкция lconst_0 безопасна по типу, если можно корректно поместить тип long на входной стек операндов, получив состояние выходного типа.

instructionIsTypeSafe(lconst_0, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    validTypeTransition(Environment, [], long, StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).

Инструкция lconst_1 безопасна по типу тогда и только тогда, когда эквивалентная инструкция lconst_0 безопасна по типу.

instructionHasEquivalentTypeRule(lconst_1, lconst_0).
ldc

Инструкция ldc с операндом CP безопасна по типу тогда и только тогда, когда CP ссылается на запись в пуле констант, обозначающую сущность типа Type, где Type — это либо int, либо float, либо String, либо Class, либо java.lang.invoke.MethodType, либо java.lang.invoke.MethodHandle, и можно корректно поместить Type на входной стек операндов, получив состояние выходного типа.

instructionIsTypeSafe(ldc(CP), Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    functor(CP, Tag, _),
    isBootstrapLoader(BL),
    member([Tag, Type], [
        [int, int],
        [float, float],
        [string, class('java/lang/String', BL)],
        [classConst, class('java/lang/Class', BL)],
        [methodTypeConst, class('java/lang/invoke/MethodType', BL)],
        [methodHandleConst, class('java/lang/invoke/MethodHandle', BL)],
    ]),
    validTypeTransition(Environment, [], Type, StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
ldc_w

Инструкция ldc_w безопасна по типу тогда и только тогда, когда эквивалентная инструкция ldc безопасна по типу.

instructionHasEquivalentTypeRule(ldc_w(CP), ldc(CP))
ldc2_w

Инструкция ldc2_w с операндом CP безопасна по типу тогда и только тогда, когда CP ссылается на запись в пуле констант, обозначающую сущность типа Tag, где Tag — это либо long, либо double, и можно корректно поместить Tag на входной стек операндов, получив состояние выходного типа.

instructionIsTypeSafe(ldc2_w(CP), Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    functor(CP, Tag, _),
    member(Tag, [long, double]), 
    validTypeTransition(Environment, [], Tag, StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
ldiv

Инструкция ldiv безопасна по типу тогда и только тогда, когда эквивалентная инструкция ladd безопасна по типу.

instructionHasEquivalentTypeRule(ldiv, ladd).
lload

Инструкция lload с операндом Index безопасна по типу и приводит к состоянию выходного типа NextStackFrame, если инструкция загрузки с операндом Index и типом long безопасна по типу и приводит к состоянию выходного типа NextStackFrame.

instructionIsTypeSafe(lload(Index), Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    loadIsTypeSafe(Environment, Index, long, StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
lload_<n>

Инструкции lload_<n>, для 0 ≤ n ≤ 3, безопасны по типу тогда и только тогда, когда эквивалентная инструкция lload безопасна по типу.

instructionHasEquivalentTypeRule(lload_0, lload(0)).
instructionHasEquivalentTypeRule(lload_1, lload(1)).
instructionHasEquivalentTypeRule(lload_2, lload(2)).
instructionHasEquivalentTypeRule(lload_3, lload(3)).
lmul

Инструкция lmul безопасна по типу тогда и только тогда, когда эквивалентная инструкция ladd безопасна по типу.

instructionHasEquivalentTypeRule(lmul, ladd).
lneg

Инструкция lneg безопасна по типу тогда и только тогда, когда существует тип, соответствующий long на входном стеке операндов. Инструкция lneg не изменяет состояние типа.

instructionIsTypeSafe(lneg, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    validTypeTransition(Environment, [long], long,
                        StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
lookupswitch

Инструкция lookupswitch является типобезопасной, если её ключи отсортированы, можно корректно извлечь int из входного стека операндов, получив новое состояние типа BranchStackFrame, и все целевые адреса инструкции являются допустимыми целями перехода, предполагая BranchStackFrame как их входное состояние типа.

instructionIsTypeSafe(lookupswitch(Targets, Keys), Environment, _, StackFrame, 
                      afterGoto, ExceptionStackFrame) :-
    sort(Keys, Keys),
    canPop(StackFrame, [int], BranchStackFrame),
    checklist(targetIsTypeSafe(Environment, BranchStackFrame), Targets),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
lor

Инструкция lor является типобезопасной тогда и только тогда, когда эквивалентная инструкция ladd является типобезопасной.

instructionHasEquivalentTypeRule(lor, ladd).
lrem

Инструкция lrem является типобезопасной тогда и только тогда, когда эквивалентная инструкция ladd является типобезопасной.

instructionHasEquivalentTypeRule(lrem, ladd).
lreturn

Инструкция lreturn является типобезопасной, если у окружающего метода объявлен тип возвращаемого значения long, и можно корректно извлечь тип, соответствующий long, из входного стека операндов.

instructionIsTypeSafe(lreturn, Environment, _Offset, StackFrame,
                      afterGoto, ExceptionStackFrame) :- 
    thisMethodReturnType(Environment, long),
    canPop(StackFrame, [long], _PoppedStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
lshl

Инструкция lshl является типобезопасной, если можно корректно заменить типы int и long на входном стеке операндов типом long, получая состояние типа на выходе.

instructionIsTypeSafe(lshl, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    validTypeTransition(Environment, [int, long], long,
                        StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
lshr

Инструкция lshr является типобезопасной тогда и только тогда, когда эквивалентная инструкция lshl является типобезопасной.

instructionHasEquivalentTypeRule(lshr, lshl).
lstore

Инструкция lstore с операндом Index является типобезопасной и даёт состояние типа на выходе NextStackFrame, если инструкция сохранения с операндом Index и типом long является типобезопасной и даёт состояние типа на выходе NextStackFrame.

instructionIsTypeSafe(lstore(Index), Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    storeIsTypeSafe(Environment, Index, long, StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
lstore_

Инструкции lstore_, для 0 ≤ n ≤ 3, являются типобезопасными тогда и только тогда, когда эквивалентная инструкция lstore является типобезопасной.

instructionHasEquivalentTypeRule(lstore_0, lstore(0)).
instructionHasEquivalentTypeRule(lstore_1, lstore(1)).
instructionHasEquivalentTypeRule(lstore_2, lstore(2)).
instructionHasEquivalentTypeRule(lstore_3, lstore(3)).
lsub

Инструкция lsub является типобезопасной тогда и только тогда, когда эквивалентная инструкция ladd является типобезопасной.

instructionHasEquivalentTypeRule(lsub, ladd).
lushr

Инструкция lushr является типобезопасной тогда и только тогда, когда эквивалентная инструкция lshl является типобезопасной.

instructionHasEquivalentTypeRule(lushr, lshl).
lxor

Инструкция lxor является типобезопасной тогда и только тогда, когда эквивалентная инструкция ladd является типобезопасной.

instructionHasEquivalentTypeRule(lxor, ladd).
monitorenter

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

instructionIsTypeSafe(monitorenter, _Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :-
    canPop(StackFrame, [reference], NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
monitorexit

Инструкция monitorexit является типобезопасной тогда и только тогда, когда эквивалентная инструкция monitorenter является типобезопасной.

instructionHasEquivalentTypeRule(monitorexit, monitorenter).
multianewarray

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

instructionIsTypeSafe(multianewarray(CP, Dim), Environment, _Offset,
                      StackFrame, NextStackFrame, ExceptionStackFrame) :- 
    CP = arrayOf(_),
    classDimension(CP, Dimension),
    Dimension >= Dim,
    Dim > 0, 
    /* Make a list of Dim ints */
    findall(int, between(1, Dim, _), IntList),
    validTypeTransition(Environment, IntList, CP,
                        StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).

Размерность типа массива, компоненты которого также являются массивами, на единицу больше размерности типа его компонента.

classDimension(arrayOf(X), Dimension) :-
    classDimension(X, Dimension1), 
    Dimension is Dimension1 + 1.	

classDimension(_, Dimension) :-
    Dimension = 0. 
new

Инструкция new с операндом CP в смещении Offset является типобезопасной тогда и только тогда, когда CP ссылается на запись константного пула, обозначающую тип класса, тип uninitialized(Offset) не появляется в входном стеке операндов, и можно корректно поместить uninitialized(Offset) в входной стек операндов и заменить uninitialized(Offset) на top в входных локальных переменных, получая состояние типа на выходе.

instructionIsTypeSafe(new(CP), Environment, Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    StackFrame = frame(Locals, OperandStack, Flags), 
    CP = class(_, _), 
    NewItem = uninitialized(Offset),
    notMember(NewItem, OperandStack),
    substitute(NewItem, top, Locals, NewLocals),
    validTypeTransition(Environment, [], NewItem,
                        frame(NewLocals, OperandStack, Flags),
                        NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
newarray

Инструкция newarray с операндом TypeCode является типобезопасной тогда и только тогда, когда TypeCode соответствует примитивному типу ElementType, и можно корректно заменить тип int на входном стеке операндов типом «массив из ElementType», получая состояние типа на выходе.

instructionIsTypeSafe(newarray(TypeCode), Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    primitiveArrayInfo(TypeCode, _TypeChar, ElementType, _VerifierType),
    validTypeTransition(Environment, [int], arrayOf(ElementType),
                        StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).

Соответствие между кодами типов и примитивными типами задаётся следующим предикатом:

primitiveArrayInfo(4,  0'Z, boolean, int).
primitiveArrayInfo(5,  0'C, char,    int).
primitiveArrayInfo(6,  0'F, float,   float).
primitiveArrayInfo(7,  0'D, double,  double).
primitiveArrayInfo(8,  0'B, byte,    int).
primitiveArrayInfo(9,  0'S, short,   int).
primitiveArrayInfo(10, 0'I, int,     int). 
primitiveArrayInfo(11, 0'J, long,    long).
nop

Инструкция nop всегда является типобезопасной. Инструкция nop не влияет на состояние типа.

instructionIsTypeSafe(nop, _Environment, _Offset, StackFrame,
                      StackFrame, ExceptionStackFrame) :-
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
pop

Инструкция pop является типобезопасной тогда и только тогда, когда можно корректно извлечь тип категории 1 из входного стека операндов, получая состояние типа на выходе.

instructionIsTypeSafe(pop, _Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    StackFrame = frame(Locals, [Type | Rest], Flags),
    Type \= top,
    sizeOf(Type, 1),
    NextStackFrame = frame(Locals, Rest, Flags),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
pop2

Инструкция pop2 является типобезопасной тогда и только тогда, когда она представляет собой типобезопасную форму инструкции pop2.

instructionIsTypeSafe(pop2, _Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    StackFrame = frame(Locals, InputOperandStack, Flags),
    pop2SomeFormIsTypeSafe(InputOperandStack, OutputOperandStack),
    NextStackFrame = frame(Locals, OutputOperandStack, Flags),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).

Инструкция pop2 является типобезопасной формой инструкции pop2 тогда и только тогда, когда она представляет собой инструкцию типобезопасная форма 1 pop2 или инструкцию типобезопасная форма 2 pop2.

pop2SomeFormIsTypeSafe(InputOperandStack, OutputOperandStack) :-
    pop2Form1IsTypeSafe(InputOperandStack, OutputOperandStack).

pop2SomeFormIsTypeSafe(InputOperandStack, OutputOperandStack) :-
    pop2Form2IsTypeSafe(InputOperandStack, OutputOperandStack).

Инструкция pop2 является типобезопасной формой 1 pop2 инструкции тогда и только тогда, когда можно корректно извлечь два типа размера 1 из входного стека операндов, получая состояние типа на выходе.

pop2Form1IsTypeSafe([Type1, Type2 | Rest], Rest) :-
    sizeOf(Type1, 1),
    sizeOf(Type2, 1).

Инструкция pop2 является типобезопасной формой 2 pop2 инструкции тогда и только тогда, когда можно корректно извлечь тип размера 2 из входного стека операндов, получая состояние типа на выходе.

pop2Form2IsTypeSafe([top, Type | Rest], Rest) :- sizeOf(Type, 2).
putfield

Инструкция putfield с операндом CP является типобезопасной тогда и только тогда, когда CP ссылается на запись константного пула, обозначающую поле, объявленный тип которого FieldType, объявленное в классе FieldClass, и можно корректно извлечь типы, соответствующие FieldType и FieldClass, из входного стека операндов, получая состояние типа на выходе.

instructionIsTypeSafe(putfield(CP), Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    CP = field(FieldClass, FieldName, FieldDescriptor),
    parseFieldDescriptor(FieldDescriptor, FieldType),	
    canPop(StackFrame, [FieldType], PoppedFrame),
    passesProtectedCheck(Environment, FieldClass, FieldName,
                         FieldDescriptor, PoppedFrame),
    currentClassLoader(Environment, CurrentLoader),
    canPop(StackFrame, [FieldType, class(FieldClass, CurrentLoader)],
           NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
putstatic

Инструкция putstatic с операндом CP является типобезопасной, если CP ссылается на запись в пуле констант, обозначающую поле, тип которого объявлен как FieldType, и можно корректно извлечь тип, соответствующий FieldType, из входящего стека операндов, получая состояние исходящего типа.

instructionIsTypeSafe(putstatic(CP), _Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    CP = field(_FieldClass, _FieldName, FieldDescriptor),
    parseFieldDescriptor(FieldDescriptor, FieldType),
    canPop(StackFrame, [FieldType], NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
return

Инструкция return является типобезопасной, если метод, её содержащий, объявляет тип возвращаемого значения void, и выполняется одно из следующих условий:

  • Содержащий метод не является методом <init>, или

  • this уже полностью инициализирован в момент выполнения инструкции.

instructionIsTypeSafe(return, Environment, _Offset, StackFrame,
                      afterGoto, ExceptionStackFrame) :- 
    thisMethodReturnType(Environment, void),
    StackFrame = frame(_Locals, _OperandStack, Flags),
    notMember(flagThisUninit, Flags),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
saload

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

instructionIsTypeSafe(saload, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    validTypeTransition(Environment, [int, arrayOf(short)], int,
                        StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
sastore

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

instructionIsTypeSafe(sastore, _Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    canPop(StackFrame, [int, int, arrayOf(short)], NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
sipush

Инструкция sipush является типобезопасной, если можно корректно поместить тип int в входящий стек операндов, получая состояние исходящего типа.

instructionIsTypeSafe(sipush(_Value), Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    validTypeTransition(Environment, [], int, StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
swap

Инструкция swap является типобезопасной, если можно корректно заменить два типа категории 1, Type1 и Type2, входящими в стек операндов, на типы Type2 и Type1, получая состояние исходящего типа.

instructionIsTypeSafe(swap, _Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    StackFrame = frame(_Locals, [Type1, Type2 | Rest], _Flags),
    sizeOf(Type1, 1),
    sizeOf(Type2, 1),
    NextStackFrame = frame(_Locals, [Type2, Type1 | Rest], _Flags),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
tableswitch

Инструкция tableswitch является типобезопасной, если её ключи отсортированы, можно корректно извлечь int из входящего стека операндов, получая новое состояние типа BranchStackFrame, и все целевые адреса инструкции являются допустимыми целевыми адресами перехода, предполагая BranchStackFrame как их входящее состояние типа.

instructionIsTypeSafe(tableswitch(Targets, Keys), Environment, _Offset,
                      StackFrame, afterGoto, ExceptionStackFrame) :- 
    sort(Keys, Keys), 
    canPop(StackFrame, [int], BranchStackFrame),
    checklist(targetIsTypeSafe(Environment, BranchStackFrame), Targets),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
wide

Инструкции wide следуют тем же правилам, что и инструкции, которые они расширяют.

instructionHasEquivalentTypeRule(wide(WidenedInstruction),
                                 WidenedInstruction).

4.10.2. Проверка по инференции типов

Файл class, не содержащий атрибут StackMapTable (который обязательно имеет номер версии 49.0 или ниже), должен быть проверен с помощью инференции типов.

4.10.2.1. Процесс проверки по инференции типов

Во время линковки верификатор проверяет массив code атрибута Code для каждого метода файла class, выполняя анализ потока данных для каждого метода. Верификатор гарантирует, что в любой момент программы, независимо от пути кода, который используется для достижения данной точки, выполняется следующее:

  • Стек операндов всегда имеет одинаковый размер и содержит одинаковые типы значений.

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

  • Методы вызываются с соответствующими аргументами.

  • Поля присваиваются только с использованием значений соответствующих типов.

  • Все операционные коды имеют соответствующие типы аргументов в стеке операндов и в массиве локальных переменных.

  • В локальной переменной никогда нет неинициализированного экземпляра класса в коде, защищенном обработчиком исключений. Однако неинициализированный экземпляр класса может быть в стеке операндов в коде, защищенном обработчиком исключений. При возникновении исключения содержимое стека операндов отбрасывается.

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

Например, если метод вызывает другой метод, возвращающий экземпляр класса A, и этот экземпляр присваивается только полю того же типа, верификатор не пытается проверить, существует ли класс A на самом деле. Однако, если он присваивается полю типа B, определения обоих A и B должны быть загружены, чтобы гарантировать, что A является подклассом B.

4.10.2.2. Верификатор байткода

Код каждого метода проверяется независимо. Сначала байты, составляющие код, разбиваются на последовательность инструкций, а индекс в массиве code начала каждой инструкции помещается в массив. Затем верификатор повторно проходит по коду и анализирует инструкции. В ходе этого прохода строится структура данных, хранящая информацию о каждой инструкции виртуальной машины Java в методе. Операнды каждой инструкции, если они есть, проверяются на корректность. Например:

  • Ветвления должны находиться в пределах границ массива code для метода.

  • Цели всех инструкций управления потоком — это начало каждой инструкции. В случае инструкции wide, код wide считается началом инструкции, а код, определяющий операцию, модифицируемую этой инструкцией wide, не считается началом инструкции. Ветвление в середину инструкции запрещено.

  • Никакая инструкция не может получить доступ к локальной переменной или изменить её по индексу, большему или равному количеству локальных переменных, которые метод указывает, что он выделяет.

  • Все ссылки на пул констант должны указывать на запись соответствующего типа. (Например, инструкция getfield должна ссылаться на поле.)

  • Код не завершается в середине инструкции.

  • Исполнение не может выйти за пределы кода.

  • Для каждого обработчика исключений начальная и конечная точки кода, защищённого обработчиком, должны находиться в начале инструкции или, в случае конечной точки, сразу после её конца. Начальная точка должна предшествовать конечной. Код обработчика исключений должен начинаться с корректной инструкции и не должен начинаться с кода операции, модифицируемой инструкцией wide.

Для каждой инструкции метода верификатор записывает содержимое стека операндов и содержимое массива локальных переменных до выполнения этой инструкции. Для стека операндов ему необходимо знать высоту стека и тип каждого значения в нём. Для каждой локальной переменной ему необходимо знать либо тип содержимого этой локальной переменной, либо то, что локальная переменная содержит непригодное или неизвестное значение (возможно, она не инициализирована). Верификатор байткода не обязан различать целочисленные типы (например, byte, short, char) при определении типов значений в стеке операндов.

Далее инициализируется анализатор потока данных. Для первой инструкции метода локальные переменные, представляющие параметры, изначально содержат значения типов, указанные в описателе типа метода; стек операндов пустой. Все остальные локальные переменные содержат недопустимое значение. Для других инструкций, которые ещё не были проанализированы, нет информации о стеке операндов или локальных переменных.

Наконец, выполняется анализ потока данных. Для каждой инструкции бит "изменён" указывает, нужно ли рассматривать эту инструкцию. Изначально бит "изменён" установлен только для первой инструкции. Анализатор потока данных выполняет следующий цикл:

  1. Выбрать инструкцию виртуальной машины Java, у которой установлен бит "изменён". Если инструкция, у которой установлен бит "изменён", не осталась, метод успешно проверен. В противном случае снимите бит "изменён" выбранной инструкции.

  2. Моделировать влияние инструкции на стек операндов и массив локальных переменных, выполнив следующие действия:

    • Если инструкция использует значения из стека операндов, убедитесь, что на стеке есть достаточное количество значений и что верхние значения на стеке имеют соответствующий тип. В противном случае проверка завершается неудачей.

    • Если инструкция использует локальную переменную, убедитесь, что указанная локальная переменная содержит значение соответствующего типа. В противном случае проверка завершается неудачей.

    • Если инструкция помещает значения в стек операндов, убедитесь, что на стеке операндов достаточно места для новых значений. Добавьте указанные типы в начало моделируемого стека операндов.

    • Если инструкция изменяет локальную переменную, запишите, что локальная переменная теперь содержит новый тип.

  3. Определить инструкции, которые могут следовать за текущей инструкцией. Последовательные инструкции могут быть следующими:

    • Следующая инструкция, если текущая инструкция не является безусловной инструкцией передачи управления (например, goto, return или athrow). Проверка завершается неудачей, если возможно "выпасть" из последней инструкции метода.

    • Цель(и) условного или безусловного ветвления или переключения.

    • Любые обработчики исключений для этой инструкции.

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

    В особом случае передачи управления обработчику исключений, стек операндов устанавливается так, чтобы содержать единственный объект типа исключения, указанного информацией об обработчике исключений. Должно быть достаточно места на стеке операндов для этого единственного значения, как если бы его поместила инструкция.

    • Если эта последовательная инструкция посещается впервые, запишите, что значения стека операндов и локальных переменных, вычисленные на шагах 2 и 3, являются состоянием стека операндов и массива локальных переменных до выполнения последовательной инструкции. Установите бит "изменён" для последовательной инструкции.

    • Если последовательная инструкция была видна ранее, объедините значения стека операндов и локальных переменных, вычисленные на шагах 2 и 3, в уже имеющиеся значения. Установите бит "изменён", если есть какие-либо изменения в значениях.

  5. Продолжить с шага 1.

Для объединения двух стеков операндов количество значений в каждом стеке должно быть одинаковым. Типы значений в стеках также должны быть одинаковыми, за исключением того, что значения разных типов reference могут появляться в соответствующих местах в двух стеках. В этом случае объединённый стек операндов содержит ссылку на экземпляр первого общего суперкласса двух типов. Такой тип reference всегда существует, поскольку тип Object является суперклассом всех типов классов и интерфейсов. Если стеки операндов нельзя объединить, проверка метода завершается неудачей.

Для объединения двух состояний массива локальных переменных соответствующие пары локальных переменных сравниваются. Если два типа не идентичны, то, если оба не содержат значения reference, верификатор записывает, что локальная переменная содержит непригодное значение. Если обе пары локальных переменных содержат значения reference, объединённое состояние содержит ссылку на экземпляр первого общего суперкласса двух типов.

Если анализатор потока данных выполняется на методе без сообщения об ошибке проверки, значит метод успешно проверен верификатором файлов class.

Некоторые инструкции и типы данных усложняют работу анализатора потока данных. Теперь рассмотрим каждый из них более подробно.

4.10.2.3. Значения типов long и double

Значения типов long и double обрабатываются процессом проверки особым образом.

Всякий раз, когда значение типа long или double перемещается в локальную переменную с индексом n, индекс n+1 помечается особым образом, чтобы указать, что он зарезервирован значением с индексом n и не должен использоваться как индекс локальной переменной. Любое значение, ранее находившееся в индексе n+1, становится непригодным для использования.

Всякий раз, когда значение перемещается в локальную переменную с индексом n, индекс n-1 проверяется на наличие значения типа long или double. Если это так, локальная переменная с индексом n-1 изменяется, чтобы указать, что она теперь содержит непригодное значение. Поскольку локальная переменная с индексом n перезаписана, локальная переменная с индексом n-1 не может представлять значение типа long или double.

Обработка значений типов long или double в стеке операндов проще; верификатор обрабатывает их как отдельные значения в стеке. Например, код верификации для команды dadd (сложение двух значений типа double) проверяет, что две верхние позиции в стеке — это оба значения типа double. При расчете длины стека операндов значения типов long и double имеют длину два.

Нетипизированные инструкции, которые манипулируют стеком операндов, должны обрабатывать значения типов long и double как атомарные (неделимые). Например, верификатор сообщает об ошибке, если верхнее значение в стеке — это double, и он встречает инструкцию, такую как pop или dup. Вместо этого необходимо использовать инструкции pop2 или dup2.

4.10.2.4. Методы инициализации экземпляров и только что созданные объекты

Создание нового экземпляра класса — многоступенчатый процесс. Выражение:

...
new myClass(i, j, k);
...

может быть реализовано следующим образом:

...
new #1            // Allocate uninitialized space for myClass
dup               // Duplicate object on the operand stack
iload_1           // Push i
iload_2           // Push j
iload_3           // Push k
invokespecial #5  // Invoke myClass.<init>
...

Эта последовательность инструкций оставляет только что созданный и инициализированный объект на вершине стека операндов. (Дополнительные примеры компиляции в набор инструкций виртуальной машины Java приведены в §3.)

Метод инициализации экземпляра (§2.9) для класса myClass видит новый неинициализированный объект как свой аргумент this в локальной переменной 0. Прежде чем этот метод вызовет другой метод инициализации экземпляра класса myClass или его непосредственного суперкласса для this, единственной операцией, которую этот метод может выполнить с this, является присвоение полей, объявленных в myClass.

При выполнении анализа потока данных для методов экземпляра верификатор инициализирует локальную переменную 0 объектом текущего класса или, для методов инициализации экземпляров, локальная переменная 0 содержит специальный тип, указывающий на неинициализированный объект. После вызова соответствующего метода инициализации экземпляра (из текущего класса или текущего суперкласса) для этого объекта все вхождения этого специального типа в модели стека операндов верификатора и в массиве локальных переменных заменяются типом текущего класса. Верификатор отклоняет код, который использует новый объект до его инициализации или инициализирует объект более одного раза. Кроме того, он гарантирует, что каждый нормальный возврат метода вызвал метод инициализации экземпляра либо в классе этого метода, либо в непосредственном суперклассе.

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

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

new InputStream(new Foo(), new InputStream("foo"))

может иметь два неинициализированных экземпляра класса InputStream в стеке операндов одновременно. При вызове метода инициализации экземпляра на экземпляре класса только те вхождения специального типа в стеке операндов или в массиве локальных переменных, которые являются тем же объектом, что и экземпляр класса, заменяются.

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

4.10.2.5. Исключения и finally

Для реализации конструкции try-finally компилятор языка программирования Java, генерирующий файлы class с номером версии 50.0 или ниже, может использовать механизм обработки исключений вместе с двумя специальными инструкциями: jsr ("переход к подпрограмме") и ret ("возврат из подпрограммы"). Блок finally компилируется как подпрограмма в коде виртуальной машины Java для метода, подобно коду обработчика исключений. При выполнении инструкции jsr, вызывающей подпрограмму, она помещает адрес возврата, адрес инструкции после выполняемой инструкции jsr, на стек операндов как значение типа returnAddress. Код подпрограммы сохраняет адрес возврата в локальной переменной. В конце подпрограммы инструкция ret извлекает адрес возврата из локальной переменной и передает управление инструкции по адресу возврата.

Управление может быть передано блоку finally (подпрограмму finally можно вызвать) несколькими различными способами. Если блок try завершается нормально, подпрограмма finally вызывается посредством инструкции jsr перед вычислением следующего выражения. Оператор break или continue внутри блока try, передающий управление за пределы блока try, сначала выполняет jsr в код блока finally. Если блок try выполняет оператор return, скомпилированный код выполняет следующее:

  1. Сохраняет значение возврата (если таковое имеется) в локальной переменной.

  2. Выполняет jsr в код блока finally.

  3. После возврата из блока finally возвращает сохраненное в локальной переменной значение.

Компилятор устанавливает специальный обработчик исключений, который перехватывает любые исключения, сгенерированные блоком try. Если в блоке try происходит сгенерирование исключения, этот обработчик выполняет следующее:

  1. Сохраняет исключение в локальной переменной.

  2. Выполняет jsr в блок finally.

  3. После возврата из блока finally, выбрасывает исключение.

Дополнительную информацию об реализации конструкции try-finally см. в §3.13.

Код блока finally представляет собой особую проблему для верификатора. Обычно, если к определенной инструкции можно добраться по нескольким путям, и определенная локальная переменная содержит несовместимые значения через эти пути, то локальная переменная становится неиспользуемой. Однако блок finally может быть вызван из нескольких мест, что приводит к различным ситуациям:

  • Вызов из обработчика исключений может иметь определенную локальную переменную, содержащую исключение.

  • Вызов для реализации return может иметь локальную переменную, содержащую возвращаемое значение.

  • Вызов из нижней части блока try может иметь неопределенное значение в той же локальной переменной.

Сам код блока finally может пройти проверку, но после завершения обновления всех преемников инструкции ret, верификатор заметит, что локальная переменная, которую обработчик исключений ожидает содержать исключение, или что код возврата ожидает содержать возвращаемое значение, теперь содержит неопределенное значение.

Проверка кода, содержащего блок finally, усложнена. Основная идея заключается в следующем:

  • Каждая инструкция отслеживает список целевых jsr инструкций, необходимых для достижения этой инструкции. Для большинства кода этот список пуст. Для инструкций внутри кода блока finally он имеет длину один. Для многократно вложенного кода finally (крайне редкий случай!) он может быть больше единицы.

  • Для каждой инструкции и каждой необходимой инструкции jsr для достижения этой инструкции поддерживается битовый вектор всех локальных переменных, используемых или изменяемых с момента выполнения инструкции jsr.

  • При выполнении инструкции ret, которая реализует возврат из подпрограммы, должно быть только одно возможное подпрограмму, из которой инструкция может возвращаться. Две разные подпрограммы не могут "слить" своё выполнение в одну инструкцию ret.

  • Для выполнения анализа потока данных для инструкции ret используется специальная процедура. Поскольку верификатор знает подпрограмму, из которой инструкция должна возвращаться, он может найти все инструкции jsr, которые вызывают подпрограмму, и объединить состояние стека операндов и массива локальных переменных в момент инструкции ret в стек операндов и массив локальных переменных инструкций, следующих за jsr. Объединение использует специальный набор значений для локальных переменных:

    • Для любой локальной переменной, которую битовый вектор (построенный выше) указывает как используемую или изменяемую подпрограммой, используйте тип локальной переменной в момент инструкции ret.

    • Для других локальных переменных используйте тип локальной переменной перед инструкцией jsr.

4.11. Ограничения виртуальной машины Java

Следующие ограничения виртуальной машины Java являются неявными в формате файла class:

  • Пул констант для каждого класса или интерфейса ограничен 65535 записями полем с размером 16 бит constant_pool_count структуры ClassFile (§4.1). Это является внутренним ограничением общей сложности одного класса или интерфейса.

  • Количество полей, которые могут быть объявлены классом или интерфейсом, ограничено 65535 значениями размера элемента fields_count структуры ClassFile (§4.1).

    Обратите внимание, что значение элемента fields_count структуры ClassFile не включает поля, унаследованные от суперклассов или суперинтерфейсов.

  • Количество методов, которые могут быть объявлены классом или интерфейсом, ограничено 65535 значениями размера элемента methods_count структуры ClassFile (§4.1).

    Обратите внимание, что значение элемента methods_count структуры ClassFile не включает методы, унаследованные от суперклассов или суперинтерфейсов.

  • Количество непосредственных суперинтерфейсов класса или интерфейса ограничено 65535 значениями размера элемента interfaces_count структуры ClassFile (§4.1).

  • Максимальное число локальных переменных в массиве локальных переменных кадра, созданного при вызове метода (§2.6), ограничено 65535 значениями размера элемента max_locals атрибута Code, содержащего код метода (§4.7.3), и 16-битным индексированием локальных переменных набора команд виртуальной машины Java.

    Обратите внимание, что значения типов long и double каждое рассматривается как резервирующее две локальные переменные и вносящее два единицы в значение max_locals, поэтому использование локальных переменных этих типов ещё больше уменьшает это ограничение.

  • Размер стека операндов в кадре (§2.6) ограничен 65535 значениями полем max_stack атрибута Code (§4.7.3).

    Обратите внимание, что значения типов long и double каждое рассматривается как вносящее две единицы в значение max_stack, поэтому использование значений этих типов в стеке операндов ещё больше уменьшает это ограничение.

  • Количество параметров метода ограничено 255 значениями в определении дескриптора метода (§4.3.3), где ограничение включает одну единицу для this в случае вызовов методов экземпляров или интерфейсов.

    Обратите внимание, что дескриптор метода определяется в терминах длины параметров метода, в котором параметр типа long или double вносит две единицы в длину, поэтому параметры этих типов ещё больше уменьшают ограничение.

  • Длина имен полей и методов, дескрипторов полей и методов и других значений константных строк (включая те, на которые ссылаются атрибуты ConstantValue (§4.7.2)) ограничена 65535 символами 16-битным беззнаковым элементом length структуры CONSTANT_Utf8_info (§4.4.7).

    Обратите внимание, что ограничение относится к количеству байтов в кодировке, а не к количеству закодированных символов. UTF-8 кодирует некоторые символы с использованием двух или трех байтов. Таким образом, строки, содержащие многобайтовые символы, дополнительно ограничены.

  • Количество измерений в массиве ограничено 255 значениями размером опкода dimensions инструкции multianewarray и ограничениями, наложенными на инструкции multianewarray, anewarray и newarray (§4.9.1, §4.9.2).

© Oracle and/or its affiliates. All rights reserved.
Licensed under the Oracle Technology Network License Agreement.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API