Spec-Zone.ru › Java Virtual Machine Specification 17

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

Содержание

4.1. Структура ClassFile
4.2. Имена
4.2.1. Имена бинарных классов и интерфейсов
4.2.2. Неквалифицированные имена
4.2.3. Имена модулей и пакетов
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_Dynamic_info и CONSTANT_InvokeDynamic_info
4.4.11. Структура CONSTANT_Module_info
4.4.12. Структура CONSTANT_Package_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.7.25. Атрибут Module
4.7.26. Атрибут ModulePackages
4.7.27. Атрибут ModuleMainClass
4.7.28. Атрибут NestHost
4.7.29. Атрибут NestMembers
4.7.30. Атрибут Record
4.7.31. Атрибут PermittedSubclasses
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
iconst_<i>
idiv
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
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-битные величины строятся путём чтения двух и четырёх последовательных байтов по 8 бит соответственно. Многобайтовые данные всегда хранятся в формате big-endian, где старшие байты идут первыми. В этой главе определены типы данных u1, u2 и u4 для представления беззнаковых одно-, двух- или четырёхбайтовых величин соответственно.

В API платформы Java SE формат файла class поддерживается интерфейсами java.io.DataInput и java.io.DataOutput и классами, такими как java.io.DataInputStream и java.io.DataOutputStream. Например, значения типов u1, u2 и u4 могут быть прочитаны методами, такими как readUnsignedByte, readUnsignedShort и readInt интерфейса java.io.DataInput.

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

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

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

Ссылка на символ ASCII в этой главе должна интерпретироваться как соответствующий Unicode-код символа 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.

Реализация Java Virtual Machine, соответствующая Java SE N, должна поддерживать точно те номера больших версий формата файла class, которые указаны в четвёртом столбце таблицы 4.1-A ("Поддерживаемые большие версии"). Запись A .. B означает номера больших версий от A до B включительно. Третий столбец ("Большая") показывает номер большей версии, введённой в каждой версии Java SE, то есть первой версии, которая могла бы принять файл class, содержащий этот элемент major_version. Для очень ранних релизов вместо версии Java SE показана версия JDK.

Таблица 4.1-A. Номера больших версий формата файла class

Java SE Дата выхода Большая Поддерживаемые большие версии
1.0.2 Май 1996 45 45
1.1 Февраль 1997 45 45
1.2 Декабрь 1998 46 45 .. 46
1.3 Май 2000 47 45 .. 47
1.4 Февраль 2002 48 45 .. 48
5.0 Сентябрь 2004 49 45 .. 49
6 Декабрь 2006 50 45 .. 50
7 Июль 2011 51 45 .. 51
8 Март 2014 52 45 .. 52
9 Сентябрь 2017 53 45 .. 53

10

Март 2018 54 45 .. 54

11

Сентябрь 2018 55 45 .. 55

12

Март 2019 56 45 .. 56

13

Сентябрь 2019 57 45 .. 57

14

Март 2020 58 45 .. 58

15

Сентябрь 2020 59 45 .. 59

16

Март 2021 60 45 .. 60

17

Сентябрь 2021 61 45 .. 61

Для файла class, чья major_version равна 56 или выше, minor_version должно быть 0 или 65535.

Для файла class, чья major_version находится в диапазоне от 45 до 55 включительно, minor_version может иметь любое значение.

Необходимо взглянуть на исторический контекст поддержки JDK для версий формата файла class. JDK 1.0.2 поддерживал версии от 45.0 до 45.3 включительно. JDK 1.1 поддерживал версии от 45.0 до 45.65535 включительно. Когда JDK 1.2 ввёл поддержку большой версии 46, единственной поддержанной меньшей версией в рамках этой большой версии была 0. Более поздние версии JDK продолжали практику ввода поддержки новой большой версии (47, 48 и т. д.), но поддерживали только меньшую версию 0 в рамках новой большой версии. Наконец, введение предварительных функций в Java SE 12 (см. ниже) мотивировало стандартную роль меньшей версии формата файла class, поэтому JDK 12 поддерживал меньшие версии 0 и 65535 в рамках большой версии 56. Последующие JDK вводят поддержку N.0 и N.65535, где N — соответствующая большая версия реализованной платформы Java SE. Например, JDK 13 поддерживает 57.0 и 57.65535.

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

Файл class считается зависимым от предварительных функций Java SE N (N ≥ 12), если он имеет major_version, соответствующую Java SE N (согласно таблице 4.1-A) и minor_version равную 65535.

Реализация Java Virtual Machine, соответствующая Java SE N (N ≥ 12), должна действовать следующим образом:

  • Файл class, который зависит от предварительных функций Java SE N, может загружаться только при включённых предварительных функциях Java SE N.

  • Файл class, который зависит от предварительных функций другой версии Java SE, никогда не должен загружаться.

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

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

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

Название флага Значение Интерпретация
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_MODULE

0x8000 Представляет собой модуль, а не класс или интерфейс.

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

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

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

Если флаг ACC_INTERFACE не установлен, любые другие флаги в таблице 4.1-B могут быть установлены, за исключением ACC_ANNOTATION и ACC_MODULE. Однако в таком 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, не имел назначенного значения, и реализация Oracle Java Virtual Machine игнорировала флаг, если он был установлен.

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

Интерфейс аннотаций (JLS §9.6) должен иметь установленный флаг ACC_ANNOTATION. Если установлен флаг ACC_ANNOTATION, то должен быть также установлен флаг ACC_INTERFACE.

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

Все биты элемента access_flags, не назначенные в таблице 4.1-B, зарезервированы для будущего использования. Они должны быть установлены в ноль в сгенерированных 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. Эти структуры представляют все поля, как переменные класса, так и переменные экземпляров, объявленные этим типом класса или интерфейса.

fields[]

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

methods_count

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

methods[]

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

Структуры method_info представляют все методы, объявленные этим типом класса или интерфейса, включая методы экземпляра, статические методы, методы инициализации экземпляра (§2.9.1) и любой метод инициализации класса или интерфейса (§2.9.2). Таблица 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.

Если флаг ACC_MODULE установлен в элементе access_flags, то никакой другой флаг в элементе access_flags не может быть установлен, и для остальной части структуры ClassFile применяются следующие правила:

  • Требования к версии Java: ≥ 53.0 (т.е., Java SE 9 и выше)

  • module-info: module-info

  • super_class, interfaces_count, fields_count, methods_count: ноль

  • attributes: Должен быть один атрибут Module. За исключением Module, ModulePackages, ModuleMainClass, InnerClasses, SourceFile, SourceDebugExtension, RuntimeVisibleAnnotations и RuntimeInvisibleAnnotations, не допускается присутствие предопределенных атрибутов (§4.7).

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.2.3. Имена модулей и пакетов

Имена модулей, на которые ссылаются из атрибута Module, хранятся в структурах CONSTANT_Module_info в пуле констант (§4.4.11). Структура CONSTANT_Module_info содержит структуру CONSTANT_Utf8_info, которая обозначает имя модуля. Имена модулей не кодируются в "внутреннем формате", как имена классов и интерфейсов, то есть ASCII точки (.), разделяющие идентификаторы в имени модуля, не заменяются ASCII косыми чертами (/).

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

  • Имя модуля не должно содержать символов в диапазоне от '\u0000' до '\u001F' включительно.

  • ASCII обратная косая черта (\) зарезервирована для использования в качестве escape-символа в именах модулей. Она не должна появляться в имени модуля, если за ней не следуют ASCII обратная косая черта, ASCII двоеточие (:) или ASCII символ "@" (@). Последовательность ASCII символов \\ может использоваться для кодирования обратной косой черты в имени модуля.

  • ASCII двоеточие (:) и символ "@" (@) зарезервированы для будущего использования в именах модулей. Они не должны появляться в именах модулей, если не экранированы. Последовательности ASCII символов \: и \@ могут использоваться для кодирования двоеточия и символа "@" в имени модуля.

Имена пакетов, на которые ссылаются из атрибута Module, хранятся в структурах CONSTANT_Package_info в пуле констант (§4.4.12). Структура CONSTANT_Package_info содержит структуру CONSTANT_Utf8_info, представляющую имя пакета, закодированное во внутреннем формате.

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-байтового тега, указывающего тип константы, обозначаемой записью. Существует 17 типов констант, перечисленных в таблице 4.4-A с соответствующими тегами, упорядоченными по номерам разделов в этой главе. Каждый байт тега должен следовать за двумя или более байтами, предоставляющими информацию о конкретной константе. Формат дополнительной информации зависит от байта тега, то есть содержимое массива info изменяется в зависимости от значения tag.

