Spec-Zone.ru › Java Virtual Machine Specification 11

Глава 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.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-символу.

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 представляют собой номера версии minor и major данного файла class. Вместе номера major и minor версии определяют версию формата файла class. Если файл class имеет номер версии major M и номер версии minor m, то версию формата файла class обозначают как M.m. Таким образом, версии формата файлов class можно упорядочить лексикографически, например, 1.5 < 2.0 < 2.1.

Реализация виртуальной машины Java может поддерживать формат файла class версии v только тогда, когда v находится в некотором непрерывном диапазоне Mi.0 ≤ v ≤ Mj.m. Этот диапазон зависит от версии Java SE Platform, которой соответствует реализация. Реализация, соответствующая данной версии Java SE Platform, должна поддерживать диапазон, указанный в Таблице 4.1-A для этой версии, и никакие другие диапазоны. (В исторических случаях вместо версии Java SE Platform показана версия JDK.)

Таблица 4.1-A. Диапазоны версий формата файла class (по Java SE Platform)

Java SE Диапазон версий формата файла class
1.0.2 45.0 ≤ v ≤ 45.3
1.1 45.0 ≤ v ≤ 45.65535
1.2 45.0 ≤ v ≤ 46.0
1.3 45.0 ≤ v ≤ 47.0
1.4 45.0 ≤ v ≤ 48.0
5.0 45.0 ≤ v ≤ 49.0
6 45.0 ≤ v ≤ 50.0
7 45.0 ≤ v ≤ 51.0
8 45.0 ≤ v ≤ 52.0
9 45.0 ≤ v ≤ 53.0
10 45.0 ≤ v ≤ 54.0
11 45.0 ≤ v ≤ 55.0

constant_pool_count

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

constant_pool[]

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

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

access_flags

Значение элемента access_flags представляет собой маску флагов, используемых для обозначения прав доступа и свойств этого класса или интерфейса. Интерпретация каждого флага, когда он установлен, указана в Таблице 4.1-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 должны устанавливать флаг ACC_SUPER. В Java SE 8 и выше виртуальная машина Java считает флаг ACC_SUPER установленным в каждом файле class, независимо от фактического значения флага в файле class и версии файла class.

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

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

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

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

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

this_class

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

super_class

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

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

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

interfaces_count

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

interfaces[]

Каждое значение в массиве interfaces должно быть допустимым индексом в таблице 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:

  • major_version, minor_version: ≥ 53.0 (т.е. Java SE 9 и выше)

  • this_class: 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 обратный слеш (\) зарезервирован для использования в качестве управляющего символа в именах модулей. Он не должен появляться в имени модуля, если за ним не следуют ASCII обратный слеш, ASCII двоеточие (:) или ASCII символ @ (@). Последовательность ASCII символов \\ может использоваться для кодирования обратного слеша в имени модуля.

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

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

4.3. Дескрипторы

