Spec-Zone.ru › Java Virtual Machine Specification 8

Глава 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.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.9.1. Подписи
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. Атрибут RuntimeVisibleTypeAnnotations
4.7.20.1. Объединение target_info
4.7.20.2. Структура type_path
4.7.21. Атрибут RuntimeInvisibleTypeAnnotations
4.7.22. Атрибут AnnotationDefault
4.7.23. Атрибут BootstrapMethods
4.7.24. Атрибут MethodParameters
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_<n>
anewarray
areturn
arraylength
astore, astore_<n>
athrow
baload
bastore
bipush
caload
castore
checkcast
d2f, d2i, d2l
dadd
daload
dastore
dcmp<op>
dconst_<d>
ddiv
dload, dload_<n>
dmul
dneg
drem
dreturn
dstore, dstore_<n>
dsub
dup
dup_x1
dup_x2
dup2
dup2_x1
dup2_x2
f2d, f2i, f2l
fadd
faload
fastore
fcmp<op>
fconst_<f>
fdiv
fload, fload_<n>
fmul
fneg
frem
freturn
fstore, fstore_<n>
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_<n>
imul
ineg
instanceof
invokedynamic
invokeinterface
invokespecial
invokestatic
invokevirtual
ior
irem
ireturn
ishl, ishr, iushr
istore, istore_<n>
isub
ixor
l2d, l2f, l2i
ladd
laload
land
lastore
lcmp
END_OF_DOCUMENT_MARKER
lconst_<l>
ldc, ldc_w, ldc2_w
ldiv
lload, lload_<n>
lmul
lneg
lookupswitch
lor
lrem
lreturn
lshl, lshr, lushr
lstore, lstore_<n>
lsub
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

В этом разделе описан формат файла class виртуальной машины Java. Каждый файл 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-символ в этом разделе должна интерпретироваться как соответствующий этому символу Юникод-код.

END_OF_DOCUMENT_MARKER

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

Файл class состоит из одной структуры 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 содержит магическое число, идентифицирующее формат файла class; его значение равно 0xCAFEBABE.

minor_version, major_version

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

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

Реализация Java Virtual Machine Oracle в JDK версии 1.0.2 поддерживает версии формата файла class от 45.0 до 45.3 включительно. Релизы JDK 1.1.* поддерживают версии формата файла class в диапазоне от 45.0 до 45.65535 включительно. Для k ≥ 2, релиз JDK 1.k поддерживает версии формата файла class в диапазоне от 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-A.

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

Имя флага Значение Интерпретация
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_INTERFACE. Если флаг ACC_INTERFACE не установлен, этот файл class определяет класс, а не интерфейс.

Если флаг ACC_INTERFACE установлен, также должен быть установлен флаг ACC_ABSTRACT, а флаги ACC_FINAL, ACC_SUPER и ACC_ENUM не должны быть установлены.

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

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

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

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

Тип аннотации должен иметь установленный флаг ACC_ANNOTATION. Если установлен флаг ACC_ANNOTATION, также должен быть установлен флаг ACC_INTERFACE.

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

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

this_class

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

super_class

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

Если значение элемента super_class равно нулю, этот файл class должен представлять класс 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, представляющей интерфейс, являющийся непосредственным суперинтерфейсом этого класса или интерфейса, в порядке следования слева направо, указанном в источнике для типа.

fields_count

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

fields[]

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

methods_count

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

methods[]
END_OF_DOCUMENT_MARKER

Каждое значение в таблице 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 указывает количество атрибутов в таблице attributes этого класса.

attributes[]

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

Атрибуты, определённые в этом спецификации как присутствующие в таблице attributes структуры ClassFile, перечислены в таблице 4.7-C.

Правила, касающиеся атрибутов, определённых для присутствия в таблице attributes структуры ClassFile, приведены в §4.7.

Правила, касающиеся не предопределённых атрибутов в таблице attributes структуры ClassFile, приведены в §4.7.1.

4.2. Внутренний вид имён

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

Имена классов и интерфейсов, которые появляются в структурах файлов class, всегда представлены в полном квалифицированном виде, известном как бинарные имена (JLS §13.1). Такие имена всегда представлены в виде структур CONSTANT_Utf8_info (§4.4.7), и, следовательно, могут быть взяты, если не ограничены, из всего кодировочного пространства Юникод. Имена классов и интерфейсов ссылаются на те структуры 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) и поэтому могут быть взяты, если не ограничены, из всего кодового пространства Юникода.

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

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

Синтаксис {x} в правой части правила означает ноль или более вхождений x.

Фраза (one of) в правой части правила означает, что каждый из терминальных символов в следующей строке или строках является альтернативным определением.

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

Дескриптор поля представляет тип класса, экземпляра или локальной переменной.