Таблица 4.4-A. Теги пула констант (по разделам)

Тип константы Тег Раздел
CONSTANT_Class 7 §4.4.1
CONSTANT_Fieldref 9 §4.4.2
CONSTANT_Methodref 10 §4.4.2
CONSTANT_InterfaceMethodref 11 §4.4.2
CONSTANT_String 8 §4.4.3
CONSTANT_Integer 3 §4.4.4
CONSTANT_Float 4 §4.4.4
CONSTANT_Long 5 §4.4.5
CONSTANT_Double 6 §4.4.5
CONSTANT_NameAndType 12 §4.4.6
CONSTANT_Utf8 1 §4.4.7
CONSTANT_MethodHandle 15 §4.4.8
CONSTANT_MethodType 16 §4.4.9
CONSTANT_Dynamic 17 §4.4.10
CONSTANT_InvokeDynamic 18 §4.4.10
CONSTANT_Module 19 §4.4.11
CONSTANT_Package 20 §4.4.12

В файле class, номер версии которого v, каждая запись в таблице constant_pool должна иметь тег, который был определён впервые в версии v или ранее формата файла class (§4.1). То есть, каждая запись должна обозначать тип константы, разрешённой для использования в файле class. Таблица 4.4-B перечисляет каждый тег с первой версией формата файла class, в которой он был определён. Также показана версия Java SE Platform, которая ввела эту версию формата файла class.

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

Тип константы Тег class формата файла Java SE
CONSTANT_Utf8 1 45.3 1.0.2
CONSTANT_Integer 3 45.3 1.0.2
CONSTANT_Float 4 45.3 1.0.2
CONSTANT_Long 5 45.3 1.0.2
CONSTANT_Double 6 45.3 1.0.2
CONSTANT_Class 7 45.3 1.0.2
CONSTANT_String 8 45.3 1.0.2
CONSTANT_Fieldref 9 45.3 1.0.2
CONSTANT_Methodref 10 45.3 1.0.2
CONSTANT_InterfaceMethodref 11 45.3 1.0.2
CONSTANT_NameAndType 12 45.3 1.0.2
CONSTANT_MethodHandle 15 51.0 7
CONSTANT_MethodType 16 51.0 7
CONSTANT_Dynamic 17 55.0 11
CONSTANT_InvokeDynamic 18 51.0 7
CONSTANT_Module 19 53.0 9
CONSTANT_Package 20 53.0 9

Некоторые записи в таблице constant_pool являются загружаемыми, поскольку они представляют сущности, которые могут быть помещены в стек во время выполнения для обеспечения дальнейших вычислений. В файле class, номер версии которого v, запись в таблице constant_pool является загружаемой, если у неё есть тег, который был признан загружаемым в версии v или ранее формата файла class. Таблица 4.4-C перечисляет каждый тег с первой версией формата файла class, в которой он считался загружаемым. Также показана версия Java SE Platform, которая ввела эту версию формата файла class.

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

Таблица 4.4-C. Теги загружаемого пула констант

Тип константы Тег class формата файла Java SE
CONSTANT_Integer 3 45.3 1.0.2
CONSTANT_Float 4 45.3 1.0.2
CONSTANT_Long 5 45.3 1.0.2
CONSTANT_Double 6 45.3 1.0.2
CONSTANT_Class 7 49.0 5.0
CONSTANT_String 8 45.3 1.0.2
CONSTANT_MethodHandle 15 51.0 7
CONSTANT_MethodType 16 51.0 7
CONSTANT_Dynamic 17 55.0 11

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), представляющей тип класса или интерфейса, который имеет поле или метод в качестве члена.

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

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

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

name_and_type_index

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

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

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

string_index

Значение элемента string_index должно быть корректным индексом в таблице constant_pool. Запись constant_pool в этом индексе должна быть структурой CONSTANT_Utf8_info (§4.4.7), представляющей последовательность кодов Unicode, с которой должен быть инициализирован объект 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 binary32 (§2.3.2). Байты элемента хранятся в формате big-endian (старший байт первым).

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

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

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

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

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

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

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

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

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

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

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