Дескриптор — это строка, представляющая тип поля или метода. Дескрипторы представлены в формате файла 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 ИмяКласса ;
ArrayType:
[ ComponentType
ComponentType:
FieldType

Символы BaseType, L и ; ObjectType, и [ ArrayType являются символами ASCII.

ИмяКласса представляет имя двоичного класса или интерфейса в внутреннем представлении (§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 ИмяКласса ; reference экземпляр класса ИмяКласса
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, которая представила эту версию формата файла 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, которая представила эту версию формата файла 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_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), представляющей последовательность кодов Юникода, с которыми должен быть инициализирован объект String.

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

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

CONSTANT_Integer_info {
    u1 tag;
    u4 bytes;
}

CONSTANT_Float_info {
    u1 tag;
    u4 bytes;
}

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

tag

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

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

bytes

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

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

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

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

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

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

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

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

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

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

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

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

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

Все константы длиной 8 байт занимают две записи в таблице constant_pool файла class. Если структура CONSTANT_Long_info или CONSTANT_Double_info находится в записи с индексом 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 (§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_Utf8_info (§4.4.7), представляющей либо валидное неоквалифицированное имя поля или метода (§4.2.2), либо специальное имя метода <init> (§2.9.1).

descriptor_index

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

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

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

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

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

tag

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

length

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

bytes[]

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

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

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

Содержание строки закодировано в модифицированной кодировке UTF-8. Строки в модифицированной UTF-8 закодированы таким образом, что последовательности кодовых точек, содержащие только не нулевые ASCII-символы, могут быть представлены с использованием только 1 байта на кодовую точку, но все кодовые точки в кодовой таблице Unicode могут быть представлены. Модифицированные 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, версия 10.0.0.

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

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

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

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

tag

Элемент tag имеет значение CONSTANT_MethodHandle (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 {
    u1 tag;
    u2 descriptor_index;
}

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

тег

Элемент имеет значение CONSTANT_MethodType (16).

descriptor_index

Значение элемента должно быть корректным индексом в таблице 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 структуры 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_NameAndType_info (§4.4.6). Эта запись указывает имя и дескриптор.

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

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

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

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

CONSTANT_Module_info {
    u1 tag;
    u2 name_index;
}

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

тег

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

name_index

Значение элемента должно быть корректным индексом в таблице 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 {
    u1 tag;
    u2 name_index;
}

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

тег

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

name_index

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

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

name_index

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

descriptor_index

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

attributes_count

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

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 Объявлен strictfp; режим с плавающей точкой — FP-строгий.
ACC_SYNTHETIC 0x1000 Объявлен синтетическим; отсутствует в исходном коде.

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

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

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

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

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

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

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

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

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

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

name_index

Значение элемента name_index должно быть допустимым индексом в таблице constant_pool. Элемент constant_pool в этом индексе должен быть структурой CONSTANT_Utf8_info (§4.4.7), представляющей либо допустимое неопределённое имя, обозначающее метод (§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 формата файла class (§4.1, §4.5, §4.6, §4.7.3).

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

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

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

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

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

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

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

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

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

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

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

    • ConstantValue

    • Code

    • StackMapTable

    • BootstrapMethods

    • NestHost

    • NestMembers

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

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

    • Exceptions

    • InnerClasses

    • EnclosingMethod

    • Synthetic

    • Signature

    • SourceFile

    • LineNumberTable

    • LocalVariableTable

    • LocalVariableTypeTable

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

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

    • 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

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

Таблица 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
ConstantValue field_info 45.3
Code method_info 45.3
Exceptions method_info 45.3
RuntimeVisibleParameterAnnotations, RuntimeInvisibleParameterAnnotations method_info 49.0
AnnotationDefault method_info 49.0
MethodParameters method_info 52.0
Synthetic ClassFile, field_info, method_info 45.3
Deprecated ClassFile, field_info, method_info 45.3
Signature ClassFile, field_info, method_info 49.0
RuntimeVisibleAnnotations, RuntimeInvisibleAnnotations ClassFile, field_info, method_info 49.0
LineNumberTable Code 45.3
LocalVariableTable Code 45.3
LocalVariableTypeTable Code 49.0
StackMapTable Code 50.0
RuntimeVisibleTypeAnnotations, RuntimeInvisibleTypeAnnotations ClassFile, field_info, method_info, Code 52.0

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

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

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

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

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

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

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

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

exception_table_length

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

exception_table[]

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

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

start_pc, end_pc

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

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

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

handler_pc

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

catch_type

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

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

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

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

attributes_count

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

attributes[]

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

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

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

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

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

4.7.4. Атрибут StackMapTable

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

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

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

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

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

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

attribute_name_index

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

attribute_length

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

number_of_entries

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

entries[]

Каждая запись в таблице stack map frames описывает одну кадр stack map метода. Порядок кадров stack map в таблице имеет значение.

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

Каждый кадр stack map, описанный в таблице, опирается на предыдущий кадр для части своей семантики. Первый кадр stack map метода неявный и вычисляется из описания метода с помощью проверяющего типа (§4.10.1.6). Структура stack map frames на index поэтому описывает второй кадр stack map метода.

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

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

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

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

union verification_type_info {
    Top_variable_info;
    Integer_variable_info;
    Float_variable_info;
    Long_variable_info;
    Double_variable_info;
    Null_variable_info;
    UninitializedThis_variable_info;
    Object_variable_info;
    Uninitialized_variable_info;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Кадр stack map представлен объединением, состоящим из одного байта — тега, указывающего на используемый элемент объединения, и нуля или более байтов, предоставляющих дополнительную информацию о теге.

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

Тег указывает тип кадра кадра stack map:

  • Тип кадра 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 для кадра приводится явно.

    chop_frame {
        u1 frame_type = CHOP; /* 248-250 */
        u2 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 — атрибут переменной длины в таблице атрибутов структуры ClassFile (§4.1).

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

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

attribute_length

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

number_of_classes

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

classes[]

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

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

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

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

inner_class_info_index

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

inner_class_access_flags

Значение поля inner_class_access_flags — маска флагов, используемых для обозначения разрешений доступа и свойств класса или интерфейса C, как объявленных в исходном коде, из которого был скомпилирован этот файл. Он используется компилятором для восстановления исходной информации, когда исходный код недоступен. Флаги указаны в Таблице 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, зарезервированы для будущего использования. Они должны быть установлены в ноль в сгенерированных файлах ClassFile и должны игнорироваться реализациями Java Virtual Machine.

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

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

4.7.7. Атрибут EnclosingMethod

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

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

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

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

Элементы структуры EnclosingMethod:

attribute_name_index

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

attribute_length

Значение поля attribute_length должно быть четырьмя.

class_index

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

method_index

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

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

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

Компилятор 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.values() и Enum.valueOf().

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

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

Synthetic_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
}

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

attribute_name_index

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

attribute_length

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

4.7.9. Атрибут Signature

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

В таблице attributes структуры ClassFile, field_info или method_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. Они поддерживают рефлексию и отладку, а также компиляцию, когда доступны только файлы class.

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

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

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

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

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

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

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

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

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

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

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

    JavaTypeSignature:
    ReferenceTypeSignature
    BaseType

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

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

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

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

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

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

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

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

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

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

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

VoidDescriptor:
V

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

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

FieldSignature:
ReferenceTypeSignature

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

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

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

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

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

attribute_name_index

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

attribute_length

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

line_number_table_length

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

line_number_table[]

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

start_pc

Значение элемента start_pc должно быть допустимым индексом в массиве code этого атрибута 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. Атрибут устаревания

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

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

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

Deprecated_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
}

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

attribute_name_index

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

attribute_length

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

4.7.16. Атрибут видимых в исполняемой среде аннотаций

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

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

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

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

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

attribute_name_index

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

attribute_length

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

num_annotations

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

annotations[]

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

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

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

type_index

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

num_element_value_pairs

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

element_value_pairs[]

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

element_name_index

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

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

value

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

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 String const_value_index CONSTANT_Utf8
e Тип перечисления enum_const_value Не применимо
c Class class_info_index Не применимо
@ Тип аннотации annotation_value Не применимо
[ Тип массива array_value Не применимо

Элемент value представляет значение пары «элемент-значение». Элемент является объединением, чьи элементы представлены ниже:

const_value_index

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

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

enum_const_value

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

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

type_name_index

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

const_name_index

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

class_info_index

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

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

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

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

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

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

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

annotation_value

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

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

array_value

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

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

num_values

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

values[]

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

4.7.17. Атрибут RuntimeInvisibleAnnotations

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

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

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

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

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

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

attribute_name_index

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

attribute_length

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

num_annotations

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

annotations[]

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

4.7.18. Атрибут RuntimeVisibleParameterAnnotations

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

В таблице 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, или атрибута Code (§4.1, §4.5, §4.6, §4.7.3). Атрибут RuntimeVisibleTypeAnnotations записывает видимые во время выполнения аннотации типов, используемых в объявлении соответствующего класса, поля или метода, или в выражении в теле соответствующего метода. Атрибут RuntimeVisibleTypeAnnotations также записывает видимые во время выполнения аннотации объявлений параметра типа для обобщённых классов, интерфейсов, методов и конструкторов.

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

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

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

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

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

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

attribute_name_index

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

attribute_length

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

num_annotations

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

annotations[]

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

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

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

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

target_type

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

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

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

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

target_info

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

Элементы союза target_info описаны в §4.7.20.1.

target_path

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

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

type_index, num_element_value_pairs, element_value_pairs[]

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

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

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

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

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

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

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

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

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

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

    type_parameter_target {
        u1 type_parameter_index;
    }
        

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

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

    supertype_target {
        u2 supertype_index;
    }
        

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

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

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

    type_parameter_bound_target {
        u1 type_parameter_index;
        u1 bound_index;
    }
        

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

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

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

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

    empty_target {
    }
        

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

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

    formal_parameter_target {
        u1 formal_parameter_index;
    }
        

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    type_path_kind

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

    Таблица 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 для @C Outer . @B Middle . @A Inner

Аннотация path_length path
@A 2 [{type_path_kind: 1; type_argument_index: 0}, {type_path_kind: 1; type_argument_index: 0}]
@B 1 [{type_path_kind: 1; type_argument_index: 0}]
@C 0 []

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

Аннотация path_length path
@A 3 [{type_path_kind: 1; type_argument_index: 0}, {type_path_kind: 1; type_argument_index: 0}, {type_path_kind: 3; type_argument_index: 0}]
@B 4 [{type_path_kind: 1; type_argument_index: 0}, {type_path_kind: 1; type_argument_index: 0}, {type_path_kind: 3; type_argument_index: 0}, {type_path_kind: 0; type_argument_index: 0}]
@C 3 [{type_path_kind: 1; type_argument_index: 0}, {type_path_kind: 3; type_argument_index: 0}, {type_path_kind: 1; type_argument_index: 0}]
@D 2 [{type_path_kind: 1; type_argument_index: 0}, {type_path_kind: 3; type_argument_index: 0}]

4.7.21. Атрибут RuntimeInvisibleTypeAnnotations

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

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

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

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

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

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

attribute_name_index

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

attribute_length

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

num_annotations

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

annotations[]

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

4.7.22. Атрибут AnnotationDefault

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

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

В таблице attributes структуры ClassFile должен быть ровно один атрибут BootstrapMethods, если таблица constant_pool структуры 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 Virtual Machine должна применять при проверке формата (§4.8). Задача сопоставления описателей параметров в описателе метода с элементами массива parameters выполняется библиотеками рефлексии Java SE Platform.

parameters[]

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

name_index

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

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

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

access_flags

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

0x0010 (ACC_FINAL)

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

0x1000 (ACC_SYNTHETIC)

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

0x8000 (ACC_MANDATED)

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

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

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

4.7.25. Атрибут модуля

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

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

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

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

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

attribute_name_index

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

attribute_length

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

module_name_index

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

module_flags

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

0x0020 (ACC_OPEN)

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

0x1000 (ACC_SYNTHETIC)

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

0x8000 (ACC_MANDATED)

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

module_version_index

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

requires_count

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

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

Если текущий модуль не является , то должно быть не менее единицы.

requires[]

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

requires_index

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

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

requires_flags

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

0x0020 (ACC_TRANSITIVE)

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

0x0040 (ACC_STATIC_PHASE)

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

0x1000 (ACC_SYNTHETIC)

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

0x8000 (ACC_MANDATED)

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

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

requires_version_index

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

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

exports_count

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

exports[]

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

Элементы каждой записи следующие:

exports_index

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

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

exports_flags

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

0x1000 (ACC_SYNTHETIC)

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

0x8000 (ACC_MANDATED)

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

exports_to_count

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

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

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

exports_to_index[]

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

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

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

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

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

Таблица ClassFile структуры 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.8. Проверка формата

При загрузке потенциального файла class Java Virtual Machine (§5.3) сначала проверяет, что файл имеет базовый формат файла 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 не могут появляться в массиве code.

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

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

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

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

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

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

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

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

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

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

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

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

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

  • Операнды каждой инструкции ldc и каждой инструкции ldc_w должны представлять допустимый индекс в таблицу constant_pool. Запись пула констант, на которую ссылается этот индекс, должна быть загружаемой (§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 11 издание.

Из-за этих потенциальных проблем виртуальная машина 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 Virtual Machine может попытаться выполнить проверку с помощью вывода типов (§4.10.2).

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

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

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

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

Это означает, что реализация Java Virtual Machine не может выбрать прибегнуть к выводу типов в одном случае и не делать этого в другом. Она должна либо отклонять файлы 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 Virtual Machine, таких как классы и методы (§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 предикат Пролога ("доступы к данным"), которые обладают определённым ожидаемым поведением, но чьи формальные определения не приводятся в данном спецификации.

classClassName(Class, ClassName)

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

classIsInterface(Class)

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

classIsNotFinal(Class)

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

classSuperClassName(Class, SuperClassName)

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

classInterfaces(Class, Interfaces)

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

classMethods(Class, Methods)

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

classAttributes(Class, Attributes)

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

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

classDefiningLoader(Class, Loader)

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

isBootstrapLoader(Loader)

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

loadedClass(Name, InitiatingLoader, ClassDefinition)

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

methodName(Method, Name)

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

methodAccessFlags(Method, AccessFlags)

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

methodDescriptor(Method, Descriptor)

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

methodAttributes(Method, Attributes)

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

isInit(Method)

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

isNotInit(Method)

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

isNotFinal(Method, Class)

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

isStatic(Method, Class)

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

isNotStatic(Method, Class)

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

isPrivate(Method, Class)

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

isNotPrivate(Method, Class)

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

isProtected(MemberClass, MemberName, MemberDescriptor)

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

isNotProtected(MemberClass, MemberName, MemberDescriptor)

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

parseFieldDescriptor(Descriptor, Type)

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

parseMethodDescriptor(Descriptor, ArgTypeList, ReturnType)

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

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

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

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

samePackageName(Class1, Class2)

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

differentPackageName(Class1, Class2)

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

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

  • класса

  • метода

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  • ... (Остальные пункты переведены аналогично)

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

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

  • Описание не упоминает явно среду.

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

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

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

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

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

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 или подтип (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. Следовательно, код Пролога выше не ссылается на passesProtectedCheck (§4.10.1.8), в то время как код Пролога для 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, если она является инструкцией pop2 безопасной по типу формы 1 или инструкцией pop2 безопасной по типу формы 2.

pop2SomeFormIsTypeSafe(InputOperandStack, OutputOperandStack) :-
    pop2Form1IsTypeSafe(InputOperandStack, OutputOperandStack).

pop2SomeFormIsTypeSafe(InputOperandStack, OutputOperandStack) :-
    pop2Form2IsTypeSafe(InputOperandStack, OutputOperandStack).

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

pop2Form1IsTypeSafe([Type1, Type2 | Rest], Rest) :-
    popCategory1([Type1 | Rest], Type1, Rest),
    popCategory1([Type2 | Rest], Type2, Rest).

Инструкция pop2 является инструкцией pop2 безопасной по типу формы 2, если можно корректно извлечь тип размера 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). Проверка завершается неудачно, если есть возможность "выпасть" из последней инструкции метода.

    • Цель(и) условного или безусловного перехода или switch.

    • Любые обработчики исключений для этой инструкции.

  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 Virtual Machine приведены в §3 (Компиляция для Java Virtual Machine).)

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

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

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

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

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