FieldDescriptor:
FieldType
FieldType:
BaseType
ObjectType
ArrayType
BaseType:
(one of)
B C D F I J S Z
ObjectType:
L ClassName ;
ArrayType:
[ ComponentType
ComponentType:
FieldType

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

ClassName представляет имя двоичного класса или интерфейса, закодированное во внутреннем формате (§4.2.1).

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

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

Таблица 4.3-A. Интерпретация дескрипторов полей

FieldType термин Тип Интерпретация
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[][][] — [[[D.

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

Дескриптор метода содержит ноль или более дескрипторов параметров, представляющих типы параметров, которые принимает метод, и дескриптор возвращаемого значения, представляющий тип возвращаемого значения (если оно есть).

MethodDescriptor:
( {ParameterDescriptor} ) ReturnDescriptor
ParameterDescriptor:
FieldType
ReturnDescriptor:
FieldType
VoidDescriptor
VoidDescriptor:
V

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

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

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

равен:

(IDLjava/lang/Thread;)Ljava/lang/Object;

Обратите внимание, что используются внутренние формы двоичных имён Thread и Object.

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

Дескриптор метода одинаков для метода класса или метода экземпляра. Хотя метод экземпляра получает this, ссылку на объект, к которому вызывается метод, помимо своих аргументов, этот факт не отражён в дескрипторе метода. Ссылка на this передаётся неявно инструкциями Java Virtual Machine, которые вызывают методы экземпляра (§2.6.1, §4.11).

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

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

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

cp_info {
    u1 tag;
    u1 info[];
}

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

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

Тип константы Значение
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 — но не оператор new — могут ссылаться на "классы" массивов через структуры CONSTANT_Class_info в таблице constant_pool. Для таких классов массивов имя класса является описателем типа массива (§4.3.2).

Например, имя класса, представляющего двумерный массив типа int[][], равно [[I, а имя класса, представляющего тип 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

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

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

bytes

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

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

Значение, представленное структурой CONSTANT_Float_info, определяется следующим образом. Байты значения сначала преобразуются в целое число int константы 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

Элемент 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

((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 преобразуются в константу long bits, которая равна

((long) high_bytes << 32) + low_bytes
      

Затем:

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

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

  • Если bits находится в диапазоне от 0x7ff0000000000001L до 0x7fffffffffffffffL или от 0xfff0000000000001L до 0xffffffffffffffffL, то значение двойной точности будет 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;
    	  

Затем значение с плавающей запятой равно результату математического выражения double 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

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

name_index

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

descriptor_index

Значение элемента descriptor_index должно быть допустимым индексом в таблице 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 (а не длину результирующей строки).

bytes[]

Массив bytes содержит байты строки.

Ни один байт не может иметь значение (byte)0.

Ни один байт не может лежать в диапазоне от (byte)0xf0 до (byte)0xff.

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

  • Кодовые точки в диапазоне от '\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 никогда не имели вложенных нулей. Во-вторых, используются только форматы стандартного UTF-8 с 1, 2 и 3 байтами. Виртуальная машина Java не распознает четырехбайтовый формат стандартного UTF-8; она использует собственный формат с двумя и тремя байтами.

Для получения дополнительной информации о стандартном формате UTF-8 см. раздел 3.9 Формы кодирования Unicode в Стандарте Unicode, версия 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. Запись 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) или 8 (REF_newInvokeSpecial), то запись constant_pool в таблице по этому индексу должна быть структурой CONSTANT_Methodref_info (§4.4.2), представляющей метод или конструктор класса (§2.9), для которого должен быть создан дескриптор метода.

  • Если значение элемента reference_kind равно 6 (REF_invokeStatic) или 7 (REF_invokeSpecial), то, если номер версии файла class меньше 52.0, запись constant_pool в таблице по этому индексу должна быть структурой CONSTANT_Methodref_info, представляющей метод класса; если номер версии файла class равен 52.0 или выше, запись constant_pool в таблице по этому индексу должна быть либо структурой CONSTANT_Methodref_info, либо структурой CONSTANT_InterfaceMethodref_info (§4.4.2), представляющей метод класса или интерфейса, для которого должен быть создан дескриптор метода.

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

Если значение элемента reference_kind равно 5 (REF_invokeVirtual), 6 (REF_invokeStatic), 7 (REF_invokeSpecial) или 9 (REF_invokeInterface), имя метода, представленного структурой CONSTANT_Methodref_info или CONSTANT_InterfaceMethodref_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 таблицы метода-загрузчика (§4.7.23) этого файла 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.5-A.

Таблица 4.5-A. Флаги доступа и свойств поля

Имя флага Значение Интерпретация
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.

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

Поля интерфейсов должны иметь установленные флаги ACC_PUBLIC, ACC_STATIC и ACC_FINAL; они могут иметь установленный флаг ACC_SYNTHETIC и не должны иметь каких-либо других флагов в таблице 4.5-A (JLS §9.3).

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

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

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

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 указывает количество дополнительных атрибутов данного поля.

attributes[]

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

У поля может быть любое количество дополнительных атрибутов.

Атрибуты, определённые в данном стандарте для таблицы attributes структуры field_info, перечислены в таблице 4.7-C.

Правила относительно атрибутов, определённых для таблицы attributes структуры field_info, приведены в §4.7.

Правила относительно неопределённых атрибутов в таблице attributes структуры field_info приведены в §4.7.1.

END_OF_DOCUMENT_MARKER

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.6-A.

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

Название флага Значение Интерпретация
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-strict.
ACC_SYNTHETIC 0x1000 Объявлен синтетическим; отсутствует в исходном коде.

Методы классов могут иметь любые флаги в Таблице 4.6-A. Однако каждый метод класса может иметь не более одного из его флагов ACC_PUBLIC, ACC_PRIVATE и ACC_PROTECTED (JLS §8.4.3).

Методы интерфейсов могут иметь любые флаги в Таблице 4.6-A, кроме ACC_PROTECTED, ACC_FINAL, ACC_SYNCHRONIZED и ACC_NATIVE (JLS §9.4). В файле class с номером версии меньше 52.0 каждый метод интерфейса должен иметь установленные флаги ACC_PUBLIC и ACC_ABSTRACT; в файле class с номером версии 52.0 или выше каждый метод интерфейса должен иметь ровно один из флагов ACC_PUBLIC и ACC_PRIVATE.

Если у метода класса или интерфейса установлен флаг ACC_ABSTRACT, он не должен иметь установленных флагов ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNCHRONIZED, ACC_NATIVE или ACC_STRICT.

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

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

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

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

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

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

name_index

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

descriptor_index

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

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

attributes_count

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

attributes[]

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

Метод может иметь любое количество дополнительных атрибутов.

Атрибуты, определённые в этой спецификации как появляющиеся в таблице attributes структуры method_info, перечислены в Таблице 4.7-C.

Правила, касающиеся атрибутов, определённых для появления в таблице attributes структуры method_info, приведены в §4.7.

Правила, касающиеся неопределённых атрибутов в таблице attributes структуры method_info, приведены в §4.7.1.

4.7. Атрибуты

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

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

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.

Данным спецификацией определены 23 атрибута. Они представлены трижды для удобства навигации:

  • Таблица 4.7-A упорядочена по номерам разделов атрибутов в данной главе. Каждый атрибут сопровождается первой версией формата файла class, в котором он был определён, и соответствующей версией платформы Java SE. (Для старых версий файлов class используется версия выпуска JDK вместо версии платформы Java SE).

  • Таблица 4.7-B упорядочена по первой версии формата файла class, в котором каждый атрибут был определён.

  • Таблица 4.7-C упорядочена по расположению в файле class, где каждый атрибут должен быть определён.

В контексте их использования в этой спецификации, то есть в таблицах attributes структур файлов class, в которых они появляются, имена этих предварительно определённых атрибутов зарезервированы.

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

Предварительно определённые атрибуты сгруппированы в три группы в соответствии с их назначением:

  1. Пять атрибутов критически важны для корректной интерпретации файла class виртуальной машиной Java:

    • ConstantValue

    • Code

    • StackMapTable

    • Exceptions

    • BootstrapMethods

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

  2. Двенадцать атрибутов критически важны для корректной интерпретации файла class библиотеками классов платформы Java SE:

    • InnerClasses

    • EnclosingMethod

    • Synthetic

    • Signature

    • RuntimeVisibleAnnotations

    • RuntimeInvisibleAnnotations

    • RuntimeVisibleParameterAnnotations

    • RuntimeInvisibleParameterAnnotations

    • RuntimeVisibleTypeAnnotations

    • RuntimeInvisibleTypeAnnotations

    • AnnotationDefault

    • MethodParameters

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

  3. Шесть атрибутов не являются критическими для корректной интерпретации файла class ни виртуальной машиной Java, ни библиотеками классов платформы Java SE, но полезны для инструментов:

    • SourceFile

    • SourceDebugExtension

    • LineNumberTable

    • LocalVariableTable

    • LocalVariableTypeTable

    • Deprecated

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

Таблица 4.7-A. Предопределённые class атрибуты файлов (по разделам)

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

Таблица 4.7-B. Предопределённые class атрибуты файлов (по версии class файла)

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

Таблица 4.7-C. Предопределённые class атрибуты файлов (по расположению)

Атрибут Расположение class файла
SourceFile ClassFile 45.3
InnerClasses ClassFile 45.3
EnclosingMethod ClassFile 49.0
SourceDebugExtension ClassFile 49.0
BootstrapMethods ClassFile 51.0
ConstantValue field_info 45.3
Code method_info 45.3
Exceptions method_info 45.3
RuntimeVisibleParameterAnnotations, RuntimeInvisibleParameterAnnotations method_info 49.0
AnnotationDefault method_info 49.0
MethodParameters method_info 52.0
Synthetic ClassFile, field_info, method_info 45.3
Deprecated ClassFile, field_info, method_info 45.3
Signature ClassFile, field_info, method_info 49.0
RuntimeVisibleAnnotations, RuntimeInvisibleAnnotations ClassFile, field_info, method_info 49.0
LineNumberTable Code 45.3
LocalVariableTable Code 45.3
LocalVariableTypeTable Code 49.0
StackMapTable Code 50.0
RuntimeVisibleTypeAnnotations, RuntimeInvisibleTypeAnnotations ClassFile, field_info, method_info, Code 52.0

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

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

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

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

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

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

4.7.2. Атрибут ConstantValue

Атрибут ConstantValue — атрибут фиксированной длины в таблице attributes структуры field_info (§4.5). Атрибут ConstantValue представляет собой значение константного выражения (JLS §15.28) и используется следующим образом:

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

  • В противном случае виртуальная машина Java должна проигнорировать атрибут.

В таблице attributes структуры field_info может быть, максимум, один атрибут 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 должно быть равно двум.

constantvalue_index

Значение элемента constantvalue_index должно быть допустимым индексом в таблице constant_pool. Элемент constant_pool в этом индексе задаёт значение константы, представленной этим атрибутом. Элемент constant_pool должен иметь тип, соответствующий полю, как указано в таблице 4.7.2-A.

Таблица 4.7.2-A. Типы атрибута константного значения

Тип поля Тип элемента
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).

Если метод является либо native, либо abstract, его структура method_info не должна содержать атрибут Code в своей таблице attributes. В противном случае, его структура method_info должна содержать ровно один атрибут Code в своей таблице attributes.

Атрибут 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 не должен быть пустым) и меньше 65536.

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), представляющей класс исключений, для которого этот обработчик исключений предназначен. Обработчик исключений будет вызван только если брошенное исключение является экземпляром данного класса или одного из его подклассов.

Верификатор проверяет, что класс является Throwable или подклассом Throwable (§4.9.2).

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

Это используется для реализации finally (§3.13).

attributes_count

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

attributes[]

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

Атрибут Code может иметь любое количество дополнительных атрибутов.

Определяемые этой спецификацией атрибуты, которые могут присутствовать в таблице attributes атрибута Code, перечислены в таблице 4.7-C.

Правила, касающиеся атрибутов, определённых для присутствия в таблице attributes атрибута Code, приведены в §4.7.

Правила, касающиеся неопределённых атрибутов в таблице attributes атрибута Code, приведены в §4.7.1.

4.7.4. Атрибут StackMapTable

Атрибут StackMapTable является атрибутом переменной длины в таблице атрибутов атрибута Code (§4.7.3). Атрибут StackMapTable используется во время процесса проверки типами (§4.10.1).

В таблице атрибутов атрибута Code может находиться не более одного атрибута StackMapTable.

В файле класса, версия которого 50.0 или выше, если атрибут Code метода не содержит атрибут StackMapTable, у него есть неявный атрибут StackMapTable (§4.10.1). Этот неявный атрибут эквивалентен атрибуту 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_name_index

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

attribute_length

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

number_of_entries

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

entries[]

Каждая запись в таблице stack_map_frames описывает одну кадровую карту стека метода. Порядок кадровых карт стека в таблице stack_map_frames важен.

Кадровая карта стека (stack map frame) указывает (явным или неявным образом) смещение байткода, к которому она относится, и типы проверки локальных переменных и элементов стека операндов для этого смещения.

Каждая кадровая карта стека, описанная в таблице stack_map_frames, использует некоторые семантики предыдущей кадровой карты стека. Первая кадровая карта стека метода неявна и вычисляется из описателя метода проверяющим типом (§4.10.1.6). Структура stack_map_frame на позиции entries[1] поэтому описывает вторую кадровую карту стека метода.

Смещение байткода, к которому относится кадровая карта стека, вычисляется путем взятия значения delta_offset, указанного в кадровой карте стека (явным или неявным образом), и добавления его к смещению байткода предыдущей кадровой карты стека, если предыдущая кадровая карта не является начальной кадровой картой метода. В этом случае смещение байткода, к которому относится кадровая карта стека, - это значение offset, указанное в кадровой карте стека.

Использование смещения delta_offset, а не хранения фактического смещения байткода, гарантирует, что кадровые карты стека отсортированы в правильном порядке. Кроме того, использование формулы delta_offset для всех явных кадровых карт (в отличие от неявной первой кадровой карты) гарантирует отсутствие дубликатов.

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

Тип проверки (verification type) задает тип одной или двух локаций, где локация — это либо одна локальная переменная, либо один элемент стека операндов. Тип проверки представлен объединением типов, 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;
}

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

  • Элемент TOP указывает, что локальная переменная имеет тип проверки TOP.

    Top_variable_info {
        u1 tag = ITEM_Top; /* 0 */
    }
        
  • Элемент INTEGER указывает, что локация имеет тип проверки INTEGER.

    Integer_variable_info {
        u1 tag = ITEM_Integer; /* 1 */
    }
        
  • Элемент FLOAT указывает, что локация имеет тип проверки FLOAT.

    Float_variable_info {
        u1 tag = ITEM_Float; /* 2 */
    }
        
  • Тип LONG указывает, что локация имеет тип проверки LONG.

    Null_variable_info {
        u1 tag = ITEM_Null; /* 5 */
    }
        
  • Элемент DOUBLE указывает, что локация имеет тип проверки DOUBLE.

    UninitializedThis_variable_info {
        u1 tag = ITEM_UninitializedThis; /* 6 */
    }
        
  • Элемент CLASS указывает, что локация имеет тип проверки, являющийся классом, представленным структурой CONSTANT_Class_info (§4.4.1), найденной в таблице CONSTANT_Class_info по индексу, указанному элементом class_index.

    Object_variable_info {
        u1 tag = ITEM_Object; /* 7 */
        u2 cpool_index;
    }
        
  • Элемент NEW указывает, что локация имеет тип проверки NEW. Элемент new_index указывает смещение в массиве new_array атрибута Code, содержащем данный атрибут StackMapTable, инструкции new, которая создала объект, хранящийся в локации.

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

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

  • Элемент INTEGER указывает, что первая из двух локаций имеет тип проверки INTEGER.

    Long_variable_info {
        u1 tag = ITEM_Long; /* 4 */
    }
        
  • Элемент FLOAT указывает, что первая из двух локаций имеет тип проверки FLOAT.

    Double_variable_info {
        u1 tag = ITEM_Double; /* 3 */
    }
        
  • Элементы LONG и DOUBLE указывают тип проверки второй из двух локаций следующим образом:

    • Если первая из двух локаций — локальная переменная, то:

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

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

    • Если первая из двух локаций — элемент стека операндов, то:

      • Это не верхний элемент стека операндов.

      • Следующий элемент стека операндов имеет тип проверки DOUBLE.

Кадровая карта стека представлена объединением типов, 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;
}

Тег указывает тип кадровой карты стека:

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

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

    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. Этот тип кадра указывает, что в кадре точно такие же локальные переменные, как в предыдущем кадре, а стек операндов содержит один элемент. Значение offset_delta для кадра задается явно, в отличие от типа кадра same_locals_1_stack_item_frame. Тип проверки одного элемента стека следует после offset_delta.

    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]. Этот тип кадра указывает, что в кадре локальные переменные такие же, как в предыдущем кадре, за исключением последних k локальных переменных, которые отсутствуют, а стек операндов пуст. Значение k определяется формулой 251 - frame_type. Значение offset_delta для кадра задается явно.

    Предполагается, что типы проверки локальных переменных в предыдущем кадре задаются locals, структурированным массивом, как в типе кадра full_frame. Если locals[M-1] в предыдущем кадре представляло локальную переменную X, а locals[M] представляло локальную переменную Y, то эффект удаления одной локальной переменной заключается в том, что locals[M-1] в новом кадре представляет локальную переменную X, а locals[M] не определено.

    Ошибка, если k больше числа локальных переменных в locals для предыдущего кадра, то есть если число локальных переменных в новом кадре будет меньше нуля.

  • Тип кадра same_frame_extended представлен тегом 251. Этот тип кадра указывает, что в кадре точно такие же локальные переменные, как в предыдущем кадре, а стек операндов пуст. Значение offset_delta для кадра задается явно, в отличие от типа кадра same_frame.

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

    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, если locals[M] является либо Long_variable_info, либо Double_variable_info.

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

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

    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, если locals[M] является либо Long_variable_info, либо Double_variable_info.

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

    Нулевой элемент в stack представляет тип проверки нижнего элемента стека операндов, а последующие элементы в 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, если stack[M] является либо Long_variable_info, либо Double_variable_info.

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

4.7.5. Атрибут Exceptions

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

В таблице attributes структуры 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 (§4.4.1), которая представляет класс или интерфейс, не являющийся членом пакета, то в таблице attributes структуры ClassFile для C должен присутствовать ровно один атрибут InnerClasses.

Атрибут 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) должен ссылаться на каждого такого члена (JLS §13.1), даже если этот член не упоминается в классе иначе.

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

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