Все 8-байтовые константы занимают две записи в таблице constant_pool файла class. Если структура CONSTANT_Long_info или CONSTANT_Double_info является записью с индексом n в таблице constant_pool, то следующая доступная запись в таблице расположена по индексу 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 binary64 (§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, значение типа double будет NaN.

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

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

Тогда значение с плавающей точкой равно значению 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 (12).

name_index

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

descriptor_index

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

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

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

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

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

tag

Элемент tag имеет значение CONSTANT_Utf8 (1).

length

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

bytes[]

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

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

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

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

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

    Таблица 4.7.

    0 биты 6-0

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

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

    Таблица 4.8.

    x:

    Таблица 4.9.

    1 1 0 биты 10-6

    y:

    Таблица 4.10.

    1 0 биты 5-0


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

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

    Таблица 4.11.

    x:

    Таблица 4.12.

    1 1 1 0 биты 15-12

    y:

    Таблица 4.13.

    1 0 биты 11-6

    z:

    Таблица 4.14.

    1 0 биты 5-0


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

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

    Таблица 4.15.

    u:

    Таблица 4.16.

    1 1 1 0 1 1 0 1

    v:

    Таблица 4.17.

    1 0 1 0 (bits 20-16)-1

    w:

    Таблица 4.18.

    1 0 bits 15-10

    x:

    Таблица 4.19.

    1 1 1 0 1 1 0 1

    y:

    Таблица 4.20.

    1 0 1 1 bits 9-6

    z:

    Таблица 4.21.

    1 0 bits 5-0


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

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

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

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

Дополнительную информацию о стандартном формате UTF-8 см. в разделе 3.9 Форматы кодирования Unicode стандарта Unicode, версия 13.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 (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.1), для которого должен быть создан дескриптор метода.

  • Если значение элемента 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 (16).

descriptor_index

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

4.4.10. Структуры CONSTANT_Dynamic_info и CONSTANT_InvokeDynamic_info

Большинство структур в таблице constant_pool представляют сущности напрямую, комбинируя имена, дескрипторы и значения, статически записанные в таблице. В отличие от этого, структуры CONSTANT_Dynamic_info и CONSTANT_InvokeDynamic_info представляют сущности косвенно, ссылаясь на код, который вычисляет сущность динамически. Код, называемый методом инициализации, вызывается виртуальной машиной Java во время разрешения символьных ссылок, полученных из этих структур (§5.1, §5.4.3.6). Каждая структура определяет метод инициализации, а также вспомогательное имя и тип, которые характеризуют вычисляемую сущность. Подробнее:

  • Структура CONSTANT_Dynamic_info используется для представления динамически вычисленной константы, произвольного значения, которое генерируется вызовом метода инициализации в ходе инструкции ldc (§ldc) и др. Вспомогательный тип, указанный в структуре, ограничивает тип динамически вычисленной константы.

  • Структура CONSTANT_InvokeDynamic_info используется для представления динамически вычисленной точки вызова, экземпляра java.lang.invoke.CallSite, который генерируется вызовом метода инициализации в ходе инструкции invokedynamic (§invokedynamic). Вспомогательный тип, указанный в структуре, ограничивает тип типа метода динамически вычисленной точки вызова.

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

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

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

tag

Элемент tag структуры CONSTANT_Dynamic_info имеет значение CONSTANT_Dynamic (17).

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

bootstrap_method_attr_index

Значение элемента bootstrap_method_attr_index должно быть корректным индексом в массиве bootstrap_methods таблицы метода инициализации этого файла class (§4.7.23).

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

name_and_type_index

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

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

В структуре CONSTANT_InvokeDynamic_info указанный дескриптор должен быть дескриптором метода (§4.3.3).

4.4.11. Структура CONSTANT_Module_info

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

CONSTANT_Module_info {
    u1 tag;
    u2 name_index;
}

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

tag

Элемент tag имеет значение CONSTANT_Module (19).

name_index

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

Структура CONSTANT_Module_info разрешена только в константном пуле файла class, который объявляет модуль, то есть структуры ClassFile, где элемент access_flags имеет установленный флаг ACC_MODULE. Во всех других файлах class структура CONSTANT_Module_info запрещена.

4.4.12. Структура CONSTANT_Package_info

Структура CONSTANT_Package_info используется для представления пакета, экспортированного или открытого модулем:

CONSTANT_Package_info {
    u1 tag;
    u2 name_index;
}

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

tag

Элемент tag имеет значение CONSTANT_Package (20).

name_index

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

Структура CONSTANT_Package_info разрешена только в константном пуле файла class, который объявляет модуль, то есть структуры ClassFile, где элемент access_flags имеет установленный флаг ACC_MODULE. Во всех других файлах class структура CONSTANT_Package_info запрещена.

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; доступно только внутри определяющего класса и других классов, принадлежащих к тому же вложенному набору (§5.4.4).

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

Все биты элемента 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.

4.6. Методы

Каждый метод, включая каждый метод инициализации экземпляра (§2.9.1) и метод инициализации класса или интерфейса (§2.9.2), описывается структурой 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; доступен только внутри определяющего класса и других классов, принадлежащих к тому же вложенному элементу (§5.4.4).

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

В файле class, версия которого не меньше 46 и не больше 60: Объявлен strictfp.

ACC_SYNTHETIC 0x1000 Объявлен синтетическим; отсутствует в исходном коде.

Значение 0x0800 интерпретируется как флаг ACC_STRICT только в файле class, версия которого не меньше 46 и не больше 60. Для методов в таком файле class правила ниже определяют, может ли быть установлен флаг ACC_STRICT в сочетании с другими флагами. (Установка флага ACC_STRICT ограничивала инструкции с плавающей точкой методов в Java SE 1.2—16 (§2.8).) Для методов в файле class, версия которого меньше 46 или больше 60, значение 0x0800 не интерпретируется как флаг ACC_STRICT, а является неназначенным; устанавливать флаг ACC_STRICT в таком файле class бессмысленно.

Методы классов могут иметь любые флаги в таблице 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 не должен быть установлен, а также (в файле class, версия которого не меньше 46 и не больше 60) не должен быть установлен флаг ACC_STRICT.

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

В файле class с версией 51.0 или выше у метода с именем <clinit> должен быть установлен флаг ACC_STATIC.

Метод инициализации класса или интерфейса (§2.9.2) вызывается виртуальной машиной Java неявно. Значение его элемента access_flags игнорируется, за исключением установки флага ACC_STATIC и (в файле class, версия которого не меньше 46 и не больше 60) флага ACC_STRICT, и метод освобождается от предыдущих правил о допустимых сочетаниях флагов.

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

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

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

Все биты элемента access_flags, не назначенные в таблице 4.6-A, зарезервированы для будущего использования. (Это включает бит, соответствующий 0x0800, в файле class, версия которого меньше 46 или больше 60.) Они должны быть установлены в ноль в сгенерированных файлах class и должны игнорироваться реализациями виртуальной машины Java.

name_index

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

descriptor_index

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

  • Если этот метод находится в классе, а не в интерфейсе, и имя метода - <init>, то дескриптор должен обозначать метод void.

  • Если имя метода - <clinit>, то дескриптор должен обозначать метод void, и в файле class с версией 51.0 или выше, метод, не принимающий аргументов.

В будущей версии спецификации может потребоваться, чтобы последний дескриптор параметра дескриптора метода был типом массива, если флаг 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 и record_component_info формата файла class (§4.1, §4.5, §4.6, §4.7.3, §4.7.30).

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

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.

Этот стандарт предопределяет 30 атрибутов. Они приведены трижды для удобства навигации:

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

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

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

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

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

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

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

    • ConstantValue

    • Code

    • StackMapTable

    • BootstrapMethods

    • NestHost

    • NestMembers

    • PermittedSubclasses

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

  2. Десять атрибутов не являются критическими для правильной интерпретации файла class виртуальной машиной Java, но являются критическими для корректной интерпретации файла class библиотеками классов платформы Java SE или полезны для инструментов (в этом случае раздел, определяющий атрибут, описывает его как "необязательный"):

    • Exceptions

    • InnerClasses

    • EnclosingMethod

    • Synthetic

    • Signature

    • Record

    • SourceFile

    • LineNumberTable

    • LocalVariableTable

    • LocalVariableTypeTable

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

  3. Тринадцать атрибутов не являются критическими для правильной интерпретации файла class виртуальной машиной Java, но содержат метаданные о файле class, которые либо предоставляются библиотеками классов платформы Java SE, либо доступны инструментами (в этом случае раздел, определяющий атрибут, описывает его как "необязательный"):

    • SourceDebugExtension

    • Deprecated

    • RuntimeVisibleAnnotations

    • RuntimeInvisibleAnnotations

    • RuntimeVisibleParameterAnnotations

    • RuntimeInvisibleParameterAnnotations

    • RuntimeVisibleTypeAnnotations

    • RuntimeInvisibleTypeAnnotations

    • AnnotationDefault

    • MethodParameters

    • Module

    • ModulePackages

    • ModuleMainClass

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

Таблица 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

Module

§4.7.25 53.0 9

ModulePackages

§4.7.26 53.0 9

ModuleMainClass

§4.7.27 53.0 9

NestHost

§4.7.28 55.0 11

NestMembers

§4.7.29 55.0 11

Record

§4.7.30 60.0 16

PermittedSubclasses

§4.7.31 61.0 17

Таблица 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

Module

53.0 9 §4.7.25

ModulePackages

53.0 9 §4.7.26

ModuleMainClass

53.0 9 §4.7.27

NestHost

55.0 11 §4.7.28

NestMembers

55.0 11 §4.7.29

Record

60.0 16 §4.7.30

PermittedSubclasses

61.0 17 §4.7.31

Таблица 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

Module, ModulePackages, ModuleMainClass

ClassFile 53.0

NestHost, NestMembers

ClassFile 55.0

Record

ClassFile 60.0

PermittedSubclasses

ClassFile 61.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

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

Атрибут Расположение class файл
Synthetic ClassFile, field_info, method_info 45.3
Deprecated ClassFile, field_info, method_info 45.3
Signature

ClassFile, field_info, method_info, record_component_info

49.0
RuntimeVisibleAnnotations, RuntimeInvisibleAnnotations

ClassFile, field_info, method_info, record_component_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, record_component_info

52.0

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

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

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

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

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

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

В таблице 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_index

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

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

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

4.7.3. Атрибут Code

Атрибут Code — атрибут переменной длины в таблице attributes структуры method_info (§4.6). Атрибут Code содержит инструкции Java Virtual Machine и вспомогательную информацию для метода, включая метод инициализации экземпляра и метод инициализации класса или интерфейса (§2.9.1, §2.9.2).

Если метод является либо 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 Virtual Machine, реализующие метод.

При чтении массива 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 Virtual Machine: если код Java Virtual Machine для метода имеет длину ровно 65535 байт и заканчивается инструкцией длиной 1 байт, то эта инструкция не может быть защищена обработчиком исключений. Разработчик компилятора может обойти эту ошибку, ограничив максимальный размер сгенерированного кода Java Virtual Machine для любого метода, метода инициализации экземпляра или статического инициализатора (размер любого массива кода) до 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 — это атрибут переменной длины в таблице attributes атрибута Code (§4.7.3). Атрибут StackMapTable используется во время процесса проверки с помощью проверки типов (§4.10.1).

В таблице attributes атрибута Code может быть не более одного атрибута StackMapTable.

В файле class с номером версии 50.0 или выше, если атрибут Code метода не содержит атрибут 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 следующие:

attribute_name_index

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

attribute_length

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

number_of_entries

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

entries[]

Каждый элемент в таблице entries описывает один кадр карты стека метода. Порядок кадров карты стека в таблице entries имеет значение.

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

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

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

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

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

Тип проверки определяет тип одной или двух локаций, где локация — это либо одна локальная переменная, либо один элемент стека операндов. Тип проверки представлен объединением с дискриминатором, 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_variable_info указывает, что локальная переменная имеет тип проверки top.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    0-й элемент в 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];
    }
        

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

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

В таблице 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.

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. Значение элемента outer_class_info_index не должно быть равно значению элемента inner_class_info_index.

inner_name_index

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

В противном случае значение элемента inner_name_index должно быть допустимым индексом в таблице constant_pool, а запись в этой позиции должна быть структурой CONSTANT_Utf8_info, представляющей исходное простое имя 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.1)

  • метод инициализации класса или интерфейса (§2.9.2)

  • неявные объявленные члены классов enum и record (JLS §8.9.3, JLS §8.10.3)

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

Ограничением формата файла class является то, что только формальные параметры и модули могут быть помечены как ACC_MANDATED (§4.7.24, §4.7.25), чтобы указать, что, несмотря на то, что они сгенерированы компилятором, они не считаются артефактами реализации. Нет способа пометить другие сгенерированные компилятором конструкции, чтобы они тоже не считались артефактами реализации (JLS §13.1). Это ограничение означает, что рефлексивные API платформы Java SE могут неточно указывать статус «обязательного» таких конструкций.

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

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

