Глава 4. Формат файла class
Содержание
- 4.1. Структура
ClassFile - 4.2. Имена
- 4.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.4.1. Структура
- 4.5. Поля
- 4.6. Методы
- 4.7. Атрибуты
-
- 4.7.1. Определение и именование новых атрибутов
- 4.7.2. Атрибут
ConstantValue - 4.7.3. Атрибут
Code - 4.7.4. Атрибут
StackMapTable - 4.7.5. Атрибут
Exceptions - 4.7.6. Атрибут
InnerClasses - 4.7.7. Атрибут
EnclosingMethod - 4.7.8. Атрибут
Synthetic - 4.7.9. Атрибут
Signature - 4.7.10. Атрибут
SourceFile - 4.7.11. Атрибут
SourceDebugExtension - 4.7.12. Атрибут
LineNumberTable - 4.7.13. Атрибут
LocalVariableTable - 4.7.14. Атрибут
LocalVariableTypeTable - 4.7.15. Атрибут
Deprecated - 4.7.16. Атрибут
RuntimeVisibleAnnotations - 4.7.17. Атрибут
RuntimeInvisibleAnnotations - 4.7.18. Атрибут
RuntimeVisibleParameterAnnotations - 4.7.19. Атрибут
RuntimeInvisibleParameterAnnotations - 4.7.20. Атрибут
RuntimeVisibleTypeAnnotations - 4.7.21. Атрибут
RuntimeInvisibleTypeAnnotations - 4.7.22. Атрибут
AnnotationDefault - 4.7.23. Атрибут
BootstrapMethods - 4.7.24. Атрибут
MethodParameters - 4.7.25. Атрибут
Module - 4.7.26. Атрибут
ModulePackages - 4.7.27. Атрибут
ModuleMainClass - 4.7.28. Атрибут
NestHost - 4.7.29. Атрибут
NestMembers - 4.7.30. Атрибут
Record - 4.7.31. Атрибут
PermittedSubclasses
- 4.8. Проверка формата
- 4.9. Ограничения кода виртуальной машины Java
- 4.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. Проверка типов инструкций
-
- 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
-
В этой главе описывается формат файла class виртуальной машины Java. Каждый файл class содержит определение одного класса, интерфейса или модуля. Хотя классу, интерфейсу или модулю не обязательно иметь внешнее представление, буквально содержащееся в файле (например, потому что класс генерируется загрузчиком классов), мы будем условно называть любое допустимое представление класса, интерфейса или модуля как находящееся в формате файла class.
Файл class состоит из потока байтов по 8 бит. 16-битные и 32-битные величины строятся путём чтения двух и четырёх последовательных байтов по 8 бит соответственно. Многобайтовые данные всегда хранятся в формате big-endian, где старшие байты идут первыми. В этой главе определены типы данных u1, u2 и u4 для представления беззнаковых одно-, двух- или четырёхбайтовых величин соответственно.
В API платформы Java SE формат файла class поддерживается интерфейсами java.io.DataInput и java.io.DataOutput и классами, такими как java.io.DataInputStream и java.io.DataOutputStream. Например, значения типов u1, u2 и u4 могут быть прочитаны методами, такими как readUnsignedByte, readUnsignedShort и readInt интерфейса java.io.DataInput.
В этой главе формат файла class представляется с помощью псевдоструктур, написанных в обозначении структуры, похожей на C. Чтобы избежать путаницы с полями классов и экземпляров классов и т.д., содержимое структур, описывающих формат файла class, называется элементами. Последовательные элементы хранятся в файле class последовательно, без заполнения или выравнивания.
Таблицы, состоящие из нуля или более элементов переменной длины, используются в нескольких структурах файлов class. Хотя мы используем синтаксис массивов языка C для ссылки на элементы таблицы, тот факт, что таблицы являются потоками структур различной длины, означает, что невозможно напрямую перевести индекс таблицы в смещение байта в таблице.
Когда мы ссылаемся на структуру данных как на массив, она состоит из нуля или более смежных элементов фиксированной длины и может индексироваться как массив.
Ссылка на символ ASCII в этой главе должна интерпретироваться как соответствующий Unicode-код символа ASCII.
END_OF_DOCUMENT_MARKER Файл 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имеет номер версии majorMи номер версии minorm, то версия его формата файлаclassобозначается какM.m.Реализация виртуальной машины Java, соответствующая Java SE
N, должна поддерживать только указанные в четвертом столбце таблицы 4.1-A, "Поддерживаемые major версии", значения major версии формата файлаclass. Обозначение A .. B означает major версии от A до B включительно. Третий столбец "Major" показывает major версию, введённую каждой версией Java SE, то есть первой версией, которая могла бы принять файлclass, содержащий этот элементmajor_version. Для очень ранних релизов вместо версии Java SE указывается версия JDK.Таблица 4.1-A. Версии major формата файла
classJava SE Выпущена Major Поддерживаемые major версии 1.0.2 Май 1996 45 45 1.1 Февраль 1997 45 45 1.2 Декабрь 1998 46 45 .. 46 1.3 Май 2000 47 45 .. 47 1.4 Февраль 2002 48 45 .. 48 5.0 Сентябрь 2004 49 45 .. 49 6 Декабрь 2006 50 45 .. 50 7 Июль 2011 51 45 .. 51 8 Март 2014 52 45 .. 52 9 Сентябрь 2017 53 45 .. 53 10 Март 2018 54 45 .. 54 11 Сентябрь 2018 55 45 .. 55 12 Март 2019 56 45 .. 56 13 Сентябрь 2019 57 45 .. 57 14 Март 2020 58 45 .. 58 15 Сентябрь 2020 59 45 .. 59 16 Март 2021 60 45 .. 60 17 Сентябрь 2021 61 45 .. 61 18 Март 2022 62 45 .. 62 19 Сентябрь 2022 63 45 .. 63 20 Март 2023 64 45 .. 64 21 Сентябрь 2023 65 45 .. 65 Для файла
class, у которогоmajor_versionравно 56 или выше,minor_versionдолжно быть 0 или 65535.Для файла
class, у которогоmajor_versionнаходится в диапазоне от 45 до 55 включительно,minor_versionможет принимать любое значение.Следует рассмотреть историческую перспективу поддержки JDK для версий формата файлов
class. JDK 1.0.2 поддерживал версии от 45.0 до 45.3 включительно. JDK 1.1 поддерживал версии от 45.0 до 45.65535 включительно. Когда JDK 1.2 ввёл поддержку major версии 46, единственной minor версией, поддерживаемой под этой major версией, была 0. Более поздние версии JDK продолжили эту практику, вводя поддержку новой major версии (47, 48 и т. д.), но поддерживая только minor версию 0 под новой major версией. Наконец, появление предварительных функций в Java SE 12 (см. ниже) мотивировало стандартную роль minor версии формата файлаclass, поэтому JDK 12 поддерживал minor версии 0 и 65535 под major версией 56. Позднее версии JDK ввели поддержкуN.0 иN.65535, гдеN— соответствующая major версия реализованной платформы Java SE. Например, JDK 13 поддерживает 57.0 и 57.65535.Платформа Java SE может определять предварительные функции. Реализация виртуальной машины Java, соответствующая Java SE
N(N≥ 12), должна поддерживать все предварительные функции Java SENи не поддерживать ни одну из предварительных функций других релизов Java SE. Реализация по умолчанию должна отключать поддерживаемые предварительные функции и должна предоставлять способ их включения, но не должна предоставлять возможность включения только некоторых из них.Файл
classсчитается зависящим от предварительных функций Java SEN(N≥ 12), если у него естьmajor_version, соответствующая Java SEN(согласно таблице 4.1-A), иminor_versionравна 65535.Реализация виртуальной машины Java, соответствующая Java SE
N(N≥ 12), должна действовать следующим образом:-
Файл
class, зависящий от предварительных функций Java SEN, может быть загружен только при включённых предварительных функциях Java SEN. -
Файл
class, зависящий от предварительных функций другого релиза Java SE, никогда не должен быть загружен. -
Файл
class, не зависящий от предварительных функций ни одного релиза Java SE, может быть загружен независимо от того, включены ли предварительные функции Java SEN.
-
- 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_PUBLIC0x0001 Объявлен public; доступен извне пакета.ACC_FINAL0x0010 Объявлен final; наследование запрещено.ACC_SUPER0x0020 Методы суперкласса обрабатываются особым образом при вызове инструкцией invokespecial. ACC_INTERFACE0x0200 Представляет собой интерфейс, а не класс. ACC_ABSTRACT0x0400 Объявлен abstract; не может быть инстанцирован.ACC_SYNTHETIC0x1000 Объявлен синтетическим; отсутствует в исходном коде. ACC_ANNOTATION0x2000 Объявлен как интерфейс аннотаций. ACC_ENUM0x4000 Объявлен как класс enum.ACC_MODULE0x8000 Представляет собой модуль, а не класс или интерфейс. Флаг
ACC_MODULEуказывает, что этотclassфайл определяет модуль, а не класс или интерфейс. Если флагACC_MODULEустановлен, дляclassфайла применяются особые правила, которые указаны в конце этого раздела. Если флагACC_MODULEне установлен, дляclassфайла применяются правила, расположенные непосредственно ниже текущего абзаца.Интерфейс определяется установленным флагом
ACC_INTERFACE. Если флагACC_INTERFACEне установлен, этотclassфайл определяет класс, а не интерфейс или модуль.Если установлен флаг
ACC_INTERFACE, то должен быть установлен также флагACC_ABSTRACT, а флагиACC_FINAL,ACC_SUPER,ACC_ENUMиACC_MODULEне должны быть установлены.Если флаг
ACC_INTERFACEне установлен, любые другие флаги в таблице 4.1-B могут быть установлены, за исключениемACC_ANNOTATIONиACC_MODULE. Однако в такомclassфайле не должно быть установлены одновременно флагиACC_FINALиACC_ABSTRACT(JLS §8.1.1.2).Флаг
ACC_SUPERуказывает, какая из двух альтернативных семантик должна быть выражена инструкцией invokespecial (§invokespecial), если она присутствует в этом классе или интерфейсе. Компиляторы в набор команд Java Virtual Machine должны устанавливать флагACC_SUPER. В Java SE 8 и выше Java Virtual Machine считает флагACC_SUPERустановленным в каждомclassфайле, независимо от фактического значения флага вclassфайле и версииclassфайла.Флаг
ACC_SUPERсуществует для обратной совместимости с кодом, скомпилированным более старыми компиляторами для языка программирования Java. До JDK 1.0.2 компилятор генерировалaccess_flags, в котором флаг, теперь представляющийACC_SUPER, не имел назначенного значения, и реализация Oracle Java Virtual Machine игнорировала флаг, если он был установлен.Флаг
ACC_SYNTHETICуказывает, что этот класс или интерфейс был сгенерирован компилятором и не появляется в исходном коде.Интерфейс аннотаций (JLS §9.6) должен иметь установленный флаг
ACC_ANNOTATION. Если установлен флагACC_ANNOTATION, то должен быть также установлен флагACC_INTERFACE.Флаг
ACC_ENUMуказывает, что этот класс или его суперкласс объявлен как класс перечислений (JLS §8.9).Все биты элемента
access_flags, не назначенные в таблице 4.1-B, зарезервированы для будущего использования. Они должны быть установлены в ноль в сгенерированныхclassфайлах и должны игнорироваться реализациями Java Virtual Machine. - this_class
-
Значение элемента
this_classдолжно быть допустимым индексом в таблицеconstant_pool. Элементconstant_poolв этом индексе должен быть структуройCONSTANT_Class_info(§4.4.1), представляющей класс или интерфейс, определённый этимclassфайлом. - super_class
-
Для класса значение элемента
super_classлибо должно быть равно нулю, либо должно быть допустимым индексом в таблицеconstant_pool. Если значение элементаsuper_classотлично от нуля, элементconstant_poolв этом индексе должен быть структуройCONSTANT_Class_info, представляющей непосредственный суперкласс класса, определённого этимclassфайлом. Ни непосредственный суперкласс, ни любой из его суперклассов не должны иметь флагACC_FINAL, установленный в элементеaccess_flagsего структурыClassFile.Если значение элемента
super_classравно нулю, то этотclassфайл должен представлять классObject, единственный класс или интерфейс без непосредственного суперкласса.Для интерфейса значение элемента
super_classвсегда должно быть допустимым индексом в таблицеconstant_pool. Элементconstant_poolв этом индексе должен быть структуройCONSTANT_Class_info, представляющей классObject. - interfaces_count
-
Значение элемента
interfaces_countзадаёт количество непосредственных суперинтерфейсов этого класса или типа интерфейса. - interfaces[]
-
Каждое значение в массиве
interfacesдолжно быть допустимым индексом в таблицеconstant_pool. Элементconstant_poolв каждом значенииinterfaces[i], где 0 ≤ i <interfaces_count, должен быть структуройCONSTANT_Class_info, представляющей интерфейс, являющийся непосредственным суперинтерфейсом этого класса или типа интерфейса в порядке слева направо, указанном в исходном коде для типа. - fields_count
-
Значение элемента
fields_countзадаёт количество структурfield_infoв таблицеfields. Эти структуры представляют все поля, как переменные класса, так и переменные экземпляров, объявленные этим типом класса или интерфейса. - fields[]
-
Каждое значение в таблице
fieldsдолжно быть структуройfield_info(§4.5), дающей полное описание поля в этом классе или интерфейсе. Таблицаfieldsвключает только те поля, которые объявлены в этом классе или интерфейсе. Она не включает элементы, представляющие поля, унаследованные от суперклассов или суперинтерфейсов. - methods_count
-
Значение элемента
methods_countзадаёт количество структурmethod_infoв таблицеmethods. - methods[]
-
Каждое значение в таблице
methodsдолжно быть структуройmethod_info(§4.6), дающей полное описание метода в этом классе или интерфейсе. Если ни один из флаговACC_NATIVEиACC_ABSTRACTне установлен в элементеaccess_flagsструктурыmethod_info, инструкции Java Virtual Machine, реализующие метод, также предоставляются.Структуры
method_infoпредставляют все методы, объявленные этим типом класса или интерфейса, включая методы экземпляра, статические методы, методы инициализации экземпляра (§2.9.1) и любой метод инициализации класса или интерфейса (§2.9.2). Таблицаmethodsне включает элементы, представляющие методы, унаследованные от суперклассов или суперинтерфейсов. - attributes_count
-
Значение элемента
attributes_countзадаёт количество атрибутов в таблицеattributesэтого класса. - attributes[]
-
Каждое значение таблицы
attributesдолжно быть структуройattribute_info(§4.7).Атрибуты, определённые в этом спецификации, присутствующие в таблице
attributesструктурыClassFile, перечислены в таблице 4.7-C.Правила, касающиеся атрибутов, определённых для присутствия в таблице
attributesструктурыClassFile, даны в §4.7.Правила, касающиеся неопределённых атрибутов в таблице
attributesструктурыClassFile, даны в §4.7.1.
Если флаг ACC_MODULE установлен в элементе access_flags, то никакой другой флаг в элементе access_flags не может быть установлен, и для остальной части структуры ClassFile применяются следующие правила:
-
Требования к версии Java Virtual Machine,
minor_version: ≥ 53.0 (т.е., Java SE 9 и выше) -
module-info:module-info -
super_class,interfaces_count,fields_count,methods_count: ноль -
attributes: ОдинModuleатрибут должен быть присутствующим. За исключениемModule,ModulePackages,ModuleMainClass,InnerClasses,SourceFile,SourceDebugExtension,RuntimeVisibleAnnotationsиRuntimeInvisibleAnnotations, ни один из предопределённых атрибутов (§4.7) не должен появляться.
Имена классов и интерфейсов, которые появляются в структурах файлов 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.
Имена методов, полей, локальных переменных и формальных параметров хранятся как неквалифицированные имена. Неквалифицированное имя должно содержать по меньшей мере один символ Юникода и не должно содержать ни одного из ASCII символов . ; [ / (то есть, точки или точки с запятой или левой квадратной скобки или слэша).
Имена методов дополнительно ограничены так, что, за исключением специальных имён методов <init> и <clinit> (§2.9), они не должны содержать ASCII символов < или > (то есть, левой угловой скобки или правой угловой скобки).
Обратите внимание, что никакая инструкция вызова метода не может ссылаться на <clinit>, и только инструкция invokespecial (§invokespecial) может ссылаться на <init>.
Имена модулей, ссылающиеся на атрибут 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, которая представляет имя пакета, закодированное во внутреннем формате.
Дескриптор — это строка, представляющая тип поля или метода. Дескрипторы представлены в формате файла class с использованием изменённых строк UTF-8 (§4.4.7), и, следовательно, могут быть взяты, если не ограничены иначе, из всего кодового пространства Юникода.
Дескрипторы определяются с помощью грамматики. Грамматика — это набор правил, описывающих, как последовательности символов могут образовывать синтаксически правильные дескрипторы различных типов. Терминальные символы грамматики показаны шрифтом fixed width. Нетерминальные символы показаны шрифтом курсив. Определение нетерминала вводится именем определяемого нетерминала, за которым следует двоеточие. Затем следуют одно или несколько альтернативных определений для нетерминала на последующих строках.
Синтаксис {x} в правой части правила обозначает ноль или более вхождений x.
Фраза (одно из) в правой части правила означает, что каждый из терминальных символов в следующей строке или строках является альтернативным определением.
Дескриптор поля представляет тип класса, экземпляра или локальной переменной.
B C D F I J S Z L ИмяКласса ;
Символы 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.
Дескриптор метода содержит ноль или более дескрипторов параметров, представляющих типы параметров, которые принимает метод, и дескриптор возвращаемого значения, представляющий тип возвращаемого значения (если таковое имеется).
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).
Инструкции виртуальной машины Java не полагаются на расположение в памяти классов, интерфейсов, экземпляров классов или массивов во время выполнения. Вместо этого инструкции ссылаются на символическую информацию в таблице constant_pool.
Все записи в таблице constant_pool имеют следующий общий формат:
cp_info {
u1 tag;
u1 info[];
}
Каждая запись в таблице constant_pool должна начинаться с 1-байтового тега, указывающего тип константы, обозначаемой этой записью. Существует 17 типов констант, перечисленных в таблице 4.4-A вместе с соответствующими тегами, и упорядоченных по номеру раздела в этой главе. За каждым байтом тега следуют два или более байта, содержащие информацию о конкретной константе. Формат дополнительной информации зависит от байта тега, то есть содержимое массива info изменяется в зависимости от значения tag.
Таблица 4.4-A. Теги пула констант (по разделам)
| Тип константы | Тег | Раздел |
|---|---|---|
CONSTANT_Class | 7 | §4.4.1 |
CONSTANT_Fieldref | 9 | §4.4.2 |
CONSTANT_Methodref | 10 | §4.4.2 |
CONSTANT_InterfaceMethodref | 11 | §4.4.2 |
CONSTANT_String | 8 | §4.4.3 |
CONSTANT_Integer | 3 | §4.4.4 |
CONSTANT_Float | 4 | §4.4.4 |
CONSTANT_Long | 5 | §4.4.5 |
CONSTANT_Double | 6 | §4.4.5 |
CONSTANT_NameAndType | 12 | §4.4.6 |
CONSTANT_Utf8 | 1 | §4.4.7 |
CONSTANT_MethodHandle | 15 | §4.4.8 |
CONSTANT_MethodType | 16 | §4.4.9 |
CONSTANT_Dynamic | 17 | §4.4.10 |
CONSTANT_InvokeDynamic | 18 | §4.4.10 |
CONSTANT_Module | 19 | §4.4.11 |
CONSTANT_Package | 20 | §4.4.12 |
В файле class, номер версии которого v, каждая запись в таблице constant_pool должна иметь тег, который был определен впервые в версии v или более ранней версии формата файла class (§4.1). То есть, каждая запись должна обозначать тип константы, разрешённой для использования в файле формата class. Таблица 4.4-B перечисляет каждый тег с первой версией формата файла class, в которой он был определён. Также указана версия Java SE Platform, которая ввела эту версию формата файла class.
Таблица 4.4-B. Теги пула констант (по тегу)
| Тип константы | Тег |
class формат файла | Java SE |
|---|---|---|---|
CONSTANT_Utf8 | 1 | 45.3 | 1.0.2 |
CONSTANT_Integer | 3 | 45.3 | 1.0.2 |
CONSTANT_Float | 4 | 45.3 | 1.0.2 |
CONSTANT_Long | 5 | 45.3 | 1.0.2 |
CONSTANT_Double | 6 | 45.3 | 1.0.2 |
CONSTANT_Class | 7 | 45.3 | 1.0.2 |
CONSTANT_String | 8 | 45.3 | 1.0.2 |
CONSTANT_Fieldref | 9 | 45.3 | 1.0.2 |
CONSTANT_Methodref | 10 | 45.3 | 1.0.2 |
CONSTANT_InterfaceMethodref | 11 | 45.3 | 1.0.2 |
CONSTANT_NameAndType | 12 | 45.3 | 1.0.2 |
CONSTANT_MethodHandle | 15 | 51.0 | 7 |
CONSTANT_MethodType | 16 | 51.0 | 7 |
CONSTANT_Dynamic | 17 | 55.0 | 11 |
CONSTANT_InvokeDynamic | 18 | 51.0 | 7 |
CONSTANT_Module | 19 | 53.0 | 9 |
CONSTANT_Package | 20 | 53.0 | 9 |
Некоторые записи в таблице constant_pool являются загружаемыми, так как они представляют сущности, которые могут быть помещены на стек во время выполнения для дальнейших вычислений. В файле class, номер версии которого v, запись в таблице constant_pool является загружаемой, если её тег был впервые определён как загружаемый в версии v или более ранней версии формата файла class. Таблица 4.4-C перечисляет каждый тег с первой версией формата файла class, в которой он считался загружаемым. Также указана версия Java SE Platform, которая ввела эту версию формата файла class.
Во всех случаях, кроме CONSTANT_Class, тег был впервые признан загружаемым в той же версии формата файла class, что и определение тега.
Таблица 4.4-C. Теги загружаемых констант пула
| Тип константы | Тег |
class формат файла | Java SE |
|---|---|---|---|
CONSTANT_Integer | 3 | 45.3 | 1.0.2 |
CONSTANT_Float | 4 | 45.3 | 1.0.2 |
CONSTANT_Long | 5 | 45.3 | 1.0.2 |
CONSTANT_Double | 6 | 45.3 | 1.0.2 |
CONSTANT_Class | 7 | 49.0 | 5.0 |
CONSTANT_String | 8 | 45.3 | 1.0.2 |
CONSTANT_MethodHandle | 15 | 51.0 | 7 |
CONSTANT_MethodType | 16 | 51.0 | 7 |
CONSTANT_Dynamic | 17 | 55.0 | 11 |
Структура 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 измерений.
Поля, методы и методы интерфейсов представлены аналогичными структурами:
CONSTANT_Fieldref_info {
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
CONSTANT_Methodref_info {
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
CONSTANT_InterfaceMethodref_info {
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
Элементы этих структур следующие:
- tag
-
Элемент
tagструктурыCONSTANT_Fieldref_infoимеет значениеCONSTANT_Fieldref(9).Элемент
tagструктурыCONSTANT_Methodref_infoимеет значениеCONSTANT_Methodref(10).Элемент
tagструктурыCONSTANT_InterfaceMethodref_infoимеет значениеCONSTANT_InterfaceMethodref(11). - class_index
-
Значение элемента
class_indexдолжно быть корректным индексом в таблицеconstant_pool. Элементconstant_poolв этом индексе должен быть структуройCONSTANT_Class_info(§4.4.1), представляющей тип класса или интерфейса, являющегося членом поля или метода.В структуре
CONSTANT_Fieldref_infoэлементclass_indexможет быть типом класса или типом интерфейса.В структуре
CONSTANT_Methodref_infoэлементclass_indexдолжен быть типом класса, а не типом интерфейса.В структуре
CONSTANT_InterfaceMethodref_infoэлементclass_indexдолжен быть типом интерфейса, а не типом класса. - name_and_type_index
-
Значение элемента
name_and_type_indexдолжно быть корректным индексом в таблицеconstant_pool. Элементconstant_poolв этом индексе должен быть структуройCONSTANT_NameAndType_info(§4.4.6). Эта структураconstant_poolуказывает имя и описание поля или метода.В структуре
CONSTANT_Fieldref_infoуказанное описание должно быть описателем поля (§4.3.2). В противном случае указанное описание должно быть описателем метода (§4.3.3).Если имя метода в структуре
CONSTANT_Methodref_infoначинается с '<' ('\u003c'), то имя должно быть специальным именем<init>, представляющим метод инициализации экземпляра (§2.9.1). Тип возвращаемого значения такого метода должен бытьvoid.
Структура 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.
Структуры CONSTANT_Integer_info и CONSTANT_Float_info представляют числовые константы (int и float) размером 4 байта:
CONSTANT_Integer_info {
u1 tag;
u4 bytes;
}
CONSTANT_Float_info {
u1 tag;
u4 bytes;
}
Элементы этих структур следующие:
- tag
-
Элемент
tagструктурыCONSTANT_Integer_infoимеет значениеCONSTANT_Integer(3).Элемент
tagструктурыCONSTANT_Float_infoимеет значениеCONSTANT_Float(4). - bytes
-
Элемент
bytesструктурыCONSTANT_Integer_infoпредставляет значение константыint. Байты значения хранятся в формате big-endian (старший байт первым).Элемент
bytesструктурыCONSTANT_Float_infoпредставляет значение константыfloatв формате IEEE 754 binary32 floating-point (§2.3.2). Байты элемента хранятся в формате big-endian (старший байт первым).Значение, представленное структурой
CONSTANT_Float_info, определяется следующим образом. Сначала байты значения преобразуются в числовую константу bits. Затем:-
Если bits равно
0x7f800000, значениеfloatбудет положительной бесконечностью. -
Если bits равно
0xff800000, значениеfloatбудет отрицательной бесконечностью. -
Если bits находится в диапазоне
0x7f800001до0x7fffffffили в диапазоне0xff800001до0xffffffff, значениеfloatбудет NaN. -
Во всех остальных случаях пусть
s,eиmбудут тремя значениями, которые могут быть вычислены из bits:int s = ((bits >> 31) == 0) ? 1 : -1; int e = ((bits >> 23) & 0xff); int m = (e == 0) ? (bits & 0x7fffff) << 1 : (bits & 0x7fffff) | 0x800000;
Тогда значение
floatравно результату математического выраженияs · m · 2e-150. -
Структуры CONSTANT_Long_info и CONSTANT_Double_info представляют 8-байтовые числовые (long и double) константы:
CONSTANT_Long_info {
u1 tag;
u4 high_bytes;
u4 low_bytes;
}
CONSTANT_Double_info {
u1 tag;
u4 high_bytes;
u4 low_bytes;
}
Все 8-байтовые константы занимают две записи в таблице constant_pool файла class. Если структура CONSTANT_Long_info или CONSTANT_Double_info является записью с индексом n в таблице constant_pool, то следующая доступная запись в таблице находится по индексу n+2. Индекс constant_pool n+1 должен быть валиден, но считается недоступным.
В ретроспективе, использование двух записей в пуле констант для 8-байтовых констант было неудачным решением.
Элементы этих структур следующие:
- tag
-
Элемент
tagструктурыCONSTANT_Long_infoимеет значениеCONSTANT_Long(5).Элемент
tagструктурыCONSTANT_Double_infoимеет значениеCONSTANT_Double(6). - high_bytes, low_bytes
-
Беззнаковые элементы
high_bytesиlow_bytesструктурыCONSTANT_Long_infoвместе представляют значение константыlong((long) high_bytes << 32) + low_bytesгде байты каждого из
high_bytesиlow_bytesхранятся в формате big-endian (старший байт первым).Элементы
high_bytesиlow_bytesструктурыCONSTANT_Double_infoвместе представляют значениеdoubleв формате с плавающей запятой IEEE 754 binary64 (§2.3.2). Байты каждого элемента хранятся в формате big-endian (старший байт первым).Значение, представленное структурой
CONSTANT_Double_info, определяется следующим образом. Элементыhigh_bytesиlow_bytesпреобразуются в константуlongbits, которая равна((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. -
Структура 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).
Структура 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 (биты 20-16)-1
w:Таблица 4.18.
1 0 биты 15-10
x:Таблица 4.19.
1 1 1 0 1 1 0 1
y:Таблица 4.20.
1 0 1 1 биты 9-6
z:Таблица 4.21.
1 0 биты 5-0
Эти шесть байтов представляют кодовую точку со значением:
0x10000 + ((v & 0x0f) << 16) + ((w & 0x3f) << 10) + ((y & 0x0f) << 6) + (z & 0x3f)
Байты многобайтовых символов хранятся в файле class в формате big-endian (старший байт вначале).
Существует два отличия между этим форматом и стандартным форматом UTF-8. Во-первых, нулевой символ (char)0 кодируется с использованием 2-байтового формата, а не 1-байтового, чтобы модифицированные строки UTF-8 никогда не содержали вложенных нулей. Во-вторых, используются только 1-байтовый, 2-байтовый и 3-байтовый форматы стандартного UTF-8. Виртуальная машина Java не распознаёт 4-байтовый формат стандартного UTF-8; вместо этого она использует свой собственный формат дважды по три байта.
Дополнительную информацию о стандартном формате UTF-8 см. в разделе 3.9 Формы кодирования Unicode стандарта Unicode, Версия 15.0.
Структура 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>. -
Структура CONSTANT_MethodType_info используется для представления типа метода:
CONSTANT_MethodType_info {
u1 tag;
u2 descriptor_index;
}
Элементы структуры CONSTANT_MethodType_info следующие:
Большинство структур в таблице constant_pool представляют сущности напрямую, комбинируя имена, дескрипторы и значения, записанные статически в таблице. В отличие от этого, структуры CONSTANT_Dynamic_info и CONSTANT_InvokeDynamic_info представляют сущности косвенно, указывая на код, который вычисляет сущность динамически. Код, называемый методом инициализации, вызывается виртуальной машиной Java при разрешении символических ссылок, полученных из этих структур (§5.1, §5.4.3.6). Каждая структура указывает метод инициализации, а также вспомогательное имя и тип, которые характеризуют вычисляемую сущность. Более подробно:
-
Структура
CONSTANT_Dynamic_infoиспользуется для представления динамически вычисляемой константы, произвольного значения, которое генерируется вызовом метода инициализации в ходе инструкции ldc (§ldc), среди прочих. Вспомогательный тип, указанный в структуре, ограничивает тип динамически вычисляемой константы. -
Структура
CONSTANT_InvokeDynamic_infoиспользуется для представления динамически вычисляемого места вызова, экземпляраjava.lang.invoke.CallSite, который генерируется вызовом метода инициализации в ходе инструкции invokedynamic (§invokedynamic). Вспомогательный тип, указанный в структуре, ограничивает тип метода динамически вычисляемого места вызова.
CONSTANT_Dynamic_info {
u1 tag;
u2 bootstrap_method_attr_index;
u2 name_and_type_index;
}
CONSTANT_InvokeDynamic_info {
u1 tag;
u2 bootstrap_method_attr_index;
u2 name_and_type_index;
}
Элементы этих структур следующие:
- tag
-
Элемент
tagструктурыCONSTANT_Dynamic_infoимеет значениеCONSTANT_Dynamic(17).Элемент
tagструктурыCONSTANT_InvokeDynamic_infoимеет значениеCONSTANT_InvokeDynamic(18). - bootstrap_method_attr_index
-
Значение элемента
bootstrap_method_attr_indexдолжно быть допустимым индексом в массивеbootstrap_methodsтаблицы метода инициализации данного файлаclass(§4.7.23).Структуры
CONSTANT_Dynamic_infoуникальны тем, что синтаксически разрешено ссылаться на себя через таблицу метода инициализации. Вместо того, чтобы требовать обнаружения таких циклов при загрузке классов (что может быть потенциально дорогим проверкой), мы допускаем циклы изначально, но требуем неудачу на этапе разрешения (§5.4.3.6). - name_and_type_index
-
Значение элемента
name_and_type_indexдолжно быть допустимым индексом в таблицеconstant_pool. Элементconstant_poolв этом индексе должен быть структуройCONSTANT_NameAndType_info(§4.4.6). Этот элементconstant_poolуказывает имя и дескриптор.В структуре
CONSTANT_Dynamic_infoуказанный дескриптор должен быть дескриптором поля (§4.3.2).В структуре
CONSTANT_InvokeDynamic_infoуказанный дескриптор должен быть дескриптором метода (§4.3.3).
Структура CONSTANT_Module_info используется для представления модуля:
CONSTANT_Module_info {
u1 tag;
u2 name_index;
}
Элементы структуры CONSTANT_Module_info следующие:
Структура CONSTANT_Module_info разрешена только в константном пуле файла class, который объявляет модуль, то есть в структуре ClassFile, где элемент access_flags имеет флаг ACC_MODULE. Во всех остальных файлах class структура CONSTANT_Module_info является недопустимой.
Структура CONSTANT_Package_info используется для представления пакета, экспортированного или открытого модулем:
CONSTANT_Package_info {
u1 tag;
u2 name_index;
}
Элементы структуры CONSTANT_Package_info следующие:
- tag
-
Элемент
tagимеет значениеCONSTANT_Package(20). - name_index
-
Значение элемента
name_indexдолжно быть допустимым индексом в таблицеconstant_pool. Элементconstant_poolв этом индексе должен быть структуройCONSTANT_Utf8_info(§4.4.7) представляющей допустимое имя пакета, закодированное во внутреннем формате (§4.2.3).
Структура CONSTANT_Package_info разрешена только в константном пуле файла class, который объявляет модуль, то есть в структуре ClassFile, где элемент access_flags имеет флаг ACC_MODULE. Во всех остальных файлах class структура CONSTANT_Package_info является недопустимой.
Каждое поле описывается структурой 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_PUBLIC0x0001 Объявленное public; может быть обращено извне пакета.ACC_PRIVATE0x0002 Объявленное
private; доступно только внутри определяемого класса и других классов, принадлежащих тому же вложенному набору (§5.4.4).ACC_PROTECTED0x0004 Объявленное protected; может быть обращено в подклассах.ACC_STATIC0x0008 Объявленное static.ACC_FINAL0x0010 Объявленное final; никогда напрямую не присваивается после создания объекта (JLS §17.5).ACC_VOLATILE0x0040 Объявленное volatile; не может быть кэшировано.ACC_TRANSIENT0x0080 Объявленное transient; не записывается и не считывается менеджером постоянных объектов.ACC_SYNTHETIC0x1000 Объявленное синтетическим; отсутствует в исходном коде. ACC_ENUM0x4000 Объявлено элементом класса enum.Поля классов могут устанавливать любые флаги в Таблице 4.5-A. Однако каждое поле класса может иметь не более одного установленного флага
ACC_PUBLIC,ACC_PRIVATEиACC_PROTECTED(JLS §8.3.1), и не должно иметь одновременно установленные флагиACC_FINALиACC_VOLATILE(JLS §8.3.1.4).Поля интерфейсов должны иметь установленные флаги
ACC_PUBLIC,ACC_STATICиACC_FINAL; они могут иметь установленный флагACC_SYNTHETICи не должны иметь установленных других флагов в Таблице 4.5-A (JLS §9.3).Флаг
ACC_SYNTHETICуказывает, что это поле было сгенерировано компилятором и не отображается в исходном коде.Флаг
ACC_ENUMуказывает, что это поле используется для хранения элемента класса перечисления (JLS §8.9).Все биты элемента
access_flags, не назначенные в Таблице 4.5-A, зарезервированы для будущего использования. Они должны быть установлены в ноль в сгенерированных файлахclassи должны игнорироваться реализациями Java Virtual Machine. - name_index
-
Значение элемента
name_indexдолжно быть допустимым индексом в таблицеconstant_pool. Элементconstant_poolв этом индексе должен быть структуройCONSTANT_Utf8_info(§4.4.7), которая представляет допустимое неопределённое имя, обозначающее поле (§4.2.2). - descriptor_index
-
Значение элемента
descriptor_indexдолжно быть допустимым индексом в таблицеconstant_pool. Элементconstant_poolв этом индексе должен быть структуройCONSTANT_Utf8_info(§4.4.7), которая представляет допустимый описатель поля (§4.3.2). - attributes_count
-
Значение элемента
attributes_countуказывает количество дополнительных атрибутов этого поля. - attributes[]
-
Каждое значение таблицы
attributesдолжно быть структуройattribute_info(§4.7).Поле может иметь любое количество необязательных атрибутов.
Определяемые этой спецификацией атрибуты, присутствующие в таблице
attributesструктурыfield_info, перечислены в Таблице 4.7-C.Правила, касающиеся атрибутов, определённых для присутствия в таблице
attributesструктурыfield_info, приведены в §4.7.Правила, касающиеся неопределённых атрибутов в таблице
attributesструктурыfield_info, приведены в §4.7.1.
Каждый метод, включая методы инициализации экземпляра (§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_PUBLIC0x0001 Объявлен public; может быть доступен извне своего пакета.ACC_PRIVATE0x0002 Объявлен
private; доступен только внутри определяющего класса и других классов, принадлежащих тому же вложенному классу (§5.4.4).ACC_PROTECTED0x0004 Объявлен protected; может быть доступен внутри подклассов.ACC_STATIC0x0008 Объявлен static.ACC_FINAL0x0010 Объявлен final; не должен переопределяться (§5.4.5).ACC_SYNCHRONIZED0x0020 Объявлен synchronized; вызов обернут использованием монитора.ACC_BRIDGE0x0040 Мостовой метод, сгенерированный компилятором. ACC_VARARGS0x0080 Объявлен с переменным числом аргументов. ACC_NATIVE0x0100 Объявлен native; реализован на языке, отличном от Java.ACC_ABSTRACT0x0400 Объявлен abstract; реализация не предоставлена.ACC_STRICT0x0800 В файле
class, чья основная версия номер не менее 46 и не более 60: Объявленstrictfp.ACC_SYNTHETIC0x1000 Объявлен синтетическим; отсутствует в исходном коде. Значение 0x0800 интерпретируется как флаг
ACC_STRICTтолько в файлеclass, чья основная версия номера не меньше 46 и не больше 60. Для методов в таком файлеclassправила ниже определяют, может ли флагACC_STRICTбыть установлен в сочетании с другими флагами. (Установка флагаACC_STRICTограничивала инструкции с плавающей точкой методов в Java SE 1.2 до 16 (§2.8).) Для методов в файлеclass, чья основная версия номера меньше 46 или больше 60, значение 0x0800 не интерпретируется как флагACC_STRICT, а, скорее, является незарезервированным; не имеет смысла «установить флагACC_STRICT» в таком файлеclass.Методы классов могут иметь любой из флагов в таблице 4.6-A. Однако каждый метод класса может иметь не более одного из его флагов
ACC_PUBLIC,ACC_PRIVATEиACC_PROTECTED(JLS §8.4.3).Методы интерфейсов могут иметь любой из флагов в таблице 4.6-A, за исключением
ACC_PROTECTED,ACC_FINAL,ACC_SYNCHRONIZEDиACC_NATIVE(JLS §9.4). В файлеclass, чья версия номера меньше 52.0, каждый метод интерфейса должен иметь установленные флагиACC_PUBLICиACC_ABSTRACT; в файлеclass, чья версия номера 52.0 или выше, каждый метод интерфейса должен иметь ровно один из установленных флаговACC_PUBLICиACC_PRIVATE.Если у метода класса или интерфейса установлен флаг
ACC_ABSTRACT, он не должен иметь установленных флаговACC_PRIVATE,ACC_STATIC,ACC_FINAL,ACC_SYNCHRONIZEDилиACC_NATIVE, а также (в файлеclass, чья основная версия номера не меньше 46 и не больше 60) флагACC_STRICT.Метод инициализации экземпляра (§2.9.1) может иметь не более одного из флагов
ACC_PUBLIC,ACC_PRIVATEиACC_PROTECTED, а также флагиACC_VARARGSиACC_SYNTHETIC, а также (в файлеclass, чья основная версия номера не меньше 46 и не больше 60) флагACC_STRICT, но не должен иметь установленных других флагов в таблице 4.6-A.В файле
class, чья версия номера 51.0 или выше, метод, чьё имя<clinit>, должен иметь установленный флагACC_STATIC.Метод инициализации класса или интерфейса (§2.9.2) вызывается виртуальной машиной Java неявно. Значение элемента
access_flagsигнорируется, за исключением установки флагаACC_STATICи (в файлеclass, чья основная версия номера не меньше 46 и не больше 60) флагаACC_STRICT, и метод освобождается от предыдущих правил о допустимых комбинациях флагов.Флаг
ACC_BRIDGEиспользуется для указания мостового метода, сгенерированного компилятором языка Java.Флаг
ACC_VARARGSуказывает, что этот метод принимает переменное число аргументов на уровне исходного кода. Метод, объявленный с переменным числом аргументов, должен быть скомпилирован с флагомACC_VARARGS, установленным в 1. Все остальные методы должны быть скомпилированы с флагомACC_VARARGS, установленным в 0.Флаг
ACC_SYNTHETICуказывает, что этот метод был сгенерирован компилятором и не отображается в исходном коде, если это не один из методов, перечисленных в §4.7.8.Все биты элемента
access_flags, не назначенные в таблице 4.6-A, зарезервированы для будущего использования. (Это включает бит, соответствующий 0x0800, в файлеclass, чья основная версия номера меньше 46 или больше 60.) Они должны быть установлены в ноль в сгенерированных файлахclassи должны игнорироваться реализациями виртуальной машины Java. - name_index
-
Значение элемента
name_indexдолжно быть допустимым индексом в таблицеconstant_pool. Элементconstant_poolв этом индексе должен быть структуройCONSTANT_Utf8_info(§4.4.7), представляющей либо допустимое неопределенное имя, обозначающее метод (§4.2.2), или (если этот метод находится в классе, а не в интерфейсе) специальное имя метода<init>или специальное имя метода<clinit>. - descriptor_index
-
Значение элемента
descriptor_indexдолжно быть допустимым индексом в таблицеconstant_pool. Элементconstant_poolв этом индексе должен быть структуройCONSTANT_Utf8_info, представляющей допустимый дескриптор метода (§4.3.3). Кроме того:-
Если этот метод находится в классе, а не в интерфейсе, и имя метода
<init>, то дескриптор должен обозначать методvoid. -
Если имя метода
<clinit>, то дескриптор должен обозначать методvoid, и в файлеclass, чья версия номера 51.0 или выше, метод, не принимающий аргументов.
В будущей версии данного спецификации может потребоваться, чтобы последний дескриптор параметра дескриптора метода был типом массива, если флаг
ACC_VARARGSустановлен в элементеaccess_flags. -
- attributes_count
-
Значение элемента
attributes_countуказывает количество дополнительных атрибутов данного метода. - attributes[]
-
Каждое значение таблицы
attributesдолжно быть структуройattribute_info(§4.7).Метод может иметь любое количество необязательных атрибутов, связанных с ним.
Атрибуты, определённые в данной спецификации как присутствующие в таблице
attributesструктурыmethod_info, перечислены в таблице 4.7-C.Правила, касающиеся атрибутов, определённых для появления в таблице
attributesструктурыmethod_info, приведены в §4.7.Правила, касающиеся неопределённых атрибутов в таблице
attributesструктурыmethod_info, приведены в §4.7.1.
Атрибуты используются в структурах ClassFile, field_info, method_info, Code_attribute и record_component_info формата файла class (§4.1, §4.5, §4.6, §4.7.3, §4.7.30).
Все атрибуты имеют следующий общий формат:
attribute_info {
u2 attribute_name_index;
u4 attribute_length;
u1 info[attribute_length];
}
Для всех атрибутов элемент attribute_name_index должен быть корректным беззнаковым 16-битным индексом в константном пуле класса. Элемент constant_pool в позиции attribute_name_index должен быть структурой CONSTANT_Utf8_info (§4.4.7), представляющей имя атрибута. Значение элемента attribute_length указывает длину последующей информации в байтах. Длина не включает начальные шесть байтов, содержащие элементы attribute_name_index и attribute_length.
Этот спецификацией определены 30 атрибутов. Они перечислены трижды для удобства навигации:
-
Таблица 4.7-A упорядочена по номерам разделов атрибутов в этой главе. Каждый атрибут показан с первой версией формата файла
class, в котором он был определён. Также указана версия Java SE Platform, которая представила ту версию формата файлаclass(§4.1). -
Таблица 4.7-B упорядочена по первой версии формата файла
class, в котором каждый атрибут был определён. -
Таблица 4.7-C упорядочена по местоположению в файле
class, где каждый атрибут должен появиться.
В контексте использования в этой спецификации, то есть в таблицах attributes структур файлов class, в которых они появляются, имена этих предопределённых атрибутов зарезервированы.
Любые условия наличия предопределённого атрибута в таблице attributes указаны явно в разделе, описывающем атрибут. Если условия не указаны, то атрибут может появляться любое количество раз в таблице attributes.
Предопределённые атрибуты разделены на три группы по назначению:
-
Семь атрибутов критически важны для корректной интерпретации файла
classвиртуальной машиной Java:-
ConstantValue -
Code -
StackMapTable -
BootstrapMethods -
NestHost -
NestMembers -
PermittedSubclasses
В файле
classс номером версии v каждый из этих атрибутов должен распознаваться и правильно считываться реализацией виртуальной машины Java, если реализация поддерживает версию v формата файлаclass, и атрибут был впервые определён в версии v или ранее формата файлаclass, и атрибут появляется в месте, где он должен появляться. -
-
Десять атрибутов не являются критическими для корректной интерпретации файла
classвиртуальной машиной Java, но либо критичны для корректной интерпретации файлаclassбиблиотеками классов Java SE Platform, либо полезны для инструментов (в этом случае раздел, описывающий атрибут, описывает его как "необязательный"):-
Exceptions -
InnerClasses -
EnclosingMethod -
Synthetic -
Signature -
Record -
SourceFile -
LineNumberTable -
LocalVariableTable -
LocalVariableTypeTable
В файле
classс номером версии v каждый из этих атрибутов должен распознаваться и правильно считываться реализацией виртуальной машины Java, если реализация поддерживает версию v формата файлаclass, и атрибут был впервые определён в версии v или ранее формата файлаclass, и атрибут появляется в месте, где он должен появляться. -
-
Тринадцать атрибутов не являются критическими для корректной интерпретации файла
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 |
| | §4.7.25 | 53.0 | 9 |
| | §4.7.26 | 53.0 | 9 |
| | §4.7.27 | 53.0 | 9 |
| | §4.7.28 | 55.0 | 11 |
| | §4.7.29 | 55.0 | 11 |
| | §4.7.30 | 60.0 | 16 |
| | §4.7.31 | 61.0 | 17 |
Таблица 4.7-B. Предопределенные class атрибуты файла (по формату файла class)
| Атрибут |
class файл | Java SE | Раздел |
|---|---|---|---|
ConstantValue | 45.3 | 1.0.2 | §4.7.2 |
Code | 45.3 | 1.0.2 | §4.7.3 |
Exceptions | 45.3 | 1.0.2 | §4.7.5 |
SourceFile | 45.3 | 1.0.2 | §4.7.10 |
LineNumberTable | 45.3 | 1.0.2 | §4.7.12 |
LocalVariableTable | 45.3 | 1.0.2 | §4.7.13 |
InnerClasses | 45.3 | 1.1 | §4.7.6 |
Synthetic | 45.3 | 1.1 | §4.7.8 |
Deprecated | 45.3 | 1.1 | §4.7.15 |
EnclosingMethod | 49.0 | 5.0 | §4.7.7 |
Signature | 49.0 | 5.0 | §4.7.9 |
SourceDebugExtension | 49.0 | 5.0 | §4.7.11 |
LocalVariableTypeTable | 49.0 | 5.0 | §4.7.14 |
RuntimeVisibleAnnotations | 49.0 | 5.0 | §4.7.16 |
RuntimeInvisibleAnnotations | 49.0 | 5.0 | §4.7.17 |
RuntimeVisibleParameterAnnotations | 49.0 | 5.0 | §4.7.18 |
RuntimeInvisibleParameterAnnotations | 49.0 | 5.0 | §4.7.19 |
AnnotationDefault | 49.0 | 5.0 | §4.7.22 |
StackMapTable | 50.0 | 6 | §4.7.4 |
BootstrapMethods | 51.0 | 7 | §4.7.23 |
RuntimeVisibleTypeAnnotations | 52.0 | 8 | §4.7.20 |
RuntimeInvisibleTypeAnnotations | 52.0 | 8 | §4.7.21 |
MethodParameters | 52.0 | 8 | §4.7.24 |
| | 53.0 | 9 | §4.7.25 |
| | 53.0 | 9 | §4.7.26 |
| | 53.0 | 9 | §4.7.27 |
| | 55.0 | 11 | §4.7.28 |
| | 55.0 | 11 | §4.7.29 |
| | 60.0 | 16 | §4.7.30 |
| | 61.0 | 17 | §4.7.31 |
Таблица 4.7-C. Предопределенные class атрибуты файла (по расположению)
| Атрибут | Расположение |
class файл |
|---|---|---|
SourceFile | ClassFile | 45.3 |
InnerClasses | ClassFile | 45.3 |
EnclosingMethod | ClassFile | 49.0 |
SourceDebugExtension | ClassFile | 49.0 |
BootstrapMethods | ClassFile | 51.0 |
| | ClassFile | 53.0 |
| | ClassFile | 55.0 |
| | ClassFile | 60.0 |
| | ClassFile | 61.0 |
ConstantValue | field_info | 45.3 |
Code | method_info | 45.3 |
Exceptions | method_info | 45.3 |
RuntimeVisibleParameterAnnotations, RuntimeInvisibleParameterAnnotations
| method_info | 49.0 |
AnnotationDefault | method_info | 49.0 |
MethodParameters | method_info | 52.0 |
Таблица 4.7-C (продолжение). Предопределенные class атрибуты файла (по расположению)
| Атрибут | Расположение |
class файл |
|---|---|---|
Synthetic |
ClassFile, field_info, method_info
| 45.3 |
Deprecated |
ClassFile, field_info, method_info
| 45.3 |
Signature | | 49.0 |
RuntimeVisibleAnnotations, RuntimeInvisibleAnnotations
| | 49.0 |
LineNumberTable | Code | 45.3 |
LocalVariableTable | Code | 45.3 |
LocalVariableTypeTable | Code | 49.0 |
StackMapTable | Code | 50.0 |
RuntimeVisibleTypeAnnotations, RuntimeInvisibleTypeAnnotations
| | 52.0 |
Компиляторы могут определять и генерировать файлы class, содержащие новые атрибуты в таблицах attributes структур файлов class, структур field_info, структур method_info и атрибутах Code (§4.7.3). Реализации Java Virtual Machine могут распознавать и использовать новые атрибуты, найденные в этих таблицах attributes. Однако любой атрибут, не определенный в рамках этого спецификации, не должен влиять на семантику файла class. Реализации Java Virtual Machine обязаны игнорировать атрибуты, которые они не распознают.
Например, разрешено определение нового атрибута для поддержки отладки, специфичной для поставщика. Поскольку реализации Java Virtual Machine обязаны игнорировать атрибуты, которые они не распознают, файлы class, предназначенные для конкретной реализации Java Virtual Machine, будут пригодны для использования другими реализациями, даже если эти реализации не могут использовать дополнительную отладочную информацию, содержащуюся в файлах class.
Реализации Java Virtual Machine строго запрещено генерировать исключение или иным образом отказываться от использования файлов class просто из-за наличия нового атрибута. Конечно, инструменты, работающие с файлами class, могут работать неправильно, если им предоставлены файлы class, которые не содержат всех необходимых атрибутов.
Два атрибута, которые предполагаются различными, но которые случайно используют одно и то же имя атрибута и имеют одинаковую длину, будут конфликтовать в реализациях, которые распознают любой из этих атрибутов. Атрибуты, определенные помимо этого спецификации, должны иметь имена, выбранные в соответствии с соглашениями об именовании пакетов, описанными в Спецификации языка Java, Java SE 21 Edition (JLS §6.1).
Будущие версии этой спецификации могут определить дополнительные атрибуты.
Атрибут 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,booleanCONSTANT_IntegerfloatCONSTANT_FloatlongCONSTANT_LongdoubleCONSTANT_DoubleStringCONSTANT_String
Атрибут Code является атрибутом переменной длины в таблице attributes структуры method_info (§4.6). Атрибут Code содержит инструкции Java Virtual Machine и вспомогательную информацию для метода, включая метод инициализации экземпляра и метод инициализации класса или интерфейса (§2.9.1, §2.9.2).
Если метод является либо native, либо abstract, и не является методом инициализации класса или интерфейса, то его структура method_info не должна содержать атрибут Code в таблице attributes. В противном случае, его структура method_info должна содержать ровно один атрибут Code в таблице attributes.
Атрибут Code имеет следующий формат:
Code_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 max_stack;
u2 max_locals;
u4 code_length;
u1 code[code_length];
u2 exception_table_length;
{ u2 start_pc;
u2 end_pc;
u2 handler_pc;
u2 catch_type;
} exception_table[exception_table_length];
u2 attributes_count;
attribute_info attributes[attributes_count];
}
Элементы структуры Code_attribute следующие:
- attribute_name_index
-
Значение элемента
attribute_name_indexдолжно быть корректным индексом в таблицеconstant_pool. Элементconstant_poolв этой позиции должен быть структуройCONSTANT_Utf8_info(§4.4.7), представляющей строку "Code". - attribute_length
-
Значение элемента
attribute_lengthуказывает длину атрибута, за исключением начальных шести байт. - max_stack
-
Значение элемента
max_stackзадаёт максимальную глубину стека операндов этого метода (§2.6.2) в любой момент во время выполнения метода. - max_locals
-
Значение элемента
max_localsзадаёт количество локальных переменных в массиве локальных переменных, выделенном при вызове этого метода (§2.6.1), включая локальные переменные, используемые для передачи параметров методу при его вызове.Наибольший индекс локальной переменной для значения типа
longилиdoubleсоставляетmax_locals - 2. Наибольший индекс локальной переменной для значения любого другого типа составляетmax_locals - 1. - code_length
-
Значение элемента
code_lengthзадаёт количество байтов в массивеcodeдля этого метода.Значение
code_lengthдолжно быть больше нуля (так как массивcodeне должен быть пустым) и меньше 65536. - code[]
-
Массив
codeсодержит фактические байты кода Java Virtual Machine, реализующие метод.При чтении массива
codeв память на машине с байтовой адресацией, если первый байт массива выровнен на границе 4 байта, смещения tableswitch и lookupswitch длиной 32 бита будут выровнены на границе 4 байта. (См. описания этих инструкций для получения дополнительной информации о последствиях выравнивания массиваcode.)Подробные ограничения на содержимое массива
codeподробно описаны в отдельном разделе (§4.9). - exception_table_length
-
Значение элемента
exception_table_lengthзадаёт количество записей в массивеexception_table. - exception_table[]
-
Каждая запись в массиве
exception_tableописывает один обработчик исключений в массивеcode. Порядок обработчиков в массивеexception_tableимеет значение (§2.10).Каждая запись
exception_tableсодержит следующие четыре элемента:- start_pc, end_pc
-
Значения двух элементов
start_pcиend_pcуказывают диапазоны в массивеcode, в которых обработчик исключений активен. Значениеstart_pcдолжно быть корректным индексом в массивеcodeкода инструкции. Значениеend_pcдолжно быть корректным индексом в массивеcodeкода инструкции или должно быть равноcode_length, длине массиваcode. Значениеstart_pcдолжно быть меньше значенияend_pc.start_pcвключительно, иend_pcисключительно; то есть обработчик исключений должен быть активен, пока счётчик команд находится в интервале [start_pc,end_pc).Тот факт, что
end_pcисключителен, является исторической ошибкой в проектировании Java Virtual Machine: если код Java Virtual Machine для метода имеет ровно 65535 байт и заканчивается инструкцией длиной 1 байт, то эта инструкция не может быть защищена обработчиком исключений. Разработчик компилятора может обойти эту ошибку, ограничив максимальный размер сгенерированного кода Java Virtual Machine для любого метода, метода инициализации экземпляра или статической инициализации (размера любого массива кода) до 65534 байт. - handler_pc
-
Значение элемента
handler_pcуказывает начало обработчика исключений. Значение элемента должно быть корректным индексом в массивеcodeи должно быть индексом кода инструкции. - catch_type
-
Если значение элемента
catch_typeне равно нулю, оно должно быть корректным индексом в таблицеconstant_pool. Элементconstant_poolв этой позиции должен быть структуройCONSTANT_Class_info(§4.4.1), представляющей класс исключений, который этот обработчик исключений предназначен для перехвата. Обработчик исключений будет вызван только в том случае, если сгенерированное исключение является экземпляром данного класса или одного из его подклассов.Валидатор проверяет, что класс является
Throwableили подклассомThrowable(§4.9.2).Если значение элемента
catch_typeравно нулю, этот обработчик исключений вызывается для всех исключений.Это используется для реализации
finally(§3.13).
- attributes_count
-
Значение элемента
attributes_countуказывает количество атрибутов атрибутаCode. - attributes[]
-
Каждое значение в таблице
attributesдолжно быть структуройattribute_info(§4.7).Атрибут
Codeможет иметь любое количество дополнительных атрибутов.Определенные этим стандартом атрибуты, которые должны присутствовать в таблице
attributesатрибутаCode, перечислены в Таблице 4.7-C.Правила, касающиеся атрибутов, определенных для появления в таблице
attributesатрибутаCode, приведены в §4.7.Правила, касающиеся непредварительно определенных атрибутов в таблице
attributesатрибутаCode, приведены в §4.7.1.
Атрибут StackMapTable — это атрибут переменной длины в таблице attributes атрибута Code (§4.7.3). Атрибут StackMapTable используется во время процесса проверки с помощью проверки типов (§4.10.1).
В таблице attributes атрибута Code может присутствовать не более одного атрибута StackMapTable.
В файле class, версия которого 50.0 или выше, если атрибут Code метода не содержит атрибут StackMapTable, он имеет явный атрибут карты стека (§4.10.1). Этот явный атрибут карты стека эквивалентен атрибуту StackMapTable с значением number_of_entries равным нулю.
Атрибут StackMapTable имеет следующий формат:
StackMapTable_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 number_of_entries;
stack_map_frame entries[number_of_entries];
}
Элементы структуры StackMapTable_attribute следующие:
- attribute_name_index
-
Значение элемента
attribute_name_indexдолжно быть корректным индексом в таблицеconstant_pool. Записьconstant_poolв этой таблице должна быть структуройCONSTANT_Utf8_info(§4.4.7), представляющей строку "StackMapTable". - attribute_length
-
Значение элемента
attribute_lengthуказывает длину атрибута, за исключением первых шести байт. - number_of_entries
-
Значение элемента
number_of_entriesзадаёт количество записейstack_map_frameв таблицеentries. - entries[]
-
Каждая запись в таблице
entriesописывает один кадр карты стека метода. Порядок кадров карты стека в таблицеentriesважен.
Кадр карты стека определяет (явно или неявно) смещение байткода, к которому он относится, и типы проверки для локальных переменных и элементов стека операндов для этого смещения.
Каждый кадр карты стека, описанный в таблице entries, опирается на предыдущий кадр для некоторых своих семантик. Первый кадр карты стека метода неявный и вычисляется из описания метода проверяющим типы (§4.10.1.6). Структура stack_map_frame в entries[0] поэтому описывает второй кадр карты стека метода.
Смещение байткода, к которому относится кадр карты стека, вычисляется путём взятия значения offset_delta, указанного в кадре (явно или неявно), и прибавления offset_delta + 1 к смещению байткода предыдущего кадра, если предыдущий кадр не является начальным кадром метода. В этом случае смещение байткода, к которому относится кадр карты стека, — это значение offset_delta, указанное в кадре.
Используя смещение, а не хранение фактического смещения байткода, мы гарантируем, по определению, что кадры карты стека отсортированы правильно. Кроме того, используя последовательно формулу offset_delta + 1 для всех явных кадров (в отличие от неявного первого кадра), мы гарантируем отсутствие дубликатов.
Мы говорим, что инструкция в байткоде имеет соответствующий кадр карты стека, если инструкция начинается со смещения i в массиве code атрибута Code, и атрибут Code имеет атрибут StackMapTable, чей массив entries содержит кадр карты стека, применяемый к смещению байткода i.
Тип проверки определяет тип одного или двух местоположений, где местоположение — это либо одна локальная переменная, либо один элемент стека операндов. Тип проверки представлен объединённым типом, verification_type_info, который состоит из однобайтового тега, указывающего, какой элемент объединения используется, за которым следуют ноль или более байт, предоставляющих дополнительную информацию о теге.
union verification_type_info {
Top_variable_info;
Integer_variable_info;
Float_variable_info;
Long_variable_info;
Double_variable_info;
Null_variable_info;
UninitializedThis_variable_info;
Object_variable_info;
Uninitialized_variable_info;
}
Тип проверки, который указывает одно местоположение в массиве локальных переменных или в стеке операндов, представлен следующими элементами объединения verification_type_info:
-
Элемент
Top_variable_infoуказывает, что локальная переменная имеет тип проверкиtop.Top_variable_info { u1 tag = ITEM_Top; /* 0 */ } -
Элемент
Integer_variable_infoуказывает, что местоположение имеет тип проверкиint.Integer_variable_info { u1 tag = ITEM_Integer; /* 1 */ } -
Элемент
Float_variable_infoуказывает, что местоположение имеет тип проверкиfloat.Float_variable_info { u1 tag = ITEM_Float; /* 2 */ } -
Тип
Null_variable_infoуказывает, что местоположение имеет тип проверкиnull.Null_variable_info { u1 tag = ITEM_Null; /* 5 */ } -
Элемент
UninitializedThis_variable_infoуказывает, что местоположение имеет тип проверкиuninitializedThis.UninitializedThis_variable_info { u1 tag = ITEM_UninitializedThis; /* 6 */ } -
Элемент
Object_variable_infoуказывает, что местоположение имеет тип проверки, который является классом, представленным структуройCONSTANT_Class_info(§4.4.1), найденной в таблицеconstant_poolпо индексу, заданномуcpool_index.Object_variable_info { u1 tag = ITEM_Object; /* 7 */ u2 cpool_index; } -
Элемент
Uninitialized_variable_infoуказывает, что местоположение имеет тип проверкиuninitialized(Offset). ЭлементOffsetуказывает смещение в массивеcodeатрибутаCode, содержащего этот атрибутStackMapTable, инструкции new (§new), которая создала объект, хранящийся в местоположении.Uninitialized_variable_info { u1 tag = ITEM_Uninitialized; /* 8 */ u2 offset; }
Тип проверки, который указывает два местоположения в массиве локальных переменных или в стеке операндов, представлен следующими элементами объединения verification_type_info:
-
Элемент
Long_variable_infoуказывает, что первое из двух местоположений имеет тип проверкиlong.Long_variable_info { u1 tag = ITEM_Long; /* 4 */ } -
Элемент
Double_variable_infoуказывает, что первое из двух местоположений имеет тип проверкиdouble.Double_variable_info { u1 tag = ITEM_Double; /* 3 */ } -
Элементы
Long_variable_infoиDouble_variable_infoуказывают тип проверки второго из двух местоположений следующим образом:-
Если первое из двух местоположений — локальная переменная, то:
-
Это не локальная переменная с наибольшим индексом.
-
Следующая локальная переменная с большим номером имеет тип проверки
top.
-
-
Если первое из двух местоположений — элемент стека операндов, то:
-
Это не самое верхнее местоположение в стеке операндов.
-
Следующее местоположение ближе к вершине стека операндов имеет тип проверки
top.
-
-
Кадр карты стека представляется объединением типов, stack_map_frame, которое состоит из однобайтового тега, указывающего, какой элемент объединения используется, за которым следуют ноль или более байт, предоставляющих дополнительную информацию о теге.
union stack_map_frame {
same_frame;
same_locals_1_stack_item_frame;
same_locals_1_stack_item_frame_extended;
chop_frame;
same_frame_extended;
append_frame;
full_frame;
}
Этот тег указывает тип кадра кадра карты стека:
-
Тип кадра
same_frameпредставлен тегами в диапазоне [0-63]. Этот тип кадра указывает, что кадр имеет точно такие же локальные переменные, как предыдущий кадр, и что стек операндов пуст. Значениеoffset_deltaдля кадра равно значению элемента тега,frame_type.same_frame { u1 frame_type = SAME; /* 0-63 */ } -
Тип кадра
same_locals_1_stack_item_frameпредставлен тегами в диапазоне [64, 127]. Этот тип кадра указывает, что кадр имеет точно такие же локальные переменные, как предыдущий кадр, и что стек операндов имеет одну запись. Значениеoffset_deltaдля кадра определяется по формулеframe_type - 64. Тип проверки одной записи стека следует за типом кадра.same_locals_1_stack_item_frame { u1 frame_type = SAME_LOCALS_1_STACK_ITEM; /* 64-127 */ verification_type_info stack[1]; } -
Теги в диапазоне [128-246] зарезервированы для будущего использования.
-
Тип кадра
same_locals_1_stack_item_frame_extendedпредставлен тегом 247. Этот тип кадра указывает, что кадр имеет точно такие же локальные переменные, как предыдущий кадр, и что стек операндов имеет одну запись. Значениеoffset_deltaдля кадра задаётся явно, в отличие от типа кадраsame_locals_1_stack_item_frame. Тип проверки одной записи стека появляется послеoffset_delta.same_locals_1_stack_item_frame_extended { u1 frame_type = SAME_LOCALS_1_STACK_ITEM_EXTENDED; /* 247 */ u2 offset_delta; verification_type_info stack[1]; } -
Тип кадра
chop_frameпредставлен тегами в диапазоне [248-250]. Этот тип кадра указывает, что кадр имеет такие же локальные переменные, как предыдущий кадр, за исключением последних k локальных переменных, которых нет, и что стек операндов пуст. Значение k определяется по формуле251 - frame_type. Значениеoffset_deltaдля кадра задаётся явно.Предположим, что типы проверки локальных переменных в предыдущем кадре задаются
locals, массив, структурированный как в типе кадраfull_frame. Еслиlocals[M-1]в предыдущем кадре представляло локальную переменную X иlocals[M]представляло локальную переменную Y, то эффект удаления одной локальной переменной заключается в том, чтоlocals[M-1]в новом кадре представляет локальную переменную X, аlocals[M]не определено.Ошибка, если k больше, чем количество локальных переменных в
localsдля предыдущего кадра, то есть если количество локальных переменных в новом кадре будет меньше нуля. -
Тип кадра
same_frame_extendedпредставлен тегом 251. Этот тип кадра указывает, что кадр имеет точно такие же локальные переменные, как предыдущий кадр, и что стек операндов пуст. Значениеoffset_deltaдля кадра задаётся явно, в отличие от типа кадраsame_frame.same_frame_extended { u1 frame_type = SAME_FRAME_EXTENDED; /* 251 */ u2 offset_delta; } -
Тип кадра
append_frameпредставлен тегами в диапазоне [252-254]. Этот тип кадра указывает, что кадр имеет такие же локальные переменные, как предыдущий кадр, за исключением того, что определено k дополнительных локальных переменных, и что стек операндов пуст. Значение k определяется по формулеframe_type - 251. Значениеoffset_deltaдля кадра задаётся явно.append_frame { u1 frame_type = APPEND; /* 252-254 */ u2 offset_delta; verification_type_info locals[frame_type - 251]; }Нулевой элемент в
localsпредставляет тип проверки первой дополнительной локальной переменной. Еслиlocals[M]представляет локальную переменнуюN, то:-
locals[M+1]представляет локальную переменнуюN+1, еслиlocals[M]является одним изTop_variable_info,Integer_variable_info,Float_variable_info,Null_variable_info,UninitializedThis_variable_info,Object_variable_infoилиUninitialized_variable_info; и -
locals[M+1]представляет локальную переменнуюN+2, еслиlocals[M]является либоLong_variable_info, либоDouble_variable_info.
Ошибка, если для любого индекса i,
locals[i]представляет локальную переменную, индекс которой больше максимального количества локальных переменных для метода. -
-
Тип кадра
full_frameпредставлен тегом 255. Значениеoffset_deltaдля кадра задаётся явно.full_frame { u1 frame_type = FULL_FRAME; /* 255 */ u2 offset_delta; u2 number_of_locals; verification_type_info locals[number_of_locals]; u2 number_of_stack_items; verification_type_info stack[number_of_stack_items]; }Нулевой элемент в
localsпредставляет тип проверки локальной переменной 0. Еслиlocals[M]представляет локальную переменнуюN, то:-
locals[M+1]представляет локальную переменнуюN+1, еслиlocals[M]является одним изTop_variable_info,Integer_variable_info,Float_variable_info,Null_variable_info,UninitializedThis_variable_info,Object_variable_infoилиUninitialized_variable_info; и -
locals[M+1]представляет локальную переменнуюN+2, еслиlocals[M]является либоLong_variable_info, либоDouble_variable_info.
Ошибка, если для любого индекса i,
locals[i]представляет локальную переменную, индекс которой больше максимального количества локальных переменных для метода.Нулевой элемент в
stackпредставляет тип проверки дна стека операндов, а последующие элементы вstackпредставляют типы проверки элементов стека, более близких к вершине стека операндов. Мы называем дно стека операндов элементом стека 0, а последующие элементы стека операндов — элементами стека 1, 2 и т. д. Еслиstack[M]представляет элемент стекаN, то:-
stack[M+1]представляет элемент стекаN+1, еслиstack[M]является одним изTop_variable_info,Integer_variable_info,Float_variable_info,Null_variable_info,UninitializedThis_variable_info,Object_variable_infoилиUninitialized_variable_info; и -
stack[M+1]представляет элемент стекаN+2, еслиstack[M]является либоLong_variable_info, либоDouble_variable_info.
Ошибка, если для любого индекса i,
stack[i]представляет элемент стека, индекс которого больше максимального размера стека операндов для метода. -
Атрибут 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; они проверяются только на этапе компиляции.
Атрибут InnerClasses — атрибут переменной длины в таблице attributes структуры ClassFile (§4.1).
Если пул констант класса или интерфейса C содержит хотя бы одну запись CONSTANT_Class_info (§4.4.1), представляющую класс или интерфейс, не являющийся членом пакета, то в таблице attributes структуры ClassFile для C должен быть ровно один атрибут InnerClasses.
Атрибут InnerClasses имеет следующий формат:
InnerClasses_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 number_of_classes;
{ u2 inner_class_info_index;
u2 outer_class_info_index;
u2 inner_name_index;
u2 inner_class_access_flags;
} classes[number_of_classes];
}
Элементы структуры InnerClasses_attribute таковы:
- attribute_name_index
-
Значение элемента
attribute_name_indexдолжно быть допустимым индексом в таблицеconstant_pool. Записьconstant_poolв этой позиции должна быть структуройCONSTANT_Utf8_info(§4.4.7), представляющей строку "InnerClasses". - attribute_length
-
Значение элемента
attribute_lengthуказывает длину атрибута, исключая первые шесть байт. - number_of_classes
-
Значение элемента
number_of_classesуказывает количество элементов в массивеclasses. - classes[]
-
Каждая запись в таблице
constant_pool, которая представляет класс или интерфейс C, не являющийся членом пакета, должна иметь ровно одну соответствующую запись в массивеclasses.Если класс или интерфейс имеет члены, которые являются классами или интерфейсами, его таблица
constant_pool(и, следовательно, его атрибутInnerClasses) должны ссылаться на каждого такого члена (JLS §13.1), даже если этот член не упоминается иначе в классе.Кроме того, таблица
constant_poolкаждого вложенного класса и вложенного интерфейса должна ссылаться на его внешний класс, таким образом, каждый вложенный класс и вложенный интерфейс будет иметь информациюInnerClassesдля каждого внешнего класса и для каждого из своих вложенных классов и интерфейсов.Каждый элемент массива
classesсодержит следующие четыре элемента:- inner_class_info_index
-
Значение элемента
inner_class_info_indexдолжно быть допустимым индексом в таблицеconstant_pool. Записьconstant_poolв этой позиции должна быть структуройCONSTANT_Class_info, представляющей C. - outer_class_info_index
-
Если C не является членом класса или интерфейса — то есть, если C — это базовый класс или интерфейс (JLS §7.6) или локальный класс (JLS §14.3) или анонимный класс (JLS §15.9.5) — то значение элемента
outer_class_info_indexдолжно быть равно нулю.В противном случае значение элемента
outer_class_info_indexдолжно быть допустимым индексом в таблицеconstant_pool, а запись по этому индексу должна быть структуройCONSTANT_Class_info, представляющей класс или интерфейс, членом которого является C. Значение элементаouter_class_info_indexне должно быть равно значению элементаinner_class_info_index. - inner_name_index
-
Если C анонимный (JLS §15.9.5), значение элемента
inner_name_indexдолжно быть равно нулю.В противном случае значение элемента
inner_name_indexдолжно быть допустимым индексом в таблицеconstant_pool, а запись по этому индексу должна быть структуройCONSTANT_Utf8_info, которая представляет исходное простое имя C, как указано в исходном коде, из которого был скомпилирован этот файлclass. - inner_class_access_flags
-
Значение элемента
inner_class_access_flags— это маска флагов, используемых для обозначения разрешений доступа и свойств класса или интерфейса C, как объявлено в исходном коде, из которого был скомпилирован этот файлclass. Она используется компилятором для восстановления исходной информации, когда исходный код недоступен. Флаги указаны в таблице 4.7.6-A.Таблица 4.7.6-A. Флаги доступа и свойств вложенных классов
Название флага Значение Толкование ACC_PUBLIC0x0001 Отмечен или неявный publicв исходном коде.ACC_PRIVATE0x0002 Отмечен privateв исходном коде.ACC_PROTECTED0x0004 Отмечен protectedв исходном коде.ACC_STATIC0x0008 Отмечен или неявный staticв исходном коде.ACC_FINAL0x0010 Отмечен или неявный finalв исходном коде.ACC_INTERFACE0x0200 Был interfaceв исходном коде.ACC_ABSTRACT0x0400 Отмечен или неявный abstractв исходном коде.ACC_SYNTHETIC0x1000 Объявлен синтетическим; отсутствует в исходном коде. ACC_ANNOTATION0x2000 Объявлен как интерфейс аннотации. ACC_ENUM0x4000 Объявлен как класс enum.Все биты элемента
inner_class_access_flags, не назначенные в таблице 4.7.6-A, зарезервированы для будущего использования. Они должны быть установлены в ноль в сгенерированных файлахclassи должны игнорироваться реализациями Java Virtual Machine.
Если файл
classимеет номер версии 51.0 или выше и имеет атрибутInnerClassesв своей таблицеattributes, то для всех записей в массивеclassesатрибутаInnerClassesзначение элементаouter_class_info_indexдолжно быть равно нулю, если значение элементаinner_name_indexравно нулю.
Реализация Java Virtual Machine от Oracle не проверяет согласованность атрибута InnerClasses с файлом class, представляющим класс или интерфейс, на который ссылается атрибут.
Атрибут EnclosingMethod — атрибут фиксированной длины в таблице attributes структуры ClassFile (§4.1). Класс должен иметь атрибут EnclosingMethod только в том случае, если он представляет локальный класс или анонимный класс (JLS §14.3, JLS §15.9.5).
В таблице attributes структуры ClassFile может быть не более одного атрибута EnclosingMethod.
Атрибут EnclosingMethod имеет следующий формат:
EnclosingMethod_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 class_index;
u2 method_index;
}
Элементы структуры EnclosingMethod_attribute таковы:
- attribute_name_index
-
Значение элемента
attribute_name_indexдолжно быть допустимым индексом в таблицеconstant_pool. Записьconstant_poolв этой позиции должна быть структуройCONSTANT_Utf8_info(§4.4.7), представляющей строку "EnclosingMethod". - attribute_length
-
Значение элемента
attribute_lengthдолжно быть равно четырём. - class_index
-
Значение элемента
class_indexдолжно быть допустимым индексом в таблицеconstant_pool. Записьconstant_poolв этой позиции должна быть структуройCONSTANT_Class_info(§4.4.1), представляющей самый внутренний класс, который включает объявление текущего класса. - method_index
-
Если текущий класс не вложен непосредственно в метод или конструктор, то значение элемента
method_indexдолжно быть равно нулю.В частности,
method_indexдолжен быть равен нулю, если текущий класс был вложен в исходном коде инициализатором экземпляра, статической инициализацией, инициализатором экземпляра переменной или инициализатором переменной класса. (Первые два относятся как к локальным, так и к анонимным классам, а последние два — к анонимным классам, объявленным в правой части присваивания переменной.)В противном случае значение элемента
method_indexдолжно быть допустимым индексом в таблицеconstant_pool. Записьconstant_poolв этой позиции должна быть структуройCONSTANT_NameAndType_info(§4.4.6), представляющей имя и тип метода в классе, на который ссылается атрибутclass_indexвыше.Компилятор Java отвечает за обеспечение того, чтобы метод, определенный через
method_index, действительно был ближайшим лексически охватывающим методом класса, содержащего этот атрибутEnclosingMethod.
Атрибут Synthetic — это атрибут фиксированной длины в таблице attributes структуры ClassFile, field_info или method_info (§4.1, §4.5, §4.6). Элемент класса, который не присутствует в исходном коде, должен быть помечен атрибутом Synthetic или иметь установленный флаг ACC_SYNTHETIC. Исключения из этого требования составляют члены, сгенерированные компилятором, которые не считаются артефактами реализации, а именно:
Атрибут Synthetic был введён в JDK 1.1 для поддержки вложенных классов и интерфейсов.
Ограничение формата файла class заключается в том, что только формальные параметры и модули могут быть помечены как ACC_MANDATED (§4.7.24, §4.7.25), чтобы указать, что, несмотря на генерацию компилятором, они не считаются артефактами реализации. Нет способа пометить другие сгенерированные компилятором конструкции, чтобы они также не считались артефактами реализации (JLS §13.1). Это ограничение означает, что рефлексивные API платформы Java SE могут неточно указывать статус «обязательности» таких конструкций.
Атрибут Synthetic имеет следующий формат:
Synthetic_attribute {
u2 attribute_name_index;
u4 attribute_length;
}
Элементы структуры Synthetic_attribute следующие:
- attribute_name_index
-
Значение элемента
attribute_name_indexдолжно быть допустимым индексом в таблицеconstant_pool. Элементconstant_poolв этом индексе должен быть структуройCONSTANT_Utf8_info(§4.4.7) с представлением строки "Synthetic". - attribute_length
-
Значение элемента
attribute_lengthдолжно быть равно нулю.
Атрибут Signature — это атрибут фиксированной длины в таблице attributes структуры ClassFile, field_info, method_info или record_component_info (§4.1, §4.5, §4.6, §4.7.30). Атрибут Signature хранит подпись (§4.7.9.1) для класса, интерфейса, конструктора, метода, поля или компонента записи, объявление которого в языке программирования Java использует переменные типа или параметризованные типы. Подробности о таких конструкциях см. в Спецификации языка Java, издание Java SE 21.
В таблице attributes структуры ClassFile, field_info, method_info или record_component_info может быть не более одного атрибута Signature.
Атрибут Signature имеет следующий формат:
Signature_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 signature_index;
}
Элементы структуры Signature_attribute следующие:
- attribute_name_index
-
Значение элемента
attribute_name_indexдолжно быть допустимым индексом в таблицеconstant_pool. Элементconstant_poolв этом индексе должен быть структуройCONSTANT_Utf8_info(§4.4.7) с представлением строки "Signature". - attribute_length
-
Значение элемента
attribute_lengthдолжно быть равно двум. - signature_index
-
Значение элемента
signature_indexдолжно быть допустимым индексом в таблицеconstant_pool. Элементconstant_poolв этом индексе должен быть структуройCONSTANT_Utf8_info(§4.4.7) представляющей подпись класса, если этот атрибутSignatureявляется атрибутом структурыClassFile; подпись метода, если этот атрибутSignatureявляется атрибутом структурыmethod_info; или подпись поля в противном случае.
Реализация виртуальной машины Java от Oracle не проверяет корректность атрибутов Signature во время загрузки или линковки классов. Вместо этого атрибуты Signature проверяются методами библиотек классов платформы Java SE, которые экспонируют общие подписи классов, интерфейсов, конструкторов, методов и полей. Примеры включают getGenericSuperclass в Class и toGenericString в java.lang.reflect.Executable.
Подписи кодируют объявления, написанные на языке программирования Java, которые используют типы, находящиеся за пределами системы типов Java Virtual Machine. Они поддерживают рефлексию и отладку, а также компиляцию, когда доступны только файлы class.
Компилятор Java должен генерировать подпись для любого класса, интерфейса, конструктора, метода, поля или компонента записи, объявление которого использует переменные типа или параметризованные типы. В частности, компилятор Java должен генерировать:
-
Подпись класса для любого объявления класса или интерфейса, которое является либо обобщенным, либо имеет параметризованный тип в качестве суперкласса или суперинтерфейса, или то и другое.
-
Подпись метода для любого объявления метода или конструктора, которое является либо обобщенным, либо имеет переменную типа или параметризованный тип в качестве возвращаемого типа или типа формального параметра, либо имеет переменную типа в
throws-законе, или любую комбинацию из этого.Если
throws-закон объявления метода или конструктора не включает переменные типа, то компилятор может рассматривать объявление как не имеющееthrows-закона для целей генерации подписи метода. -
Подпись поля для любого объявления поля, формального параметра, локальной переменной или компонента записи, тип которого использует переменную типа или параметризованный тип.
Подписи задаются с помощью грамматики, которая следует обозначениям §4.3.1. В дополнение к этой нотации:
-
Синтаксис [x] в правой части продукции обозначает ноль или одно вхождение x. То есть, x является необязательным символом. Альтернатива, содержащая необязательный символ, на самом деле определяет две альтернативы: одну, которая опускает необязательный символ, и одну, которая его включает.
-
Очень длинная правая часть может быть продолжена на второй строке, ясно отступив вторую строку.
Грамматика включает терминальный символ Идентификатор, чтобы обозначить имя типа, поля, метода, формального параметра, локальной переменной или переменной типа, как сгенерированное Java-компилятором. Такое имя не должно содержать ни одного из ASCII-символов . ; [ / < > : (то есть, символы, запрещённые в именах методов (§4.2.2), а также двоеточие), но может содержать символы, которые не должны появляться в идентификаторе в языке программирования Java (JLS §3.8).
Подписи опираются на иерархию нетерминалов, известных как подписи типов:
-
Подпись типа Java представляет собой либо ссылочный тип, либо примитивный тип языка программирования Java.
JavaTypeSignature:Следующая продукция из §4.3.2 повторяется здесь для удобства:
BaseType:(один из)
BCDFIJSZ -
Подпись ссылочного типа представляет собой ссылочный тип языка программирования Java, то есть тип класса или интерфейса, переменная типа или тип массива.
Подпись типа класса представляет собой (возможно, параметризованный) тип класса или интерфейса. Подпись типа класса должна быть сформулирована таким образом, чтобы её можно было надёжно сопоставить с бинарным именем обозначаемого класса путём стирания любых аргументов типа и преобразования каждого символа
.в символ$.Подпись переменной типа представляет собой переменную типа.
Подпись типа массива представляет собой одну размерность типа массива.
ReferenceTypeSignature:ClassTypeSignature:PackageSpecifier:Identifier/{PackageSpecifier}SimpleClassTypeSignature:Identifier [TypeArguments]TypeArguments:TypeArgument:WildcardIndicator:+
-ClassTypeSignatureSuffix:TypeVariableSignature:TIdentifier;ArrayTypeSignature:
Подпись класса кодирует информацию о типе (возможно, обобщённого) объявления класса или интерфейса. Она описывает все параметры типа класса или интерфейса и перечисляет его (возможно, параметризованные) непосредственные суперкласс и непосредственные суперинтерфейсы, если таковые имеются. Параметр типа описывается его именем, за которым следуют все возможные ограничения класса и интерфейса.
Подпись метода кодирует информацию о типе (возможно, обобщённого) объявления метода. Она описывает все параметры типа метода; типы всех формальных параметров; (возможно, параметризованный) возвращаемый тип, если таковой имеется; и типы всех исключений, объявленных в throws-законе метода.
Следующая продукция из §4.3.3 приводится здесь для удобства:
VПодпись метода, закодированная атрибутом Signature, может не соответствовать точно описателю метода в структуре method_info (§4.3.3). В частности, нет гарантии, что количество типов формальных параметров в подписи метода будет таким же, как количество описателей параметров в описателе метода. Эти числа совпадают для большинства методов, но некоторые конструкторы языка программирования Java имеют неявный объявленный параметр, который компилятор представляет описателем параметра, но может опустить из подписи метода. См. примечание в §4.7.18 для аналогичной ситуации, связанной с аннотациями параметров.
Подпись поля кодирует тип (возможно, параметризованный) объявления поля, формального параметра, локальной переменной или компонента записи.
Атрибут 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. Она не будет интерпретироваться как имя каталога, содержащего файл, или абсолютный путь к файлу; такая платформа-специфичная дополнительная информация должна предоставляться интерпретатором времени выполнения или инструментом разработки в момент фактического использования имени файла.
Атрибут 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.
Атрибут 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даёт соответствующий номер строки в исходном файле.
Атрибут 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.
Атрибут 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.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` должно быть равно нулю.
Атрибут RuntimeVisibleAnnotations — это атрибут переменной длины в таблице атрибутов структуры класса, интерфейса, поля, метода или компонента записи (§4.1, §4.5, §4.6, §4.7.30). Атрибут RuntimeVisibleAnnotations хранит видимые на этапе выполнения аннотации объявления соответствующего класса, поля, метода или компонента записи.
В таблице атрибутов структуры класса, интерфейса, поля, метода или компонента записи может быть не более одного атрибута RuntimeVisibleAnnotations.
Атрибут RuntimeVisibleAnnotations имеет следующий формат:
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имеет следующий формат: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_value_pair` содержит два элемента:
- element_name_index
-
Значение элемента `element_name_index` должно быть допустимым индексом в таблице констант. Элемент таблицы констант по этому индексу должен быть структурой `CONSTANT_Utf8_info` (§4.4.7). Элемент указывает имя элемента пары "имя-значение", представленной данным элементом.
Другими словами, элемент обозначает элемент интерфейса аннотаций, заданный
type_index. - value
-
Значение элемента `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;
}
Элемент использует один символ ASCII для указания типа значения пары «элемент-значение». Это определяет, какой элемент союза используется. Таблица 4.7.16.1-A отображает допустимые символы для элемента , тип, указываемый каждым символом, и элемент, используемый в союзе для каждого символа. Четвертый столбец таблицы используется в описании одного элемента союза.
Таблица 4.7.16.1-A. Интерпретация значений тегов как типов
| Элемент | Тип | Элемент | Тип константы |
|---|---|---|---|
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
-
Элемент обозначает константу примитивного типа или типа
Stringв качестве значения этой пары «элемент-значение».Значение элемента должно быть допустимым индексом в таблице
constant_pool. Записьconstant_poolв этом индексе должна быть подходящего типа для элемента , как указано в четвёртом столбце таблицы 4.7.16.1-A. - enum_const_value
-
Элемент обозначает константу перечисления в качестве значения этой пары «элемент-значение».
Элемент состоит из следующих двух элементов:
- 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
-
Значение элемента должно быть допустимым индексом в таблице
constant_pool. Записьconstant_poolв этом индексе должна быть структуройCONSTANT_Utf8_info(§4.4.7). Записьconstant_poolдаёт простое имя константы перечисления, представленной этой структуройelement_value.
- 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(§4.7.16), которая предоставляет аннотацию, представленную этой структуройelement_value. - array_value
-
Элемент обозначает массив как значение этой пары «элемент-значение».
Элемент состоит из следующих двух элементов:
- num_values
-
Значение элемента указывает количество элементов в массиве, представленном этой структурой
element_value. - values[]
-
Каждое значение в таблице
valuesдаёт соответствующий элемент массива, представленного этой структуройelement_value.
Атрибут RuntimeInvisibleAnnotations — это атрибут переменной длины в таблице attributes структуры ClassFile, field_info, method_info или record_component_info (§4.1, §4.5, §4.6, §4.7.30). Атрибут RuntimeInvisibleAnnotations хранит аннотации, невидимые во время выполнения, на объявлении соответствующего класса, метода, поля или компонента записи.
В таблице attributes структуры ClassFile, field_info, method_info или record_component_info может находиться не более одного атрибута RuntimeInvisibleAnnotations.
Атрибут RuntimeInvisibleAnnotations похож на атрибут RuntimeVisibleAnnotations (§4.7.16), за исключением того, что аннотации, представленные атрибутом RuntimeInvisibleAnnotations, не должны предоставляться API рефлексии для возврата, если Java виртуальная машина не получила указаний на сохранение этих аннотаций, например, через флаг командной строки. В отсутствии таких инструкций 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.
Атрибут 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]для хранения аннотаций, соответствующих второму описателю параметра.
Атрибут RuntimeInvisibleParameterAnnotations — это атрибут переменной длины в таблице attributes структуры method_info (§4.6). Атрибут RuntimeInvisibleParameterAnnotations хранит анотации, невидимые во время выполнения, на объявлениях формальных параметров соответствующего метода.
В таблице attributes структуры method_info может быть не более одного атрибута RuntimeInvisibleParameterAnnotations.
Атрибут RuntimeInvisibleParameterAnnotations аналогичен атрибуту RuntimeVisibleParameterAnnotations (§4.7.18), за исключением того, что анотации, представленные атрибутом RuntimeInvisibleParameterAnnotations, не должны предоставляться для возврата рефлексивными API, если Java Virtual Machine не получила специальной инструкции на сохранение этих анотаций, например, с помощью флага командной строки. В противном случае Java Virtual Machine игнорирует этот атрибут.
Атрибут 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]не соответствует первому описателю параметра в описателе метода.
Атрибут RuntimeVisibleTypeAnnotations — атрибут переменной длины в таблице attributes структуры ClassFile, field_info, method_info, или record_component_info, или атрибут Code (§4.1, §4.5, §4.6, §4.7.30, §4.7.3). Атрибут RuntimeVisibleTypeAnnotations хранит видимые во время выполнения аннотации типов, используемых в объявлении соответствующего класса, поля, метода или компонента записи, или в выражении в теле соответствующего метода. Атрибут RuntimeVisibleTypeAnnotations также хранит видимые во время выполнения аннотации объявлений параметров типа для обобщенных классов, интерфейсов, методов и конструкторов.
В таблице attributes структуры ClassFile, field_info, method_info или record_component_info или атрибута Code может быть не более одного атрибута RuntimeVisibleTypeAnnotations.
Таблица 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_type0x10-0x17 и 0x40-0x42 соответствуют контекстам типов 1-11, а значенияtarget_type0x43-0x4B соответствуют контекстам типов 12-17.Значение элемента
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 | тип в объявлении поля или компонента записи | |
| 0x14 | тип возвращаемого значения метода или конструктора | method_info |
| 0x15 | тип получателя метода или конструктора | method_info |
| 0x16 | тип в объявлении формального параметра метода, конструктора или лямбда-выражения | method_info |
| 0x17 | тип в части throws метода или конструктора | method_info |
| 0x40-0x4B | типы в объявлениях локальных переменных, переменных ресурсов, параметрах исключений, выражениях | Code |
Элементы объединения 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, указывает первый аргумент типа.
В любом месте, где тип используется в объявлении или выражении, структура type_path определяет, какая часть типа аннотирована. Аннотация может появиться на самом типе, но если тип является ссылочным типом, то есть дополнительные места, где может появиться аннотация:
-
Если тип массива T
[]используется в объявлении или выражении, то аннотация может появиться на любом составляющем типе типа массива, включая тип элемента. -
Если вложенный тип T1
.T2 используется в объявлении или выражении, то аннотация может появиться на имени внутреннего типа члена и любом окружающем типе, для которого допустима аннотация типа (JLS §9.7.4). -
Если параметризованный тип T
<A>или T<? extends A>или T<? super A>используется в объявлении или выражении, то аннотация может появиться на любом типе аргумента или на границе любого аргумента типа-подстановки.
Например, рассмотрим разные части String[][], которые аннотированы в:
@Foo String[][] // Annotates the class type String String @Foo [][] // Annotates the array type String[][] String[] @Foo [] // Annotates the array type String[]
или разные части вложенного типа Outer.Middle.Inner, которые аннотированы в:
@Foo Outer.Middle.Inner Outer.@Foo Middle.Inner Outer.Middle.@Foo Inner
или разные части параметризованных типов Map<String,Object> и List<...>, которые аннотированы в:
@Foo Map<String,Object> Map<@Foo String,Object> Map<String,@Foo Object> List<@Foo ? extends String> List<? extends @Foo String>
Структура type_path имеет следующий формат:
type_path {
u1 path_length;
{ u1 type_path_kind;
u1 type_argument_index;
} path[path_length];
}
Значение элемента path_length задаёт количество записей в массиве path:
-
Если значение
path_lengthравно0, и аннотируемый тип является вложенным типом, то аннотация применяется к самой внешней части типа, для которой допустима аннотация типа. -
Если значение
path_lengthравно0, и аннотируемый тип не является вложенным типом, то аннотация появляется непосредственно на самом типе. -
Если значение
path_lengthне равно нулю, то каждая запись в массивеpathпредставляет итерационный шаг слева направо к точному расположению аннотации в типе массива, вложенном типе или параметризованном типе. (В типе массива итерация посещает сам тип массива, затем его составной тип, затем составной тип этого составного типа и так далее, пока не достигнет типа элемента.) Каждая запись содержит следующие два элемента:- type_path_kind
-
Допустимые значения для элемента
type_path_kindперечислены в таблице 4.7.20.2-A.Таблица 4.7.20.2-A. Интерпретация значений
type_path_kindЗначение Интерпретация 0Аннотация глубже в типе массива 1Аннотация глубже во вложенном типе 2Аннотация на границе аргумента типа-подстановки параметризованного типа 3Аннотация на аргументе типа параметризованного типа
- type_argument_index
-
Если значение элемента
type_path_kindравно0,1или2, то значение элементаtype_argument_indexравно0.Если значение элемента
type_path_kindравно3, то значение элементаtype_argument_indexуказывает, какой аргумент типа параметризованного типа аннотирован, где0обозначает первый аргумент типа параметризованного типа.
Таблица 4.7.20.2-B. Структуры type_path для @A Map<@B ? extends @C String, @D List<@E Object>>
| Аннотация | path_length | path |
|---|---|---|
@A | 0 | [] |
@B | 1 | [{type_path_kind: 3; type_argument_index: 0}] |
@C | 2 | [{type_path_kind: 3; type_argument_index: 0}, {type_path_kind: 2; type_argument_index: 0}] |
@D | 1 | [{type_path_kind: 3; type_argument_index: 1}] |
@E | 2 | [{type_path_kind: 3; type_argument_index: 1}, {type_path_kind: 3; type_argument_index: 0}] |
Таблица 4.7.20.2-C. Структуры type_path для @I String @F [] @G [] @H []
| Аннотация | path_length | path |
|---|---|---|
@F | 0 | [] |
@G | 1 | [{type_path_kind: 0; type_argument_index: 0}] |
@H | 2 | [{type_path_kind: 0; type_argument_index: 0}, {type_path_kind: 0; type_argument_index: 0}] |
@I | 3 | [{type_path_kind: 0; type_argument_index: 0}, {type_path_kind: 0; type_argument_index: 0}, {type_path_kind: 0; type_argument_index:
0}] |
Таблица 4.7.20.2-D. Структуры type_path для @A List<@B Comparable<@F Object @C [] @D [] @E
[]>>
| Аннотация | path_length | path |
|---|---|---|
@A | 0 | [] |
@B | 1 | [{type_path_kind: 3; type_argument_index: 0}] |
@C | 2 | [{type_path_kind: 3; type_argument_index: 0}, {type_path_kind: 3; type_argument_index: 0}] |
@D | 3 | [{type_path_kind: 3; type_argument_index: 0}, {type_path_kind: 3; type_argument_index: 0}, {type_path_kind: 0; type_argument_index:
0}] |
@E | 4 | [{type_path_kind: 3; type_argument_index: 0}, {type_path_kind: 3; type_argument_index: 0}, {type_path_kind: 0; type_argument_index:
0}, {type_path_kind: 0; type_argument_index: 0}] |
@F | 5 | [{type_path_kind: 3; type_argument_index: 0}, {type_path_kind: 3; type_argument_index: 0}, {type_path_kind: 0; type_argument_index:
0}, {type_path_kind: 0; type_argument_index: 0}, {type_path_kind: 0; type_argument_index: 0}] |
Таблица 4.7.20.2-E. Структуры type_path для @A Outer . @B Middle . @C Inner
| Предполагается: |
class Outer {
class Middle {
class Inner {}
}
} | |
|---|---|---|
| Аннотация | path_length | path |
@A | 0 | [] |
@B | 1 | [{type_path_kind: 1; type_argument_index: 0}] |
@C | 2 | [{type_path_kind: 1; type_argument_index: 0}, {type_path_kind: 1; type_argument_index: 0}] |
Таблица 4.7.20.2-F. Структуры type_path для Outer . @A MiddleStatic . @B Inner
| Предполагается: |
class Outer {
static class MiddleStatic {
class Inner {}
}
} | |
|---|---|---|
| Аннотация | path_length | path |
@A | 0 | [] |
@B | 1 | [{type_path_kind: 1; type_argument_index: 0}] |
В типе Outer . MiddleStatic . Inner аннотации типа на простом имени Outer недопустимы, потому что имя типа справа, MiddleStatic, не относится к внутреннему классу Outer. |
Таблица 4.7.20.2-G. Структуры type_path для Outer . MiddleStatic . @A InnerStatic
| Предполагается: |
class Outer {
static class MiddleStatic {
static class InnerStatic {}
}
} | |
|---|---|---|
| Аннотация | path_length | path |
@A | 0 | [] |
В типе Outer . MiddleStatic . InnerStatic аннотации типа на простом имени Outer недопустимы, потому что имя типа справа, MiddleStatic, не относится к внутреннему классу Outer. Аналогично, аннотации типа на простом имени MiddleStatic недопустимы, потому что имя типа справа, InnerStatic, не относится к внутреннему классу MiddleStatic. |
Таблица 4.7.20.2-H. Структуры type_path для Outer . Middle<@A Foo . @B Bar> . Inner<@D String @C []>
| Предполагается: |
class Outer {
class Middle<T> {
class Inner<U> {}
}
}
| |
|---|---|---|
| Аннотация | path_length | path |
@A | 2 | [{type_path_kind: 1; type_argument_index: 0}, {type_path_kind: 3; type_argument_index: 0}] |
@B | 3 | [{type_path_kind: 1; type_argument_index: 0}, {type_path_kind: 3; type_argument_index: 0}, {type_path_kind: 1; type_argument_index:
0}] |
@C | 3 | [{type_path_kind: 1; type_argument_index: 0}, {type_path_kind: 1; type_argument_index: 0}, {type_path_kind: 3; type_argument_index:
0}] |
@D | 4 | [{type_path_kind: 1; type_argument_index: 0}, {type_path_kind: 1; type_argument_index: 0}, {type_path_kind: 3; type_argument_index:
0}, {type_path_kind: 0; type_argument_index: 0}] |
Атрибут RuntimeInvisibleTypeAnnotations — атрибут переменной длины в таблице attributes структуры ClassFile, field_info, method_info или record_component_info, или атрибута Code (§4.1, §4.5, §4.6, §4.7.30, §4.7.3). Атрибут RuntimeInvisibleTypeAnnotations хранит невидимые во время выполнения аннотации типов, используемые в соответствующем объявлении класса, поля, метода или компонента записи, или в выражении в соответствующем теле метода. Атрибут RuntimeInvisibleTypeAnnotations также хранит аннотации на объявления параметров типа обобщенных классов, интерфейсов, методов и конструкторов.
В таблице attributes структуры ClassFile, field_info, method_info или record_component_info, или атрибута Code может быть не более одного атрибута RuntimeInvisibleTypeAnnotations.
Таблица attributes содержит атрибут RuntimeInvisibleTypeAnnotations только в том случае, если типы аннотированы в видах объявления или выражения, которые соответствуют родительской структуре или атрибуту таблицы attributes.
Атрибут RuntimeInvisibleTypeAnnotations имеет следующий формат:
RuntimeInvisibleTypeAnnotations_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 num_annotations;
type_annotation annotations[num_annotations];
}
Элементы структуры RuntimeInvisibleTypeAnnotations_attribute следующие:
- attribute_name_index
-
Значение элемента
attribute_name_indexдолжно быть допустимым индексом в таблицеconstant_pool. Элементconstant_poolв этом индексе должен быть структуройCONSTANT_Utf8_info, представляющей строку "RuntimeInvisibleTypeAnnotations". - attribute_length
-
Значение элемента
attribute_lengthуказывает длину атрибута, за исключением начальных шести байтов. - num_annotations
-
Значение элемента
num_annotationsзадает количество аннотаций типов, невидимых во время выполнения, представленных структурой. - annotations[]
-
Каждый элемент таблицы
annotationsпредставляет собой отдельную невидимую во время выполнения аннотацию типа, используемую в объявлении или выражении. Структураtype_annotationопределена в §4.7.20.
Атрибут 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.
Атрибут BootstrapMethods — атрибут переменной длины в таблице attributes структуры ClassFile (§4.1). Атрибут BootstrapMethods записывает методы инициализации, используемые для вычисления констант и вызовов динамически (§4.4.10).
Должен быть ровно один атрибут BootstrapMethods в таблице attributes структуры ClassFile, если таблица 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).
Атрибут MethodParameters — это атрибут переменной длины в таблице attributes структуры method_info (§4.6). Атрибут MethodParameters записывает информацию о формальных параметрах метода, таких как их имена.
В таблице attributes структуры method_info может быть не более одного атрибута MethodParameters.
Атрибут MethodParameters имеет следующий формат:
MethodParameters_attribute {
u2 attribute_name_index;
u4 attribute_length;
u1 parameters_count;
{ u2 name_index;
u2 access_flags;
} parameters[parameters_count];
}
Элементы структуры MethodParameters_attribute следующие:
- attribute_name_index
-
Значение элемента
attribute_name_indexдолжно быть допустимым индексом в таблицеconstant_pool. Элементconstant_poolпо этому индексу должен быть структуройCONSTANT_Utf8_info(§4.4.7), представляющей строку "MethodParameters". - attribute_length
-
Значение элемента
attribute_lengthуказывает длину атрибута, за исключением начальных шести байтов. - parameters_count
-
Значение элемента
parameters_countуказывает количество описателей параметров в описателе метода (§4.3.3), на который ссылается элементdescriptor_indexв охватывающей структуреmethod_info.Это не ограничение, которое должно соблюдаться реализацией виртуальной машины Java во время проверки формата (§4.8). Задача сопоставления описателей параметров в описателе метода с элементами в массиве
parametersниже выполняется библиотеками рефлексии платформы Java SE. - parameters[]
-
Каждый элемент массива
parametersсодержит следующую пару элементов:- name_index
-
Значение элемента
name_indexдолжно быть либо нулём, либо допустимым индексом в таблицеconstant_pool.Если значение элемента
name_indexравно нулю, то этот элементparametersуказывает формальный параметр без имени.Если значение элемента
name_indexотлично от нуля, элементconstant_poolпо этому индексу должен быть структуройCONSTANT_Utf8_info, представляющей действительное неопределённое имя, обозначающее формальный параметр (§4.2.2). - access_flags
-
Значение элемента
access_flagsследующее:- 0x0010 (
ACC_FINAL) -
Указывает, что формальный параметр был объявлен
final. - 0x1000 (
ACC_SYNTHETIC) -
Указывает, что формальный параметр не был явно или неявно объявлен в исходном коде в соответствии со спецификацией языка, на котором был написан исходный код (JLS §13.1). (Формальный параметр является артефактом реализации компилятора, который сгенерировал этот файл
class.) - 0x8000 (
ACC_MANDATED) -
Указывает, что формальный параметр был неявно объявлен в исходном коде в соответствии со спецификацией языка, на котором был написан исходный код (JLS §13.1). (Формальный параметр предписывается спецификацией языка, поэтому все компиляторы языка должны его генерировать.)
- 0x0010 (
Элемент с индексом i в массиве parameters соответствует i'тому описателю параметра в описателе метода, который его содержит. (Элемент parameters_count имеет длину один байт, потому что описание метода ограничено 255 параметрами.) По сути, это означает, что массив parameters хранит информацию обо всех параметрах метода. Можно представить другие схемы, где элементы массива parameters указывают на соответствующие им описатели параметров, но это излишне усложнит атрибут MethodParameters.
Элемент с индексом i в массиве parameters может или не может соответствовать i'тому типу в атрибуте Signature метода, который его содержит (если он присутствует), или i'тому аннотации в аннотациях параметров метода, который его содержит.
Атрибут Module - это атрибут переменной длины в таблице attributes структуры ClassFile (§4.1). Атрибут Module указывает модули, требуемые модулем; пакеты, экспортируемые и открытые модулем; и службы, используемые и предоставляемые модулем.
В таблице attributes структуры ClassFile может быть не более одного атрибута Module.
Атрибут Module имеет следующий формат:
Module_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 module_name_index;
u2 module_flags;
u2 module_version_index;
u2 requires_count;
{ u2 requires_index;
u2 requires_flags;
u2 requires_version_index;
} requires[requires_count];
u2 exports_count;
{ u2 exports_index;
u2 exports_flags;
u2 exports_to_count;
u2 exports_to_index[exports_to_count];
} exports[exports_count];
u2 opens_count;
{ u2 opens_index;
u2 opens_flags;
u2 opens_to_count;
u2 opens_to_index[opens_to_count];
} opens[opens_count];
u2 uses_count;
u2 uses_index[uses_count];
u2 provides_count;
{ u2 provides_index;
u2 provides_with_count;
u2 provides_with_index[provides_with_count];
} provides[provides_count];
}
Элементы структуры Module_attribute следующие:
- attribute_name_index
-
Значение элемента
attribute_name_indexдолжно быть действительным индексом в таблицеconstant_pool. Записьconstant_poolпо этому индексу должна быть структуройCONSTANT_Utf8_info(§4.4.7), представляющей строку "Module". - attribute_length
-
Значение элемента
attribute_lengthуказывает длину атрибута, исключая начальные шесть байтов. - module_name_index
-
Значение элемента
module_name_indexдолжно быть действительным индексом в таблицеconstant_pool. Записьconstant_poolпо этому индексу должна быть структуройCONSTANT_Module_info(§4.4.11), обозначающей текущий модуль. - module_flags
-
Значение элемента
module_flagsследующее:- 0x0020 (
ACC_OPEN) -
Указывает, что этот модуль открыт.
- 0x1000 (
ACC_SYNTHETIC) -
Указывает, что этот модуль не был явно или неявно объявлен.
- 0x8000 (
ACC_MANDATED) -
Указывает, что этот модуль был неявно объявлен.
- 0x0020 (
- module_version_index
-
Значение элемента
module_version_indexдолжно быть либо нулем, либо действительным индексом в таблицеconstant_pool. Если значение элемента равно нулю, то информация о версии текущего модуля отсутствует. Если значение элемента ненулевое, то записьconstant_poolпо этому индексу должна быть структуройCONSTANT_Utf8_info, представляющей версию текущего модуля. - requires_count
-
Значение элемента
requires_countуказывает количество записей в таблицеrequires.Если текущий модуль
java.base, тоrequires_countдолжно быть нулем.Если текущий модуль не
java.base, тоrequires_countдолжно быть не менее одного. - requires[]
-
Каждая запись в таблице
requiresуказывает зависимость текущего модуля. Элементы в каждой записи следующие:- requires_index
-
Значение элемента
requires_indexдолжно быть действительным индексом в таблицеconstant_pool. Записьconstant_poolпо этому индексу должна быть структуройCONSTANT_Module_info, обозначающей модуль, от которого зависит текущий модуль.Не более одной записи в таблице
requiresможет указывать модуль данного имени с его элементомrequires_index. - requires_flags
-
Значение элемента
requires_flagsследующее:- 0x0020 (
ACC_TRANSITIVE) -
Указывает, что любой модуль, который зависит от текущего модуля, неявно объявляет зависимость от модуля, указанного этой записью.
- 0x0040 (
ACC_STATIC_PHASE) -
Указывает, что эта зависимость является обязательной на статическом этапе, т. е. во время компиляции, но является необязательной на динамическом этапе, т. е. во время выполнения.
- 0x1000 (
ACC_SYNTHETIC) -
Указывает, что эта зависимость не была явно или неявно объявлена в исходном коде объявления модуля.
- 0x8000 (
ACC_MANDATED) -
Указывает, что эта зависимость была неявно объявлена в исходном коде объявления модуля.
- 0x0020 (
- requires_version_index
-
Значение элемента
requires_version_indexдолжно быть либо нулем, либо действительным индексом в таблицеconstant_pool. Если значение элемента равно нулю, то информация о версии зависимости отсутствует. Если значение элемента ненулевое, то записьconstant_poolпо этому индексу должна быть структуройCONSTANT_Utf8_info, представляющей версию модуля, указанногоrequires_index.
Если только текущий модуль не
java.base, ровно одна запись в таблицеrequiresдолжна иметь все следующее:-
Элемент
requires_index, который указываетjava.base. -
Элемент
requires_flags, в котором не установлен флагACC_SYNTHETIC. (ФлагACC_MANDATEDможет быть установлен.) -
Если номер версии файла
classравен 54.0 или выше, элементrequires_flags, в котором не установлены флагиACC_TRANSITIVEиACC_STATIC_PHASE.
- exports_count
-
Значение элемента
exports_countуказывает количество записей в таблицеexports. - exports[]
-
Каждая запись в таблице
exportsуказывает пакет, экспортируемый текущим модулем, таким образом, что типыpublicиprotectedв пакете, а также их членыpublicиprotected, могут быть доступны извне текущего модуля, возможно, из ограниченного набора "дружественных" модулей.Элементы в каждой записи следующие:
- exports_index
-
Значение элемента
exports_indexдолжно быть действительным индексом в таблицеconstant_pool. Записьconstant_poolпо этому индексу должна быть структуройCONSTANT_Package_info(§4.4.12), представляющей пакет, экспортируемый текущим модулем.Не более одной записи в таблице
exportsможет указывать пакет данного имени с его элементомexports_index. - exports_flags
-
Значение элемента
exports_flagsследующее:- 0x1000 (
ACC_SYNTHETIC) -
Указывает, что этот экспорт не был явно или неявно объявлен в исходном коде объявления модуля.
- 0x8000 (
ACC_MANDATED) -
Указывает, что этот экспорт был неявно объявлен в исходном коде объявления модуля.
- 0x1000 (
- exports_to_count
-
Значение
exports_to_countуказывает количество записей в таблицеexports_to_index.Если
exports_to_countравно нулю, то этот пакет экспортируется текущим модулем неквалифицированным образом; код в любом другом модуле может получить доступ к типам и членам в пакете.Если
exports_to_countненулевое, то этот пакет экспортируется текущим модулем квалифицированным образом; только код в модулях, перечисленных в таблицеexports_to_index, может получить доступ к типам и членам в пакете. - exports_to_index[]
-
Значение каждой записи в таблице
exports_to_indexдолжно быть действительным индексом в таблицеconstant_pool. Записьconstant_poolпо этому индексу должна быть структуройCONSTANT_Module_info, обозначающей модуль, чей код может получить доступ к типам и членам в этом экспортируемом пакете.Для каждой записи в таблице
exportsне более одной записи в ее таблицеexports_to_indexможет указывать модуль данного имени.
- opens_count
-
Значение элемента
opens_countуказывает количество записей в таблицеopens.opens_countдолжно быть нулем, если текущий модуль открыт. - opens[] ```
-
Каждая запись в таблице
opensуказывает на пакет, открытый текущим модулем, таким образом, что все типы в пакете и все их члены могут быть доступны извне текущего модуля с помощью библиотек рефлексии платформы Java SE, возможно, из ограниченного набора модулей «друзей».Элементы каждой записи следующие:
- opens_index
-
Значение элемента
opens_indexдолжно быть допустимым индексом в таблицеconstant_pool. Записьconstant_poolв этом индексе должна быть структуройCONSTANT_Package_info, представляющей пакет, открытый текущим модулем.В таблице
opensможет быть не более одной записи, указывающей на пакет с заданным именем и элементомopens_index. - opens_flags
-
Значение элемента
opens_flagsследующее:- 0x1000 (
ACC_SYNTHETIC) -
Указывает, что это открытие не было явно или неявно объявлено в источнике объявления модуля.
- 0x8000 (
ACC_MANDATED) -
Указывает, что это открытие было неявно объявлено в источнике объявления модуля.
- 0x1000 (
- opens_to_count
-
Значение элемента
opens_to_countуказывает количество записей в таблицеopens_to_index.Если
opens_to_countравно нулю, то этот пакет открывается текущим модулем в неопределённой форме; код в любом другом модуле может рефлективно получить доступ к типам и членам в пакете.Если
opens_to_countотлично от нуля, то этот пакет открывается текущим модулем в квалифицированной форме; только код в модулях, перечисленных в таблицеopens_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может быть не более одной записи, указывающей на реализацию сервиса с заданным именем.
Атрибут 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может быть не более одной записи, указывающей на пакет с заданным именем.
Атрибут 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), представляющей главный класс текущего модуля.
Атрибут 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).
Атрибут 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, которые ссылаются на текущий класс или интерфейс. Элементы массива, которые не соответствуют этим критериям, игнорируются контролем доступа.
Атрибут Record — атрибут переменной длины в таблице attributes структуры ClassFile (§4.1). Атрибут Record указывает, что текущий класс является классом-записью (JLS §8.10), и хранит информацию о компонентах записи класса-записи (JLS §8.10.1).
В таблице attributes структуры ClassFile может быть не более одного атрибута Record.
Атрибут Record имеет следующий формат:
Record_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 components_count;
record_component_info components[components_count];
}
Элементы структуры Record_attribute следующие:
- attribute_name_index
-
Значение элемента
attribute_name_indexдолжно быть корректным индексом в таблицеconstant_pool. Элемент таблицыconstant_poolпо этому индексу должен быть структуройCONSTANT_Utf8_info(§4.4.7), представляющей строку "Record". - attribute_length
-
Значение элемента
attribute_lengthуказывает длину атрибута, за исключением начальных шести байтов. - components_count
-
Значение элемента
components_countуказывает количество элементов в таблицеcomponents. - components[]
-
Каждый элемент таблицы
componentsопределяет компонент записи текущего класса в порядке объявления компонентов записи. Структураrecord_component_infoимеет следующий формат:record_component_info { u2 name_index; u2 descriptor_index; u2 attributes_count; attribute_info attributes[attributes_count]; }Элементы структуры
record_component_infoследующие:- name_index
-
Значение элемента
name_indexдолжно быть корректным индексом в таблицеconstant_pool. Элемент таблицыconstant_poolпо этому индексу должен быть структуройCONSTANT_Utf8_info(§4.4.7), представляющей корректное неопределённое имя, обозначающее компонент записи (§4.2.2). - descriptor_index
-
Значение элемента
descriptor_indexдолжно быть корректным индексом в таблицеconstant_pool. Элемент таблицыconstant_poolпо этому индексу должен быть структуройCONSTANT_Utf8_info(§4.4.7), представляющей дескриптор поля, который кодирует тип компонента записи (§4.3.2). - attributes_count
-
Значение элемента
attributes_countуказывает количество дополнительных атрибутов этого компонента записи. - attributes[]
-
Каждое значение таблицы
attributesдолжно быть структуройattribute_info(§4.7).Компонент записи может иметь любое количество дополнительных атрибутов.
Атрибуты, определённые в этом спецификации как появляющиеся в таблице
attributesструктурыrecord_component_info, перечислены в Таблице 4.7-C.Правила, касающиеся атрибутов, определённых для появления в таблице
attributesструктурыrecord_component_info, приведены в §4.7.Правила, касающиеся неопределённых атрибутов в таблице
attributesструктурыrecord_component_info, приведены в §4.7.1.
Атрибут PermittedSubclasses — это атрибут переменной длины в таблице attributes структуры ClassFile (§4.1). Атрибут PermittedSubclasses записывает классы и интерфейсы, которые имеют право непосредственно расширять или реализовывать текущий класс или интерфейс (§5.3.5).
Язык программирования Java использует модификатор sealed для указания класса или интерфейса, который ограничивает свои непосредственные подклассы или непосредственные подинтерфейсы. Можно предположить, что этот модификатор соответствует флагу ACC_SEALED в файле class, так как связанный модификатор final соответствует флагу ACC_FINAL. На самом деле, класс или интерфейс sealed указывается в файле class наличием атрибута PermittedSubclasses.
В таблице attributes структуры ClassFile может быть не более одного атрибута PermittedSubclasses, у которого элемент access_flags не имеет установленного флага ACC_FINAL.
В таблице attributes структуры ClassFile не должно быть атрибута PermittedSubclasses, у которого элемент access_flags имеет установленный флаг ACC_FINAL.
sealed отличается от final: у класса sealed есть список разрешённых подклассов, а у класса final нет подклассов. Таким образом, структура ClassFile может иметь атрибут PermittedSubclasses или иметь установленный флаг ACC_FINAL, но не оба.
Атрибут PermittedSubclasses имеет следующий формат:
PermittedSubclasses_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 number_of_classes;
u2 classes[number_of_classes];
}
Элементы структуры PermittedSubclasses_attribute следующие:
- attribute_name_index
-
Значение элемента
attribute_name_indexдолжно быть допустимым индексом в таблицеconstant_pool. Записьconstant_poolв этом индексе должна быть структуройCONSTANT_Utf8_info(§4.4.7), представляющей строку "PermittedSubclasses". - attribute_length
-
Значение элемента
attribute_lengthуказывает длину атрибута, исключая начальные шесть байтов. - number_of_classes
-
Значение элемента
number_of_classesуказывает количество записей в массивеclasses. - classes[]
-
Каждое значение в массиве
classesдолжно быть допустимым индексом в таблицеconstant_pool. Записьconstant_poolв этом индексе должна быть структуройCONSTANT_Class_info(§4.4.1), представляющей класс или интерфейс, имеющий право непосредственно расширять или реализовывать текущий класс или интерфейс.Массив
classesиспользуется при создании класса или интерфейса, который пытается непосредственно расширить или реализовать текущий класс или интерфейс (§5.3.5). Элементы массива, представляющие классы или интерфейсы, которые не пытаются непосредственно расширить или реализовать текущий класс или интерфейс, игнорируются.
При загрузке потенциального файла class виртуальной машиной Java (§5.3) виртуальная машина Java сначала проверяет, что файл имеет базовый формат файла class (§4.1). Этот процесс называется проверкой формата. Проверки проводятся следующим образом:
-
Первые четыре байта должны содержать правильное магическое число.
-
Все предопределённые атрибуты (§4.7) должны иметь правильную длину, за исключением
StackMapTable,RuntimeVisibleAnnotations,RuntimeInvisibleAnnotations,RuntimeVisibleParameterAnnotations,RuntimeInvisibleParameterAnnotations,RuntimeVisibleTypeAnnotations,RuntimeInvisibleTypeAnnotationsиAnnotationDefault. -
Файл
classне должен быть усечён или иметь дополнительные байты в конце. -
Пул констант должен удовлетворять ограничениям, описанным в §4.4.
Например, каждая структура
CONSTANT_Class_infoв пуле констант должна содержать в своём элементеname_indexдопустимый индекс пула констант для структурыCONSTANT_Utf8_info. -
Все ссылки на поля и методы в пуле констант должны иметь допустимые имена, допустимые классы и допустимые описатели (§4.3).
Проверка формата не гарантирует, что заданное поле или метод фактически существуют в заданном классе, ни то, что предоставленные описатели относятся к реальным классам. Проверка формата гарантирует только, что эти элементы хорошо сформированы. Более подробная проверка выполняется при проверке самих байткодов и во время разрешения.
Эти проверки целостности базового файла class необходимы для любого толкования содержимого файла class. Проверка формата отличается от проверки байткодов, хотя исторически они были связаны, поскольку оба являются формой проверки целостности.
Код метода, метода инициализации экземпляра (§2.9.1) или метода инициализации класса или интерфейса (§2.9.2) хранится в массиве code атрибута Code структуры method_info файла class (§4.7.3). В этом разделе описаны ограничения, связанные с содержанием структуры Code_attribute.
Статические ограничения файла class определяют правильность формирования файла. Эти ограничения приведены в предыдущих разделах, за исключением статических ограничений на код в файле class. Статические ограничения кода в файле class определяют, как инструкции виртуальной машины Java должны быть размещены в массиве code и какими должны быть операнды отдельных инструкций.
Статические ограничения инструкций в массиве code следующие:
-
В массиве
codeмогут присутствовать только экземпляры инструкций, описанных в §6.5. Экземпляры инструкций, использующих зарезервированные опкоды (§6.2) или любые опкоды, не описанные в этом спецификации, не должны присутствовать в массивеcode.Если номер версии файла
classравен 51.0 или выше, то экземпляры инструкций, использующих опкоды jsr, jsr_w или ret, не должны присутствовать в массивеcode. -
Опкод первой инструкции в массиве
codeначинается с индекса0. -
Для каждой инструкции в массиве
code, кроме последней, индекс опкода следующей инструкции равен индексу опкода текущей инструкции плюс длина этой инструкции, включая все её операнды.Инструкция wide обрабатывается как любая другая инструкция в этих целях; опкод, указывающий на операцию, которую инструкция wide должна изменить, рассматривается как один из операндов этой инструкции wide. Этот опкод никогда не должен быть непосредственно доступен для вычисления.
-
Последний байт последней инструкции в массиве
codeдолжен быть байтом с индексомcode_length - 1.
Статические ограничения на операнды инструкций в массиве code следующие:
-
Цель каждой инструкции перехода и ветвления (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.
Структурные ограничения на массив 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, то этот экземпляр не может использоваться в качестве адреса возврата.
Несмотря на то, что компилятор языка программирования Java должен генерировать только файлы class, удовлетворяющие всем статическим и структурным ограничениям в предыдущих разделах, виртуальная машина Java не гарантирует, что любой файл, который ей требуется загрузить, был сгенерирован этим компилятором или имеет правильную структуру. Такие приложения, как веб-браузеры, не загружают исходный код, который затем компилируют; эти приложения загружают уже скомпилированные файлы class. Браузеру необходимо определить, был ли файл class сгенерирован надёжным компилятором или злоумышленником, пытающимся использовать виртуальную машину Java.
Дополнительной проблемой проверки на этапе компиляции является расхождение версий. Пользователь может успешно скомпилировать класс, скажем, PurchaseStockOptions, в качестве подкласса TradingClass. Однако определение TradingClass может измениться после компиляции класса таким образом, что это несовместимо с существующими бинарными файлами. Методы могут быть удалены или изменены их возвращаемые типы или модификаторы. Поля могут изменить тип или измениться с экземпляра переменных на переменные класса. Модификаторы доступа к методу или переменной могут измениться с public на private. Подробнее об этих проблемах см. главу 13 «Двоичная совместимость» в Спецификации языка Java, издание Java SE 21.
Из-за этих потенциальных проблем виртуальной машине 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) есть непосредственный суперкласс.
Файл class, номер версии которого 50.0 или выше (§4.1), должен быть проверен с использованием правил проверки типов, описанных в этом разделе.
Если и только если номер версии файла class равен 50.0, то при неудачной проверке типов реализация Java Virtual Machine может попытаться выполнить проверку с помощью вывода типов (§4.10.2).
Это прагматичное изменение, предназначенное для облегчения перехода к новой дисциплине проверки. Многие инструменты, манипулирующие файлами class, могут изменять байткоды метода таким образом, что требует корректировки кадров карты стека метода. Если инструмент не выполняет необходимые корректировки кадров карты стека, проверка типов может завершиться неудачно, даже если байткод в принципе корректен (и, следовательно, он бы прошел проверку по старой схеме вывода типов). Чтобы дать разработчикам время на адаптацию своих инструментов, реализации Java Virtual Machine могут перейти к более старой дисциплине проверки, но только в течение ограниченного времени.
В случаях, когда проверка типов завершается неудачно, но вызов вывода типов приводит к успеху, ожидается определённый штраф за производительность. Такой штраф неизбежен. Он также должен служить сигналом поставщикам инструментов о необходимости корректировки их выходных данных и предоставляет им дополнительный стимул для внесения этих корректировок.
Подводя итог, откат на проверку с помощью вывода типов поддерживает как постепенное добавление кадров карты стека в Java SE Platform (если они отсутствуют в файле class версии 50.0, откат разрешён), так и постепенное удаление инструкций jsr и jsr_w из Java SE Platform (если они присутствуют в файле class версии 50.0, откат разрешён).
Если реализация 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).
Мы постулируем существование 28 предикат Prolog ("доступные функции"), которые должны обладать определённым поведением, но чьи формальные определения не приводятся в данном спецификации.
- classClassName(Class, ClassName)
-
Извлекает имя,
ClassName, классаClass. - classIsInterface(Class)
-
Истина, если класс,
Class, является интерфейсом. - classIsNotFinal(Class)
-
Истина, если класс,
Class, не является классомfinal. - classSuperClassName(Class, SuperClassName)
-
Извлекает имя,
SuperClassName, суперкласса классаClass. - classInterfaces(Class, Interfaces)
-
Извлекает список,
Interfaces, прямых суперинтерфейсов классаClass. - classMethods(Class, Methods)
-
Извлекает список,
Methods, методов, объявленных в классеClass. - classDeclaresMember(Class, MemberName, MemberDescriptor)
-
Утверждает, что класс,
Class, объявляет поле или метод с именемMemberNameи дескрипторомMemberDescriptor. Это утверждение не учитывает члены, объявленные в суперклассах или суперинтерфейсахClass. - 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.Каждый атрибут представлен как применение префикса функции вида
attribute(AttributeName, AttributeContents), гдеAttributeName— имя атрибута. Формат содержимого атрибута не определён. - isInit(Method)
-
Истина, если
Method(независимо от класса) является<init>. - isNotInit(Method)
-
Истина, если
Method(независимо от класса) не является<init>. - isNotFinal(Method, Class)
-
Истина, если
Methodв классеClassне являетсяfinal. - isStatic(Method, Class)
-
Истина, если
Methodв классеClassявляетсяstatic. - isNotStatic(Method, Class)
-
Истина, если
Methodв классеClassне являетсяstatic. - isPrivate(Method, Class)
-
Истина, если
Methodв классеClassявляетсяprivate. - isNotPrivate(Method, Class)
-
Истина, если
Methodв классеClassне являетсяprivate. - isProtected(MemberClass, MemberName, MemberDescriptor)
-
Истина, если существует член с именем
MemberNameи дескрипторомMemberDescriptorв классеMemberClassи он являетсяprotected. - isNotProtected(MemberClass, MemberName, MemberDescriptor)
-
Истина, если существует член с именем
MemberNameи дескрипторомMemberDescriptorв классеMemberClassи он не являетсяprotected. - parseFieldDescriptor(Descriptor, Type)
-
Преобразует дескриптор поля,
Descriptor, в соответствующий тип проверкиType(§4.10.1.2). - parseMethodDescriptor(Descriptor, ArgTypeList, ReturnType)
-
Преобразует дескриптор метода,
Descriptor, в список типов проверки,ArgTypeList, соответствующих типам аргументов метода, и тип проверки,ReturnType, соответствующий возвращаемому типу. - parseCodeAttribute(Class, Method, FrameSize, MaxStack, ParsedCode, Handlers, StackMap)
-
Извлекает поток инструкций,
ParsedCode, методаMethodвClass, а также максимальный размер стека операндов,MaxStack, максимальное количество локальных переменных,FrameSize, обработчики исключений,Handlers, и карту стекаStackMap.Представление потока инструкций и атрибута карты стека должно соответствовать указанному в §4.10.1.3 и §4.10.1.4.
- samePackageName(Class1, Class2)
-
Истина, если имена пакетов
Class1иClass2совпадают. - differentPackageName(Class1, Class2)
-
Истина, если имена пакетов
Class1иClass2различны.
При проверке типов тела метода удобно получить информацию о методе. Для этой цели мы определяем среду, являющуюся шестёркой:
-
класс
-
метод
-
объявленный возвращаемый тип метода
-
инструкции в методе
-
максимальный размер стека операндов
-
список обработчиков исключений
Мы определяем доступные функции для извлечения информации из среды.
allInstructions(Environment, Instructions) :-
Environment = environment(_Class, _Method, _ReturnType,
Instructions, _, _).
exceptionHandlers(Environment, Handlers) :-
Environment = environment(_Class, _Method, _ReturnType,
_Instructions, _, Handlers).
maxOperandStackLength(Environment, MaxStack) :-
Environment = environment(_Class, _Method, _ReturnType,
_Instructions, MaxStack, _Handlers).
thisClass(Environment, class(ClassName, L)) :-
Environment = environment(Class, _Method, _ReturnType,
_Instructions, _, _),
classDefiningLoader(Class, L),
classClassName(Class, ClassName).
thisMethodReturnType(Environment, ReturnType) :-
Environment = environment(_Class, _Method, ReturnType,
_Instructions, _, _).
Мы определяем дополнительные предикаты для извлечения информации более высокого уровня из среды.
offsetStackFrame(Environment, Offset, StackFrame) :-
allInstructions(Environment, Instructions),
member(stackMap(Offset, StackFrame), Instructions).
currentClassLoader(Environment, Loader) :-
thisClass(Environment, class(_, Loader)).
Наконец, мы определяем общий предикат, используемый во всех правилах типов:
notMember(_, []). notMember(X, [A | More]) :- X \= A, notMember(X, More).
Принципом, определяющим, какие доступные функции постулируются, а какие полностью специфицированы, является то, что мы не хотим излишне специфицировать представление файла class. Предоставление конкретных доступных функций для Class или Method термина принудило бы нас полностью специфицировать формат для термина Prolog, представляющего файл class.
Проверяющий тип использует систему типов, основанную на иерархии типов проверки, показанной ниже.
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).
Индивидуальные байткодовые инструкции представлены в 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 для загрузки константы
int91 будет представлена какldc(int(91)). -
float(Value)для записи константного пула, являющейся структуройCONSTANT_Float_info(§4.4.4).Value— константаfloat, представленная элементомbytesструктуры. -
long(Value)для записи константного пула, являющейся структуройCONSTANT_Long_info(§4.4.5).Value— константаlong, представленная элементамиhigh_bytesиlow_bytesструктуры. -
double(Value)для записи константного пула, являющейся структуройCONSTANT_Double_info(§4.4.5).Value— константаdouble, представленная элементамиhigh_bytesиlow_bytesструктуры. -
class(ClassName)для записи константного пула, являющейся структуройCONSTANT_Class_info(§4.4.1).ClassName— имя класса или интерфейса, на которое ссылается элементname_indexструктуры. -
string(Value)для записи константного пула, являющейся структуройCONSTANT_String_info(§4.4.3).Value— строка, на которую ссылается элементstring_indexструктуры. -
methodHandle(Kind, Reference)для записи константного пула, являющейся структуройCONSTANT_MethodHandle_info(§4.4.8).Kind— значение элементаreference_kindструктуры.Reference— значение элементаreference_indexструктуры. -
methodType(MethodDescriptor)для записи константного пула, являющейся структуройCONSTANT_MethodType_info(§4.4.9).MethodDescriptor— описатель метода, на который ссылается элементdescriptor_indexструктуры. -
dconstant(ConstantName, FieldDescriptor)для записи константного пула, являющейся структуройCONSTANT_Dynamic_info(§4.4.10).ConstantNameиFieldDescriptorсоответствуют имени и описателю поля, на которые ссылается элементname_and_type_indexструктуры. (Элементbootstrap_method_attr_indexне имеет значения для проверки.)
Кадры карты стека в 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).
Методы 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).
Не-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).
Все инструкции загрузки являются вариациями на общую схему, различаясь типом значения, которое загружает инструкция.
Загрузка значения типа 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).
Все инструкции, которые обращаются к членам, должны учитывать правила, касающиеся членов 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).
В общем случае правило типа для инструкции задаётся относительно среды Environment, которая определяет класс и метод, в котором происходит инструкция (§4.10.1.1), и смещение Offset внутри метода, в котором происходит инструкция. Правило гласит, что если состояние входного типа StackFrame удовлетворяет определённым требованиям, то:
-
Инструкция является безошибочной с точки зрения типа.
-
Можно доказать, что состояние типа после завершения инструкции в нормальном режиме имеет определённую форму, заданную
NextStackFrame, а состояние типа после завершения инструкции с ошибкой задаётсяExceptionStackFrame.Состояние типа после завершения инструкции с ошибкой такое же, как состояние входного типа, за исключением того, что стек операндов пустой.
exceptionStackFrame(StackFrame, ExceptionStackFrame) :- StackFrame = frame(Locals, _OperandStack, Flags), ExceptionStackFrame = frame(Locals, [], Flags).
У многих инструкций правила типов полностью изоморфны правилам других инструкций. Если инструкция b1 изоморфна другой инструкции b2, то правило типа для b1 такое же, как правило типа для b2.
instructionIsTypeSafe(Instruction, Environment, Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
instructionHasEquivalentTypeRule(Instruction, IsomorphicInstruction),
instructionIsTypeSafe(IsomorphicInstruction, Environment, Offset,
StackFrame, NextStackFrame,
ExceptionStackFrame).
Описание каждого правила на английском языке призвано быть читабельным, интуитивно понятным и кратким. В связи с этим описание избегает повторения всех контекстных предположений, указанных выше. В частности:
-
Описание не упоминает явно среду.
-
Когда в описании говорится о стеке операндов или локальных переменных, имеется в виду компоненты стека операндов и локальных переменных состояния типа: либо состояние входного типа, либо выходного.
-
Состояние типа после завершения инструкции с ошибкой почти всегда идентично состоянию входного типа. Описание затрагивает состояние типа после завершения инструкции с ошибкой только в том случае, если это не так.
-
Описание говорит о извлечении и добавлении типов в стек операндов, и не обсуждает явно проблемы подпотока или переполнения стека. Описание предполагает, что эти операции могут быть выполнены успешно, но правила Prolog для управления стеком операндов гарантируют, что необходимые проверки выполняются.
-
Описание рассматривает только манипуляции с логическими типами. На практике некоторые типы занимают более одного слова. Описание абстрагируется от этих деталей представления, но правила Prolog, которые манипулируют данными, этого не делают.
Любые неоднозначности можно разрешить, обратившись к формальным правилам Prolog.
aaload
Инструкция aaload безопасна с точки зрения типов, если можно корректно заменить типы, соответствующие int и типу массива с типом компонента ComponentType, где ComponentType является подтипом Object, при этом ComponentType даёт состояние выходного типа.
instructionIsTypeSafe(aaload, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
nth1OperandStackIs(2, StackFrame, ArrayType),
arrayComponentType(ArrayType, ComponentType),
isBootstrapLoader(BL),
validTypeTransition(Environment,
[int, arrayOf(class('java/lang/Object', BL))],
ComponentType, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Тип компонента массива X является X. Мы определяем тип компонента null как null.
arrayComponentType(arrayOf(X), X). arrayComponentType(null, null).
aastore
Инструкция aastore безопасна с точки зрения типов, если можно корректно извлечь типы, соответствующие Object, int и типу массива Object со стека входных операндов, получив состояние выходного типа.
instructionIsTypeSafe(aastore, _Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
isBootstrapLoader(BL),
canPop(StackFrame,
[class('java/lang/Object', BL),
int,
arrayOf(class('java/lang/Object', BL))],
NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
aconst_null
Инструкция aconst_null безопасна с точки зрения типов, если можно корректно поместить тип null в стек входных операндов, получив состояние выходного типа.
instructionIsTypeSafe(aconst_null, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [], null, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
aload, aload_<n>
Инструкция aload с операндом Index безопасна с точки зрения типов и даёт состояние выходного типа NextStackFrame, если инструкция загрузки с операндом Index и типом reference безопасна с точки зрения типов и даёт состояние выходного типа NextStackFrame.
instructionIsTypeSafe(aload(Index), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
loadIsTypeSafe(Environment, Index, reference, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкции aload_<n> для 0 ≤ n ≤ 3 безопасны с точки зрения типов, если эквивалентная инструкция aload безопасна.
instructionHasEquivalentTypeRule(aload_0, aload(0)). instructionHasEquivalentTypeRule(aload_1, aload(1)). instructionHasEquivalentTypeRule(aload_2, aload(2)). instructionHasEquivalentTypeRule(aload_3, aload(3)).
anewarray
Инструкция anewarray с операндом CP безопасна с точки зрения типов, если CP ссылается на запись константного пула, обозначающую класс, интерфейс или тип массива, и можно корректно заменить тип, соответствующий int на стеке входных операндов, массивом с типом компонента CP, получив состояние выходного типа.
instructionIsTypeSafe(anewarray(CP), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
(CP = class(_, _) ; CP = arrayOf(_)),
validTypeTransition(Environment, [int], arrayOf(CP),
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
areturn
Инструкция areturn безопасна с точки зрения типов, если объявленный тип возвращаемого значения окружающего метода, ReturnType, является типом reference и можно корректно извлечь тип, соответствующий ReturnType со стека входных операндов.
instructionIsTypeSafe(areturn, Environment, _Offset, StackFrame,
afterGoto, ExceptionStackFrame) :-
thisMethodReturnType(Environment, ReturnType),
isAssignable(ReturnType, reference),
canPop(StackFrame, [ReturnType], _PoppedStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
arraylength
Инструкция arraylength безопасна с точки зрения типов, если можно корректно заменить тип массива на стеке входных операндов типом int, получив состояние выходного типа.
instructionIsTypeSafe(arraylength, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
nth1OperandStackIs(1, StackFrame, ArrayType),
arrayComponentType(ArrayType, _),
validTypeTransition(Environment, [top], int, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
astore, astore_<n>
Инструкция astore с операндом Index безопасна с точки зрения типов и даёт состояние выходного типа NextStackFrame, если инструкция сохранения с операндом Index и типом reference безопасна с точки зрения типов и даёт состояние выходного типа NextStackFrame.
instructionIsTypeSafe(astore(Index), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
storeIsTypeSafe(Environment, Index, reference, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкции astore_<n> для 0 ≤ n ≤ 3 безопасны с точки зрения типов, если эквивалентная инструкция astore безопасна.
instructionHasEquivalentTypeRule(astore_0, astore(0)). instructionHasEquivalentTypeRule(astore_1, astore(1)). instructionHasEquivalentTypeRule(astore_2, astore(2)). instructionHasEquivalentTypeRule(astore_3, astore(3)).
athrow
Инструкция athrow безопасна с точки зрения типов, если вершина стека операндов соответствует Throwable.
instructionIsTypeSafe(athrow, _Environment, _Offset, StackFrame,
afterGoto, ExceptionStackFrame) :-
isBootstrapLoader(BL),
canPop(StackFrame, [class('java/lang/Throwable', BL)], _PoppedStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
baload
Инструкция baload безопасна с точки зрения типов, если можно корректно заменить типы, соответствующие int и типу малого массива на стеке входных операндов на int, получив состояние выходного типа.
instructionIsTypeSafe(baload, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :
nth1OperandStackIs(2, StackFrame, ArrayType),
isSmallArray(ArrayType),
validTypeTransition(Environment, [int, top], int,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Тип массива является типом малого массива, если он является массивом byte, массивом boolean или подтипом (null).
isSmallArray(arrayOf(byte)). isSmallArray(arrayOf(boolean)). isSmallArray(null).
bastore
Инструкция bastore безопасна с точки зрения типов, если можно корректно извлечь типы, соответствующие int, int и типу малого массива со стека входных операндов, получив состояние выходного типа.
instructionIsTypeSafe(bastore, _Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
nth1OperandStackIs(3, StackFrame, ArrayType),
isSmallArray(ArrayType),
canPop(StackFrame, [int, int, top], NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
bipush
Инструкция bipush безопасна с точки зрения типов, если эквивалентная инструкция sipush безопасна.
instructionHasEquivalentTypeRule(bipush(Value), sipush(Value)).
caload
Инструкция caload безопасна с точки зрения типов, если можно корректно заменить типы, соответствующие int и массиву char на стеке входных операндов на int, получив состояние выходного типа.
instructionIsTypeSafe(caload, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [int, arrayOf(char)], int,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
castore
Инструкция castore является безошибочной с точки зрения типов тогда и только тогда, когда можно корректно извлечь типы, соответствующие int, int и массиву char из стека входных операндов, что приводит к состоянию типа выходного значения.
instructionIsTypeSafe(castore, _Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
canPop(StackFrame, [int, int, arrayOf(char)], NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
checkcast
Инструкция checkcast с операндом CP является безошибочной с точки зрения типов тогда и только тогда, когда CP ссылается на запись константного пула, обозначающую либо класс, либо массив, и можно корректно заменить тип Object вверху стека входных операндов типом, обозначенным CP, что приводит к состоянию типа выходного значения.
instructionIsTypeSafe(checkcast(CP), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
(CP = class(_, _) ; CP = arrayOf(_)),
isBootstrapLoader(BL),
validTypeTransition(Environment, [class('java/lang/Object', BL)], CP,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
d2f, d2i, d2l
Инструкция d2f является безошибочной с точки зрения типов, если можно корректно извлечь double из стека входных операндов и заменить его на float, что приводит к состоянию типа выходного значения.
instructionIsTypeSafe(d2f, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [double], float,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция d2i является безошибочной с точки зрения типов, если можно корректно извлечь double из стека входных операндов и заменить его на int, что приводит к состоянию типа выходного значения.
instructionIsTypeSafe(d2i, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [double], int,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция d2l является безошибочной с точки зрения типов, если можно корректно извлечь double из стека входных операндов и заменить его на long, что приводит к состоянию типа выходного значения.
instructionIsTypeSafe(d2l, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [double], long,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
dadd
Инструкция dadd является безошибочной с точки зрения типов тогда и только тогда, когда можно корректно заменить типы, соответствующие double и double в стеке входных операндов на double, что приводит к состоянию типа выходного значения.
instructionIsTypeSafe(dadd, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [double, double], double,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
daload
Инструкция daload является безошибочной с точки зрения типов тогда и только тогда, когда можно корректно заменить типы, соответствующие int и массиву из double в стеке входных операндов на double, что приводит к состоянию типа выходного значения.
instructionIsTypeSafe(daload, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [int, arrayOf(double)], double,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
dastore
Инструкция dastore является безошибочной с точки зрения типов тогда и только тогда, когда можно корректно извлечь типы, соответствующие double, int и массиву из double из стека входных операндов, что приводит к состоянию типа выходного значения.
instructionIsTypeSafe(dastore, _Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
canPop(StackFrame, [double, int, arrayOf(double)], NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
dcmp<op>
Инструкция dcmpg является безошибочной с точки зрения типов тогда и только тогда, когда можно корректно заменить типы, соответствующие double и double в стеке входных операндов на int, что приводит к состоянию типа выходного значения.
instructionIsTypeSafe(dcmpg, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [double, double], int,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция dcmpl является безошибочной с точки зрения типов тогда и только тогда, когда эквивалентная инструкция dcmpg является безошибочной.
instructionHasEquivalentTypeRule(dcmpl, dcmpg).
dconst_<d>
Инструкция dconst_0 является безошибочной с точки зрения типов, если можно корректно поместить тип double в стек входных операндов, что приводит к состоянию типа выходного значения.
instructionIsTypeSafe(dconst_0, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [], double, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция dconst_1 является безошибочной с точки зрения типов тогда и только тогда, когда эквивалентная инструкция dconst_0 является безошибочной.
instructionHasEquivalentTypeRule(dconst_1, dconst_0).
ddiv
Инструкция ddiv является безошибочной с точки зрения типов тогда и только тогда, когда эквивалентная инструкция dadd является безошибочной.
instructionHasEquivalentTypeRule(ddiv, dadd).
dload, dload_<n>
Инструкция dload с операндом Index является безошибочной с точки зрения типов и приводит к состоянию типа выходного значения NextStackFrame, если инструкция загрузки с операндом Index и типом double является безошибочной и приводит к состоянию типа выходного значения NextStackFrame.
instructionIsTypeSafe(dload(Index), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
loadIsTypeSafe(Environment, Index, double, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкции dload_<n> для 0 ≤ n ≤ 3 являются безошибочными с точки зрения типов тогда и только тогда, когда эквивалентная инструкция dload является безошибочной.
instructionHasEquivalentTypeRule(dload_0, dload(0)). instructionHasEquivalentTypeRule(dload_1, dload(1)). instructionHasEquivalentTypeRule(dload_2, dload(2)). instructionHasEquivalentTypeRule(dload_3, dload(3)).
dmul
Инструкция dmul является безошибочной с точки зрения типов тогда и только тогда, когда эквивалентная инструкция dadd является безошибочной.
instructionHasEquivalentTypeRule(dmul, dadd).
dneg
Инструкция dneg является безошибочной с точки зрения типов тогда и только тогда, когда в стеке входных операндов есть тип, соответствующий double. Инструкция dneg не изменяет состояние типа.
instructionIsTypeSafe(dneg, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [double], double,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
drem
Инструкция drem является безошибочной с точки зрения типов тогда и только тогда, когда эквивалентная инструкция dadd является безошибочной.
instructionHasEquivalentTypeRule(drem, dadd).
dreturn
Инструкция dreturn является безошибочной с точки зрения типов, если у метода в объявленном типе возвращаемого значения указан тип double, и можно корректно извлечь тип, соответствующий double из стека входных операндов.
instructionIsTypeSafe(dreturn, Environment, _Offset, StackFrame,
afterGoto, ExceptionStackFrame) :-
thisMethodReturnType(Environment, double),
canPop(StackFrame, [double], _PoppedStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
dstore, dstore_<n>
Инструкция dstore с операндом Index является безошибочной с точки зрения типов и приводит к состоянию типа выходного значения NextStackFrame, если инструкция сохранения с операндом Index и типом double является безошибочной и приводит к состоянию типа выходного значения NextStackFrame.
instructionIsTypeSafe(dstore(Index), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
storeIsTypeSafe(Environment, Index, double, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкции dstore_<n> для 0 ≤ n ≤ 3 являются безошибочными с точки зрения типов тогда и только тогда, когда эквивалентная инструкция dstore является безошибочной.
instructionHasEquivalentTypeRule(dstore_0, dstore(0)). instructionHasEquivalentTypeRule(dstore_1, dstore(1)). instructionHasEquivalentTypeRule(dstore_2, dstore(2)). instructionHasEquivalentTypeRule(dstore_3, dstore(3)).
dsub
Инструкция dsub является безошибочной с точки зрения типов тогда и только тогда, когда эквивалентная инструкция dadd является безошибочной.
instructionHasEquivalentTypeRule(dsub, dadd).
dup
Инструкция dup является безошибочной с точки зрения типов тогда и только тогда, когда можно корректно заменить категорию 1 типа, Type, на типы Type и Type, что приводит к состоянию типа выходного значения.
instructionIsTypeSafe(dup, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
StackFrame = frame(Locals, InputOperandStack, Flags),
popCategory1(InputOperandStack, Type, _),
canSafelyPush(Environment, InputOperandStack, Type, OutputOperandStack),
NextStackFrame = frame(Locals, OutputOperandStack, Flags),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
dup_x1
Инструкция dup_x1 является безошибочной с точки зрения типов тогда и только тогда, когда можно корректно заменить два типа категории 1, Type1 и Type2, в стеке входных операндов на типы Type1, Type2 и Type1, что приводит к состоянию типа выходного значения.
instructionIsTypeSafe(dup_x1, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
StackFrame = frame(Locals, InputOperandStack, Flags),
popCategory1(InputOperandStack, Type1, Stack1),
popCategory1(Stack1, Type2, Rest),
canSafelyPushList(Environment, Rest, [Type1, Type2, Type1],
OutputOperandStack),
NextStackFrame = frame(Locals, OutputOperandStack, Flags),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
dup_x2
Инструкция dup_x2 является безопасной по типу, если она является безопасной по типу формой инструкции dup_x2.
instructionIsTypeSafe(dup_x2, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
StackFrame = frame(Locals, InputOperandStack, Flags),
dup_x2FormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack),
NextStackFrame = frame(Locals, OutputOperandStack, Flags),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция dup_x2 является безопасной по типу формой инструкции dup_x2 тогда и только тогда, когда она является инструкцией безопасная по типу форма 1 dup_x2 или инструкцией безопасная по типу форма 2 dup_x2.
dup_x2FormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
dup_x2Form1IsTypeSafe(Environment, InputOperandStack, OutputOperandStack).
dup_x2FormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
dup_x2Form2IsTypeSafe(Environment, InputOperandStack, OutputOperandStack).
Инструкция dup_x2 является инструкцией безопасная по типу форма 1 dup_x2 тогда и только тогда, когда можно корректно заменить три типа категории 1, Type1, Type2, Type3 на стеке операндов входного состояния типами Type1, Type2, Type3, Type1, что приведёт к состоянию выходного типа.
dup_x2Form1IsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
popCategory1(InputOperandStack, Type1, Stack1),
popCategory1(Stack1, Type2, Stack2),
popCategory1(Stack2, Type3, Rest),
canSafelyPushList(Environment, Rest, [Type1, Type3, Type2, Type1],
OutputOperandStack).
Инструкция dup_x2 является инструкцией безопасная по типу форма 2 dup_x2 тогда и только тогда, когда можно корректно заменить тип категории 1, Type1, и тип категории 2, Type2, на стеке операндов входного состояния типами Type1, Type2, Type1, что приведёт к состоянию выходного типа.
dup_x2Form2IsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
popCategory1(InputOperandStack, Type1, Stack1),
popCategory2(Stack1, Type2, Rest),
canSafelyPushList(Environment, Rest, [Type1, Type2, Type1],
OutputOperandStack).
dup2
Инструкция dup2 является безопасной по типу, если она является безопасной по типу формой инструкции dup2.
instructionIsTypeSafe(dup2, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
StackFrame = frame(Locals, InputOperandStack, Flags),
dup2FormIsTypeSafe(Environment,InputOperandStack, OutputOperandStack),
NextStackFrame = frame(Locals, OutputOperandStack, Flags),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция dup2 является безопасной по типу формой инструкции dup2 тогда и только тогда, когда она является инструкцией безопасная по типу форма 1 dup2 или инструкцией безопасная по типу форма 2 dup2.
dup2FormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
dup2Form1IsTypeSafe(Environment,InputOperandStack, OutputOperandStack).
dup2FormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
dup2Form2IsTypeSafe(Environment,InputOperandStack, OutputOperandStack).
Инструкция dup2 является инструкцией безопасная по типу форма 1 dup2 тогда и только тогда, когда можно корректно заменить два типа категории 1, Type1 и Type2 на стеке операндов входного состояния типами Type1, Type2, Type1, Type2, что приведёт к состоянию выходного типа.
dup2Form1IsTypeSafe(Environment, InputOperandStack, OutputOperandStack):-
popCategory1(InputOperandStack, Type1, TempStack),
popCategory1(TempStack, Type2, _),
canSafelyPushList(Environment, InputOperandStack, [Type2, Type1],
OutputOperandStack).
Инструкция dup2 является инструкцией безопасная по типу форма 2 dup2 тогда и только тогда, когда можно корректно заменить тип категории 2, Type, на стеке операндов входного состояния типами Type, Type, что приведёт к состоянию выходного типа.
dup2Form2IsTypeSafe(Environment, InputOperandStack, OutputOperandStack):-
popCategory2(InputOperandStack, Type, _),
canSafelyPush(Environment, InputOperandStack, Type, OutputOperandStack).
dup2_x1
Инструкция dup2_x1 является безопасной по типу, если она является безопасной по типу формой инструкции dup2_x1.
instructionIsTypeSafe(dup2_x1, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
StackFrame = frame(Locals, InputOperandStack, Flags),
dup2_x1FormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack),
NextStackFrame = frame(Locals, OutputOperandStack, Flags),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция dup2_x1 является безопасной по типу формой инструкции dup2_x1 тогда и только тогда, когда она является инструкцией безопасная по типу форма 1 dup2_x1 или инструкцией безопасная по типу форма 2 dup_x2.
dup2_x1FormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
dup2_x1Form1IsTypeSafe(Environment, InputOperandStack, OutputOperandStack).
dup2_x1FormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
dup2_x1Form2IsTypeSafe(Environment, InputOperandStack, OutputOperandStack).
Инструкция dup2_x1 является инструкцией безопасная по типу форма 1 dup2_x1 тогда и только тогда, когда можно корректно заменить три типа категории 1, Type1, Type2, Type3, на стеке операндов входного состояния типами Type1, Type2, Type3, Type1, Type2, что приведёт к состоянию выходного типа.
dup2_x1Form1IsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
popCategory1(InputOperandStack, Type1, Stack1),
popCategory1(Stack1, Type2, Stack2),
popCategory1(Stack2, Type3, Rest),
canSafelyPushList(Environment, Rest, [Type2, Type1, Type3, Type2, Type1],
OutputOperandStack).
Инструкция dup2_x1 является инструкцией безопасная по типу форма 2 dup2_x1 тогда и только тогда, когда можно корректно заменить тип категории 2, Type1, и тип категории 1, Type2, на стеке операндов входного состояния типами Type1, Type2, Type1, что приведёт к состоянию выходного типа.
dup2_x1Form2IsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
popCategory2(InputOperandStack, Type1, Stack1),
popCategory1(Stack1, Type2, Rest),
canSafelyPushList(Environment, Rest, [Type1, Type2, Type1],
OutputOperandStack).
dup2_x2
Инструкция dup2_x2 является безопасной по типу, если она является безопасной по типу формой инструкции dup2_x2.
instructionIsTypeSafe(dup2_x2, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
StackFrame = frame(Locals, InputOperandStack, Flags),
dup2_x2FormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack),
NextStackFrame = frame(Locals, OutputOperandStack, Flags),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция dup2_x2 является безопасной по типу формой инструкции dup2_x2 в следующих случаях:
-
это инструкция безопасная по типу форма 1 dup2_x2.
-
это инструкция безопасная по типу форма 2 dup2_x2.
-
это инструкция безопасная по типу форма 3 dup2_x2.
-
это инструкция безопасная по типу форма 4 dup2_x2.
dup2_x2FormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
dup2_x2Form1IsTypeSafe(Environment, InputOperandStack, OutputOperandStack).
dup2_x2FormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
dup2_x2Form2IsTypeSafe(Environment, InputOperandStack, OutputOperandStack).
dup2_x2FormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
dup2_x2Form3IsTypeSafe(Environment, InputOperandStack, OutputOperandStack).
dup2_x2FormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
dup2_x2Form4IsTypeSafe(Environment, InputOperandStack, OutputOperandStack).
Инструкция dup2_x2 является инструкцией безопасная по типу форма 1 dup2_x2 тогда и только тогда, когда можно корректно заменить четыре типа категории 1, Type1, Type2, Type3, Type4, на стеке операндов входного состояния типами Type1, Type2, Type3, Type4, Type1, Type2, что приведёт к состоянию выходного типа.
dup2_x2Form1IsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
popCategory1(InputOperandStack, Type1, Stack1),
popCategory1(Stack1, Type2, Stack2),
popCategory1(Stack2, Type3, Stack3),
popCategory1(Stack3, Type4, Rest),
canSafelyPushList(Environment, Rest,
[Type2, Type1, Type4, Type3, Type2, Type1],
OutputOperandStack).
Инструкция dup2_x2 является инструкцией безопасная по типу форма 2 dup2_x2 тогда и только тогда, когда можно корректно заменить тип категории 2, Type1, и два типа категории 1, Type2, Type3, на стеке операндов входного состояния типами Type1, Type2, Type3, Type1, что приведёт к состоянию выходного типа.
dup2_x2Form2IsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
popCategory2(InputOperandStack, Type1, Stack1),
popCategory1(Stack1, Type2, Stack2),
popCategory1(Stack2, Type3, Rest),
canSafelyPushList(Environment, Rest,
[Type1, Type3, Type2, Type1],
OutputOperandStack).
Инструкция dup2_x2 является инструкцией безопасная по типу форма 3 dup2_x2 тогда и только тогда, когда можно корректно заменить два типа категории 1, Type1, Type2, и тип категории 2, Type3, на стеке операндов входного состояния типами Type1, Type2, Type3, Type1, Type2, что приведёт к состоянию выходного типа.
dup2_x2Form3IsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
popCategory1(InputOperandStack, Type1, Stack1),
popCategory1(Stack1, Type2, Stack2),
popCategory2(Stack2, Type3, Rest),
canSafelyPushList(Environment, Rest,
[Type2, Type1, Type3, Type2, Type1],
OutputOperandStack).
Инструкция dup2_x2 является инструкцией безопасная по типу форма 4 dup2_x2 тогда и только тогда, когда можно корректно заменить два типа категории 2, Type1, Type2, на стеке операндов входного состояния типами Type1, Type2, Type1, что приведёт к состоянию выходного типа.
dup2_x2Form4IsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
popCategory2(InputOperandStack, Type1, Stack1),
popCategory2(Stack1, Type2, Rest),
canSafelyPushList(Environment, Rest, [Type1, Type2, Type1],
OutputOperandStack).
f2d, f2i, f2l
Инструкция f2d является безопасной по типу, если можно корректно извлечь float со стека операндов входного состояния и заменить его на double, что приведёт к состоянию выходного типа.
instructionIsTypeSafe(f2d, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [float], double,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция f2i является безопасной по типу, если можно корректно извлечь float со стека операндов входного состояния и заменить его на int, что приведёт к состоянию выходного типа.
instructionIsTypeSafe(f2i, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [float], int,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция f2l является безопасной по типу, если можно корректно извлечь float со стека операндов входного состояния и заменить его на long, что приведёт к состоянию выходного типа.
instructionIsTypeSafe(f2l, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [float], long,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
fadd
Инструкция fadd является безопасной по типу, если можно корректно заменить типы, соответствующие float и float, на стеке операндов входного состояния на float, что приведёт к состоянию выходного типа.
instructionIsTypeSafe(fadd, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [float, float], float,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
faload
Инструкция faload является безошибочной с точки зрения типов, если можно корректно заменить типы, соответствующие int и массиву float на стеке входных операндов на float, что даст состояние типа вывода.
instructionIsTypeSafe(faload, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [int, arrayOf(float)], float,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
fastore
Инструкция fastore является безошибочной с точки зрения типов, если можно корректно извлечь типы, соответствующие float, int и массиву float со стека входных операндов, что даст состояние типа вывода.
instructionIsTypeSafe(fastore, _Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
canPop(StackFrame, [float, int, arrayOf(float)], NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
fcmp<op>
Инструкция fcmpg является безошибочной с точки зрения типов, если можно корректно заменить типы, соответствующие float и float на стеке входных операндов на int, что даст состояние типа вывода.
instructionIsTypeSafe(fcmpg, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [float, float], int,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция fcmpl является безошибочной с точки зрения типов, если эквивалентная инструкция fcmpg является безошибочной с точки зрения типов.
instructionHasEquivalentTypeRule(fcmpl, fcmpg).
fconst_<f>
Инструкция fconst_0 является безошибочной с точки зрения типов, если можно корректно поместить тип float на стек входных операндов, что даст состояние типа вывода.
instructionIsTypeSafe(fconst_0, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [], float, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Правила для других вариантов fconst эквивалентны.
instructionHasEquivalentTypeRule(fconst_1, fconst_0). instructionHasEquivalentTypeRule(fconst_2, fconst_0).
fdiv
Инструкция fdiv является безошибочной с точки зрения типов, если эквивалентная инструкция fadd является безошибочной с точки зрения типов.
instructionHasEquivalentTypeRule(fdiv, fadd).
fload, fload_<n>
Инструкция fload с операндом Index является безошибочной с точки зрения типов и приводит к состоянию типа вывода NextStackFrame, если инструкция загрузки с операндом Index и типом float является безошибочной с точки зрения типов и приводит к состоянию типа вывода NextStackFrame.
instructionIsTypeSafe(fload(Index), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
loadIsTypeSafe(Environment, Index, float, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкции fload_<n> для 0 ≤ n ≤ 3 являются безошибочными с точки зрения типов, если эквивалентная инструкция fload является безошибочной с точки зрения типов.
instructionHasEquivalentTypeRule(fload_0, fload(0)). instructionHasEquivalentTypeRule(fload_1, fload(1)). instructionHasEquivalentTypeRule(fload_2, fload(2)). instructionHasEquivalentTypeRule(fload_3, fload(3)).
fmul
Инструкция fmul является безошибочной с точки зрения типов, если эквивалентная инструкция fadd является безошибочной с точки зрения типов.
instructionHasEquivalentTypeRule(fmul, fadd).
fneg
Инструкция fneg является безошибочной с точки зрения типов, если на стеке входных операндов существует тип, соответствующий float. Инструкция fneg не изменяет состояние типа.
instructionIsTypeSafe(fneg, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [float], float,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
frem
Инструкция frem является безошибочной с точки зрения типов, если эквивалентная инструкция fadd является безошибочной с точки зрения типов.
instructionHasEquivalentTypeRule(frem, fadd).
freturn
Инструкция freturn является безошибочной с точки зрения типов, если у окружающего метода тип возврата объявлен как float, и можно корректно извлечь тип, соответствующий float со стека входных операндов.
instructionIsTypeSafe(freturn, Environment, _Offset, StackFrame,
afterGoto, ExceptionStackFrame) :-
thisMethodReturnType(Environment, float),
canPop(StackFrame, [float], _PoppedStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
fstore, fstore_<n>
Инструкция fstore с операндом Index является безошибочной с точки зрения типов и приводит к состоянию типа вывода NextStackFrame, если инструкция сохранения с операндом Index и типом float является безошибочной с точки зрения типов и приводит к состоянию типа вывода NextStackFrame.
instructionIsTypeSafe(fstore(Index), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
storeIsTypeSafe(Environment, Index, float, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкции fstore_<n> для 0 ≤ n ≤ 3 являются безошибочными с точки зрения типов, если эквивалентная инструкция fstore является безошибочной с точки зрения типов.
instructionHasEquivalentTypeRule(fstore_0, fstore(0)). instructionHasEquivalentTypeRule(fstore_1, fstore(1)). instructionHasEquivalentTypeRule(fstore_2, fstore(2)). instructionHasEquivalentTypeRule(fstore_3, fstore(3)).
fsub
Инструкция fsub является безошибочной с точки зрения типов, если эквивалентная инструкция fadd является безошибочной с точки зрения типов.
instructionHasEquivalentTypeRule(fsub, fadd).
getfield
Инструкция getfield с операндом CP является безошибочной с точки зрения типов, если CP ссылается на запись константного пула, обозначающую поле с объявленным типом FieldType, которое является членом класса FieldClassName, и можно корректно заменить тип, соответствующий FieldClassName, типом FieldType на стеке входных операндов, что даст состояние типа вывода. FieldClassName не должен быть типом массива. Для полей protected применяются дополнительные проверки (§4.10.1.8).
instructionIsTypeSafe(getfield(CP), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
CP = field(FieldClassName, FieldName, FieldDescriptor),
parseFieldDescriptor(FieldDescriptor, FieldType),
passesProtectedCheck(Environment, FieldClassName, FieldName,
FieldDescriptor, StackFrame),
currentClassLoader(Environment, CurrentLoader),
validTypeTransition(Environment,
[class(FieldClassName, CurrentLoader)], FieldType,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
getstatic
Инструкция getstatic с операндом CP является безошибочной с точки зрения типов, если CP ссылается на запись константного пула, обозначающую поле с объявленным типом FieldType, и можно корректно поместить FieldType на стек входных операндов, что даст состояние типа вывода.
instructionIsTypeSafe(getstatic(CP), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
CP = field(_FieldClassName, _FieldName, FieldDescriptor),
parseFieldDescriptor(FieldDescriptor, FieldType),
validTypeTransition(Environment, [], FieldType,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
goto, goto_w
Инструкция goto является безошибочной с точки зрения типов, если её целевой операнд является допустимой точкой ветвления.
instructionIsTypeSafe(goto(Target), Environment, _Offset, StackFrame,
afterGoto, ExceptionStackFrame) :-
targetIsTypeSafe(Environment, StackFrame, Target),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция goto_w является безошибочной с точки зрения типов, если эквивалентная инструкция goto является безошибочной с точки зрения типов.
instructionHasEquivalentTypeRule(goto_w(Target), goto(Target)).
i2b, i2c, i2d, i2f, i2l, i2s
Инструкция i2b является безошибочной с точки зрения типов, если эквивалентная инструкция ineg является безошибочной с точки зрения типов.
instructionHasEquivalentTypeRule(i2b, ineg).
Инструкция i2c является безошибочной с точки зрения типов, если эквивалентная инструкция ineg является безошибочной с точки зрения типов.
instructionHasEquivalentTypeRule(i2c, ineg).
Инструкция i2d является безошибочной, если можно корректно извлечь int со стека входных операндов и заменить его на double, что даст состояние типа вывода.
instructionIsTypeSafe(i2d, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [int], double,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция i2f является безошибочной, если можно корректно извлечь int со стека входных операндов и заменить его на float, что даст состояние типа вывода.
instructionIsTypeSafe(i2f, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [int], float,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция i2l является безошибочной, если можно корректно извлечь int со стека входных операндов и заменить его на long, что даст состояние типа вывода.
instructionIsTypeSafe(i2l, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [int], long,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция i2s является безошибочной с точки зрения типов, если эквивалентная инструкция ineg является безошибочной с точки зрения типов.
instructionHasEquivalentTypeRule(i2s, ineg).
iadd
Инструкция iadd является безошибочной с точки зрения типов, если можно корректно заменить типы, соответствующие int и int на стеке входных операндов на int, что даст состояние типа вывода.
instructionIsTypeSafe(iadd, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [int, int], int,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
iaload
Инструкция iaload является безопасной с точки зрения типов тогда и только тогда, когда можно корректно заменить типы, соответствующие int, и массив из int на стеке операндов входного состояния на int, что приводит к состоянию типов выходного состояния.
instructionIsTypeSafe(iaload, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [int, arrayOf(int)], int,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
iand
Инструкция iand безопасна с точки зрения типов тогда и только тогда, когда эквивалентная инструкция iadd безопасна с точки зрения типов.
instructionHasEquivalentTypeRule(iand, iadd).
iastore
Инструкция iastore безопасна с точки зрения типов тогда и только тогда, когда можно корректно извлечь типы, соответствующие int, int и массив из int со стека операндов входного состояния, что приводит к состоянию типов выходного состояния.
instructionIsTypeSafe(iastore, _Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
canPop(StackFrame, [int, int, arrayOf(int)], NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
iconst_<i>
Инструкция iconst_m1 безопасна с точки зрения типов, если можно корректно поместить тип int на стек операндов входного состояния, что приводит к состоянию типов выходного состояния.
instructionIsTypeSafe(iconst_m1, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [], int, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Правила для других вариантов инструкции iconst аналогичны.
instructionHasEquivalentTypeRule(iconst_0, iconst_m1). instructionHasEquivalentTypeRule(iconst_1, iconst_m1). instructionHasEquivalentTypeRule(iconst_2, iconst_m1). instructionHasEquivalentTypeRule(iconst_3, iconst_m1). instructionHasEquivalentTypeRule(iconst_4, iconst_m1). instructionHasEquivalentTypeRule(iconst_5, iconst_m1).
idiv
Инструкция idiv безопасна с точки зрения типов тогда и только тогда, когда эквивалентная инструкция iadd безопасна с точки зрения типов.
instructionHasEquivalentTypeRule(idiv, iadd).
if_acmp<cond>
Инструкция if_acmpeq безопасна с точки зрения типов тогда и только тогда, когда можно корректно извлечь типы, соответствующие reference и reference со стека операндов входного состояния, что приводит к состоянию типов выходного состояния NextStackFrame, и операнд инструкции, Target, является допустимой точкой ветвления, предполагая состояние типов входного состояния NextStackFrame.
instructionIsTypeSafe(if_acmpeq(Target), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
canPop(StackFrame, [reference, reference], NextStackFrame),
targetIsTypeSafe(Environment, NextStackFrame, Target),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Правило для if_acmpne идентично.
instructionHasEquivalentTypeRule(if_acmpne(Target), if_acmpeq(Target)).
if_icmp<cond>
Инструкция if_icmpeq безопасна с точки зрения типов тогда и только тогда, когда можно корректно извлечь типы, соответствующие int и int со стека операндов входного состояния, что приводит к состоянию типов выходного состояния NextStackFrame, и операнд инструкции, Target, является допустимой точкой ветвления, предполагая состояние типов входного состояния NextStackFrame.
instructionIsTypeSafe(if_icmpeq(Target), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
canPop(StackFrame, [int, int], NextStackFrame),
targetIsTypeSafe(Environment, NextStackFrame, Target),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Правила для всех остальных вариантов инструкции if_icmp<cond> идентичны.
instructionHasEquivalentTypeRule(if_icmpge(Target), if_icmpeq(Target)). instructionHasEquivalentTypeRule(if_icmpgt(Target), if_icmpeq(Target)). instructionHasEquivalentTypeRule(if_icmple(Target), if_icmpeq(Target)). instructionHasEquivalentTypeRule(if_icmplt(Target), if_icmpeq(Target)). instructionHasEquivalentTypeRule(if_icmpne(Target), if_icmpeq(Target)).
if<cond>
Инструкция ifeq безопасна с точки зрения типов тогда и только тогда, когда можно корректно извлечь тип, соответствующий int со стека операндов входного состояния, что приводит к состоянию типов выходного состояния NextStackFrame, и операнд инструкции, Target, является допустимой точкой ветвления, предполагая состояние типов входного состояния NextStackFrame.
instructionIsTypeSafe(ifeq(Target), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
canPop(StackFrame, [int], NextStackFrame),
targetIsTypeSafe(Environment, NextStackFrame, Target),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Правила для всех остальных вариантов инструкции if<cond> идентичны.
instructionHasEquivalentTypeRule(ifge(Target), ifeq(Target)). instructionHasEquivalentTypeRule(ifgt(Target), ifeq(Target)). instructionHasEquivalentTypeRule(ifle(Target), ifeq(Target)). instructionHasEquivalentTypeRule(iflt(Target), ifeq(Target)). instructionHasEquivalentTypeRule(ifne(Target), ifeq(Target)).
ifnonnull, ifnull
Инструкция ifnonnull безопасна с точки зрения типов тогда и только тогда, когда можно корректно извлечь тип, соответствующий reference со стека операндов входного состояния, что приводит к состоянию типов выходного состояния NextStackFrame, и операнд инструкции, Target, является допустимой точкой ветвления, предполагая состояние типов входного состояния NextStackFrame.
instructionIsTypeSafe(ifnonnull(Target), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
canPop(StackFrame, [reference], NextStackFrame),
targetIsTypeSafe(Environment, NextStackFrame, Target),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция ifnull безопасна с точки зрения типов тогда и только тогда, когда эквивалентная инструкция ifnonnull безопасна с точки зрения типов.
instructionHasEquivalentTypeRule(ifnull(Target), ifnonnull(Target)).
iinc
Инструкция iinc с первым операндом Index безопасна с точки зрения типов тогда и только тогда, когда LIndex имеет тип int. Инструкция iinc не изменяет состояние типа.
instructionIsTypeSafe(iinc(Index, _Value), _Environment, _Offset,
StackFrame, StackFrame, ExceptionStackFrame) :-
StackFrame = frame(Locals, _OperandStack, _Flags),
nth0(Index, Locals, int),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
iload, iload_<n>
Инструкция iload с операндом Index безопасна с точки зрения типов и приводит к состоянию типов выходного состояния NextStackFrame, если инструкция загрузки с операндом Index и типом int безопасна с точки зрения типов и приводит к состоянию типов выходного состояния NextStackFrame.
instructionIsTypeSafe(iload(Index), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
loadIsTypeSafe(Environment, Index, int, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкции iload_<n>, для 0 ≤ n ≤ 3, безопасны с точки зрения типов тогда и только тогда, когда эквивалентная инструкция iload безопасна с точки зрения типов.
instructionHasEquivalentTypeRule(iload_0, iload(0)). instructionHasEquivalentTypeRule(iload_1, iload(1)). instructionHasEquivalentTypeRule(iload_2, iload(2)). instructionHasEquivalentTypeRule(iload_3, iload(3)).
imul
Инструкция imul безопасна с точки зрения типов тогда и только тогда, когда эквивалентная инструкция iadd безопасна с точки зрения типов.
instructionHasEquivalentTypeRule(imul, iadd).
ineg
Инструкция ineg безопасна с точки зрения типов тогда и только тогда, когда на стеке операндов входного состояния есть тип, соответствующий int. Инструкция ineg не изменяет состояние типа.
instructionIsTypeSafe(ineg, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [int], int, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
instanceof
Инструкция instanceof с операндом CP безопасна с точки зрения типов тогда и только тогда, когда CP ссылается на запись константного пула, обозначающую класс или массив, и можно корректно заменить тип Object на вершине стека операндов входного состояния на тип int, что приводит к состоянию типов выходного состояния.
instructionIsTypeSafe(instanceof(CP), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
(CP = class(_, _) ; CP = arrayOf(_)),
isBootstrapLoader(BL),
validTypeTransition(Environment, [class('java/lang/Object', BL)], int,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
invokedynamic
Инструкция invokedynamic безопасна с точки зрения типов тогда и только тогда, когда выполняются все следующие условия:
-
Её первый операнд,
CP, ссылается на запись константного пула, обозначающую динамическую точку вызова с именемCallSiteNameи описателемDescriptor. -
CallSiteNameне равно<init>. -
CallSiteNameне равно<clinit>. -
Можно корректно заменить типы, соответствующие типам аргументов, указанным в
Descriptor, на стеке операндов входного состояния типом возвращаемого значения, указанным вDescriptor, что приводит к состоянию типов выходного состояния.
instructionIsTypeSafe(invokedynamic(CP,0,0), Environment, _Offset,
StackFrame, NextStackFrame, ExceptionStackFrame) :-
CP = dmethod(CallSiteName, Descriptor),
CallSiteName \= '<init>',
CallSiteName \= '<clinit>',
parseMethodDescriptor(Descriptor, OperandArgList, ReturnType),
reverse(OperandArgList, StackArgList),
validTypeTransition(Environment, StackArgList, ReturnType,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
invokeinterface
Инструкция invokeinterface является типобезопасной, если выполняются все следующие условия:
-
Её первый операнд,
CP, ссылается на запись в пуле констант, обозначающую метод интерфейса, названныйMethodName, с описателемDescriptor, являющимся членом интерфейсаMethodIntfName. -
MethodNameне равен<init>. -
MethodNameне равен<clinit>. -
Её второй операнд,
Count, является допустимым операндом-счётчиком (см. ниже). -
Можно корректно заменить типы, соответствующие типу
MethodIntfNameи типам аргументов, указанным вDescriptor, на стеке входных операндов, с возвращаемым типом, указанным вDescriptor, что даст состояние выходных типов.
instructionIsTypeSafe(invokeinterface(CP, Count, 0), Environment, _Offset,
StackFrame, NextStackFrame, ExceptionStackFrame) :-
CP = imethod(MethodIntfName, MethodName, Descriptor),
MethodName \= '<init>',
MethodName \= '<clinit>',
parseMethodDescriptor(Descriptor, OperandArgList, ReturnType),
currentClassLoader(Environment, CurrentLoader),
reverse([class(MethodIntfName, CurrentLoader) | OperandArgList],
StackArgList),
canPop(StackFrame, StackArgList, TempFrame),
validTypeTransition(Environment, [], ReturnType,
TempFrame, NextStackFrame),
countIsValid(Count, StackFrame, TempFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Операнд Count инструкции invokeinterface является допустимым, если он равен размеру аргументов инструкции. Это равно разнице между размером InputFrame и OutputFrame.
countIsValid(Count, InputFrame, OutputFrame) :-
InputFrame = frame(_Locals1, OperandStack1, _Flags1),
OutputFrame = frame(_Locals2, OperandStack2, _Flags2),
length(OperandStack1, Length1),
length(OperandStack2, Length2),
Count =:= Length1 - Length2.
invokespecial
Инструкция invokespecial является типобезопасной, если выполняются все следующие условия:
-
Её первый операнд,
CP, ссылается на запись в пуле констант, обозначающую метод, названныйMethodName, с описателемDescriptor, который является членом классаMethodClassName. -
Либо:
-
MethodNameне равно<init>. -
MethodNameне равно<clinit>. -
Можно корректно заменить типы, соответствующие текущему классу и типам аргументов, указанным в
Descriptor, на стеке входных операндов, с возвращаемым типом, указанным вDescriptor, что даст состояние выходных типов. -
Можно корректно заменить типы, соответствующие классу
MethodClassNameи типам аргументов, указанным вDescriptor, на стеке входных операндов, с возвращаемым типом, указанным вDescriptor.
-
instructionIsTypeSafe(invokespecial(CP), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
CP = method(MethodClassName, MethodName, Descriptor),
MethodName \= '<init>',
MethodName \= '<clinit>',
parseMethodDescriptor(Descriptor, OperandArgList, ReturnType),
thisClass(Environment, class(CurrentClassName, CurrentLoader)),
isAssignable(class(CurrentClassName, CurrentLoader),
class(MethodClassName, CurrentLoader)),
reverse([class(CurrentClassName, CurrentLoader) | OperandArgList],
StackArgList),
validTypeTransition(Environment, StackArgList, ReturnType,
StackFrame, NextStackFrame),
reverse([class(MethodClassName, CurrentLoader) | OperandArgList],
StackArgList2),
validTypeTransition(Environment, StackArgList2, ReturnType,
StackFrame, _ResultStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Условие isAssignable накладывает структурное ограничение, что invokespecial, кроме метода инициализации экземпляра, должен называть метод в текущем классе/интерфейсе или в суперклассе/суперинтерфейсе.
Первое условие validTypeTransition накладывает структурное ограничение, что invokespecial, кроме метода инициализации экземпляра, нацелен на объект-получатель текущего класса или его предков. Чтобы понять почему, рассмотрим, что StackArgList моделирует список типов на стеке операндов, ожидаемых методом, начиная с текущего класса (класса, выполняющего invokespecial). Фактические типы на стеке операндов находятся в StackFrame. Действие validTypeTransition заключается в извлечении первого типа из стека операндов в StackFrame и проверке, является ли он подтипом первого элемента StackArgList, а именно текущего класса. Таким образом, фактический тип получателя совместим с текущим классом.
Внимательный читатель может заметить, что наложение этого структурного ограничения опережает структурное ограничение, относящееся к invokespecial метода protected. Таким образом, приведенный выше код Prolog не ссылается на passesProtectedCheck (§4.10.1.8), в то время как код Prolog для invokespecial метода инициализации экземпляра использует passesProtectedCheck, чтобы убедиться, что фактический тип получателя совместим с текущим классом, когда называются определённые protected методы инициализации экземпляра.
Второе условие validTypeTransition накладывает структурное ограничение, что любая инструкция вызова метода должна нацеливаться на объект-получатель, тип которого совместим с типом, указанным в инструкции. Чтобы понять почему, рассмотрим, что StackArgList2 моделирует список типов на стеке операндов, ожидаемых методом, начиная с типа, указанного в инструкции. Опять же, фактические типы на стеке операндов находятся в StackFrame, и эффект validTypeTransition заключается в проверке, что фактический тип получателя в StackFrame совместим с типом, указанным в инструкции в StackArgList2.
-
Или:
-
ИмяМетода равно
<init>. -
Descriptorзадаёт тип возвращаемого значенияvoid. -
Можно корректно извлечь типы, соответствующие типам аргументов, указанным в
Descriptor, и неинициализированному типуUninitializedArg, со стека входных операндов, получаяOperandStack. -
Состояние выходных типов выводится из состояния входных типов путём сначала замены стека входных операндов на
OperandStack, а затем заменой всех экземпляровUninitializedArgтипом инициализируемого экземпляра. -
Если инструкция вызывает метод инициализации экземпляра для экземпляра класса, созданного ранее инструкцией new, и метод является
protected, то использование соответствует специальным правилам доступа кprotectedчленам (§4.10.1.8).
-
instructionIsTypeSafe(invokespecial(CP), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
CP = method(MethodClassName, '<init>', Descriptor),
parseMethodDescriptor(Descriptor, OperandArgList, void),
reverse(OperandArgList, StackArgList),
canPop(StackFrame, StackArgList, TempFrame),
TempFrame = frame(Locals, [uninitializedThis | OperandStack], Flags),
currentClassLoader(Environment, CurrentLoader),
rewrittenUninitializedType(uninitializedThis, Environment,
class(MethodClassName, CurrentLoader), This),
rewrittenInitializationFlags(uninitializedThis, Flags, NextFlags),
substitute(uninitializedThis, This, OperandStack, NextOperandStack),
substitute(uninitializedThis, This, Locals, NextLocals),
NextStackFrame = frame(NextLocals, NextOperandStack, NextFlags),
ExceptionStackFrame = frame(Locals, [], Flags).
instructionIsTypeSafe(invokespecial(CP), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
CP = method(MethodClassName, '<init>', Descriptor),
parseMethodDescriptor(Descriptor, OperandArgList, void),
reverse(OperandArgList, StackArgList),
canPop(StackFrame, StackArgList, TempFrame),
TempFrame = frame(Locals, [uninitialized(Address) | OperandStack], Flags),
currentClassLoader(Environment, CurrentLoader),
rewrittenUninitializedType(uninitialized(Address), Environment,
class(MethodClassName, CurrentLoader), This),
rewrittenInitializationFlags(uninitialized(Address), Flags, NextFlags),
substitute(uninitialized(Address), This, OperandStack, NextOperandStack),
substitute(uninitialized(Address), This, Locals, NextLocals),
NextStackFrame = frame(NextLocals, NextOperandStack, NextFlags),
ExceptionStackFrame = frame(Locals, [], Flags),
passesProtectedCheck(Environment, MethodClassName, '<init>',
Descriptor, NextStackFrame).
Для вычисления того, на какой тип должен быть переписан тип неинициализированного аргумента, существуют два случая:
-
Если мы инициализируем объект в его конструкторе, его тип изначально
uninitializedThis. Этот тип будет переписан на тип класса метода<init>. -
Второй случай возникает при инициализации объекта, созданного инструкцией new. Тип неинициализированного аргумента переписывается на
MethodClass, тип держателя метода<init>. Мы проверяем, есть ли действительно инструкция new по адресуAddress.
rewrittenUninitializedType(uninitializedThis, Environment,
MethodClass, MethodClass) :-
MethodClass = class(MethodClassName, CurrentLoader),
thisClass(Environment, MethodClass).
rewrittenUninitializedType(uninitializedThis, Environment,
MethodClass, MethodClass) :-
MethodClass = class(MethodClassName, CurrentLoader),
thisClass(Environment, class(thisClassName, thisLoader)),
superclassChain(thisClassName, thisLoader, [MethodClass | Rest]).
rewrittenUninitializedType(uninitialized(Address), Environment,
MethodClass, MethodClass) :-
allInstructions(Environment, Instructions),
member(instruction(Address, new(MethodClass)), Instructions).
rewrittenInitializationFlags(uninitializedThis, _Flags, []).
rewrittenInitializationFlags(uninitialized(_), Flags, Flags).
substitute(_Old, _New, [], []).
substitute(Old, New, [Old | FromRest], [New | ToRest]) :-
substitute(Old, New, FromRest, ToRest).
substitute(Old, New, [From1 | FromRest], [From1 | ToRest]) :-
From1 \= Old,
substitute(Old, New, FromRest, ToRest).
Правило для invokespecial метода <init> является единственной причиной передачи отдельной стековой рамки исключения. Озабоченность состоит в том, что при инициализации объекта в его конструкторе invokespecial может вызвать метод суперкласса <init>, и этот вызов может завершиться ошибкой, оставив this неинициализированным. Эта ситуация не может быть создана с помощью исходного кода языка программирования Java, но может быть создана программированием напрямую в байткоде.
В этой ситуации оригинальная рамка содержит неинициализированный объект в локальной переменной 0 и имеет флаг flagThisUninit. Нормальное завершение invokespecial инициализирует неинициализированный объект и отключает флаг flagThisUninit. Но если вызов метода <init> генерирует исключение, неинициализированный объект может остаться в частично инициализированном состоянии и должен быть навсегда непригодным для использования. Это представлено кадрами исключения, содержащими сломанный объект (новое значение локальной переменной) и флаг flagThisUninit (старый флаг). Нет способа перейти от, по-видимому, инициализированного объекта с флагом flagThisUninit к должным образом инициализированному объекту, поэтому объект навсегда непригоден для использования.
Если бы не эта ситуация, флаги стековой рамки исключения всегда были бы такими же, как и флаги входной стековой рамки.
invokestatic
Инструкция invokestatic является типобезопасной, если выполняются все следующие условия:
-
Её первый операнд,
CP, ссылается на запись в пуле констант, обозначающую метод, названныйMethodName, с описателемDescriptor. -
MethodNameне равно<init>. -
MethodNameне равно<clinit>. -
Можно корректно заменить типы, соответствующие типам аргументов, указанным в
Descriptor, на стеке входных операндов, с возвращаемым типом, указанным вDescriptor, что даст состояние выходных типов.
instructionIsTypeSafe(invokestatic(CP), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
CP = method(_MethodClassName, MethodName, Descriptor),
MethodName \= '<init>',
MethodName \= '<clinit>',
parseMethodDescriptor(Descriptor, OperandArgList, ReturnType),
reverse(OperandArgList, StackArgList),
validTypeTransition(Environment, StackArgList, ReturnType,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
invokevirtual
Инструкция invokevirtual является типабезопасной, если все перечисленные ниже условия выполняются:
-
Её первый операнд,
CP, ссылается на запись в пуле констант, обозначающую метод с именемMethodNameи дескрипторомDescriptor, который является членом классаMethodClassName. -
MethodNameне является<init>. -
MethodNameне является<clinit>. -
Можно корректно заменить типы, соответствующие классу
MethodClassNameи типам аргументов, указанным вDescriptor, на стеке входящих операндов, на возвращаемый тип, указанный вDescriptor, что приводит к состоянию исходящего типа. -
Если метод является
protected, использование соответствует специальным правилам, управляющим доступом к членамprotected(§4.10.1.8).
instructionIsTypeSafe(invokevirtual(CP), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
CP = method(MethodClassName, MethodName, Descriptor),
MethodName \= '<init>',
MethodName \= '<clinit>',
parseMethodDescriptor(Descriptor, OperandArgList, ReturnType),
reverse(OperandArgList, ArgList),
currentClassLoader(Environment, CurrentLoader),
reverse([class(MethodClassName, CurrentLoader) | OperandArgList],
StackArgList),
validTypeTransition(Environment, StackArgList, ReturnType,
StackFrame, NextStackFrame),
canPop(StackFrame, ArgList, PoppedFrame),
passesProtectedCheck(Environment, MethodClassName, MethodName,
Descriptor, PoppedFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
ior, irem
Инструкция ior является типабезопасной, если эквивалентная инструкция iadd является типабезопасной.
instructionHasEquivalentTypeRule(ior, iadd).
Инструкция irem является типабезопасной, если эквивалентная инструкция iadd является типабезопасной.
instructionHasEquivalentTypeRule(irem, iadd).
ireturn
Инструкция ireturn является типабезопасной, если объявленный возвращаемый тип окружающего метода равен int, и можно корректно извлечь тип, соответствующий int, со стека входящих операндов.
instructionIsTypeSafe(ireturn, Environment, _Offset, StackFrame,
afterGoto, ExceptionStackFrame) :-
thisMethodReturnType(Environment, int),
canPop(StackFrame, [int], _PoppedStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
ishl, ishr, iushr
Инструкция ishl является типабезопасной, если эквивалентная инструкция iadd является типабезопасной.
instructionHasEquivalentTypeRule(ishl, iadd).
Инструкция ishr является типабезопасной, если эквивалентная инструкция iadd является типабезопасной.
instructionHasEquivalentTypeRule(ishr, iadd).
Инструкция iushr является типабезопасной, если эквивалентная инструкция iadd является типабезопасной.
instructionHasEquivalentTypeRule(iushr, iadd).
istore, istore_<n>
Инструкция istore с операндом Index является типабезопасной и приводит к состоянию исходящего типа NextStackFrame, если инструкция сохранения с операндом Index и типом int является типабезопасной и приводит к состоянию исходящего типа NextStackFrame.
instructionIsTypeSafe(istore(Index), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
storeIsTypeSafe(Environment, Index, int, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкции istore_<n>, для 0 ≤ n ≤ 3, являются типабезопасными, если эквивалентная инструкция istore является типабезопасной.
instructionHasEquivalentTypeRule(istore_0, istore(0)). instructionHasEquivalentTypeRule(istore_1, istore(1)). instructionHasEquivalentTypeRule(istore_2, istore(2)). instructionHasEquivalentTypeRule(istore_3, istore(3)).
isub, ixor
Инструкция isub является типабезопасной, если эквивалентная инструкция iadd является типабезопасной.
instructionHasEquivalentTypeRule(isub, iadd).
Инструкция ixor является типабезопасной, если эквивалентная инструкция iadd является типабезопасной.
instructionHasEquivalentTypeRule(ixor, iadd).
l2d, l2f, l2i
Инструкция l2d является типабезопасной, если можно корректно извлечь long со стека входящих операндов и заменить его на double, что приводит к состоянию исходящего типа.
instructionIsTypeSafe(l2d, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [long], double,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция l2f является типабезопасной, если можно корректно извлечь long со стека входящих операндов и заменить его на float, что приводит к состоянию исходящего типа.
instructionIsTypeSafe(l2f, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [long], float,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция l2i является типабезопасной, если можно корректно извлечь long со стека входящих операндов и заменить его на int, что приводит к состоянию исходящего типа.
instructionIsTypeSafe(l2i, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [long], int,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
ladd
Инструкция ladd является типабезопасной, если можно корректно заменить типы, соответствующие long и long, на стеке входящих операндов, на long, что приводит к состоянию исходящего типа.
instructionIsTypeSafe(ladd, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [long, long], long,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
laload
Инструкция laload является типабезопасной, если можно корректно заменить типы, соответствующие int и массиву long, на стеке входящих операндов, на long, что приводит к состоянию исходящего типа.
instructionIsTypeSafe(laload, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [int, arrayOf(long)], long,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
land
Инструкция land является типабезопасной, если эквивалентная инструкция ladd является типабезопасной.
instructionHasEquivalentTypeRule(land, ladd).
lastore
Инструкция lastore является типабезопасной, если можно корректно извлечь типы, соответствующие long, int и массиву long, со стека входящих операндов, что приводит к состоянию исходящего типа.
instructionIsTypeSafe(lastore, _Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
canPop(StackFrame, [long, int, arrayOf(long)], NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
lcmp
Инструкция lcmp является типабезопасной, если можно корректно заменить типы, соответствующие long и long, на стеке входящих операндов, на int, что приводит к состоянию исходящего типа.
instructionIsTypeSafe(lcmp, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [long, long], int,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
lconst_<l>
Инструкция lconst_0 является типабезопасной, если можно корректно поместить тип long на стек входящих операндов, что приводит к состоянию исходящего типа.
instructionIsTypeSafe(lconst_0, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [], long, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция lconst_1 является типабезопасной, если эквивалентная инструкция lconst_0 является типабезопасной.
instructionHasEquivalentTypeRule(lconst_1, lconst_0).
ldc, ldc_w, ldc2_w
Инструкция ldc с операндом CP является типабезопасной, если CP ссылается на запись в пуле констант, обозначающую сущность типа Type, где Type загружаема (§4.4), но не является long или double, и можно корректно поместить Type на стек входящих операндов, что приводит к состоянию исходящего типа.
instructionIsTypeSafe(ldc(CP), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
loadableConstant(CP, Type),
Type \= long,
Type \= double,
validTypeTransition(Environment, [], Type, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
loadableConstant(CP, Type) :-
member([CP, Type], [
[int(_), int],
[float(_), float],
[long(_), long],
[double(_), double]
]).
loadableConstant(CP, Type) :-
isBootstrapLoader(BL),
member([CP, Type], [
[class(_), class('java/lang/Class', BL)],
[string(_), class('java/lang/String', BL)],
[methodHandle(_,_), class('java/lang/invoke/MethodHandle', BL)],
[methodType(_,_), class('java/lang/invoke/MethodType', BL)]
]).
loadableConstant(CP, Type) :-
CP = dconstant(_, FieldDescriptor),
parseFieldDescriptor(FieldDescriptor, Type).
Инструкция ldc_w является типабезопасной, если эквивалентная инструкция ldc является типабезопасной.
instructionHasEquivalentTypeRule(ldc_w(CP), ldc(CP))
Инструкция ldc2_w с операндом CP является типабезопасной, если CP ссылается на запись в пуле констант, обозначающую сущность типа Type, где Type является либо long, либо double, и можно корректно поместить Type на стек входящих операндов, что приводит к состоянию исходящего типа.
instructionIsTypeSafe(ldc2_w(CP), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
loadableConstant(CP, Type),
(Type = long ; Type = double),
validTypeTransition(Environment, [], Type, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
ldiv
Инструкция ldiv является типабезопасной, если эквивалентная инструкция ladd является типабезопасной.
instructionHasEquivalentTypeRule(ldiv, ladd).
lload, lload_<n>
Инструкция lload с операндом Index является безошибочной и возвращает состояние типа выходящего значения NextStackFrame, если инструкция загрузки с операндом Index и типом long безошибочна и возвращает состояние типа выходящего значения NextStackFrame.
instructionIsTypeSafe(lload(Index), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
loadIsTypeSafe(Environment, Index, long, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкции lload_<n>, для 0 ≤ n ≤ 3, безошибочны тогда и только тогда, когда эквивалентная инструкция lload безошибочна.
instructionHasEquivalentTypeRule(lload_0, lload(0)). instructionHasEquivalentTypeRule(lload_1, lload(1)). instructionHasEquivalentTypeRule(lload_2, lload(2)). instructionHasEquivalentTypeRule(lload_3, lload(3)).
lmul
Инструкция lmul безошибочна тогда и только тогда, когда эквивалентная инструкция ladd безошибочна.
instructionHasEquivalentTypeRule(lmul, ladd).
lneg
Инструкция lneg безошибочна тогда и только тогда, когда на стеке входных операндов имеется соответствие типа long. Инструкция lneg не изменяет состояние типа.
instructionIsTypeSafe(lneg, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [long], long,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
lookupswitch
Инструкция lookupswitch безошибочна, если её ключи отсортированы, можно корректно извлечь int со стека входных операндов, получив новое состояние типа BranchStackFrame, и все целевые адреса инструкции являются допустимыми адресами перехода, предполагая BranchStackFrame в качестве их состояния типа входных данных.
instructionIsTypeSafe(lookupswitch(Targets, Keys), Environment, _, StackFrame,
afterGoto, ExceptionStackFrame) :-
sort(Keys, Keys),
canPop(StackFrame, [int], BranchStackFrame),
checklist(targetIsTypeSafe(Environment, BranchStackFrame), Targets),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
lor, lrem
Инструкция lor безошибочна тогда и только тогда, когда эквивалентная инструкция ladd безошибочна.
instructionHasEquivalentTypeRule(lor, ladd).
Инструкция lrem безошибочна тогда и только тогда, когда эквивалентная инструкция ladd безошибочна.
instructionHasEquivalentTypeRule(lrem, ladd).
lreturn
Инструкция lreturn безошибочна, если у окружающего метода объявлен возвращаемый тип long, и можно корректно извлечь тип, соответствующий long со стека входных операндов.
instructionIsTypeSafe(lreturn, Environment, _Offset, StackFrame,
afterGoto, ExceptionStackFrame) :-
thisMethodReturnType(Environment, long),
canPop(StackFrame, [long], _PoppedStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
lshl, lshr, lushr
Инструкция lshl безошибочна, если можно корректно заменить типы int и long на стеке входных операндов типом long, получив состояние типа выходных данных.
instructionIsTypeSafe(lshl, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [int, long], long,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция lshr безошибочна тогда и только тогда, когда эквивалентная инструкция lshl безошибочна.
instructionHasEquivalentTypeRule(lshr, lshl).
Инструкция lushr безошибочна тогда и только тогда, когда эквивалентная инструкция lshl безошибочна.
instructionHasEquivalentTypeRule(lushr, lshl).
lstore, lstore_<n>
Инструкция lstore с операндом Index безошибочна и возвращает состояние типа выходных данных NextStackFrame, если инструкция сохранения с операндом Index и типом long безошибочна и возвращает состояние типа выходных данных NextStackFrame.
instructionIsTypeSafe(lstore(Index), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
storeIsTypeSafe(Environment, Index, long, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкции lstore_<n>, для 0 ≤ n ≤ 3, безошибочны тогда и только тогда, когда эквивалентная инструкция lstore безошибочна.
instructionHasEquivalentTypeRule(lstore_0, lstore(0)). instructionHasEquivalentTypeRule(lstore_1, lstore(1)). instructionHasEquivalentTypeRule(lstore_2, lstore(2)). instructionHasEquivalentTypeRule(lstore_3, lstore(3)).
lsub, lxor
Инструкция lsub безошибочна тогда и только тогда, когда эквивалентная инструкция ladd безошибочна.
instructionHasEquivalentTypeRule(lsub, ladd).
Инструкция lxor безошибочна тогда и только тогда, когда эквивалентная инструкция ladd безошибочна.
instructionHasEquivalentTypeRule(lxor, ladd).
monitorenter, monitorexit
Инструкция monitorenter безошибочна тогда и только тогда, когда можно корректно извлечь тип, соответствующий reference со стека входных операндов, получив состояние типа выходных данных.
instructionIsTypeSafe(monitorenter, _Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
canPop(StackFrame, [reference], NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция monitorexit безошибочна тогда и только тогда, когда эквивалентная инструкция monitorenter безошибочна.
instructionHasEquivalentTypeRule(monitorexit, monitorenter).
multianewarray
Инструкция multianewarray с операндами CP и Dim безошибочна тогда и только тогда, когда CP ссылается на запись в пуле констант, обозначающую тип массива, размерность которого больше или равна Dim, Dim строго положительна, и можно корректно заменить Dim int типов на стеке входных операндов типом, обозначаемым CP, получив состояние типа выходных данных.
instructionIsTypeSafe(multianewarray(CP, Dim), Environment, _Offset,
StackFrame, NextStackFrame, ExceptionStackFrame) :-
CP = arrayOf(_),
classDimension(CP, Dimension),
Dimension >= Dim,
Dim > 0,
/* Make a list of Dim ints */
findall(int, between(1, Dim, _), IntList),
validTypeTransition(Environment, IntList, CP,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Размерность типа массива, компонентом которого также является тип массива, на единицу больше размерности типа его компонента.
classDimension(arrayOf(X), Dimension) :-
classDimension(X, Dimension1),
Dimension is Dimension1 + 1.
classDimension(_, Dimension) :-
Dimension = 0.
new
Инструкция new с операндом CP в смещении Offset безошибочна тогда и только тогда, когда CP ссылается на запись в пуле констант, обозначающую тип класса или интерфейса, тип uninitialized(Offset) не появляется на стеке входных операндов, и можно корректно поместить uninitialized(Offset) на стек входных операндов и заменить uninitialized(Offset) на top в локальных переменных входных данных, получив состояние типа выходных данных.
instructionIsTypeSafe(new(CP), Environment, Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
StackFrame = frame(Locals, OperandStack, Flags),
CP = class(_, _),
NewItem = uninitialized(Offset),
notMember(NewItem, OperandStack),
substitute(NewItem, top, Locals, NewLocals),
validTypeTransition(Environment, [], NewItem,
frame(NewLocals, OperandStack, Flags),
NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Предикат substitute определен в правиле для invokespecial (§invokespecial).
newarray
Инструкция newarray с операндом TypeCode безошибочна тогда и только тогда, когда TypeCode соответствует примитивному типу ElementType, и можно корректно заменить тип int на стеке входных операндов типом 'массив ElementType', получив состояние типа выходных данных.
instructionIsTypeSafe(newarray(TypeCode), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
primitiveArrayInfo(TypeCode, _TypeChar, ElementType, _VerifierType),
validTypeTransition(Environment, [int], arrayOf(ElementType),
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Соответствие между кодами типов и примитивными типами задаётся следующим предикатом:
primitiveArrayInfo(4, 0'Z, boolean, int). primitiveArrayInfo(5, 0'C, char, int). primitiveArrayInfo(6, 0'F, float, float). primitiveArrayInfo(7, 0'D, double, double). primitiveArrayInfo(8, 0'B, byte, int). primitiveArrayInfo(9, 0'S, short, int). primitiveArrayInfo(10, 0'I, int, int). primitiveArrayInfo(11, 0'J, long, long).
nop
Инструкция nop всегда безошибочна. Инструкция nop не влияет на состояние типа.
instructionIsTypeSafe(nop, _Environment, _Offset, StackFrame,
StackFrame, ExceptionStackFrame) :-
exceptionStackFrame(StackFrame, ExceptionStackFrame).
pop, pop2
Инструкция pop безопасна по типу, если можно корректно извлечь тип категории 1 из входного стека операндов, получая состояние типа выхода.
instructionIsTypeSafe(pop, _Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
StackFrame = frame(Locals, [Type | Rest], Flags),
popCategory1([Type | Rest], Type, Rest),
NextStackFrame = frame(Locals, Rest, Flags),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция pop2 безопасна по типу, если она является безопасной по типу формой инструкции pop2.
instructionIsTypeSafe(pop2, _Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
StackFrame = frame(Locals, InputOperandStack, Flags),
pop2SomeFormIsTypeSafe(InputOperandStack, OutputOperandStack),
NextStackFrame = frame(Locals, OutputOperandStack, Flags),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция pop2 является безопасной по типу формой инструкции pop2, если она является инструкцией безопасной по типу формы 1 pop2 или инструкцией безопасной по типу формы 2 pop2.
pop2SomeFormIsTypeSafe(InputOperandStack, OutputOperandStack) :-
pop2Form1IsTypeSafe(InputOperandStack, OutputOperandStack).
pop2SomeFormIsTypeSafe(InputOperandStack, OutputOperandStack) :-
pop2Form2IsTypeSafe(InputOperandStack, OutputOperandStack).
Инструкция pop2 является безопасной по типу формой 1 pop2 инструкцией, если можно корректно извлечь два типа размера 1 из входного стека операндов, получая состояние типа выхода.
pop2Form1IsTypeSafe([Type1, Type2 | Rest], Rest) :-
popCategory1([Type1 | Rest], Type1, Rest),
popCategory1([Type2 | Rest], Type2, Rest).
Инструкция pop2 является безопасной по типу формой 2 pop2 инструкцией, если можно корректно извлечь тип размера 2 из входного стека операндов, получая состояние типа выхода.
pop2Form2IsTypeSafe([top, Type | Rest], Rest) :-
popCategory2([top, Type | Rest], Type, Rest).
putfield
Инструкция putfield с операндом CP безопасна по типу, если все перечисленное ниже верно:
-
Её первый операнд,
CP, ссылается на запись константного пула, обозначающую поле, тип которого объявлен какFieldType, являющееся членом классаFieldClassName.FieldClassNameне должен быть типом массива. -
Или:
-
Можно корректно извлечь типы, соответствующие
FieldTypeиFieldClassName, из входного стека операндов, получая состояние типа выхода. -
Для полей
protectedвыполняются дополнительные проверки (§4.10.1.8).
-
instructionIsTypeSafe(putfield(CP), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
CP = field(FieldClassName, FieldName, FieldDescriptor),
parseFieldDescriptor(FieldDescriptor, FieldType),
canPop(StackFrame, [FieldType], PoppedFrame),
passesProtectedCheck(Environment, FieldClassName, FieldName,
FieldDescriptor, PoppedFrame),
currentClassLoader(Environment, CurrentLoader),
canPop(StackFrame, [FieldType, class(FieldClassName, CurrentLoader)],
NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
-
Или:
-
Если инструкция встречается в методе инициализации экземпляра класса
FieldClassNameи присваивает поле, объявленное классом, то можно корректно извлечь типы, соответствующиеFieldTypeиuninitializedThis, из входного стека операндов, получая состояние типа выхода. Это позволяет присваивать поля экземпляраthis, объявленные в текущем классе, до полной инициализацииthis.
-
instructionIsTypeSafe(putfield(CP), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
CP = field(FieldClassName, FieldName, FieldDescriptor),
parseFieldDescriptor(FieldDescriptor, FieldType),
Environment = environment(CurrentClass, CurrentMethod, _, _, _, _),
CurrentClass = class(FieldClassName, _),
isInit(CurrentMethod),
classDeclaresMember(CurrentClass, FieldName, FieldDescriptor),
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).
Файл class, не содержащий атрибут StackMapTable (который обязательно имеет номер версии 49.0 или ниже), должен быть проверен с помощью вывода типов.
Во время линковки верификатор проверяет массив code атрибута Code для каждого метода файла class, выполняя анализ потока данных для каждого метода. Верификатор гарантирует, что в любой момент программы, независимо от пути кода, ведущего к этой точке, выполняются следующие условия:
-
Высота стека операндов всегда одинакова, и он содержит значения одинаковых типов.
-
К локальной переменной обращаются только в том случае, если известно, что она содержит значение соответствующего типа.
-
Методы вызываются с соответствующими аргументами.
-
Полям присваиваются значения только с помощью значений соответствующих типов.
-
Все команды имеют операнды соответствующего типа в стеке операндов и в массиве локальных переменных.
Из соображений эффективности некоторые тесты, которые в принципе может выполнять верификатор, откладываются до первого фактического вызова кода метода. Благодаря этому верификатор избегает загрузки файлов class, если это не обязательно.
Например, если метод вызывает другой метод, возвращающий экземпляр класса A, а этот экземпляр присваивается только полю того же типа, верификатор не проверяет, действительно ли существует класс A. Однако, если он присваивается полю типа B, определения обоих классов A и B должны быть загружены, чтобы убедиться, что A является подклассом B.
Код каждого метода проверяется независимо. Сначала байты, составляющие код, разбиваются на последовательность инструкций, а индекс в массиве code начала каждой инструкции помещается в массив. Затем верификатор проходит по коду второй раз и анализирует инструкции. Во время этого прохода создается структура данных для хранения информации о каждой инструкции виртуальной машины Java в методе. Операнды, если таковые имеются, каждой инструкции проверяются на корректность. Например:
-
Ветвления должны находиться в пределах границ массива
codeдля метода. -
Цели всех инструкций управления потоком — это начало инструкции. В случае инструкции wide, команда wide считается началом инструкции, а команда, задающая операцию, модифицируемую инструкцией wide, не считается началом инструкции. Ветвления в середину инструкции запрещены.
-
Никакая инструкция не может получить доступ к локальной переменной или изменить ее по индексу, большему или равному числу локальных переменных, которые метод указывает для выделения.
-
Все ссылки на пул констант должны быть на запись соответствующего типа. (Например, инструкция getfield должна ссылаться на поле.)
-
Код не завершается в середине инструкции.
-
Выполнение не может завершиться в конце кода.
-
Для каждого обработчика исключений начальная и конечная точки кода, защищенного обработчиком, должны быть в начале инструкции или, в случае конечной точки, непосредственно после конца кода. Начальная точка должна предшествовать конечной. Код обработчика исключений должен начинаться с допустимой инструкции и не должен начинаться с команды, модифицированной инструкцией wide.
Для каждой инструкции метода верификатор записывает содержимое стека операндов и массива локальных переменных перед выполнением этой инструкции. Для стека операндов ему нужно знать высоту стека и тип каждого значения в нем. Для каждой локальной переменной ему нужно знать либо тип содержимого этой локальной переменной, либо то, что локальная переменная содержит неприменимое или неизвестное значение (может быть неинициализирована). Верификатор байткода не нуждается в различении целочисленных типов (например, byte, short, char) при определении типов значений в стеке операндов.
Далее инициализируется анализатор потока данных. Для первой инструкции метода локальные переменные, представляющие параметры, изначально содержат значения типов, указанных описателем типа метода; стек операндов пуст. Все остальные локальные переменные содержат недопустимое значение. Для других инструкций, которые еще не были проверены, нет информации о стеке операндов или локальных переменных.
Наконец, выполняется анализ потока данных. Для каждой инструкции бит «изменён» указывает, нужно ли рассматривать эту инструкцию. Изначально бит «изменён» установлен только для первой инструкции. Анализатор потока данных выполняет следующий цикл:
-
Выбрать инструкцию виртуальной машины Java, для которой установлен бит «изменён». Если инструкций, для которых бит «изменён», не осталось, метод успешно проверен. В противном случае снимите бит «изменён» у выбранной инструкции.
-
Моделировать эффект инструкции на стек операндов и массив локальных переменных, выполнив следующие действия:
-
Если инструкция использует значения из стека операндов, убедитесь, что на стеке достаточно значений и что верхние значения на стеке имеют соответствующий тип. В противном случае проверка завершается неудачей.
-
Если инструкция использует локальную переменную, убедитесь, что указанная локальная переменная содержит значение соответствующего типа. В противном случае проверка завершается неудачей.
-
Если инструкция помещает значения в стек операндов, убедитесь, что на стеке операндов достаточно места для новых значений. Добавьте указанные типы в верхнюю часть моделируемого стека операндов.
-
Если инструкция изменяет локальную переменную, зафиксируйте, что локальная переменная теперь содержит новый тип.
-
-
Определите инструкции, которые могут следовать за текущей инструкцией. Последовательные инструкции могут быть следующими:
-
Следующая инструкция, если текущая инструкция не является инструкцией безусловного перехода (например, goto, return или athrow). Проверка завершается неудачей, если возможно «выпасть» из последней инструкции метода.
-
Цель(и) условного или безусловного ветвления или переключения.
-
Любые обработчики исключений для этой инструкции.
-
-
Объединить состояние стека операндов и массива локальных переменных в конце выполнения текущей инструкции в каждую из последовательных инструкций следующим образом:
-
Если эта последовательная инструкция посещается впервые, запишите, что значения стека операндов и локальных переменных, рассчитанные в шаге 2, являются состоянием стека операндов и массива локальных переменных перед выполнением последовательной инструкции. Установите бит «изменён» для последовательной инструкции.
-
Если последовательная инструкция встречалась ранее, объедините значения стека операндов и локальных переменных, рассчитанные в шаге 2, с уже имеющимися значениями. Установите бит «изменён», если есть изменения в значениях.
В особом случае передачи управления обработчику исключений:
-
Запишите, что единственный объект, типа исключения, указанного обработчиком исключений, является состоянием стека операндов перед выполнением последовательной инструкции. Должно быть достаточно места в стеке операндов для этого единственного значения, как если бы инструкция его поместила.
-
Запишите, что значения локальных переменных непосредственно перед шагом 2 являются состоянием массива локальных переменных перед выполнением последовательной инструкции. Значения локальных переменных, вычисленные в шаге 2, не имеют значения.
-
-
Продолжить с шага 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.
Некоторые инструкции и типы данных усложняют анализатор потока данных. Теперь мы рассмотрим каждый из них более подробно.
Значения типов 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.
Создание нового экземпляра класса — это многоступенчатый процесс. Утверждение:
... new myClass(i, j, k); ...
может быть реализовано следующим образом:
...
new #1 // Allocate uninitialized space for myClass
dup // Duplicate object on the operand stack
iload_1 // Push i
iload_2 // Push j
iload_3 // Push k
invokespecial #5 // Invoke myClass.<init>
...
Последовательность инструкций оставляет вновь созданный и инициализированный объект в верхней части стека операндов. (Дополнительные примеры компиляции в набор инструкций виртуальной машины Java приведены в §3 (Компиляция для виртуальной машины Java).)
Метод инициализации экземпляра (§2.9.1) для класса myClass видит новый неинициализированный объект в качестве своего аргумента this в локальной переменной 0. Перед тем как этот метод вызовет другой метод инициализации экземпляра класса myClass или его непосредственного суперкласса над this, единственной операцией, которую метод может выполнить над this, является присвоение полей, объявленных в myClass.
При выполнении анализа потока данных для методов экземпляров верификатор инициализирует локальную переменную 0 значением объекта текущего класса, или, для методов инициализации экземпляров, локальная переменная 0 содержит специальный тип, указывающий на неинициализированный объект. После вызова соответствующего метода инициализации экземпляров (из текущего класса или его непосредственного суперкласса) над этим объектом, все вхождения этого специального типа в модели верификатора стека операндов и в массиве локальных переменных заменяются типом текущего класса. Верификатор отклоняет код, который использует новый объект до его инициализации или инициализирует объект более одного раза. Кроме того, он гарантирует, что каждый нормальный возврат метода вызвал метод инициализации экземпляра либо в классе этого метода, либо в его непосредственном суперклассе.
Аналогично, специальный тип создаётся и помещается в модель стека операндов верификатора в результате инструкции виртуальной машины Java new. Специальный тип указывает на инструкцию, с помощью которой был создан экземпляр класса, и тип созданного неинициализированного экземпляра класса. Когда вызывается метод инициализации экземпляра, объявленный в классе неинициализированного экземпляра класса, над этим экземпляром класса, все вхождения специального типа заменяются предназначенным типом экземпляра класса. Это изменение типа может распространиться на последующие инструкции по мере выполнения анализа потока данных.
Номер инструкции необходимо хранить как часть специального типа, так как в стеке операндов может существовать несколько неинициализированных экземпляров одного класса одновременно. Например, последовательность инструкций виртуальной машины Java, которая реализует:
new InputStream(new Foo(), new InputStream("foo"))
может иметь два неинициализированных экземпляра класса InputStream в стеке операндов одновременно. Когда метод инициализации экземпляра вызывается для экземпляра класса, только те вхождения специального типа в стеке операндов или в массиве локальных переменных, которые являются тем же объектом, что и экземпляр класса, заменяются.
Для реализации конструкции 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, скомпилированный код выполняет следующие действия:
-
Сохраняет значение возврата (если таковое имеется) в локальной переменной.
-
Выполняет jsr в код блока
finally. -
По возвращении из блока
finallyвозвращает сохранённое в локальной переменной значение.
Компилятор настраивает специальный обработчик исключений, который перехватывает любые исключения, сгенерированные блоком try. Если в блоке try возникает исключение, этот обработчик выполняет следующие действия:
-
Сохраняет исключение в локальной переменной.
-
Выполняет jsr в блок
finally. -
По возвращении из блока
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.
-
Следующие ограничения виртуальной машины 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.