inner_class_info_index

Значение элемента inner_class_info_index должно быть допустимым индексом в таблице constant_pool. Запись constant_pool в этом индексе должна быть структурой CONSTANT_Class_info, представляющей 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, представляющей класс или интерфейс, членом которого является 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.7.6-A.

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

Имя флага Значение Толкование
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.7.6-A, зарезервированы для будущего использования. Они должны быть установлены в ноль в сгенерированных файлах class и должны игнорироваться реализациями Java Virtual Machine.

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

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

4.7.7. Атрибут EnclosingMethod

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

В таблице attributes структуры ClassFile может быть не более одного атрибута 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 должен быть равен нулю, если текущий класс был непосредственно заключён в исходном коде инициализатором экземпляра, статическим инициализатором, инициализатором переменной экземпляра или инициализатором переменной класса. (Первые два касаются как локальных классов, так и анонимных классов, а последние два — анонимных классов, объявленных в правой части присваивания поля.)

В противном случае значение элемента 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 записывает сигнатуру (§4.7.9.1) для класса, интерфейса, конструктора, метода или поля, объявление которого в языке программирования Java использует переменные типов или параметризованные типы. Подробности об этих типах см. в Спецификации языка Java, издание Java SE 8.

Атрибут 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 должно быть равно двум.

signature_index

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

Реализация виртуальной машины Java от Oracle не проверяет корректность атрибутов Signature во время загрузки или связывания классов. Вместо этого атрибуты Signature проверяются методами библиотек классов платформы Java SE, которые предоставляют обобщённые сигнатуры классов, интерфейсов, конструкторов, методов и полей. Примеры включают getGenericSuperclass в Class и toGenericString в java.lang.reflect.Executable.

4.7.9.1. Подписи

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

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

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

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

    Если throws-описание объявления метода или конструктора не включает переменные типа, то компилятор может рассматривать объявление как не имеющее throws-описания для целей генерации подписи метода.

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

Подписи задаются с использованием грамматики, которая следует обозначениям §4.3.1. В дополнение к этим обозначениям:

  • Синтаксис [x] в правой части продукции обозначает ноль или одно вхождение x. То есть, x является необязательным символом. Альтернатива, содержащая необязательный символ, фактически определяет две альтернативы: одну, которая опускает необязательный символ, и одну, которая его включает.

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

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

Подписи основаны на иерархии нетерминалов, известных как подписи типов:

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

    JavaTypeSignature:
    ReferenceTypeSignature
    BaseType

    Следующая продукция из §4.3.2 приведена здесь для удобства:

    BaseType:
    (одно из)
    B C D F I J S Z
  • Подпись ссылочного типа представляет ссылочный тип языка программирования Java, то есть тип класса или интерфейса, переменную типа или тип массива.

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

    Подпись переменной типа представляет переменную типа.

    Подпись типа массива представляет одну размерность типа массива.

    ReferenceTypeSignature:
    ClassTypeSignature
    TypeVariableSignature
    ArrayTypeSignature
    ClassTypeSignature:
    L [PackageSpecifier] SimpleClassTypeSignature {ClassTypeSignatureSuffix} ;
    PackageSpecifier:
    Identifier / {PackageSpecifier}
    SimpleClassTypeSignature:
    Identifier [TypeArguments]
    TypeArguments:
    < TypeArgument {TypeArgument} >
    TypeArgument:
    [WildcardIndicator] ReferenceTypeSignature
    *
    WildcardIndicator:
    +
    -
    ClassTypeSignatureSuffix:
    . SimpleClassTypeSignature
    TypeVariableSignature:
    T Identifier ;
    ArrayTypeSignature:
    [ JavaTypeSignature

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

ClassSignature:
[TypeParameters] SuperclassSignature {SuperinterfaceSignature}
TypeParameters:
< TypeParameter {TypeParameter} >
TypeParameter:
Identifier ClassBound {InterfaceBound}
ClassBound:
: [ReferenceTypeSignature]
InterfaceBound:
: ReferenceTypeSignature
SuperclassSignature:
ClassTypeSignature
SuperinterfaceSignature:
ClassTypeSignature

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

MethodSignature:
[ПараметрыТипов] ( {ПодписьТипаJava} ) Результат {ПодписьИсключений}
Результат:
ПодписьТипаJava
ОписаниеПустого
ПодписьИсключений:
^ ПодписьТипаКласса
^ ПодписьПеременнойТипа

Следующая конструкция из §4.3.3 приведена здесь для удобства:

ОписаниеПустого:
V

Из-за артефактов, сгенерированных компилятором, подпись метода может не совпадать точно с описанием метода (§4.3.3). В частности, количество типов формальных параметров в подписи метода может быть меньше, чем количество описаний параметров в описании метода.

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

FieldSignature:
ПодписьСсылочногоТипа

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 должно быть два.

sourcefile_index

Значение элемента sourcefile_index должно быть допустимым индексом в таблице constant_pool. Элемент 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 указывает длину атрибута, исключая начальные шесть байт.

debug_extension[]

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

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

4.7.12. Атрибут LineNumberTable

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

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

Может быть более одного атрибута LineNumberTable на одну строку исходного файла в таблице attributes атрибута Code. То есть атрибуты 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. Таблица локальных переменных

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

Если несколько атрибутов таблиц локальных переменных присутствуют в таблице атрибутов кода, то они могут располагаться в любом порядке.

Может быть не более одного атрибута таблицы локальных переменных на каждую локальную переменную в таблице атрибутов кода.

Атрибут таблицы локальных переменных имеет следующий формат:

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];
}

Элементы структуры атрибута таблицы локальных переменных:

attribute_name_index

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

attribute_length

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

local_variable_table_length

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

local_variable_table[]

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

start_pc, length

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

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

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

name_index

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

descriptor_index

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

index

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

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

4.7.14. Таблица типов локальных переменных

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

Если несколько атрибутов таблицы типов локальных переменных присутствуют в таблице атрибутов кода, то они могут располагаться в любом порядке.

Может быть не более одного атрибута таблицы типов локальных переменных на каждую локальную переменную в таблице атрибутов кода.

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

Атрибут таблицы типов локальных переменных имеет следующий формат:

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];
}

Элементы структуры атрибута таблицы типов локальных переменных:

attribute_name_index

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

attribute_length

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

local_variable_type_table_length

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

local_variable_type_table[]

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

start_pc, length

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

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

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

name_index

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

signature_index

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

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 следующие:

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 должна обеспечить доступ к этим аннотациям, чтобы они могли быть возвращены соответствующими рефлексивными API.

В таблице attributes структуры ClassFile, field_info или method_info может быть не более одного атрибута RuntimeVisibleAnnotations.

Атрибут 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 указывает длину атрибута, за исключением начальных шести байтов.

num_annotations

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

annotations[]

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

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), представляющей описатель поля (§4.3.2). Описатель поля обозначает тип аннотации, представленной этой структурой annotation.

num_element_value_pairs

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

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). Запись constant_pool обозначает имя элемента пары имя-значение, представленной этой записью element_value_pairs.

Другими словами, запись обозначает элемент типа аннотации, указанного в type_index.

value

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

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

Структура 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;
}

Элемент tag использует один символ ASCII для указания типа значения пары "элемент-значение". Это определяет, какой элемент объединения value используется. Таблица 4.7.16.1-A показывает допустимые символы для элемента tag, тип, указываемый каждым символом, и элемент, используемый в объединении value для каждого символа. Четвёртый столбец таблицы используется в описании одного элемента объединения value.

Таблица 4.7.16.1-A. Интерпретация значений тегов как типов