Атрибут 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_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 разделе метода.

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

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

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

Подпись метода, закодированная атрибутом Signature, может не точно соответствовать описанию метода в структуре method_info (§4.3.3). В частности, нет гарантии, что количество типов формальных параметров в подписи метода равно количеству описаний параметров в описании метода. Эти числа совпадают для большинства методов, но некоторые конструкторы в языке программирования Java имеют неявный объявленный параметр, который компилятор представляет с описанием параметра, но может опустить из подписи метода. См. примечание в §4.7.18 для аналогичной ситуации с аннотациями параметров.

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

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

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_index

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

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

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

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

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

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

attribute_name_index

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

attribute_length

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

line_number_table_length

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

line_number_table[]

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

start_pc

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

line_number

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

4.7.13. Атрибут LocalVariableTable

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

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

В таблице attributes атрибута Code может присутствовать не более одного атрибута LocalVariableTable на каждую локальную переменную.

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

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

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

attribute_name_index

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

attribute_length

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

local_variable_table_length

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

local_variable_table[]

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

start_pc, length

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

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

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

name_index

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

descriptor_index

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

index

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

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

4.7.14. Атрибут LocalVariableTypeTable

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

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

В таблице attributes атрибута Code может присутствовать не более одного атрибута LocalVariableTypeTable на каждую локальную переменную.

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

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

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

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

attribute_name_index

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

attribute_length

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

local_variable_type_table_length

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

local_variable_type_table[]

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

start_pc, length

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

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

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

name_index

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

signature_index

Значение элемента signature_index должно быть допустимым индексом в таблице constant_pool. Элемент constant_pool по этому индексу должен содержать структуру CONSTANT_Utf8_info, представляющую подпись поля, которая кодирует тип локальной переменной в исходной программе (§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 или record_component_info (§4.1, §4.5, §4.6, §4.7.30). Атрибут RuntimeVisibleAnnotations хранит видимые во время выполнения аннотации в объявлении соответствующего класса, поля, метода или компонента записи.

В таблице attributes структуры ClassFile, field_info, method_info или record_component_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 как типов

Элемент 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 или record_component_info (§4.1, §4.5, §4.6, §4.7.30). Атрибут RuntimeInvisibleAnnotations хранит аннотации, невидимые во время выполнения, объявления соответствующего класса, метода, поля или компонента записи.

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

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

Атрибут 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 хранит аннотации, видимые во время выполнения, в объявлениях формальных параметров соответствующего метода.

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

Нет гарантии, что это число совпадает с количеством описателей параметров в описателе метода.

parameter_annotations[]

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

num_annotations

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

annotations[]

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

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

Например, компилятор может выбрать создание элементов в таблице, соответствующих только тем описателям параметров, которые представляют явно объявленные параметры в исходном коде. В языке программирования Java конструктор внутреннего класса задан с неявно объявленным параметром перед явно объявленными параметрами (JLS §8.8.1), поэтому соответствующий метод <init> в файле class имеет описатель параметра, представляющий неявно объявленный параметр перед описателями параметров, представляющими явно объявленные параметры. Если первый явно объявленный параметр аннотирован в исходном коде, то компилятор может создать parameter_annotations[0] для хранения аннотаций, соответствующих второму описателю параметра.

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

Нет гарантии, что это число совпадает с количеством описателей параметров в описателе метода.

parameter_annotations[]

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

num_annotations

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

annotations[]

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

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

См. примечание в §4.7.18 для примера, когда parameter_annotations[0] не соответствует первому описателю параметра в описателе метода.

4.7.20. Атрибут RuntimeVisibleTypeAnnotations

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

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

Таблица RuntimeVisibleTypeAnnotations содержит атрибут attributes только если типы аннотированы в видах деклараций или выражений, соответствующих родительской структуре или атрибуту таблицы 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;
        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, record_component_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, равное i, может, но не обязательно, соответствовать i-му описателю параметра в описателе метода (§4.3.3).

    Элемент formal_parameter_target записывает, что тип формального параметра аннотирован, но не записывает сам тип. Тип можно найти, просмотрев описатель метода, хотя значение formal_parameter_index, равное 0, не всегда указывает первый описатель параметра в описателе метода; см. примечание в §4.7.18 для аналогичной ситуации с таблицей parameter_annotations.

  • Элемент 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, либо байт-кода инструкции 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. Структура type_path

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

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

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

  • Если в объявлении или выражении используется параметризованный тип 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 равно 0, а аннотируемый тип не является вложенным типом, то аннотация появляется непосредственно на самом типе.

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

    type_path_kind

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

    Таблица 4.7.20.2-A. Интерпретация значений type_path_kind

    Значение Интерпретация
    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 обозначает первый аргумент типа параметризованного типа.

Таблица 4.7.20.2-B. Структуры type_path для @A Map<@B ? extends @C String, @D List<@E Object>>

Аннотация 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}]

Таблица 4.7.20.2-C. Структуры type_path для @I String @F [] @G [] @H []

Аннотация 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}]

Таблица 4.7.20.2-D. Структуры type_path для @A List<@B Comparable<@F Object @C [] @D [] @E []>>

Аннотация 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}]

Таблица 4.7.20.2-E. Структуры type_path для @A Outer . @B Middle . @C Inner

Предполагая:
class Outer {
  class Middle {
    class Inner {}
  }
}
Аннотация path_length path
@A 0 []
@B 1 [{type_path_kind: 1; type_argument_index: 0}]
@C 2 [{type_path_kind: 1; type_argument_index: 0}, {type_path_kind: 1; type_argument_index: 0}]

Таблица 4.7.20.2-F. Структуры type_path для Outer . @A MiddleStatic . @B Inner

Предполагая:
class Outer {
  static class MiddleStatic {
    class Inner {}
  }
}
Аннотация path_length path
@A 0 []
@B 1 [{type_path_kind: 1; type_argument_index: 0}]
В типе Outer . MiddleStatic . Inner, аннотации типа к простому имени Outer недопустимы, потому что имя типа справа, MiddleStatic, не относится к внутреннему классу Outer.

Таблица 4.7.20.2-G. Структуры type_path для Outer . MiddleStatic . @A InnerStatic

Предполагая:
class Outer {
  static class MiddleStatic {
    static class InnerStatic {}
  }
}
Аннотация path_length path
@A 0 []
В типе Outer . MiddleStatic . InnerStatic, аннотации типа к простому имени Outer недопустимы, потому что имя типа справа, MiddleStatic, не относится к внутреннему классу Outer. Аналогично, аннотации типа к простому имени MiddleStatic недопустимы, потому что имя типа справа, InnerStatic, не относится к внутреннему классу MiddleStatic.

Таблица 4.7.20.2-H. Структуры type_path для Outer . Middle<@A Foo . @B Bar> . Inner<@D String @C []>

Предполагая:
class Outer {
  class Middle<T> {
    class Inner<U> {}
  }
}
Аннотация path_length path
@A 2 [{type_path_kind: 1; type_argument_index: 0}, {type_path_kind: 3; type_argument_index: 0}]
@B 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}]
@C 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}]
@D 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}]

4.7.21. Атрибут RuntimeInvisibleTypeAnnotations

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

В таблице attributes структуры ClassFile, field_info, method_info или record_component_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.

В таблице 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 записывает вспомогательные методы, используемые для создания динамически вычисляемых констант и динамически вычисляемых мест вызова (§4.4.10).

Должен быть ровно один атрибут BootstrapMethods в таблице attributes структуры ClassFile, если таблица ClassFile структуры содержит хотя бы один элемент CONSTANT_Dynamic_info или CONSTANT_InvokeDynamic_info.

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

num_bootstrap_methods

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

bootstrap_methods[]

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

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

bootstrap_method_ref

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

Обработка дескриптора метода будет выполнена во время разрешения динамически вычисляемой константы или места вызова (§5.4.3.6), а затем вызвана как если бы путем вызова invokeWithArguments в java.lang.invoke.MethodHandle. Дескриптор метода должен быть способен принимать массив аргументов, описанный в §5.4.3.6, в противном случае разрешение завершится неудачей.

num_bootstrap_arguments

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

bootstrap_arguments[]

Каждый элемент массива bootstrap_arguments должен быть допустимым индексом в таблице constant_pool. Элемент constant_pool по этому индексу должен быть загружаемым (§4.4).

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 (§4.4.7) представляющей строку "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'th в массиве parameters соответствует i'th описателю параметра в описателе метода окружения. (Элемент parameters_count имеет один байт, потому что описатель метода ограничен 255 параметрами.) Эффективно, это означает, что массив parameters хранит информацию обо всех параметрах метода. Можно представить и другие схемы, где элементы массива parameters указывают на соответствующие описатели параметров, но это излишне усложнит атрибут MethodParameters.

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