tag Элемент Тип value Элемент Тип константы
B byte const_value_index CONSTANT_Integer
C char const_value_index CONSTANT_Integer
D double const_value_index CONSTANT_Double
F float const_value_index CONSTANT_Float
I int const_value_index CONSTANT_Integer
J long const_value_index CONSTANT_Long
S short const_value_index CONSTANT_Integer
Z boolean const_value_index CONSTANT_Integer
s Тип перечисления enum_const_value Не применимо
c Class class_info_index Не применимо
@ Тип аннотации annotation_value Не применимо
[ Тип массива array_value Не применимо

Элемент value представляет значение пары "элемент-значение". Элемент является объединением, чьи собственные элементы следующие:

const_value_index

Элемент const_value_index обозначает либо примитивное постоянное значение, либо String литерал в качестве значения этой пары "элемент-значение".

Значение элемента const_value_index должно быть корректным индексом в таблице constant_pool. Запись constant_pool в этом индексе должна соответствовать типу элемента tag, как указано в четвёртом столбце таблицы 4.7.16.1-A.

enum_const_value

Элемент enum_const_value обозначает константу перечисления как значение этой пары "элемент-значение".

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

type_name_index

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

const_name_index

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

class_info_index

Элемент class_info_index обозначает литерал класса как значение этой пары "элемент-значение".

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

  • Для литерала класса C.class, где C — имя класса, интерфейса или массива, соответствующим типом является C. Дескриптор возвращаемого значения в constant_pool будет ObjectType или ArrayType.

  • Для литерала класса p.class, где p — имя примитивного типа, соответствующим типом является p. Дескриптор возвращаемого значения в constant_pool будет символом BaseType.

  • Для литерала класса void.class, соответствующим типом является void. Дескриптор возвращаемого значения в constant_pool будет V.

Например, литерал класса Object.class соответствует типу Object, поэтому запись constant_pool равна Ljava/lang/Object;, тогда как литерал класса int.class соответствует типу int, поэтому запись constant_pool равна I.

Литерал класса void.class соответствует типу void, поэтому запись constant_pool равна V, тогда как литерал класса Void.class соответствует типу Void, поэтому запись constant_pool равна Ljava/lang/Void;.

annotation_value

Элемент annotation_value обозначает "вложенную" аннотацию как значение этой пары "элемент-значение".

Значение элемента annotation_value — это структура annotation (§4.7.16), которая задаёт аннотацию, представленную этой структурой element_value.

array_value

Элемент array_value обозначает массив как значение этой пары "элемент-значение".

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

num_values

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

values[]

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

4.7.17. Атрибут RuntimeInvisibleAnnotations

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

В таблице attributes структуры ClassFile, field_info или method_info может быть, но не обязательно, присутствовать не более одного атрибута RuntimeInvisibleAnnotations.

Атрибут RuntimeInvisibleAnnotations аналогичен атрибуту RuntimeVisibleAnnotations (§4.7.16), за исключением того, что аннотации, представленные атрибутом RuntimeInvisibleAnnotations, не должны быть доступны для возврата отражающими API, если виртуальная машина Java не получила инструкцию сохранить эти аннотации с помощью какого-либо механизма, специфичного для реализации, такого как флаг командной строки. В противном случае виртуальная машина 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 указывает длину атрибута, за исключением начальных шести байтов.

num_annotations

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

annotations[]

Каждая запись в таблице annotations представляет собой отдельную аннотацию, невидимую во время выполнения, в объявлении. Структура annotation описана в §4.7.16.

4.7.18. Атрибут RuntimeVisibleParameterAnnotations

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

В таблице attributes структуры method_info может быть не более одного атрибута RuntimeVisibleParameterAnnotations.

Атрибут 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 указывает длину атрибута, за исключением начальных шести байтов.

num_parameters

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

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

parameter_annotations[]

Каждая запись в таблице parameter_annotations представляет все аннотации, видимые во время выполнения, в объявлении отдельного формального параметра. i-ая запись в таблице соответствует i-му формальному параметру в описании метода (§4.3.3). Каждая запись parameter_annotations содержит следующие два элемента:

num_annotations

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

annotations[]

Каждая запись в таблице annotations представляет собой отдельную аннотацию, видимую во время выполнения, в объявлении формального параметра, соответствующего записи parameter_annotations. Структура annotation описана в §4.7.16.

4.7.19. Атрибут RuntimeInvisibleParameterAnnotations

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

В таблице attributes структуры method_info может быть не более одного атрибута RuntimeInvisibleParameterAnnotations.

Атрибут RuntimeInvisibleParameterAnnotations аналогичен атрибуту RuntimeVisibleParameterAnnotations (§4.7.18), за исключением того, что аннотации, представленные атрибутом RuntimeInvisibleParameterAnnotations, не должны быть доступны для возврата отражающими API, если виртуальная машина Java специально не получила инструкцию сохранить эти аннотации с помощью какого-либо механизма, специфичного для реализации, такого как флаг командной строки. В противном случае виртуальная машина 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 указывает длину атрибута, за исключением начальных шести байтов.

num_parameters

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

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

parameter_annotations[]

Каждая запись в таблице parameter_annotations представляет все невидимые во время выполнения аннотации в объявлении отдельного формального параметра. i-ая запись в таблице соответствует i-му формальному параметру в описании метода (§4.3.3). Каждая запись parameter_annotations содержит следующие два элемента:

num_annotations

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

annotations[]

Каждая запись в таблице annotations представляет собой отдельную аннотацию, невидимую во время выполнения, в объявлении формального параметра, соответствующего записи parameter_annotations. Структура annotation описана в §4.7.16.

4.7.20. Атрибут RuntimeVisibleTypeAnnotations

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

В таблице attributes структуры ClassFile, field_info или method_info, или атрибуте Code может быть не более одного атрибута RuntimeVisibleTypeAnnotations.

Таблица attributes содержит атрибут RuntimeVisibleTypeAnnotations только в том случае, если типы аннотированы в типах объявлений или выражений, соответствующих родительской структуре или атрибуту таблицы attributes.

Например, все аннотации типов в пункте implements объявления класса записываются в атрибут RuntimeVisibleTypeAnnotations структуры ClassFile класса. В то же время, все аннотации типа в объявлении поля записываются в атрибут RuntimeVisibleTypeAnnotations структуры field_info поля.

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

RuntimeVisibleTypeAnnotations_attribute {
    u2              attribute_name_index;
    u4              attribute_length;
    u2              num_annotations;
    type_annotation annotations[num_annotations];
}

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

attribute_name_index

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

attribute_length

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

num_annotations

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

annotations[]

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

type_annotation {
    u1 target_type;
    union {
        type_parameter_target;
        supertype_target;
        type_parameter_bound_target;
        empty_target;
        method_formal_parameter_target;
        throws_target;
        localvar_target;
        catch_target;
        offset_target;
        type_argument_target;
    } target_info;
    type_path target_path;
    u2        type_index;
    u2        num_element_value_pairs;
    {   u2            element_name_index;
        element_value value;
    } element_value_pairs[num_element_value_pairs];
}
      

Первые три элемента - target_type, target_info и target_path - указывают точное местоположение аннотированного типа. Последние три элемента - type_index, num_element_value_pairs и element_value_pairs[] - указывают тип самой аннотации и пары значений элементов.

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

target_type

Значение элемента target_type обозначает вид целевого объекта, на котором появляется аннотация. Различные виды целевых объектов соответствуют контекстам типов языка программирования Java, где типы используются в объявлениях и выражениях (JLS §4.11).

Допустимые значения target_type указаны в таблице 4.7.20-A и таблице 4.7.20-B. Каждое значение — однобайтовый тег, указывающий, какой элемент объединения target_info следует за элементом target_type, чтобы предоставить дополнительную информацию о целевом объекте.

Виды целевых объектов в таблице 4.7.20-A и таблице 4.7.20-B соответствуют контекстам типов в JLS §4.11. А именно, значения target_type 0x10-0x17 и 0x40-0x42 соответствуют контекстам типов 1-10, а значения target_type 0x43-0x4B соответствуют контекстам типов 11-16.

Значение элемента target_type определяет, появляется ли структура type_annotation в атрибуте RuntimeVisibleTypeAnnotations в структуре ClassFile, структуре field_info, структуре method_info или атрибуте Code. Таблица 4.7.20-C указывает расположение атрибута RuntimeVisibleTypeAnnotations для структуры type_annotation с каждым допустимым значением target_type.

target_info

Значение элемента target_info точно указывает, какой тип в объявлении или выражении аннотирован.

Элементы объединения target_info указаны в §4.7.20.1.

target_path

Значение элемента target_path точно указывает, какая часть типа, указанного target_info, аннотирована.

Формат структуры type_path указан в §4.7.20.2.

type_index, num_element_value_pairs, element_value_pairs[]

Значение этих элементов в структуре type_annotation такое же, как и их значение в структуре annotation (§4.7.16).

Таблица 4.7.20-A. Интерпретация значений target_type (Часть 1)

Значение Вид целевого объекта target_info элемент
0x00 объявление параметра типа обобщенного класса или интерфейса type_parameter_target
0x01 объявление параметра типа обобщенного метода или конструктора type_parameter_target
0x10 тип в пункте extends или implements объявления класса (включая непосредственного суперкласса или непосредственного суперинтерфейса объявления анонимного класса) или в пункте extends объявления интерфейса supertype_target
0x11 тип в ограничении объявления параметра типа обобщенного класса или интерфейса type_parameter_bound_target
0x12 тип в ограничении объявления параметра типа обобщенного метода или конструктора type_parameter_bound_target
0x13 тип в объявлении поля empty_target
0x14 тип возвращаемого значения метода или тип вновь созданного объекта empty_target
0x15 тип получателя метода или конструктора empty_target
0x16 тип в объявлении формального параметра метода, конструктора или лямбда-выражения formal_parameter_target
0x17 тип в пункте throws метода или конструктора throws_target

Таблица 4.7.20-B. Интерпретация значений target_type (Часть 2)

Значение Вид целевого объекта target_info элемент
0x40 тип в объявлении локальной переменной localvar_target
0x41 тип в объявлении переменной ресурса localvar_target
0x42 тип в объявлении параметра исключения catch_target
0x43 тип в выражении instanceof offset_target
0x44 тип в выражении new offset_target
0x45 тип в выражении ссылки на метод, использующем ::new offset_target
0x46 тип в выражении ссылки на метод, использующем ::Identifier offset_target
0x47 тип в выражении приведения типа type_argument_target
0x48 аргумент типа для обобщенного конструктора в выражении new или явном операторе вызова конструктора type_argument_target
0x49 аргумент типа для обобщенного метода в выражении вызова метода type_argument_target
0x4A аргумент типа для обобщенного конструктора в выражении ссылки на метод, использующем ::new type_argument_target
0x4B аргумент типа для обобщенного метода в выражении ссылки на метод, использующем ::Identifier type_argument_target

Таблица 4.7.20-C. Позиция атрибута, окружающего значения target_type

Значение Тип целевого объекта Позиция
0x00 объявление параметра типа обобщенного класса или интерфейса ClassFile
0x01 объявление параметра типа обобщенного метода или конструктора method_info
0x10 тип в части extends объявления класса или интерфейса или в части implements объявления интерфейса ClassFile
0x11 тип в ограничении объявления параметра типа обобщенного класса или интерфейса ClassFile
0x12 тип в ограничении объявления параметра типа обобщенного метода или конструктора method_info
0x13 тип в объявлении поля field_info
0x14 тип возвращаемого значения метода или конструктора method_info
0x15 тип получателя метода или конструктора method_info
0x16 тип в объявлении формального параметра метода, конструктора или лямбда-выражения method_info
0x17 тип в части throws объявления метода или конструктора method_info
0x40-0x4B типы в объявлениях локальных переменных, переменных ресурсов, параметрах исключений, выражениях Code

4.7.20.1. Объединение target_info

Элементы объединения target_info (кроме первого) точно указывают, какой тип в объявлении или выражении аннотирован. Первый элемент указывает не какой тип, а какое объявление параметра типа аннотировано. Элементы следующие:

  • Элемент type_parameter_target указывает, что аннотация появляется на объявлении i-го параметра типа обобщённого класса, обобщённого интерфейса, обобщённого метода или обобщённого конструктора.

    type_parameter_target {
        u1 type_parameter_index;
    }
        

    Значение элемента type_parameter_index указывает, какое объявление параметра типа аннотировано. Значение type_parameter_index, равное 0, указывает первое объявление параметра типа.

  • Элемент supertype_target указывает, что аннотация появляется на типе в разделе extends или implements объявления класса или интерфейса.

    supertype_target {
        u2 supertype_index;
    }
        

    Значение supertype_index, равное 65535, указывает, что аннотация появляется на суперклассе в разделе extends объявления класса.

    Любое другое значение supertype_index — это индекс в массиве interfaces, содержащемся в обозначающей структуре ClassFile, и указывает, что аннотация появляется на соответствующем суперинтерфейсе в разделе implements объявления класса или разделе extends объявления интерфейса.

  • Элемент type_parameter_bound_target указывает, что аннотация появляется на i-м ограничении j-го объявления параметра типа обобщённого класса, интерфейса, метода или конструктора.

    type_parameter_bound_target {
        u1 type_parameter_index;
        u1 bound_index;
    }
        

    Значение элемента type_parameter_index указывает, какое объявление параметра типа имеет аннотированное ограничение. Значение type_parameter_index, равное 0, указывает первое объявление параметра типа.

    Значение элемента bound_index указывает, какое ограничение объявления параметра типа, указанного элементом type_parameter_index, аннотировано. Значение bound_index, равное 0, указывает первое ограничение объявления параметра типа.

    Элемент type_parameter_bound_target записывает, что ограничение аннотировано, но не записывает сам тип, являющийся ограничением. Тип можно найти, проанализировав сигнатуру класса или метода, хранящуюся в соответствующем атрибуте Signature.

  • Элемент empty_target указывает, что аннотация появляется на типе в объявлении поля, типе возвращаемого значения метода, типе вновь созданного объекта или типе получателя метода или конструктора.

    empty_target {
    }
        

    В каждом из этих мест появляется только один тип, поэтому в объединении target_info нет информации о каждом типе.

  • Элемент formal_parameter_target указывает, что аннотация появляется на типе в объявлении формального параметра метода, конструктора или лямбда-выражения.

    formal_parameter_target {
        u1 formal_parameter_index;
    }
        

    Значение элемента formal_parameter_index указывает, объявление какого формального параметра имеет аннотированный тип. Значение formal_parameter_index, равное 0, указывает первое объявление формального параметра.

    Элемент formal_parameter_target записывает, что тип формального параметра аннотирован, но сам тип не записывает. Тип можно найти, проанализировав дескриптор метода (§4.3.3) структуры method_info, содержащей атрибут RuntimeVisibleTypeAnnotations. Значение formal_parameter_index, равное 0, указывает на первый дескриптор параметра в дескрипторе метода.

  • Элемент throws_target указывает, что аннотация появляется на i-м типе в разделе throws объявления метода или конструктора.

    throws_target {
        u2 throws_type_index;
    }
        

    Значение элемента throws_type_index — это индекс в массиве exception_index_table атрибута Exceptions структуры method_info, содержащей атрибут RuntimeVisibleTypeAnnotations.

  • Элемент localvar_target указывает, что аннотация появляется на типе в объявлении локальной переменной, включая переменную, объявленную как ресурс в операторе try-с-ресурсами.

    localvar_target {
        u2 table_length;
        {   u2 start_pc;
            u2 length;
            u2 index;
        } table[table_length];
    }
        

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

    start_pc, length

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

    index

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

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

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

    Элемент localvar_target записывает, что тип локальной переменной аннотирован, но сам тип не записывает. Тип можно найти, проанализировав соответствующий атрибут LocalVariableTable.

  • Элемент catch_target указывает, что аннотация появляется на i-м типе в объявлении параметра исключения.

    catch_target {
        u2 exception_table_index;
    }
        

    Значение элемента exception_table_index — это индекс в массиве exception_table атрибута Code, содержащего атрибут RuntimeVisibleTypeAnnotations.

    Возможность более одного типа в объявлении параметра исключения возникает из многократного раздела catch оператора try, где тип параметра исключения является объединением типов (JLS §14.20). Компилятор обычно создаёт один элемент exception_table для каждого типа в объединении, что позволяет элементу catch_target их различать. Это сохраняет соответствие между типом и его аннотациями.

  • Элемент offset_target указывает, что аннотация появляется на типе в выражении instanceof или выражении new, или типе перед :: в выражении ссылки на метод.

    offset_target {
        u2 offset;
    }
        

    Значение элемента offset указывает смещение в массиве code либо байткода инструкции instanceof, соответствующего выражению instanceof, либо байткода инструкции new, соответствующего выражению new, либо байткода инструкции, соответствующей выражению ссылки на метод.

  • Элемент type_argument_target указывает, что аннотация появляется либо на i-м типе в выражении приведения типа, либо на i-м аргументе типа в списке явных аргументов типа для следующих элементов: выражение new, явное обращение к конструктору, выражение вызова метода или выражение ссылки на метод.

    type_argument_target {
        u2 offset;
        u1 type_argument_index;
    }
        

    Значение элемента offset указывает смещение в массиве code либо байткода инструкции, соответствующей выражению приведения типа, байткода инструкции new, соответствующего выражению new, байткода инструкции, соответствующей явным вызовом конструктора, байткода инструкции, соответствующей выражению вызова метода, или байткода инструкции, соответствующей выражению ссылки на метод.

    Для выражения приведения типа значение элемента type_argument_index указывает, какой тип в операторе приведения типа аннотирован. Значение type_argument_index, равное 0, указывает первый (или единственный) тип в операторе приведения типа.

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

    Для списка явных аргументов типа значение элемента type_argument_index указывает, какой аргумент типа аннотирован. Значение type_argument_index, равное 0, указывает первый аргумент типа.

4.7.20.2. The type_path structure

Всякий раз, когда тип используется в объявлении или выражении, структура type_path определяет, какая часть типа аннотирована. Аннотация может появляться в самом типе, но если тип является ссылочным типом, то есть дополнительные места, где может появиться аннотация:

  • Если тип массива T[] используется в объявлении или выражении, то аннотация может появляться в любом типе компонента типа массива, включая тип элемента.

  • Если в объявлении или выражении используется вложенный тип T1.T2, то аннотация может появиться в имени типа верхнего уровня или любого типа-члена.

  • Если в объявлении или выражении используется параметризованный тип T<A> или T<? extends A> или T<? super A>, то аннотация может появиться в любом аргументе типа или в границе любого аргумента типа с подстановочным знаком.

Например, рассмотрим различные части String[][], которые аннотированы в:

@Foo String[][]   // Annotates the class type String
String @Foo [][]  // Annotates the array type String[][]
String[] @Foo []  // Annotates the array type String[]

или различные части вложенного типа Outer.Middle.Inner, которые аннотированы в:

@Foo Outer.Middle.Inner
Outer.@Foo Middle.Inner
Outer.Middle.@Foo Inner

или различные части параметризованных типов Map<String,Object> и List<...>, которые аннотированы в:

@Foo Map<String,Object>
Map<@Foo String,Object>
Map<String,@Foo Object>

List<@Foo ? extends String>
List<? extends @Foo String>

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

type_path {
    u1 path_length;
    {   u1 type_path_kind;
        u1 type_argument_index;
    } path[path_length];
}

Значение элемента path_length дает количество записей в массиве path:

  • Если значение path_length равно 0, то аннотация появляется непосредственно в самом типе.

  • Если значение path_length ненулевое, то каждая запись в массиве path представляет собой итеративный, слева направо шаг к точному местоположению аннотации в типе массива, вложенном типе или параметризованном типе. (В типе массива итерация посещает сам тип массива, затем его тип компонента, затем тип компонента этого типа компонента и так далее, пока не будет достигнут тип элемента.) Каждая запись содержит следующие два элемента:

    type_path_kind

    Допустимые значения для элемента type_path_kind перечислены в Таблица 4.7.20.2-A.

    Table 4.7.20.2-A. Interpretation of type_path_kind values

    Значение Интерпретация
    0 Аннотация глубже в типе массива
    1 Аннотация глубже во вложенном типе
    2 Аннотация находится в границе аргумента типа с подстановочным знаком параметризованного типа
    3 Аннотация находится в аргументе типа параметризованного типа

    type_argument_index

    Если значение элемента type_path_kind равно 0, 1 или 2, то значение элемента type_argument_index равно 0.

    Если значение элемента type_path_kind равно 3, то значение элемента type_argument_index указывает, какой аргумент типа параметризованного типа аннотирован, где 0 указывает на первый аргумент типа параметризованного типа.

Table 4.7.20.2-B. type_path structures for @A Map<@B ? extends @C String, @D List<@E Object>>

Annotation path_length path
@A 0 []
@B 1 [{type_path_kind: 3; type_argument_index: 0}]
@C 2 [{type_path_kind: 3; type_argument_index: 0}, {type_path_kind: 2; type_argument_index: 0}]
@D 1 [{type_path_kind: 3; type_argument_index: 1}]
@E 2 [{type_path_kind: 3; type_argument_index: 1}, {type_path_kind: 3; type_argument_index: 0}]

Table 4.7.20.2-C. type_path structures for @I String @F [] @G [] @H []

Annotation path_length path
@F 0 []
@G 1 [{type_path_kind: 0; type_argument_index: 0}]
@H 2 [{type_path_kind: 0; type_argument_index: 0}, {type_path_kind: 0; type_argument_index: 0}]
@I 3 [{type_path_kind: 0; type_argument_index: 0}, {type_path_kind: 0; type_argument_index: 0}, {type_path_kind: 0; type_argument_index: 0}]

Table 4.7.20.2-D. type_path structures for @A List<@B Comparable<@F Object @C [] @D [] @E []>>

Annotation path_length path
@A 0 []
@B 1 [{type_path_kind: 3; type_argument_index: 0}]
@C 2 [{type_path_kind: 3; type_argument_index: 0}, {type_path_kind: 3; type_argument_index: 0}]
@D 3 [{type_path_kind: 3; type_argument_index: 0}, {type_path_kind: 3; type_argument_index: 0}, {type_path_kind: 0; type_argument_index: 0}]
@E 4 [{type_path_kind: 3; type_argument_index: 0}, {type_path_kind: 3; type_argument_index: 0}, {type_path_kind: 0; type_argument_index: 0}, {type_path_kind: 0; type_argument_index: 0}]
@F 5 [{type_path_kind: 3; type_argument_index: 0}, {type_path_kind: 3; type_argument_index: 0}, {type_path_kind: 0; type_argument_index: 0}, {type_path_kind: 0; type_argument_index: 0}, {type_path_kind: 0; type_argument_index: 0}]

Table 4.7.20.2-E. type_path structures for @C Outer . @B Middle . @A Inner

Annotation path_length path
@A 2 [{type_path_kind: 1; type_argument_index: 0}, {type_path_kind: 1; type_argument_index: 0}]
@B 1 [{type_path_kind: 1; type_argument_index: 0}]
@C 0 []

Table 4.7.20.2-F. type_path structures for Outer . Middle<@D Foo . @C Bar> . Inner<@B String @A []>

Annotation path_length path
@A 3 [{type_path_kind: 1; type_argument_index: 0}, {type_path_kind: 1; type_argument_index: 0}, {type_path_kind: 3; type_argument_index: 0}]
@B 4 [{type_path_kind: 1; type_argument_index: 0}, {type_path_kind: 1; type_argument_index: 0}, {type_path_kind: 3; type_argument_index: 0}, {type_path_kind: 0; type_argument_index: 0}]
@C 3 [{type_path_kind: 1; type_argument_index: 0}, {type_path_kind: 3; type_argument_index: 0}, {type_path_kind: 1; type_argument_index: 0}]
@D 2 [{type_path_kind: 1; type_argument_index: 0}, {type_path_kind: 3; type_argument_index: 0}]

4.7.21. Атрибут RuntimeInvisibleTypeAnnotations

Атрибут RuntimeInvisibleTypeAnnotations — атрибут переменной длины в таблице attributes структуры ClassFile, field_info или method_info, или атрибута Code (§4.1, §4.5, §4.6, §4.7.3). Атрибут RuntimeInvisibleTypeAnnotations записывает невидимые во время выполнения аннотации на типы, используемые в соответствующем объявлении класса, поля или метода, или в выражении в соответствующем теле метода. Атрибут RuntimeInvisibleTypeAnnotations также записывает аннотации на объявления параметров типа обобщённых классов, интерфейсов, методов и конструкторов.

В таблице attributes структуры ClassFile, field_info или method_info, или атрибута Code может быть не более одного атрибута RuntimeInvisibleTypeAnnotations.

Таблица attributes содержит атрибут RuntimeInvisibleTypeAnnotations только в том случае, если типы аннотированы в видах объявления или выражения, соответствующих родительской структуре или атрибуту таблицы attributes.

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

RuntimeInvisibleTypeAnnotations_attribute {
    u2              attribute_name_index;
    u4              attribute_length;
    u2              num_annotations;
    type_annotation annotations[num_annotations];
}

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

attribute_name_index

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

attribute_length

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

num_annotations

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

annotations[]

Каждый элемент таблицы annotations представляет собой одну отдельную аннотацию типа, невидимую во время выполнения, используемую в объявлении или выражении. Структура type_annotation задана в §4.7.20.

4.7.22. Атрибут AnnotationDefault

Атрибут AnnotationDefault — атрибут переменной длины в таблице attributes некоторых структур method_info (§4.6), а именно тех, которые представляют элементы типов аннотаций (JLS §9.6.1). Атрибут AnnotationDefault записывает значение по умолчанию (JLS §9.6.2) для элемента, представленного структурой method_info. Виртуальная машина Java должна сделать это значение по умолчанию доступным, чтобы его можно было применить соответствующими рефлексивными API.

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

Атрибут 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 указывает длину атрибута, за исключением первых шести байтов.

default_value

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

4.7.23. Атрибут BootstrapMethods

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

В таблице attributes структуры ClassFile должен быть ровно один атрибут BootstrapMethods, если таблица ClassFile структуры constant_pool содержит по крайней мере один элемент 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).

Форма дескриптора метода определяется продолжающимся разрешением спецификатора места вызова в §invokedynamic, где выполнение invoke в java.lang.invoke.MethodHandle требует, чтобы дескриптор метода инициализации подстраивался под фактические передаваемые аргументы, как если бы он был вызван java.lang.invoke.MethodHandle.asType. Соответственно, элемент reference_kind структуры CONSTANT_MethodHandle_info должен иметь значение 6 или 8 (§5.4.3.5), а элемент reference_index должен указывать на статический метод или конструктор, принимающий три аргумента типа java.lang.invoke.MethodHandles.Lookup, String и java.lang.invoke.MethodType в указанном порядке. В противном случае вызов метода инициализации во время разрешения спецификатора места вызова завершится неожиданно.

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.7.24. Атрибут MethodParameters

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

В таблице attributes структуры method_info может быть не более одного атрибута MethodParameters.

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

MethodParameters_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u1 parameters_count;
    {   u2 name_index;
        u2 access_flags;
    } parameters[parameters_count];
}

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

attribute_name_index

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

attribute_length

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

parameters_count

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

Это не является ограничением, которое должна проверять реализация виртуальной машины Java во время проверки формата (§4.8). Задача сопоставления описателей параметров в описателе метода с элементами в массиве parameters ниже выполняется библиотеками рефлексии платформы Java SE.