4.7.25. The Module Атрибут

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

В таблице attributes структуры ClassFile может находиться не более одного атрибута Module.

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

Module_attribute {
    u2 attribute_name_index;
    u4 attribute_length;

    u2 module_name_index;
    u2 module_flags;
    u2 module_version_index;

    u2 requires_count;
    {   u2 requires_index;
        u2 requires_flags;
        u2 requires_version_index;
    } requires[requires_count];

    u2 exports_count;
    {   u2 exports_index;
        u2 exports_flags;
        u2 exports_to_count;
        u2 exports_to_index[exports_to_count];
    } exports[exports_count];

    u2 opens_count;
    {   u2 opens_index;
        u2 opens_flags;
        u2 opens_to_count;
        u2 opens_to_index[opens_to_count];
    } opens[opens_count];

    u2 uses_count;
    u2 uses_index[uses_count];

    u2 provides_count;
    {   u2 provides_index;
        u2 provides_with_count;
        u2 provides_with_index[provides_with_count];
    } provides[provides_count];
}

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

attribute_name_index

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

attribute_length

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

module_name_index

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

module_flags

Значение элемента module_flags таково:

0x0020 (ACC_OPEN)

Указывает, что данный модуль открыт.

0x1000 (ACC_SYNTHETIC)

Указывает, что этот модуль не был явно или неявно объявлен.

0x8000 (ACC_MANDATED)

Указывает, что этот модуль был неявно объявлен.

module_version_index

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

requires_count

Значение элемента requires_count указывает количество элементов в таблице requires.

Если текущий модуль является java.base, то requires_count должно быть равно нулю.

Если текущий модуль не является java.base, то requires_count должно быть по крайней мере единицей.

requires[]

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

requires_index

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

В таблице requires может быть не более одного элемента, указывающего модуль с данным именем и его элементом requires_index.

requires_flags

Значение элемента requires_flags таково:

0x0020 (ACC_TRANSITIVE)

Указывает, что любой модуль, зависящий от текущего модуля, неявно объявляет зависимость от модуля, указанного в данном элементе.

0x0040 (ACC_STATIC_PHASE)

Указывает, что данная зависимость обязательна на стадийной фазе (во время компиляции), но необязательна на динамической фазе (во время выполнения).

0x1000 (ACC_SYNTHETIC)

Указывает, что эта зависимость не была явно или неявно объявлена в исходном коде объявления модуля.

0x8000 (ACC_MANDATED)

Указывает, что эта зависимость была неявно объявлена в исходном коде объявления модуля.

Если текущий модуль не является java.base, а номер версии файла class равен 54.0 или выше, то ни ACC_TRANSITIVE, ни ACC_STATIC_PHASE не могут быть установлены в requires_flags.

requires_version_index

Значение элемента requires_version_index должно быть либо нулём, либо допустимым индексом в таблице constant_pool. Если значение элемента равно нулю, то информация о версии зависимости отсутствует. Если значение элемента не равно нулю, то элемент constant_pool в этом индексе должен быть структурой CONSTANT_Utf8_info, представляющей версию модуля, указанного в элементе requires_index.

Если текущий модуль не является java.base, то ровно один элемент в таблице requires должен иметь элемент requires_index, который указывает java.base, и элемент requires_flags, который не имеет установленного флага ACC_SYNTHETIC.

exports_count

Значение элемента exports_count указывает количество элементов в таблице exports.

exports[]

Каждый элемент в таблице exports указывает пакет, экспортируемый текущим модулем, так что типы public и protected в пакете, а также их члены public и protected, могут быть доступны извне текущего модуля, возможно, из ограниченного набора "дружественных" модулей.

Элементы в каждом элементе таковы:

exports_index

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

В таблице exports может быть не более одного элемента, указывающего пакет с данным именем и его элементом exports_index.

exports_flags

Значение элемента exports_flags таково:

0x1000 (ACC_SYNTHETIC)

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

0x8000 (ACC_MANDATED)

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

exports_to_count

Значение элемента exports_to_count указывает количество элементов в таблице exports_to_index.

Если exports_to_count равно нулю, то этот пакет экспортируется текущим модулем в неопределённом виде; код в любом другом модуле может получить доступ к типам и членам пакета.

Если exports_to_count не равно нулю, то этот пакет экспортируется текущим модулем в квалифицированном виде; доступ к типам и членам пакета может получить только код в модулях, указанных в таблице exports_to_index.

exports_to_index[]

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

Для каждого элемента в таблице exports в её таблице exports_to_index может быть не более одного элемента, указывающего модуль с данным именем.

opens_count

Значение элемента opens_count указывает количество элементов в таблице opens.

opens_count должно быть равно нулю, если текущий модуль открыт.

opens[]

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

Элементы каждого элемента таковы:

opens_index

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

В таблице opens может быть не более одного элемента, указывающего пакет с заданным именем и элементом opens_index.

opens_flags

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

0x1000 (ACC_SYNTHETIC)

Указывает, что это открытие не было явно или неявно объявлено в источнике объявления модуля.

0x8000 (ACC_MANDATED)

Указывает, что это открытие было неявно объявлено в источнике объявления модуля.

opens_to_count

Значение элемента opens_to_count указывает количество элементов в таблице opens_to_index.

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

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

opens_to_index[]

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

Для каждого элемента в таблице opens в её таблице opens_to_index может быть не более одного элемента, указывающего модуль с заданным именем.

uses_count

Значение элемента uses_count указывает количество элементов в таблице uses_index.

uses_index[]

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

В таблице uses_index может быть не более одного элемента, указывающего интерфейс службы с заданным именем.

provides_count

Значение элемента provides_count указывает количество элементов в таблице provides.

provides[]

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

Элементы каждого элемента таковы:

provides_index

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

В таблице provides может быть не более одного элемента, указывающего интерфейс службы с заданным именем и элементом provides_index.

provides_with_count

Значение элемента provides_with_count указывает количество элементов в таблице provides_with_index.

provides_with_count должно быть ненулевым.

provides_with_index[]

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

Для каждого элемента в таблице provides в её таблице provides_with_index может быть не более одного элемента, указывающего реализацию службы с заданным именем.

4.7.26. Атрибут ModulePackages

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

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

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

ModulePackages_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 package_count;
    u2 package_index[package_count];
}

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

attribute_name_index

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

attribute_length

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

package_count

Значение элемента package_count указывает количество элементов в таблице package_index.

package_index[]

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

В таблице package_index может быть не более одного элемента, указывающего пакет с заданным именем.

4.7.27. Атрибут ModuleMainClass

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

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

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

ModuleMainClass_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 main_class_index;
}

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

attribute_name_index

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

attribute_length

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

main_class_index

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

4.7.28. The NestHost Атрибут

Атрибут NestHost — атрибут фиксированной длины в таблице attributes структуры ClassFile. Атрибут NestHost записывает хост вложенности, к которому текущий класс или интерфейс претендуют на принадлежность (§5.4.4).

В таблице attributes структуры ClassFile может быть, самое большее, один атрибут NestHost.

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

NestHost_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 host_class_index;
}

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

attribute_name_index

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

attribute_length

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

host_class_index

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

Если хост вложенности нельзя загрузить, или он не находится в той же среде выполнения, что и текущий класс или интерфейс, или не разрешает членство в вложенности для текущего класса или интерфейса, то во время контроля доступа может произойти ошибка (§5.4.4).

4.7.29. The NestMembers Атрибут

Атрибут NestMembers — атрибут переменной длины в таблице attributes структуры ClassFile (§4.1). Атрибут NestMembers записывает классы и интерфейсы, которым разрешено претендовать на членство в вложенности, размещённой текущим классом или интерфейсом (§5.4.4).

В таблице attributes структуры ClassFile может быть, самое большее, один атрибут NestMembers.

Таблица attributes структуры ClassFile не должна содержать одновременно атрибут NestMembers и атрибут NestHost.

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

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

NestMembers_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 number_of_classes;
    u2 classes[number_of_classes];
}

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

attribute_name_index

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

attribute_length

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

number_of_classes

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

classes[]

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

Массив classes используется при контроле доступа (§5.4.4). Он должен содержать ссылки на другие классы и интерфейсы, которые находятся в той же среде выполнения и имеют атрибуты NestHost, которые ссылаются на текущий класс или интерфейс. Элементы массива, которые не соответствуют этим критериям, игнорируются контролем доступа.