parameters[]

Каждый элемент массива parameters содержит следующую пару элементов:

name_index

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

Если значение элемента name_index равно нулю, то этот элемент parameters указывает формальный параметр без имени.

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

access_flags

Значение элемента access_flags следующее:

0x0010 (ACC_FINAL)

Указывает, что формальный параметр был объявлен final.

0x1000 (ACC_SYNTHETIC)

Указывает, что формальный параметр не был явно или неявно объявлен в исходном коде в соответствии со спецификацией языка, на котором был написан исходный код (JLS §13.1). (Формальный параметр — это артефакт реализации компилятора, который создал этот файл class.)

0x8000 (ACC_MANDATED)

Указывает, что формальный параметр был неявно объявлен в исходном коде в соответствии со спецификацией языка, на котором был написан исходный код (JLS §13.1). (Формальный параметр предписывается спецификацией языка, поэтому все компиляторы языка должны его генерировать.)

Элемент с индексом i в массиве parameters соответствует i-му описателю параметра в описателе метода. (Элемент parameters_count имеет один байт, так как описание метода ограничено 255 параметрами.) Эффективно, это означает, что массив parameters хранит информацию обо всех параметрах метода. Можно представить и другие схемы, где элементы в массиве parameters указывают на соответствующие описатели параметров, но это сделало бы атрибут MethodParameters излишне сложным.

Элемент с индексом i в массиве parameters может или не может соответствовать i-му типу в атрибуте Signature окружающего метода (если он присутствует), или i-й аннотации в аннотациях параметров окружающего метода.

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

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

  • Первые четыре байта должны содержать правильное магическое число.

  • Все распознанные атрибуты должны иметь правильную длину.

  • Файл class не должен быть усеченным или иметь лишние байты в конце.

  • Константный пул должен удовлетворять ограничениям, описанным в §4.4.

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

  • Все ссылки на поля и методы в константном пуле должны иметь допустимые имена, допустимые классы и допустимые описатели (§4.3).

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

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

4.9. Ограничения кода виртуальной машины Java

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

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

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

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

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

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

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

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

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

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

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

  • Цель каждой инструкции перехода и ветвления (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 должна иметь количество пар match-offset, соответствующее значению ее операнда npairs. Пары match-offset должны быть отсортированы в порядке возрастания по числовому значению со знаком.

    Никакая цель инструкции 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 должны представлять допустимый индекс в таблице constant_pool. Запись пула констант, на которую ссылается этот индекс, должна иметь тип CONSTANT_Methodref.

  • Операнды indexbyte каждой инструкции invokespecial и invokestatic должны представлять допустимый индекс в таблице constant_pool. Если номер версии файла class меньше 52.0, запись пула констант, на которую ссылается этот индекс, должна иметь тип CONSTANT_Methodref; если номер версии файла class равен 52.0 или выше, запись пула констант, на которую ссылается этот индекс, должна иметь тип CONSTANT_Methodref или CONSTANT_InterfaceMethodref.

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

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

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

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

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

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

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

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

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

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

  • Инструкция 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, iinc или ret, должны представлять неотрицательное целое число, не превышающее 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, short и char в тип int.)

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

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

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

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

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

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

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

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

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

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

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

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

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

  • Если в локальной переменной есть неинициализированный экземпляр класса в коде, защищённом обработчиком исключений, то i) если обработчик находится внутри метода <init>, обработчик должен либо сгенерировать исключение, либо зациклиться, и ii) если обработчик не находится внутри метода <init>, неинициализированный экземпляр класса должен остаться неинициализированным.

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

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

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

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

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

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

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

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

  • Тип каждого экземпляра класса, к которому обращается инструкция 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 массива exception_table структуры Code_attribute метода, должен быть Throwable или подклассом Throwable.

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

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

  • Выполнение никогда не выходит за пределы массива 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 8.

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

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

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

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

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

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

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

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

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

В обеих стратегиях верификация в основном связана с соблюдением статических и структурных ограничений из §4.9 для массива code атрибута Code (§4.7.3). Однако есть три дополнительных проверки вне атрибута Code, которые должны быть выполнены во время верификации:

  • Обеспечение того, что классы final не являются подклассами.

  • Обеспечение того, что методы final не переопределяются (§5.4.5).

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

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

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

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

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

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

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

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

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

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

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

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

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 load_class предполагает, что ClassTerm является термином Prolog, представляющим бинарный класс, который был успешно проанализирован и загружен. Данное описание не предписывает точную структуру этого термина, но требует, чтобы определенные предикаты были определены на нём.

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

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

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

  • Во-первых, мы даём предикаты 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), а также вопросы доступа к защищённым членам (§4.10.1.8).

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

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

Мы постулируем существование 28 предикат 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.

isInit(Method)

Истина тогда и только тогда, когда Method (независимо от класса) является <init>.

isNotInit(Method)

Истина тогда и только тогда, когда Method (независимо от класса) не является <init>.

isNotFinal(Method, Class)

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

isStatic(Method, Class)

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

isNotStatic(Method, Class)

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

isPrivate(Method, Class)

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

isNotPrivate(Method, Class)

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

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. Проверка системы типов

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

Verification type hierarchy:

                             top
                 ____________/\____________
                /                          \
               /                            \
            oneWord                       twoWord
           /   |   \                     /       \
          /    |    \                   /         \
        int  float  reference        long        double
                     /     \
                    /       \_____________
                   /                      \
                  /                        \
           uninitialized                    +------------------+
            /         \                     |  Java reference  |
           /           \                    |  type hierarchy  |
uninitializedThis  uninitialized(Offset)    +------------------+  
                                                     |
                                                     |
                                                    null

Большинство типов проверки напрямую соответствуют примитивным и ссылочным типам, представленным описателями полей в таблице 4.3-A:

  • Примитивные типы double, float, int и long (описатели полей D, F, I, J) соответствуют типу проверки с тем же именем.

  • Примитивные типы byte, char, short и boolean (описатели полей B, C, S, Z) соответствуют типу проверки int.

  • Типы классов и интерфейсов соответствуют типам проверки, использующим функтор class. Тип проверки class(N, L) представляет класс с двоичным именем N, загруженный загрузчиком L. Обратите внимание, что L является инициализирующим загрузчиком (§5.3) класса, представленного class(N, L), и может, или не может, быть загрузчиком, определяющим класс.

    Например, тип класса Object будет представлен как class('java/lang/Object', BL), где BL — загрузчик-бутстрап.

  • Массивы соответствуют типам проверки, использующим функтор arrayOf. Тип проверки arrayOf(T) представляет тип массива, компонентом которого является тип проверки T.

    Например, типы int[] и Object[] будут представлены как arrayOf(int) и arrayOf(class('java/lang/Object', BL)) соответственно.

Тип проверки uninitialized(Offset) представляется применением функтора uninitialized к аргументу, представляющему числовое значение Offset.

Другие типы проверки представлены в Prolog как атомы, имена которых обозначают тип проверки.

Правила подтипизации для типов проверки таковы.

Подтипизация рефлексивна.

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. Представление инструкций

Индивидуальные инструкции байткода представлены в Prolog как термины, функтором которых является имя инструкции, а аргументами — её распарсенные операнды.

Например, инструкция 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, §4.3.3) отображаются в более читаемые имена: ведущие 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. Представление кадра карты стека

Кадры карты стека представляются в Prolog как список термов вида:

stackMap(Offset, TypeState)

где:

  • Offset — целое число, указывающее смещение байткода, к которому применяется кадр карты стека (§4.7.4).

    Порядок смещений байткода в этом списке должен совпадать с порядком в файле class.

  • TypeState — ожидаемое состояние типа входных данных для инструкции в позиции Offset.

Состояние типа — это отображение локаций в стеке операндов и локальных переменных метода на типы проверки. Оно имеет вид:

frame(Locals, OperandStack, Flags)

где:

  • Locals — список типов проверки, где i-й элемент списка (с нумерацией с 0) представляет тип локальной переменной i.

  • OperandStack — список типов проверки, где первый элемент списка представляет тип вершины стека операндов, а типы элементов стека под вершиной следуют в списке в соответствующем порядке.

    Типы размером 2 (long и double) представляются двумя элементами стека, где первый элемент — top, а второй — сам тип.

    Например, стек с значением double, значением int и значением long в состоянии типа представляется стеком с пятью элементами: top и double для значения double, int для значения int и top и long для значения long. Соответственно, OperandStack — это список [top, double, int, top, long].

  • Flags — список, который может быть пустым или содержать единственный элемент flagThisUninit.

    Если какая-либо локальная переменная в Locals имеет тип uninitializedThis, то Flags содержит единственный элемент flagThisUninit, иначе Flags — пустой список.

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