4.7.30. The Record Атрибут

Атрибут Record — атрибут переменной длины в таблице attributes структуры ClassFile (§4.1). Атрибут Record указывает, что текущий класс является классом записи (JLS §8.10), и хранит информацию о компонентах записи класса записи (JLS §8.10.1).

В таблице attributes структуры ClassFile может быть, самое большее, один атрибут Record.

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

Record_attribute {
    u2                    attribute_name_index;
    u4                    attribute_length;
    u2                    components_count;
    record_component_info components[components_count];
}

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

attribute_name_index

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

attribute_length

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

components_count

Значение элемента components_count указывает количество элементов в таблице components.

components[]

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

record_component_info {
    u2             name_index;
    u2             descriptor_index;
    u2             attributes_count;
    attribute_info attributes[attributes_count];
}
      

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

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 структуры record_component_info, перечислены в Таблице 4.7-C.

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

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

4.7.31. Атрибут PermittedSubclasses

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

Язык программирования Java использует модификатор sealed для указания класса или интерфейса, который ограничивает свои непосредственные подклассы или непосредственные подинтерфейсы. Можно предположить, что этот модификатор будет соответствовать флагу ACC_SEALED в файле class, так как связанный модификатор final соответствует флагу ACC_FINAL. Фактически, класс или интерфейс sealed указывается в файле class наличием атрибута PermittedSubclasses.

В таблице attributes структуры ClassFile может быть не более одного атрибута PermittedSubclasses, чьё поле access_flags не имеет установленного флага ACC_FINAL.

В таблице attributes структуры ClassFile не должно быть атрибута PermittedSubclasses, чьё поле access_flags имеет установленный флаг ACC_FINAL.

sealed отличается от final: у класса sealed есть список разрешённых подклассов, а у класса final нет подклассов. Таким образом, структура ClassFile может иметь атрибут PermittedSubclasses или иметь установленный флаг ACC_FINAL, но не оба одновременно.

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

PermittedSubclasses_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 number_of_classes;
    u2 classes[number_of_classes];
}

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

attribute_name_index

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

attribute_length

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

number_of_classes

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

classes[]

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

Массив classes используется при создании класса или интерфейса, который пытается непосредственно расширить или реализовать текущий класс или интерфейс (§5.3.5). Элементы массива, представляющие классы или интерфейсы, которые не пытаются непосредственно расширить или реализовать текущий класс или интерфейс, игнорируются.

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

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

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

  • Все предопределённые атрибуты (§4.7) должны иметь правильную длину, за исключением StackMapTable, RuntimeVisibleAnnotations, RuntimeInvisibleAnnotations, RuntimeVisibleParameterAnnotations, RuntimeInvisibleParameterAnnotations, RuntimeVisibleTypeAnnotations, RuntimeInvisibleTypeAnnotations и AnnotationDefault.

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

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

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

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

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

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

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