Подтипизация типов проверки распространяется точечно на состояния типов.

Массив локальных переменных метода имеет фиксированную длину по построению (см. 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. Проверка типов абстрактных и нативных методов

Методы abstract и методы native считаются безопасными с точки зрения типов, если они не переопределяют метод final.

methodIsTypeSafe(Class, Method) :-
    doesNotOverrideFinalMethod(Class, Method),
    methodAccessFlags(Method, AccessFlags),
    member(abstract, AccessFlags).

methodIsTypeSafe(Class, Method) :-
    doesNotOverrideFinalMethod(Class, Method),
    methodAccessFlags(Method, AccessFlags),
    member(native, AccessFlags).

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

doesNotOverrideFinalMethod(class('java/lang/Object', L), Method) :-
    isBootstrapLoader(L).

doesNotOverrideFinalMethod(Class, Method) :-
    isPrivate(Method, Class).

doesNotOverrideFinalMethod(Class, Method) :-
    isStatic(Method, Class).

doesNotOverrideFinalMethod(Class, Method) :-
    isNotPrivate(Method, Class),
    isNotStatic(Method, Class),
    doesNotOverrideFinalMethodOfSuperclass(Class, Method).

doesNotOverrideFinalMethodOfSuperclass(Class, Method) :-
    classSuperClassName(Class, SuperclassName),
    classDefiningLoader(Class, L),
    loadedClass(SuperclassName, L, Superclass),
    classMethods(Superclass, SuperMethodList),
    finalMethodNotOverridden(Method, Superclass, SuperMethodList).

Методы final, которые являются private и/или static, являются необычными, так как методы private и методы static не могут быть переопределены напрямую. Таким образом, если найден метод final private или метод final static, он логически не был переопределён другим методом.

finalMethodNotOverridden(Method, Superclass, SuperMethodList) :-
    methodName(Method, Name),
    methodDescriptor(Method, Descriptor),
    member(method(_, Name, Descriptor), SuperMethodList),
    isFinal(Method, Superclass),
    isPrivate(Method, Superclass).

finalMethodNotOverridden(Method, Superclass, SuperMethodList) :-
    methodName(Method, Name),
    methodDescriptor(Method, Descriptor),
    member(method(_, Name, Descriptor), SuperMethodList),
    isFinal(Method, Superclass),
    isStatic(Method, Superclass). 

Если найден не-final private метод или не-final static метод, пропустите его, так как он ортогонален переопределению.

finalMethodNotOverridden(Method, Superclass, SuperMethodList) :-
    methodName(Method, Name),
    methodDescriptor(Method, Descriptor),
    member(method(_, Name, Descriptor), SuperMethodList),
    isNotFinal(Method, Superclass),
    isPrivate(Method, Superclass),
    doesNotOverrideFinalMethodOfSuperclass(Superclass, Method).

finalMethodNotOverridden(Method, Superclass, SuperMethodList) :-
    methodName(Method, Name),
    methodDescriptor(Method, Descriptor),
    member(method(_, Name, Descriptor), SuperMethodList),
    isNotFinal(Method, Superclass),
    isStatic(Method, Superclass),
    doesNotOverrideFinalMethodOfSuperclass(Superclass, Method).

Если найден метод, который не является final, не является private и не является static методом, то действительно метод final не был переопределён. В противном случае вернитесь к верхнему уровню.

finalMethodNotOverridden(Method, Superclass, SuperMethodList) :-
    methodName(Method, Name),
    methodDescriptor(Method, Descriptor),
    member(method(_, Name, Descriptor), SuperMethodList),
    isNotFinal(Method, Superclass),
    isNotStatic(Method, Superclass),
    isNotPrivate(Method, Superclass).

finalMethodNotOverridden(Method, Superclass, SuperMethodList) :-
    methodName(Method, Name),
    methodDescriptor(Method, Descriptor),
    notMember(method(_, Name, Descriptor), SuperMethodList),
    doesNotOverrideFinalMethodOfSuperclass(Superclass, Method).

4.10.1.6. Методы проверки типов с кодом

Методы, не являющиеся abstract, не-native, являются корректными по типу, если у них есть код, и этот код корректен по типу.

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, в противном случае это класс, указанный в обработчике.

Дополнительное требование существует для обработчика внутри метода <init>, если одна из инструкций, охватываемых обработчиком, является invokespecial метода <init>. В этом случае тот факт, что обработчик работает, означает, что объект, который строится, вероятно, повреждён, поэтому важно, чтобы обработчик не заглатывал исключение и не позволял окружающему методу <init> возвратиться к вызывающей стороне. Соответственно, обработчик должен либо прерваться, сгенерировав исключение для вызывающей стороны окружающего метода <init>, либо зациклиться.

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)),
    initHandlerIsLegal(Environment, Handler).

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.
initHandlerIsLegal(Environment, Handler) :-
    notInitHandler(Environment, Handler).

notInitHandler(Environment, Handler) :-
    Environment = environment(_Class, Method, _, Instructions, _, _),
    isNotInit(Method).

notInitHandler(Environment, Handler) :-
    Environment = environment(_Class, Method, _, Instructions, _, _),
    isInit(Method),
    member(instruction(_, invokespecial(CP)), Instructions),
    CP = method(MethodClassName, MethodName, Descriptor),
    MethodName \= '<init>'. 


initHandlerIsLegal(Environment, Handler) :-
    isInitHandler(Environment, Handler),
    sublist(isApplicableInstruction(Target), Instructions,
            HandlerInstructions),
    noAttemptToReturnNormally(HandlerInstructions).

isInitHandler(Environment, Handler) :-
    Environment = environment(_Class, Method, _, Instructions, _, _),
    isInit(Method).
    member(instruction(_, invokespecial(CP)), Instructions),
    CP = method(MethodClassName, '<init>', Descriptor).

isApplicableInstruction(HandlerStart, instruction(Offset, _)) :-
    Offset >= HandlerStart.

noAttemptToReturnNormally(Instructions) :-
    notMember(instruction(_, return), Instructions).

noAttemptToReturnNormally(Instructions) :-
    member(instruction(_, athrow), Instructions). 

Теперь перейдём к потоку инструкций и кадрам карты стека.

Объединение инструкций и кадров карты стека в один поток включает в себя четыре случая:

  • Объединение пустого 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.

Инструкция удовлетворяет обработчику исключений, если выходное состояние типа инструкции равно ExcStackFrame, и цель обработчика (первая инструкция кода обработчика) безопасна с учётом входного состояния типа T. Состояние типа T получено из ExcStackFrame путём замены стека операндов стеком, единственный элемент которого — класс исключения обработчика.

instructionSatisfiesHandler(Environment, ExcStackFrame, Handler) :-
    Handler = handler(_, _, Target, _),
    currentClassLoader(Environment, CurrentLoader),
    handlerExceptionClass(Handler, ExceptionClass, CurrentLoader), 
    /* The stack consists of just the exception. */
    ExcStackFrame = frame(Locals, _, Flags),
    TrueExcStackFrame = frame(Locals, [ ExceptionClass ], Flags),
    operandStackHasLegalLength(Environment, TrueExcStackFrame),
    targetIsTypeSafe(Environment, TrueExcStackFrame, 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_<n>

Инструкция 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> для 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_<n>

Инструкция 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> для 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 или подтип thereof (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, d2i, d2l

Инструкция d2f безопасна с точки зрения типов, если можно корректно извлечь double со входного стека операндов и заменить его на float, что приводит к состоянию выходного типа.

instructionIsTypeSafe(d2f, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    validTypeTransition(Environment, [double], float,
                        StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).

Инструкция d2i безопасна с точки зрения типов, если можно корректно извлечь double со входного стека операндов и заменить его на int, что приводит к состоянию выходного типа.

instructionIsTypeSafe(d2i, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    validTypeTransition(Environment, [double], int,
                        StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).

Инструкция 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_<n>

Инструкция 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> для 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_<n>

Инструкция 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> для 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_x2FormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack),
    NextStackFrame = frame(Locals, OutputOperandStack, Flags),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).

Инструкция dup_x2 является безопасной формой по типу инструкции dup_x2, если она является инструкцией безопасная форма 1 dup_x2 или инструкцией безопасная форма 2 dup_x2.

dup_x2FormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
    dup_x2Form1IsTypeSafe(Environment, InputOperandStack, OutputOperandStack).

dup_x2FormIsTypeSafe(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),
    dup2FormIsTypeSafe(Environment,InputOperandStack, OutputOperandStack),
    NextStackFrame = frame(Locals, OutputOperandStack, Flags),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).

Инструкция dup2 является безопасной формой по типу инструкции dup2, если она является инструкцией безопасная форма 1 dup2 или инструкцией безопасная форма 2 dup2.

dup2FormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
    dup2Form1IsTypeSafe(Environment,InputOperandStack, OutputOperandStack).

dup2FormIsTypeSafe(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_x1FormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack),
    NextStackFrame = frame(Locals, OutputOperandStack, Flags),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).

Инструкция dup2_x1 является безопасной формой по типу инструкции dup2_x1, если она является инструкцией безопасная форма 1 dup2_x1 или инструкцией безопасная форма 2 dup_x2.

dup2_x1FormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
    dup2_x1Form1IsTypeSafe(Environment, InputOperandStack, OutputOperandStack).

dup2_x1FormIsTypeSafe(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_x2FormIsTypeSafe(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_x2FormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
    dup2_x2Form1IsTypeSafe(Environment, InputOperandStack, OutputOperandStack).

dup2_x2FormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
    dup2_x2Form2IsTypeSafe(Environment, InputOperandStack, OutputOperandStack).

dup2_x2FormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
    dup2_x2Form3IsTypeSafe(Environment, InputOperandStack, OutputOperandStack).

dup2_x2FormIsTypeSafe(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, f2i, f2l

Инструкция f2d безопасна по типу, если можно корректно извлечь float со стека операндов входного типа и заменить его на double, что соответствует выходному состоянию типа.

instructionIsTypeSafe(f2d, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    validTypeTransition(Environment, [float], double,
                        StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).

Инструкция f2i безопасна по типу, если можно корректно извлечь float со стека операндов входного типа и заменить его на int, что соответствует выходному состоянию типа.

instructionIsTypeSafe(f2i, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    validTypeTransition(Environment, [float], int,
                        StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).

Инструкция 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_<n>

Инструкция 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>, для 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_<n>

Инструкция 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>, для 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_w

Инструкция goto безопасна по типу тогда и только тогда, когда ее целевой операнд является допустимой точкой ветвления.

instructionIsTypeSafe(goto(Target), Environment, _Offset, StackFrame,
                      afterGoto, ExceptionStackFrame) :-
    targetIsTypeSafe(Environment, StackFrame, Target),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).

Инструкция goto_w безопасна по типу тогда и только тогда, когда эквивалентная инструкция goto безопасна по типу.

instructionHasEquivalentTypeRule(goto_w(Target), goto(Target)).
i2b, i2c, i2d, i2f, i2l, i2s

Инструкция i2b безопасна по типу тогда и только тогда, когда эквивалентная инструкция ineg безопасна по типу.

instructionHasEquivalentTypeRule(i2b, ineg).

Инструкция i2c безопасна по типу тогда и только тогда, когда эквивалентная инструкция ineg безопасна по типу.

instructionHasEquivalentTypeRule(i2c, ineg).

Инструкция i2d безопасна по типу, если можно корректно извлечь int с входного стека операндов и заменить его на double, что приводит к состоянию типа на выходе.

instructionIsTypeSafe(i2d, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    validTypeTransition(Environment, [int], double,
                        StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).

Инструкция i2f безопасна по типу, если можно корректно извлечь int с входного стека операндов и заменить его на float, что приводит к состоянию типа на выходе.

instructionIsTypeSafe(i2f, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    validTypeTransition(Environment, [int], float,
                        StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).

Инструкция i2l безопасна по типу, если можно корректно извлечь int с входного стека операндов и заменить его на long, что приводит к состоянию типа на выходе.

instructionIsTypeSafe(i2l, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    validTypeTransition(Environment, [int], long,
                        StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).

Инструкция 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_<n>

Инструкция 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>, для 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, CurrentLoader),
    reverse([class(MethodIntfName, CurrentLoader) | 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, class(CurrentClassName, CurrentLoader)), 
    reverse([class(CurrentClassName, CurrentLoader) | OperandArgList],
            StackArgList),
    validTypeTransition(Environment, StackArgList, ReturnType,
                        StackFrame, NextStackFrame),
    reverse([class(MethodClassName, CurrentLoader) | OperandArgList],
            StackArgList2),
    validTypeTransition(Environment, StackArgList2, ReturnType,
                        StackFrame, _ResultStackFrame),
    isAssignable(class(CurrentClassName, CurrentLoader),
                 class(MethodClassName, CurrentLoader)).
    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(Locals, [], Flags),
    passesProtectedCheck(Environment, MethodClassName, '<init>',
                         Descriptor, NextStackFrame).

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

  • Если мы инициализируем объект внутри его конструктора, его тип изначально равен uninitializedThis. Этот тип будет переписан на тип класса метода <init>.

  • Второй случай возникает при инициализации объекта, созданного инструкцией new. Тип неинициализированного аргумента переписывается на MethodClass, тип метода-владельца <init>. Мы проверяем, есть ли действительно инструкция new по адресу Address.

rewrittenUninitializedType(uninitializedThis, Environment,
                           MethodClass, MethodClass) :-
    MethodClass = class(MethodClassName, CurrentLoader),
    thisClass(Environment, MethodClass). 

rewrittenUninitializedType(uninitializedThis, Environment,
                           MethodClass, MethodClass) :-
    MethodClass = class(MethodClassName, CurrentLoader),
    thisClass(Environment, class(thisClassName, thisLoader)),
    superclassChain(thisClassName, thisLoader, [MethodClass | Rest]).

rewrittenUninitializedType(uninitialized(Address), Environment,
                           MethodClass, MethodClass) :-
    allInstructions(Environment, Instructions),
    member(instruction(Address, new(MethodClass)), Instructions).

rewrittenInitializationFlags(uninitializedThis, _Flags, []).
rewrittenInitializationFlags(uninitialized(_), Flags, Flags).

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).

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

В этой ситуации исходная рамка содержит неинициализированный объект в локальной переменной 0 и имеет флаг flagThisUninit. Нормальное завершение invokespecial инициализирует неинициализированный объект и отключает флаг flagThisUninit. Но если вызов метода <init> вызывает исключение, неинициализированный объект может остаться в частично инициализированном состоянии и должен быть навсегда непригодным для использования. Это представлено рамкой исключения, содержащей сломанный объект (новое значение локальной переменной) и флаг flagThisUninit (старый флаг). Нет способа перейти от объекта, который кажется инициализированным, с флагом flagThisUninit, к должным образом инициализированному объекту, поэтому объект навсегда непригоден для использования.

Если бы не было этой ситуации, флаги рамки стека исключений всегда были бы такими же, как флаги входной рамки.

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, CurrentLoader),
    reverse([class(MethodClassName, CurrentLoader) | 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, ishr, iushr

Инструкция ishl безопасна с точки зрения типов тогда и только тогда, когда эквивалентная инструкция iadd безопасна с точки зрения типов.

instructionHasEquivalentTypeRule(ishl, iadd).

Инструкция ishr безопасна с точки зрения типов тогда и только тогда, когда эквивалентная инструкция iadd безопасна с точки зрения типов.

instructionHasEquivalentTypeRule(ishr, iadd).

Инструкция iushr безопасна с точки зрения типов тогда и только тогда, когда эквивалентная инструкция iadd безопасна с точки зрения типов.

instructionHasEquivalentTypeRule(iushr, iadd).
istore, istore_<n>

Инструкция 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> для 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).
ixor

Инструкция ixor безопасна с точки зрения типов тогда и только тогда, когда эквивалентная инструкция iadd безопасна с точки зрения типов.

instructionHasEquivalentTypeRule(ixor, iadd).
l2d, l2f, l2i

Инструкция l2d безопасна с точки зрения типов, если можно корректно извлечь long из входного стека операндов и заменить его на double, что приводит к состоянию исходящего типа.

instructionIsTypeSafe(l2d, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    validTypeTransition(Environment, [long], double,
                        StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).

Инструкция l2f безопасна с точки зрения типов, если можно корректно извлечь long из входного стека операндов и заменить его на float, что приводит к состоянию исходящего типа.

instructionIsTypeSafe(l2f, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    validTypeTransition(Environment, [long], float,
                        StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).

Инструкция 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_w, ldc2_w

Инструкция 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 безопасна с точки зрения типов.

instructionHasEquivalentTypeRule(ldc_w(CP), ldc(CP))

Инструкция 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_<n>

Инструкция 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> для 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, lshr, lushr

Инструкция lshl является безопасной с точки зрения типов, если можно корректно заменить типы int и long на входном стеке операндов типом long, получив выходное состояние типа.

instructionIsTypeSafe(lshl, Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    validTypeTransition(Environment, [int, long], long,
                        StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).

Инструкция lshr является безопасной с точки зрения типов тогда и только тогда, когда эквивалентная инструкция lshl является безопасной с точки зрения типов.

instructionHasEquivalentTypeRule(lshr, lshl).

Инструкция lushr является безопасной с точки зрения типов тогда и только тогда, когда эквивалентная инструкция lshl является безопасной с точки зрения типов.

instructionHasEquivalentTypeRule(lushr, lshl).
lstore, lstore_<n>

Инструкция lstore с операндом Index является безопасной с точки зрения типов и приводит к выходному состоянию типа NextStackFrame, если инструкция сохранения с операндом Index и типом long является безопасной с точки зрения типов и приводит к выходному состоянию типа NextStackFrame.

instructionIsTypeSafe(lstore(Index), Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    storeIsTypeSafe(Environment, Index, long, StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).

Инструкции lstore_<n>, для 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).
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).

Предикат substitute определяется в правиле для invokespecial (§invokespecial).

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, pop2

Инструкция 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.

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.

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

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

  • Если одно значение — это тип ссылки, не являющейся массивом, то соответствующее значение должно быть типом ссылки (массивом или не массивом). Объединённое значение — ссылка на экземпляр первого общего предка двух типов ссылок. (Такой тип ссылки всегда существует, потому что тип Object является предком всех типов классов, интерфейсов и массивов.)

    Например, Object и String можно объединить; результатом является Object. Аналогично, Object и String[] можно объединить; результатом снова является Object. Даже Object и int[] можно объединить или String и int[]; результатом в обоих случаях является Object.

  • Если соответствующие значения являются типами ссылок на массивы, то проверяются их размерности. Если типы массивов имеют одинаковые размерности, то объединённое значение — ссылка на экземпляр типа массива, который является первым общим предком обоих типов массивов. (Если у одного или обоих типов массивов есть примитивный тип элемента, то Object используется в качестве типа элемента вместо него.) Если типы массивов имеют разные размерности, то объединённое значение — ссылка на экземпляр типа массива, размерность которого меньше из двух; тип элемента — это Cloneable или java.io.Serializable, если тип массива меньше был Cloneable или java.io.Serializable, и Object в противном случае.

    Например, Object[] и String[] можно объединить; результатом является Object[]. Cloneable[] и String[] можно объединить, или java.io.Serializable[] и String[]; результатом является Cloneable[] и java.io.Serializable[] соответственно. Даже int[] и String[] можно объединить; результатом является Object[], потому что Object используется вместо int при вычислении первого общего предка.

    Поскольку типы массивов могут иметь разные размерности, Object[] и String[][] могут быть объединены, или Object[][] и String[]; в обоих случаях результатом является Object[]. Cloneable[] и String[][] могут быть объединены; результатом является Cloneable[]. Наконец, Cloneable[][] и String[] могут быть объединены; результатом является Object[].

Если стеки операндов нельзя объединить, проверка метода завершается неудачей.

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

Если анализатор потока данных выполняется над методом без сообщения об ошибке проверки, то метод успешно проверен верификатором файла 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 Virtual Machine приведены в §3 (Компиляция для Java Virtual Machine).)

Метод инициализации экземпляра (§2.9) для класса myClass видит новый неинициализированный объект в качестве аргумента this в локальной переменной 0. Перед тем, как этот метод вызовет другой метод инициализации экземпляра класса myClass или его непосредственного суперкласса для this, единственной операцией, которую метод может выполнить над this, является присвоение полей, объявленных в myClass.

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

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

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

new InputStream(new Foo(), new InputStream("foo"))

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

4.10.2.5. Исключения и finally

Для реализации конструкции finally компилятор языка программирования Java, генерирующий файлы с номером версии 50.0 или ниже, может использовать механизм обработки исключений вместе со двумя специальными инструкциями: jsr («переход к подпрограмме») и ret («возврат из подпрограммы»). Блок finally компилируется как подпрограмма в коде виртуальной машины Java для соответствующего метода, подобно коду обработчика исключений. При выполнении инструкции jsr, вызывающей подпрограмму, её адрес возврата, адрес инструкции после выполняемой jsr, помещается в стек операндов как значение типа returnAddress. Код подпрограммы сохраняет адрес возврата в локальной переменной. В конце подпрограммы инструкция ret извлекает адрес возврата из локальной переменной и передает управление инструкции по адресу возврата.

Управление может быть передано блоку finally (подпрограмма finally может быть вызвана) несколькими способами. Если блок finally завершается нормально, подпрограмма finally вызывается с помощью инструкции jsr перед вычислением следующего выражения. Оператор break или continue внутри блока finally, передающий управление за пределы блока finally, выполняет jsr в код блока finally перед этим. Если блок finally выполняет return, скомпилированный код делает следующее:

  1. Сохраняет возвращаемое значение (если есть) в локальной переменной.

  2. Выполняет jsr в код блока finally.

  3. После возврата из блока finally возвращает сохранённое в локальной переменной значение.

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

  1. Сохраняет исключение в локальной переменной.

  2. Выполняет jsr в блок finally.

  3. После возврата из блока finally выбрасывает исключение.

Дополнительную информацию об реализации конструкции finally см. в §3.13.

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

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

  • Вызов для реализации return может иметь некоторую локальную переменную, содержащую возвращаемое значение.

  • Вызов из конца блока finally может иметь неопределённое значение в той же локальной переменной.

Сам код блока 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