Код метода, метода инициализации экземпляра (§2.9.1) или метода инициализации класса или интерфейса (§2.9.2) хранится в массиве 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 или ret, не должны встречаться в массиве code.

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

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

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

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

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

  • Цель каждой инструкции jump и branch (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) должна быть операционным кодом инструкции в пределах этого метода.

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

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

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

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

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

    Каждая инструкция lookupswitch должна иметь количество пар match-offset, которое соответствует значению ее операнда npairs. Пары match-offset должны быть отсортированы в порядке возрастания числового значения по подписанному значению сопоставления.

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

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

    • Запись типа CONSTANT_Long или CONSTANT_Double.

    • Запись типа CONSTANT_Dynamic, которая ссылается на структуру CONSTANT_NameAndType_info, которая указывает дескриптор J (обозначающий long) или D (обозначающий double).

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

    • Запись типа CONSTANT_Long или CONSTANT_Double.

    • Запись типа CONSTANT_Dynamic, которая ссылается на структуру CONSTANT_NameAndType_info, которая указывает дескриптор J (обозначающий long) или D (обозначающий 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.1).

    Никакие другие методы, имя которых начинается с символа '<' ('\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.1)

    • метод в текущем классе или интерфейсе

    • метод в суперклассе текущего класса

    • метод в непосредственном суперинтерфейсе текущего класса или интерфейса

    • метод в Object

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

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

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

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

    Общее правило для invokespecial заключается в том, что класс или интерфейс, указанный инструкцией invokespecial, должен быть "выше" вызывающего класса или интерфейса, а объект-получатель, на который направлена invokespecial, должен быть "на" или "ниже" вызывающего класса или интерфейса. Последнее условие особенно важно: класс или интерфейс может вызывать invokespecial только на своих собственных объектах. Смотрите §invokespecial для объяснения того, как последнее условие реализовано в Prolog.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    • Если тип описания — тип reference, то значение должно быть типа, совместимого с типом описания.

  • Тип каждого значения, хранящегося в массиве инструкцией 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 17.

Из-за этих потенциальных проблем виртуальная машина 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. Проверка с помощью проверки типов

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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.

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

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

  • Типы массивов (описатели полей, начинающиеся с [) соответствуют типам проверки, использующим функтор arrayOf. Обратите внимание, что примитивные типы byte, char, short и boolean не соответствуют типам проверки, но тип массива, элементарный тип которого — byte, char, short или boolean соответствует типу проверки; такие типы проверки поддерживают инструкции baload, bastore, caload, castore, saload, sastore и newarray.

    • Тип проверки arrayOf(T) представляет тип массива, элементный тип которого — тип проверки T.

    • Тип проверки arrayOf(byte) представляет тип массива, элемент которого — byte.

    • Тип проверки arrayOf(char) представляет тип массива, элемент которого — char.

    • Тип проверки arrayOf(short) представляет тип массива, элемент которого — short.

    • Тип проверки arrayOf(boolean) представляет тип массива, элемент которого — boolean.

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

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

  • Типы проверки top, oneWord, twoWord и reference представлены в Prolog в виде атомов, имя которых обозначает тип проверки.

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

Правила подтипов для типов проверки следующие.

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

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_pool, представляющей поля, методы и динамически вычисляемые места вызова. Такие записи представлены как применения функторов вида:

  • field(FieldClassName, FieldName, FieldDescriptor) для записи константного пула, являющейся структурой CONSTANT_Fieldref_info (§4.4.2).

    FieldClassName — имя класса, на который ссылается элемент class_index в структуре. FieldName и FieldDescriptor соответствуют имени и описателю поля, на которые ссылается элемент name_and_type_index структуры.

  • method(MethodClassName, MethodName, MethodDescriptor) для записи константного пула, являющейся структурой CONSTANT_Methodref_info (§4.4.2).

    MethodClassName — имя класса, на который ссылается элемент class_index структуры. MethodName и MethodDescriptor соответствуют имени и описателю метода, на которые ссылается элемент name_and_type_index структуры.

  • imethod(MethodIntfName, MethodName, MethodDescriptor) для записи константного пула, являющейся структурой CONSTANT_InterfaceMethodref_info (§4.4.2).

    MethodIntfName — имя интерфейса, на который ссылается элемент class_index структуры. MethodName и MethodDescriptor соответствуют имени и описателю метода, на которые ссылается элемент name_and_type_index структуры.

  • dmethod(CallSiteName, MethodDescriptor) для записи константного пула, являющейся структурой CONSTANT_InvokeDynamic_info (§4.4.10).

    CallSiteName и MethodDescriptor соответствуют имени и описателю метода, на которые ссылается элемент name_and_type_index структуры. (Элемент bootstrap_method_attr_index не имеет значения для проверки.)

Для ясности, мы предполагаем, что описатели полей и методов (§4.3.2, §4.3.3) отображаются в более удобочитаемые имена: начальные L и конечные ; символы опускаются из имён классов, а символы BaseType для примитивных типов отображаются на имена этих типов.

Например, инструкция getfield, операнд которой ссылается на запись константного пула, представляющую поле foo типа F в классе Bar, будет представлена как getfield(field('Bar', 'foo', 'F')).

Инструкция ldc, среди других, имеет операнд, который ссылается на загружаемую запись в таблице constant_pool. Существует девять типов загружаемых записей (см. Таблица 4.4-C), представленных применениями функторов следующих форм:

  • int(Value) для записи константного пула, являющейся структурой CONSTANT_Integer_info (§4.4.4).

    Value — константа int, представленная элементом bytes структуры.

    Например, инструкция ldc для загрузки константы int 91 будет представлена как ldc(int(91)).

  • float(Value) для записи константного пула, являющейся структурой CONSTANT_Float_info (§4.4.4).

    Value — константа float, представленная элементом bytes структуры.

  • long(Value) для записи константного пула, являющейся структурой CONSTANT_Long_info (§4.4.5).

    Value — константа long, представленная элементами high_bytes и low_bytes структуры.

  • double(Value) для записи константного пула, являющейся структурой CONSTANT_Double_info (§4.4.5).

    Value — константа double, представленная элементами high_bytes и low_bytes структуры.

  • class(ClassName) для записи константного пула, являющейся структурой CONSTANT_Class_info (§4.4.1).

    ClassName — имя класса или интерфейса, на которое ссылается элемент name_index структуры.

  • string(Value) для записи константного пула, являющейся структурой CONSTANT_String_info (§4.4.3).

    Value — строка, на которую ссылается элемент string_index структуры.

  • methodHandle(Kind, Reference) для записи константного пула, являющейся структурой CONSTANT_MethodHandle_info (§4.4.8).

    Kind — значение элемента reference_kind структуры. Reference — значение элемента reference_index структуры.

  • methodType(MethodDescriptor) для записи константного пула, являющейся структурой CONSTANT_MethodType_info (§4.4.9).

    MethodDescriptor — описатель метода, на который ссылается элемент descriptor_index структуры.

  • dconstant(ConstantName, FieldDescriptor) для записи константного пула, являющейся структурой CONSTANT_Dynamic_info (§4.4.10).

    ConstantName и FieldDescriptor соответствуют имени и описателю поля, на которые ссылается элемент name_and_type_index структуры. (Элемент bootstrap_method_attr_index не имеет значения для проверки.)

4.10.1.4. Кадры карты стека и переходы типов

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

stackMap(Offset, TypeState)

где:

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

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

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

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

frame(Locals, OperandStack, Flags)

где:

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

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

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

Большинство правил типов для отдельных инструкций (§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).

Извлечь список типов из стека.

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

Длина стека операндов не должна превышать объявленного максимального размера.

operandStackHasLegalLength(Environment, OperandStack) :-
    length(OperandStack, Length),
    maxOperandStackLength(Environment, MaxStack),
    Length =< MaxStack.

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

Инструкции dup помещают список типов на стек в основном таким же способом, как при помещении типа для допустимого перехода типа.

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

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

canPop(frame(Locals, OperandStack, Flags), Types,
       frame(Locals, PoppedOperandStack, Flags)) :-
    popMatchingList(OperandStack, Types, PoppedOperandStack).

Наконец, определённые инструкции для массивов (§aaload, §arraylength, §baload, §bastore) проверяют типы в стеке операндов, чтобы убедиться, что они являются массивами. Следующее правило получает i-й элемент стека операндов из состояния типа.

nth1OperandStackIs(i, frame(_Locals, OperandStack, _Flags), Element) :-
    nth1(i, OperandStack, Element).

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; в других методах экземпляра, тип 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. Проверка типов инструкций загрузки и сохранения

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

Загрузка значения типа 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, [Type2, Type1],
                      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, объявленное в классе FieldClassName, и можно корректно заменить тип, соответствующий FieldClassName, на тип FieldType на стеке входных операндов, что даёт состояние типа выхода. FieldClassName не должен быть типом массива. protected поля подвержены дополнительным проверкам (§4.10.1.8).

instructionIsTypeSafe(getfield(CP), Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    CP = field(FieldClassName, FieldName, FieldDescriptor),
    parseFieldDescriptor(FieldDescriptor, FieldType),
    passesProtectedCheck(Environment, FieldClassName, FieldName,
                         FieldDescriptor, StackFrame),
    currentClassLoader(Environment, CurrentLoader),
    validTypeTransition(Environment,
                        [class(FieldClassName, CurrentLoader)], FieldType,
                        StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
getstatic

Инструкция getstatic с операндом CP является безошибочной с точки зрения типов тогда и только тогда, когда CP ссылается на запись константного пула, обозначающую поле, объявленный тип которого FieldType, и можно корректно поместить FieldType на стек входных операндов, что даёт состояние типа выхода.

instructionIsTypeSafe(getstatic(CP), Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    CP = field(_FieldClassName, _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).
iconst_<i>

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

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

Правила для других вариантов инструкции iconst эквивалентны.

instructionHasEquivalentTypeRule(iconst_0, iconst_m1).
instructionHasEquivalentTypeRule(iconst_1, iconst_m1).
instructionHasEquivalentTypeRule(iconst_2, iconst_m1).
instructionHasEquivalentTypeRule(iconst_3, iconst_m1).
instructionHasEquivalentTypeRule(iconst_4, iconst_m1).
instructionHasEquivalentTypeRule(iconst_5, iconst_m1).
idiv

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

instructionHasEquivalentTypeRule(idiv, iadd).
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, ifnull

Инструкция 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 является безопасной по типам тогда и только тогда, когда эквивалентная инструкция 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)), 
    isAssignable(class(CurrentClassName, CurrentLoader),
                 class(MethodClassName,  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),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).

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

Первый validTypeTransition операнд накладывает структурное ограничение, что invokespecial, кроме метода инициализации экземпляра, нацелен на объект получателя текущего класса или более глубокого уровня. Чтобы понять почему, рассмотрим, что StackArgList моделирует список типов на стеке операндов, ожидаемых методом, начиная с текущего класса (invokespecial выполняется). Фактические типы на стеке операндов находятся в StackFrame. Эффект от validTypeTransition — извлечение первого типа из стека операндов в StackFrame и проверка, является ли он подтипом первой составляющей StackArgList, а именно, текущего класса. Таким образом, фактический тип получателя совместим с текущим классом.

Внимательный читатель заметит, что выполнение этого структурного ограничения опережает структурное ограничение, касающееся invokespecial метода protected. Таким образом, код Prolog выше не ссылается на passesProtectedCheck (§4.10.1.8), в то время как код Prolog для invokespecial метода инициализации экземпляра использует passesProtectedCheck для обеспечения совместимости фактического типа получателя с текущим классом, когда указаны определённые protected методы инициализации экземпляра.

Второй validTypeTransition операнд накладывает структурное ограничение, что любая инструкция вызова метода должна нацеливаться на объект получателя, тип которого совместим с типом, указанным в инструкции. Чтобы понять почему, рассмотрим, что StackArgList2 моделирует список типов на стеке операндов, ожидаемых методом, начиная с типа, указанного в инструкции. Опять же, фактические типы на стеке операндов находятся в StackFrame, а эффект от validTypeTransition — проверка, совместим ли фактический тип получателя в StackFrame с типом, указанным в инструкции в StackArgList2.

  • Или:

    • ИмяМетода является <init>.

    • Descriptor указывает тип возвращаемого значения void.

    • Можно корректно извлечь типы, соответствующие типам аргументов, указанным в Descriptor, и неинициализированному типу UninitializedArg, со стека входных операндов, что приводит к результату OperandStack.

    • Состояние выходного типа получается из состояния входного типа путём сначала замены стека входных операндов на OperandStack, а затем заменой всех экземпляров UninitializedArg на тип инициализируемого экземпляра.

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


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, [uninitializedThis | OperandStack], Flags),
    currentClassLoader(Environment, CurrentLoader),
    rewrittenUninitializedType(uninitializedThis, Environment,
                               class(MethodClassName, CurrentLoader), This), 
    rewrittenInitializationFlags(uninitializedThis, Flags, NextFlags),
    substitute(uninitializedThis, This, OperandStack, NextOperandStack),
    substitute(uninitializedThis, This, Locals, NextLocals),
    NextStackFrame = frame(NextLocals, NextOperandStack, NextFlags),
    ExceptionStackFrame = frame(Locals, [], Flags).
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, [uninitialized(Address) | OperandStack], Flags),
    currentClassLoader(Environment, CurrentLoader),
    rewrittenUninitializedType(uninitialized(Address), Environment,
                               class(MethodClassName, CurrentLoader), This), 
    rewrittenInitializationFlags(uninitialized(Address), Flags, NextFlags),
    substitute(uninitialized(Address), This, OperandStack, NextOperandStack),
    substitute(uninitialized(Address), 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, irem

Инструкция ior является безопасной с точки зрения типов, если эквивалентная инструкция iadd является безопасной.

instructionHasEquivalentTypeRule(ior, iadd).

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

Инструкция isub является безопасной с точки зрения типов, если эквивалентная инструкция iadd является безопасной.

instructionHasEquivalentTypeRule(isub, iadd).

Инструкция 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 загружаема (§4.4), но не является long или double, и можно корректно поместить Type на стек входных операндов, получая выходное состояние типов.

instructionIsTypeSafe(ldc(CP), Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    loadableConstant(CP, Type),
    Type \= long,
    Type \= double,
    validTypeTransition(Environment, [], Type, StackFrame, NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).

loadableConstant(CP, Type) :-
    member([CP, Type], [
        [int(_),    int],
        [float(_),  float],
        [long(_),   long],
        [double(_), double]
    ]).

loadableConstant(CP, Type) :-
    isBootstrapLoader(BL),
    member([CP, Type], [
        [class(_),          class('java/lang/Class', BL)],
        [string(_),         class('java/lang/String', BL)],
        [methodHandle(_,_), class('java/lang/invoke/MethodHandle', BL)],
        [methodType(_,_),   class('java/lang/invoke/MethodType', BL)]
    ]).

loadableConstant(CP, Type) :-
    CP = dconstant(_, FieldDescriptor),
    parseFieldDescriptor(FieldDescriptor, Type).

Инструкция ldc_w является безопасной с точки зрения типов, если эквивалентная инструкция ldc является безопасной.

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

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

instructionIsTypeSafe(ldc2_w(CP), Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    loadableConstant(CP, Type),
    (Type = long ; Type = double),
    validTypeTransition(Environment, [], Type, 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, lrem

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

instructionHasEquivalentTypeRule(lor, ladd).

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

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

instructionHasEquivalentTypeRule(lsub, ladd).

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

instructionHasEquivalentTypeRule(lxor, ladd).
monitorenter, monitorexit

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

instructionIsTypeSafe(monitorenter, _Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :-
    canPop(StackFrame, [reference], NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).

Инструкция 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),
    popCategory1([Type | Rest], Type, Rest),
    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) :-
    popCategory1([Type1 | Rest], Type1, Rest),
    popCategory1([Type2 | Rest], Type2, Rest).

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

pop2Form2IsTypeSafe([top, Type | Rest], Rest) :-
    popCategory2([top, Type | Rest], Type, Rest).
putfield

Инструкция putfield с операндом CP является безопасной по типу, если выполняются все следующие условия:

  • Её первый операнд, CP, ссылается на запись константного пула, обозначающую поле, тип которого объявлен как FieldType, объявленное в классе FieldClassName. FieldClassName не должен быть типом массива.

  • Либо:

    • Можно корректно извлечь типы, соответствующие FieldType и FieldClassName, из входного стека операндов, получив выходное состояние типов.

    • Для полей protected выполняются дополнительные проверки (§4.10.1.8).

instructionIsTypeSafe(putfield(CP), Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    CP = field(FieldClassName, FieldName, FieldDescriptor),
    parseFieldDescriptor(FieldDescriptor, FieldType),	
    canPop(StackFrame, [FieldType], PoppedFrame),
    passesProtectedCheck(Environment, FieldClassName, FieldName,
                         FieldDescriptor, PoppedFrame),
    currentClassLoader(Environment, CurrentLoader),
    canPop(StackFrame, [FieldType, class(FieldClassName, CurrentLoader)],
           NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
  • Или:

    • Если инструкция находится в методе инициализации экземпляра класса FieldClassName, то можно корректно извлечь типы, соответствующие FieldType и uninitializedThis, из входного стека операндов, получив выходное состояние типов. Это позволяет присваивать поля экземпляра this, объявленные в текущем классе, до полной инициализации this.

instructionIsTypeSafe(putfield(CP), Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :-
    CP = field(FieldClassName, _FieldName, FieldDescriptor),
    parseFieldDescriptor(FieldDescriptor, FieldType),
    Environment = environment(CurrentClass, CurrentMethod, _, _, _, _),
    CurrentClass = class(FieldClassName, _),
    isInit(CurrentMethod),
    canPop(StackFrame, [FieldType, uninitializedThis], NextStackFrame),
    exceptionStackFrame(StackFrame, ExceptionStackFrame).
putstatic

Инструкция putstatic с операндом CP является безопасной по типу, если CP ссылается на запись константного пула, обозначающую поле, тип которого объявлен как FieldType, и можно корректно извлечь тип, соответствующий FieldType, из входного стека операндов, получив выходное состояние типов.

instructionIsTypeSafe(putstatic(CP), _Environment, _Offset, StackFrame,
                      NextStackFrame, ExceptionStackFrame) :- 
    CP = field(_FieldClassName, _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),
    popCategory1([Type1 | Rest], Type1, Rest),
    popCategory1([Type2 | Rest], Type2, Rest),
    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, являются состоянием стека операндов и массива локальных переменных перед выполнением последовательной инструкции. Установите бит «изменён» для последовательной инструкции.

    • Если последовательная инструкция встречалась ранее, объедините значения стека операндов и локальных переменных, рассчитанные в шаге 2, с уже имеющимися значениями. Установите бит «изменён», если есть изменения в значениях.

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

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

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

  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 приведены в §3 (Компиляция для виртуальной машины Java).)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Сам код блока finally может пройти проверку, но после завершения обновления всех преемников инструкции ret, верификатор отметит, что локальная переменная, которую обработчик исключений ожидает содержать исключение, или что код возврата ожидает содержать возвращаемое значение, теперь содержит неопределённое значение.

Верификация кода, содержащего блок finally, сложная. Основная идея заключается в следующем:

  • Каждая инструкция отслеживает список целей jsr, необходимых для достижения этой инструкции. Для большинства кода этот список пуст. Для инструкций внутри кода блока finally он имеет длину один. Для вложенного кода finally (крайне редкий случай!) он может быть больше единицы.

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

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

  • Для выполнения анализа потока данных на инструкции ret используется специальная процедура. Поскольку верификатор знает подпрограмму, из которой инструкция должна возвращаться, он может найти все инструкции jsr, которые вызывают подпрограмму, и слить состояние стека операндов и массива локальных переменных в момент инструкции ret в стек операндов и массив локальных переменных инструкций, следующих за jsr. Слияние использует специальный набор значений для локальных переменных:

    • Для любой локальной переменной, которую битовый вектор (созданный выше) указывает, что она использовалась или изменялась подпрограммой, используйте тип локальной переменной в момент инструкции ret.

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

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

Следующие ограничения виртуальной машины Java являются неявными в формате файла class:

  • Пул констант для каждого класса или интерфейса ограничен 65535 записями полем 16-битного constant_pool_count структуры ClassFile (§4.1). Это внутреннее ограничение общей сложности одного класса или интерфейса.

  • Количество полей, которые могут быть объявлены классом или интерфейсом, ограничено 65535 размерами поля fields_count структуры ClassFile (§4.1).

    Обратите внимание, что значение поля fields_count структуры ClassFile не включает поля, унаследованные от суперклассов или суперинтерфейсов.

  • Количество методов, которые могут быть объявлены классом или интерфейсом, ограничено 65535 размерами поля methods_count структуры ClassFile (§4.1).

    Обратите внимание, что значение поля methods_count структуры ClassFile не включает методы, унаследованные от суперклассов или суперинтерфейсов.

  • Количество непосредственных суперинтерфейсов класса или интерфейса ограничено 65535 размерами поля interfaces_count структуры ClassFile (§4.1).

  • Максимальное количество локальных переменных в массиве локальных переменных кадра, созданного при вызове метода (§2.6), ограничено 65535 размерами поля max_locals атрибута Code (§4.7.3), содержащего код метода, и 16-битным индексированием локальных переменных набора инструкций виртуальной машины Java.

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

  • Размер стека операндов в кадре (§2.6) ограничен 65535 значениями полем max_stack атрибута Code (§4.7.3).

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

  • Количество параметров метода ограничено 255 по определению дескриптора метода (§4.3.3), где ограничение включает одну единицу для this в случае вызовов методов экземпляров или интерфейсов.

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

  • Длина имен полей и методов, дескрипторов полей и методов и других значений константных строк (включая те, на которые ссылаются атрибуты ConstantValue (§4.7.2)) ограничена 65535 символами 16-битным беззнаковым полем length структуры CONSTANT_Utf8_info (§4.4.7).

    Обратите внимание, что ограничение относится к количеству байтов в кодировке, а не к количеству закодированных символов. UTF-8 кодирует некоторые символы с использованием двух или трех байтов. Таким образом, строки, включающие многобайтовые символы, дополнительно ограничены.

  • Число измерений в массиве ограничено 255 размером операда dimensions инструкции multianewarray и ограничениями, наложенными на инструкции multianewarray, anewarray и newarray (§4.9.1, §4.9.2).

© Oracle and/or its affiliates. All rights reserved.
Licensed under the Oracle Technology Network License Agreement.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API