Глава 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представляют собой номер меньшей и большей версии этого файлаclass. Вместе номер большей и меньшей версии определяют версию формата файлаclass. Если файлclassимеет номер большей версииMи номер меньшей версииm, мы обозначаем версию его формата файлаclassкакM.m.Реализация Java Virtual Machine, соответствующая Java SE
N, должна поддерживать ровно те номера больших версий формата файлаclass, которые указаны в четвёртом столбце таблицы 4.1-A, «Поддерживаемые большие версии». Обозначение A .. B означает номера больших версий от A до B включительно. Третий столбец, «Большая», показывает большую версию, введённую каждым выпуском Java SE, то есть первым выпуском, который мог принять файлclass, содержащий этот элементmajor_version. Для очень ранних выпусков вместо выпуска Java SE указана версия JDK.Таблица 4.1-A. Большие версии формата файла
classJava SE Дата выпуска Большая Поддерживаемые большие версии 1.0.2 Май 1996 45 45 1.1 Февраль 1997 45 45 1.2 Декабрь 1998 46 45 .. 46 1.3 Май 2000 47 45 .. 47 1.4 Февраль 2002 48 45 .. 48 5.0 Сентябрь 2004 49 45 .. 49 6 Декабрь 2006 50 45 .. 50 7 Июль 2011 51 45 .. 51 8 Март 2014 52 45 .. 52 9 Сентябрь 2017 53 45 .. 53 10
Март 2018 54 45 .. 54 11
Сентябрь 2018 55 45 .. 55 12
Март 2019 56 45 .. 56 13
Сентябрь 2019 57 45 .. 57 14
Март 2020 58 45 .. 58 15
Сентябрь 2020 59 45 .. 59 16
Март 2021 60 45 .. 60 17
Сентябрь 2021 61 45 .. 61 18
Март 2022 62 45 .. 62 19
Сентябрь 2022 63 45 .. 63 20
Март 2023 64 45 .. 64 21
Сентябрь 2023 65 45 .. 65 22
Март 2024 66 45 .. 66 23
Сентябрь 2024 67 45 .. 67 24
Март 2025 68 45 .. 68 Для файла
class, чьяmajor_versionравна 56 или выше,minor_versionдолжно быть 0 или 65535.Для файла
class, чьяmajor_versionнаходится в диапазоне от 45 до 55 включительно,minor_versionможет иметь любое значение.Следует рассмотреть исторический контекст поддержки JDK для версий формата файла
class. JDK 1.0.2 поддерживал версии от 45.0 до 45.3 включительно. JDK 1.1 поддерживал версии от 45.0 до 45.65535 включительно. Когда JDK 1.2 ввёл поддержку большой версии 46, единственной поддерживаемой меньшей версией в рамках этой большой версии была 0. Более поздние JDK продолжали практику введения поддержки новой большой версии (47, 48 и т. д.), но поддерживали только меньшую версию 0 в рамках новой большой версии. Наконец, появление предварительных функций в Java SE 12 (см. ниже) мотивировало стандартную роль меньшей версии формата файлаclass, поэтому JDK 12 поддерживал меньшие версии 0 и 65535 в рамках большой версии 56. Последующие JDK вводят поддержкуN.0 иN.65535, гдеN- соответствующая большая версия реализованной Java SE платформы. Например, JDK 13 поддерживает 57.0 и 57.65535.Java SE Platform может определять предварительные функции. Реализация Java Virtual Machine, соответствующая 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 Virtual Machine, соответствующая 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 и должны интерпретироваться как символы ASCII. Нетерминальные символы показаны шрифтом курсив. Определение нетерминала вводится именем определяемого нетерминала, за которым следует двоеточие. За тем следуют одно или более альтернативных определений для нетерминала на следующих строках.
Синтаксис {x} в правой части правила означает ноль или более вхождений x.
Фраза (one of) в правой части правила указывает, что каждый из терминальных символов в последующей строке или строках является альтернативным определением.
Описатель поля представляет тип поля, параметра, локальной переменной или значения.
B C D F I J S Z L ИмяКласса ;
ИмяКласса представляет имя бинарного класса или интерфейса, закодированное во внутренней форме (§4.2.1).
Описатель поля указывает на имя класса или интерфейса, если это имя появляется как ИмяКласса в описателе. Это включает ИмяКласса, вложенное в ComponentType ArrayType.
Интерпретация описателей полей как типов показана в Таблице 4.3-A. См. §2.2, §2.3 и §2.4 для значения этих типов.
Описатель поля, представляющий массивный тип, является корректным только если он представляет тип с 255 или менее измерениями.
Таблица 4.3-A. Интерпретация описателей полей
| FieldType термин | Тип |
|---|---|
B | byte |
C | char |
D | double |
F | float |
I | int |
J | long |
L ИмяКласса ;
| Имя класса или интерфейса |
S | short |
Z | boolean |
| | Массив заданного типа компонента |
Описатель поля для переменной экземпляра типа int просто I.
Описатель поля для переменной экземпляра типа Object — Ljava/lang/Object;. Обратите внимание, что используется внутренняя форма бинарного имени класса Object.
Описатель поля для переменной экземпляра многомерного массива типа double[][][] — [[[D.
Описатель метода содержит ноль или более описателей параметров, представляющих типы параметров, которые принимает метод, и описатель возвращаемого значения, представляющий тип значения (если таковое имеется), которое возвращает метод.
VСимвол V указывает, что метод не возвращает значение (его результат — void).
Описатель метода указывает на имя класса или интерфейса, если имя появляется как ИмяКласса в FieldType описателя параметра или описателя возвращаемого значения.
Описатель метода для метода:
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-А вместе с соответствующими тегами, и упорядоченных по номеру раздела в этой главе. Каждый байт тега должен следовать за двумя или более байтами, предоставляющими информацию о конкретной константе. Формат дополнительной информации зависит от байта тега, то есть содержимое массива info изменяется в зависимости от значения tag.
Таблица 4.4-А. Теги пула констант (по разделам)
| Тип константы | Тег | Раздел |
|---|---|---|
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-В перечисляет каждый тег с первой версией формата файла class, в которой он был определён. Также показана версия Java SE Platform, в которой была представлена данная версия формата файла class.
Таблица 4.4-В. Теги пула констант (по тегу)
| Тип константы | Тег |
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-С перечисляет каждый тег с первой версией формата файла class, в которой он был определён как загружаемый. Также показана версия Java SE Platform, в которой была представлена данная версия формата файла class.
Во всех случаях, кроме CONSTANT_Class, тег был впервые определён как загружаемый в той же версии формата файла class, что и первое определение тега.
Таблица 4.4-С. Теги загружаемого пула констант
| Тип константы | Тег |
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 представляют 4-байтовые числовые (int и float) константы:
CONSTANT_Integer_info {
u1 tag;
u4 bytes;
}
CONSTANT_Float_info {
u1 tag;
u4 bytes;
}
Элементы этих структур следующие:
- tag
-
Элемент
tagструктурыCONSTANT_Integer_infoимеет значениеCONSTANT_Integer(3).Элемент
tagструктурыCONSTANT_Float_infoимеет значениеCONSTANT_Float(4). - bytes
-
Элемент
bytesструктурыCONSTANT_Integer_infoпредставляет значение константыint. Байты значения хранятся в формате big-endian (старший байт вначале).Элемент
bytesструктурыCONSTANT_Float_infoпредставляет значение константыfloatв формате IEEE 754 binary32 (§2.3.2). Байты элемента хранятся в формате big-endian (старший байт вначале).Значение, представленное структурой
CONSTANT_Float_info, определяется следующим образом. Байты значения сначала преобразуются в константуintbits. Затем:-
Если 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, двойное значение будет 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 байта на кодовую точку, но все кодовые точки в кодовой области Юникода могут быть представлены. Модифицированные UTF-8 строки не завершаются нулём. Кодирование выполняется следующим образом:
-
Кодовые точки в диапазоне '
\u0001' до '\u007F' представлены одним байтом:Таблица 4.7.
0 биты 6-0 7 бит данных в байте дают значение представляемой кодовой точки.
-
Нулевая кодовая точка ('
\u0000') и кодовые точки в диапазоне '\u0080' до '\u07FF' представлены парой байтовxиy:Таблица 4.8.
x:Таблица 4.9.
1 1 0 биты 10-6
y:Таблица 4.10.
1 0 биты 5-0
Два байта представляют кодовую точку со значением:
((x & 0x1f) << 6) + (y & 0x3f) -
Кодовые точки в диапазоне '
\u0800' до '\uFFFF' представляются 3 байтамиx,yиz:Таблица 4.11.
x:Таблица 4.12.
1 1 1 0 биты 15-12
y:Таблица 4.13.
1 0 биты 11-6
z:Таблица 4.14.
1 0 биты 5-0
Три байта представляют кодовую точку со значением:
((x & 0xf) << 12) + ((y & 0x3f) << 6) + (z & 0x3f)
-
Символы с кодовыми точками выше U+FFFF (так называемые дополнительные символы) представляются путём отдельного кодирования двух суррогатных кодовых единиц их представления UTF-16. Каждая из суррогатных кодовых единиц представляется тремя байтами. Это означает, что дополнительные символы представляются шестью байтами,
u,v,w,x,yиz:Таблица 4.15.
u:Таблица 4.16.
1 1 1 0 1 1 0 1
v:Таблица 4.17.
1 0 1 0 (bits 20-16)-1
w:Таблица 4.18.
1 0 bits 15-10
x:Таблица 4.19.
1 1 1 0 1 1 0 1
y:Таблица 4.20.
1 0 1 1 bits 9-6
z:Таблица 4.21.
1 0 bits 5-0
Шесть байтов представляют кодовую точку со значением:
0x10000 + ((v & 0x0f) << 16) + ((w & 0x3f) << 10) + ((y & 0x0f) << 6) + (z & 0x3f)
Байты многобайтовых символов хранятся в файле class в порядке big-endian (старший байт первым).
Существуют два отличия между этим форматом и «стандартным» форматом UTF-8. Во-первых, нулевой символ (char)0 кодируется с использованием 2-байтового формата вместо 1-байтового, чтобы изменённые строки UTF-8 никогда не содержали вложенных нулей. Во-вторых, используются только 1-байтовый, 2-байтовый и 3-байтовый форматы стандартного UTF-8. Виртуальная машина Java не распознаёт 4-байтовый формат стандартного UTF-8; вместо этого она использует свой собственный двухкратный трёхбайтовый формат.
Для получения дополнительной информации о стандартном формате UTF-8 см. раздел 3.9 Формы кодирования Unicode стандарта Unicode Standard, Version 16.0.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-А упорядочена по номерам разделов этих атрибутов в данной главе. Каждый атрибут показан с первой версией формата файла
class, в которой он был определен. Также показана версия платформы Java SE, которая представила ту версию формата файлаclass(§4.1). -
Таблица 4.7-В упорядочена по первой версии формата файла
class, в которой каждый атрибут был определен. -
Таблица 4.7-С упорядочена по расположению в файле
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, либо полезны для инструментов (в этом случае раздел, определяющий атрибут, описывает его как «необязательный»):-
Exceptions -
InnerClasses -
EnclosingMethod -
Synthetic -
Signature -
Record -
SourceFile -
LineNumberTable -
LocalVariableTable -
LocalVariableTypeTable
В файле
classс номером версии v, каждый из этих атрибутов должен быть распознан и правильно прочитан реализацией виртуальной машины Java, если реализация поддерживает версию v формата файлаclass, и атрибут был впервые определен в версии v или более ранней версии формата файлаclass, и атрибут появляется в месте, где он должен появляться. -
-
Тринадцать атрибутов не являются критическими для корректной интерпретации файла
classвиртуальной машиной Java, но содержат метаданные о файлеclass, которые либо доступны библиотеками классов платформы Java SE, либо доступны инструментами (в этом случае раздел, определяющий атрибут, описывает его как «необязательный»):-
SourceDebugExtension -
Deprecated -
RuntimeVisibleAnnotations -
RuntimeInvisibleAnnotations -
RuntimeVisibleParameterAnnotations -
RuntimeInvisibleParameterAnnotations -
RuntimeVisibleTypeAnnotations -
RuntimeInvisibleTypeAnnotations -
AnnotationDefault -
MethodParameters -
Module -
ModulePackages -
ModuleMainClass
Реализация виртуальной машины Java может использовать информацию, содержащуюся в этих атрибутах, или иначе должна проигнорировать эти атрибуты.
-
Таблица 4.7-A. Предопределённые class атрибуты файлов (по разделам)
| Атрибут | Раздел |
class файл | Java SE |
|---|---|---|---|
ConstantValue | §4.7.2 | 45.3 | 1.0.2 |
Code | §4.7.3 | 45.3 | 1.0.2 |
StackMapTable | §4.7.4 | 50.0 | 6 |
Exceptions | §4.7.5 | 45.3 | 1.0.2 |
InnerClasses | §4.7.6 | 45.3 | 1.1 |
EnclosingMethod | §4.7.7 | 49.0 | 5.0 |
Synthetic | §4.7.8 | 45.3 | 1.1 |
Signature | §4.7.9 | 49.0 | 5.0 |
SourceFile | §4.7.10 | 45.3 | 1.0.2 |
SourceDebugExtension | §4.7.11 | 49.0 | 5.0 |
LineNumberTable | §4.7.12 | 45.3 | 1.0.2 |
LocalVariableTable | §4.7.13 | 45.3 | 1.0.2 |
LocalVariableTypeTable | §4.7.14 | 49.0 | 5.0 |
Deprecated | §4.7.15 | 45.3 | 1.1 |
RuntimeVisibleAnnotations | §4.7.16 | 49.0 | 5.0 |
RuntimeInvisibleAnnotations | §4.7.17 | 49.0 | 5.0 |
RuntimeVisibleParameterAnnotations | §4.7.18 | 49.0 | 5.0 |
RuntimeInvisibleParameterAnnotations | §4.7.19 | 49.0 | 5.0 |
RuntimeVisibleTypeAnnotations | §4.7.20 | 52.0 | 8 |
RuntimeInvisibleTypeAnnotations | §4.7.21 | 52.0 | 8 |
AnnotationDefault | §4.7.22 | 49.0 | 5.0 |
BootstrapMethods | §4.7.23 | 51.0 | 7 |
MethodParameters | §4.7.24 | 52.0 | 8 |
| | §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 24 (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 байта, 32-битные смещения tableswitch и lookupswitch будут выровнены по границе 4 байта. (См. описания этих инструкций для получения дополнительной информации о последствиях выравнивания массиваcode.)Подробные ограничения на содержимое массива
codeобширны и приведены в отдельном разделе (§4.9). - exception_table_length
-
Значение элемента
exception_table_lengthуказывает количество записей в массивеexception_table. - exception_table[]
-
Каждая запись в массиве
exception_tableописывает один обработчик исключений в массивеcode. Порядок обработчиков в массивеexception_tableважен (§2.10).Каждая запись
exception_tableсодержит следующие четыре элемента:- start_pc, end_pc
-
Значения двух элементов
start_pcиend_pcуказывают диапазоны в массивеcode, в которых активен обработчик исключений. Значениеstart_pcдолжно быть допустимым индексом в массивеcodeкода инструкции. Значениеend_pcдолжно быть либо допустимым индексом в массивеcodeкода инструкции, либо равноcode_length— длине массиваcode. Значениеstart_pcдолжно быть меньше значенияend_pc.start_pc— включительно,end_pc— не включительно; то есть обработчик исключений должен быть активным, пока счётчик команд находится в интервале [start_pc,end_pc).То, что
end_pcне включительно, — историческая ошибка в разработке Java Virtual Machine: если код Java Virtual Machine для метода имеет длину ровно 65535 байт и заканчивается инструкцией длиной 1 байт, то эта инструкция не может быть защищена обработчиком исключений. Разработчик компилятора может обойти эту ошибку, ограничив максимальный размер сгенерированного кода Java Virtual Machine для любого метода, метода инициализации экземпляра или статического инициализатора (размера любого массива кода) до 65534 байт. - handler_pc
-
Значение элемента
handler_pcуказывает начало обработчика исключений. Значение элемента должно быть допустимым индексом в массивеcodeи должно быть индексом кода инструкции. - catch_type
-
Если значение элемента
catch_typeотлично от нуля, оно должно быть допустимым индексом в таблицеconstant_pool. Записьconstant_poolв этой таблице должна быть структуройCONSTANT_Class_info(§4.4.1) представляющей класс исключений, который этот обработчик исключений предназначен для перехвата. Обработчик исключений будет вызван только в том случае, если сгенерированное исключение является экземпляром данного класса или одним из его подклассов.Верификатор проверяет, что класс является
Throwableили подклассомThrowable(§4.9.2).Если значение элемента
catch_typeравно нулю, этот обработчик исключений вызывается для всех исключений.Это используется для реализации
finally(§3.13).
- attributes_count
-
Значение элемента
attributes_countуказывает количество атрибутов атрибутаCode. - attributes[]
-
Каждое значение в таблице
attributesдолжно быть структуройattribute_info(§4.7).Атрибут
Codeможет иметь любое количество необязательных атрибутов, связанных с ним.Атрибуты, определённые в этом спецификации как появляющиеся в таблице
attributesатрибутаCode, перечислены в Таблице 4.7-C.Правила, касающиеся атрибутов, определённых для появления в таблице
attributesатрибутаCode, приведены в §4.7.Правила, касающиеся неопределённых атрибутов в таблице
attributesатрибутаCode, приведены в §4.7.1.
Атрибут 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имеет значение.
A кадр карты стека задаёт (явно или неявно) смещение байткода, при котором он применяется, и типы проверки локальных переменных и элементов стека операндов для этого смещения.
Каждый кадр карты стека, описанный в таблице 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 Virtual Machine; они проверяются только на этапе компиляции.
Атрибут InnerClasses — атрибут переменной длины в таблице attributes структуры ClassFile (§4.1).
Если пул констант класса или интерфейса C содержит по крайней мере одну запись CONSTANT_Class_info (§4.4.1), представляющую класс или интерфейс, который не является членом пакета, то в таблице attributes структуры ClassFile для C должен быть ровно один атрибут InnerClasses.
Атрибут InnerClasses имеет следующий формат:
InnerClasses_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 number_of_classes;
{ u2 inner_class_info_index;
u2 outer_class_info_index;
u2 inner_name_index;
u2 inner_class_access_flags;
} classes[number_of_classes];
}
Элементы структуры InnerClasses_attribute следующие:
- attribute_name_index
-
Значение элемента
attribute_name_indexдолжно быть допустимым индексом в таблицеconstant_pool. Записьconstant_poolв этой позиции должна быть структуройCONSTANT_Utf8_info(§4.4.7), представляющей строку "InnerClasses". - attribute_length
-
Значение элемента
attribute_lengthуказывает длину атрибута, за исключением первых шести байтов. - number_of_classes
-
Значение элемента
number_of_classesуказывает количество записей в массивеclasses. - classes[]
-
Каждая запись
CONSTANT_Class_infoв таблицеconstant_pool, которая представляет класс или интерфейс C, который не является членом пакета, должна иметь ровно одну соответствующую запись в массивеclasses.Если класс или интерфейс имеет члены, которые являются классами или интерфейсами, его таблица
constant_pool(и, следовательно, его атрибутInnerClasses) должен ссылаться на каждого такого члена (JLS §13.1), даже если этот член не упоминается в классе другим способом.Кроме того, таблица
constant_poolкаждого вложенного класса и вложенного интерфейса должна ссылаться на его внешний класс, поэтому все вложенные классы и вложенные интерфейсы будут иметь информациюInnerClassesдля каждого внешнего класса и для каждого из своих вложенных классов и интерфейсов.Каждая запись в массиве
classesсодержит следующие четыре элемента:- inner_class_info_index
-
Значение элемента
inner_class_info_indexдолжно быть допустимым индексом в таблицеconstant_pool. Записьconstant_poolв этом индексе должна быть структуройCONSTANT_Class_info, представляющей C. - outer_class_info_index
-
Если C не является членом класса или интерфейса — то есть, если C — это верхнеуровневый класс или интерфейс (JLS §7.6) или локальный класс (JLS §14.3) или анонимный класс (JLS §15.9.5) — то значение элемента
outer_class_info_indexдолжно быть нулевым.В противном случае, значение элемента
outer_class_info_indexдолжно быть допустимым индексом в таблицеconstant_pool, а запись в этом индексе должна быть структуройCONSTANT_Class_info, представляющей класс или интерфейс, членом которого является C. Значение элементаouter_class_info_indexне должно быть равно значению элементаinner_class_info_index. - inner_name_index
-
Если C анонимный (JLS §15.9.5), значение элемента
inner_name_indexдолжно быть нулевым.В противном случае, значение элемента
inner_name_indexдолжно быть допустимым индексом в таблицеconstant_pool, а запись в этом индексе должна быть структуройCONSTANT_Utf8_info, представляющей исходное простое имя C, как указано в исходном коде, из которого был скомпилирован этот файлclass. - inner_class_access_flags
-
Значение элемента
inner_class_access_flags— маска флагов, используемых для обозначения разрешений доступа к классу или интерфейсу C и его свойств, как объявлено в исходном коде, из которого был скомпилирован этот файлclass. Он используется компилятором для восстановления исходной информации, когда исходный код недоступен. Флаги указаны в таблице 4.7.6-A.Таблица 4.7.6-A. Флаги доступа и свойств вложенного класса
Название флага Значение Интерпретация ACC_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 или выше, и в его таблицеattributesесть атрибутInnerClasses, то для всех записей в массиве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 24.
В таблице 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, то есть тип класса или интерфейса, переменную типа или тип массива.
Подпись типа класса представляет собой (возможно, параметризованный) тип класса или интерфейса. Подпись типа класса должна быть сформулирована таким образом, чтобы её можно было надёжно сопоставить с бинарным именем обозначаемого класса во внутренней форме (§4.2.1), стерев любые аргументы типа и преобразовав каждый символ
.в символ$.Подпись переменной типа представляет собой переменную типа.
Подпись типа массива представляет собой одну размерность типа массива.
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, то они могут быть в любом порядке.
В таблице attributes атрибута Code может быть более одного атрибута LineNumberTable на каждую строку исходного файла. То есть, атрибуты LineNumberTable вместе могут представлять заданную строку исходного файла и не должны быть взаимно однозначными с исходными строками.
Атрибут LineNumberTable имеет следующий формат:
LineNumberTable_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 line_number_table_length;
{ u2 start_pc;
u2 line_number;
} line_number_table[line_number_table_length];
}
Элементы структуры LineNumberTable_attribute следующие:
- attribute_name_index
-
Значение элемента
attribute_name_indexдолжно быть допустимым индексом в таблицеconstant_pool. Элементconstant_poolв этой позиции должен быть структуройCONSTANT_Utf8_info(§4.4.7), представляющей строку "LineNumberTable". - attribute_length
-
Значение элемента
attribute_lengthуказывает длину атрибута, за исключением начальных шести байтов. - line_number_table_length
-
Значение элемента
line_number_table_lengthуказывает количество элементов в массивеline_number_table. - line_number_table[]
-
Каждый элемент массива
line_number_tableуказывает, что номер строки в исходном файле изменяется в заданной точке в массивеcode. Каждый элементline_number_tableдолжен содержать следующие два элемента:- start_pc
-
Значение элемента
start_pcдолжно быть допустимым индексом в массивеcodeэтого атрибутаCode. Элемент указывает индекс в массивеcode, в котором начинается код для новой строки в исходном файле. - line_number
-
Значение элемента
line_numberзадаёт соответствующий номер строки в исходном файле.
Атрибут 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_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_Utf8_info, представляющую допустимое неопределённое имя, обозначающее локальную переменную (§4.2.2). - descriptor_index
-
Значение элемента
descriptor_indexдолжно быть корректным индексом в таблице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_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_Utf8_info, представляющую допустимое неопределённое имя, обозначающее локальную переменную (§4.2.2). - signature_index
-
Значение элемента
signature_indexдолжно быть корректным индексом в таблице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 должно быть равно нулю.
Атрибут видимых во время выполнения аннотаций — это атрибут переменной длины в таблице атрибутов структуры файла класса, интерфейса, метода или поля (§4.1, §4.5, §4.6, §4.7.30). Атрибут аннотаций хранит аннотации, видимые во время выполнения, в описании соответствующего класса, поля, метода или компонента записи.
В таблице атрибутов структуры файла класса, интерфейса, метода или поля может быть не более одного атрибута видимых во время выполнения аннотаций.
Атрибут видимых во время выполнения аннотаций имеет следующий формат:
RuntimeVisibleAnnotations_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 num_annotations;
annotation annotations[num_annotations];
}
Элементы структуры атрибута видимых во время выполнения аннотаций следующие:
- attribute_name_index
-
Значение элемента attribute_name_index должно быть допустимым индексом в таблице имён атрибутов. Элемент таблицы имён атрибутов с этим индексом должен быть структурой CONSTANT_Utf8_info (§4.4.7), представляющей строку "RuntimeVisibleAnnotations".
- attribute_length
-
Значение элемента attribute_length указывает длину атрибута, за исключением начальных шести байтов.
- num_annotations
-
Значение элемента num_annotations задаёт количество аннотаций, видимых во время выполнения, представленных структурой.
- annotations[]
-
Каждый элемент таблицы annotations представляет отдельную аннотацию, видимую во время выполнения, в объявлении. Структура аннотации имеет следующий формат:
annotation { u2 type_index; u2 num_element_value_pairs; { u2 element_name_index; element_value value; } element_value_pairs[num_element_value_pairs]; }Элементы структуры аннотации следующие:
- type_index
-
Значение элемента type_index должно быть допустимым индексом в таблице имён типов. Элемент таблицы имён типов с этим индексом должен быть структурой CONSTANT_Utf8_info (§4.4.7), представляющей дескриптор поля (§4.3.2). Дескриптор поля обозначает тип аннотации, представленной данной структурой.
- num_element_value_pairs
-
Значение элемента num_element_value_pairs задаёт количество пар имя-значение элемента аннотации, представленной данной структурой.
- element_value_pairs[]
-
Каждый элемент таблицы element_value_pairs представляет отдельную пару имя-значение в аннотации, представленной данной структурой. Каждый элемент element_value_pairs содержит следующие два элемента:
- 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 для указания типа значения пары элемент-значение. Это определяет, какой элемент объединения value используется. Таблица 4.7.16.1-A показывает допустимые символы для элемента тега, тип, указываемый каждым символом, и элемент, используемый в объединении value для каждого символа. Четвёртый столбец таблицы используется в описании ниже одного элемента объединения value.
Таблица 4.7.16.1-A. Интерпретация значений тегов как типов
tag Элемент | Тип |
value Элемент | Тип константы |
|---|---|---|---|
B | byte | const_value_index | CONSTANT_Integer |
C | char | const_value_index | CONSTANT_Integer |
D | double | const_value_index | CONSTANT_Double |
F | float | const_value_index | CONSTANT_Float |
I | int | const_value_index | CONSTANT_Integer |
J | long | const_value_index | CONSTANT_Long |
S | short | const_value_index | CONSTANT_Integer |
Z | boolean | const_value_index | CONSTANT_Integer |
s | String | const_value_index | CONSTANT_Utf8 |
e | Класс перечислений | enum_const_value | Не применимо |
c | Class | class_info_index | Не применимо |
@ | Интерфейс аннотаций | annotation_value | Не применимо |
[ | Тип массива | array_value | Не применимо |
Элемент value представляет значение пары элемент-значение. Этот элемент является объединением, элементы которого следующие:
- const_value_index
-
Элемент
const_value_indexобозначает константу примитивного типа или типаStringв качестве значения данной пары элемент-значение.Значение элемента
const_value_indexдолжно быть допустимым индексом в таблицеconstant_pool. Записьconstant_poolв этом индексе должна быть типа, соответствующего элементуtag, как указано в четвёртом столбце таблицы 4.7.16.1-A. - enum_const_value
-
Элемент
enum_const_valueобозначает константу перечисления как значение данной пары элемент-значение.Элемент
enum_const_valueсостоит из следующих двух элементов:- type_name_index
-
Значение элемента
type_name_indexдолжно быть допустимым индексом в таблицеconstant_pool. Записьconstant_poolв этом индексе должна быть структуройCONSTANT_Utf8_info(§4.4.7), представляющей дескриптор поля (§4.3.2). Записьconstant_poolуказывает внутреннюю форму двоичного имени типа константы перечисления, представленного этой структуройelement_value(§4.2.1). - const_name_index
-
Значение элемента
const_name_indexдолжно быть допустимым индексом в таблицеconstant_pool. Записьconstant_poolв этом индексе должна быть структуройCONSTANT_Utf8_info(§4.4.7). Записьconstant_poolуказывает простое имя константы перечисления, представленной этой структуройelement_value.
- class_info_index
-
Элемент
class_info_indexобозначает литерал класса как значение данной пары элемент-значение.Элемент
class_info_indexдолжен быть допустимым индексом в таблицеconstant_pool. Записьconstant_poolв этом индексе должна быть структуройCONSTANT_Utf8_info(§4.4.7), представляющей дескриптор возвращаемого значения (§4.3.3). Дескриптор возвращаемого значения задаёт тип, соответствующий литералу класса, представленному этой структуройelement_value. Соответствие типов литералам классов:-
Для литерала класса C
.class, где C — имя класса, интерфейса или типа массива, соответствующий тип — C. Дескриптор возвращаемого значения вconstant_poolбудет ClassType или ArrayType. -
Для литерала класса p
.class, где p — имя примитивного типа, соответствующий тип — p. Дескриптор возвращаемого значения вconstant_poolбудет символом BaseType. -
Для литерала класса
void.class, соответствующий тип —void. Дескриптор возвращаемого значения вconstant_poolбудет V.
Например, литерал класса
Object.classсоответствует типуObject, поэтому записьconstant_pool—Ljava/lang/Object;, в то время как литерал классаint.classсоответствует типуint, поэтому записьconstant_pool—I.Литерал класса
void.classсоответствует типуvoid, поэтому записьconstant_pool— V, в то время как литерал классаVoid.classсоответствует типуVoid, поэтому записьconstant_pool—Ljava/lang/Void;. -
- annotation_value
-
Элемент
annotation_valueобозначает "вложенную" аннотацию как значение данной пары элемент-значение.Значение элемента
annotation_value— структураannotation(§4.7.16), которая задаёт аннотацию, представленную данной структуройelement_value. - array_value
-
Элемент
array_valueобозначает массив как значение данной пары элемент-значение.Элемент
array_valueсостоит из следующих двух элементов:- num_values
-
Значение элемента
num_valuesзадаёт количество элементов в массиве, представленном данной структуройelement_value. - values[]
-
Каждое значение в таблице
valuesзадаёт соответствующий элемент массива, представленного данной структуройelement_value.
Атрибут 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.
Таблица RuntimeInvisibleTypeAnnotations содержит атрибут attributes только в том случае, если типы аннотированы в видах объявления или выражения, которые соответствуют родительской структуре или атрибуту таблицы 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 Virtual Machine при проверке формата (§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.
Таблица attributes структуры ClassFile не должна содержать одновременно атрибут NestMembers и атрибут NestHost.
Это правило предотвращает родительский элемент вложенности от претендования на членство в другой вложенности. Он неявно является членом вложенности, которую он организует.
Атрибут NestMembers имеет следующий формат:
NestMembers_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 number_of_classes;
u2 classes[number_of_classes];
}
Элементы структуры NestMembers_attribute следующие:
- attribute_name_index
-
Значение элемента
attribute_name_indexдолжно быть допустимым индексом в таблицеconstant_pool. Элементconstant_poolпо этому индексу должен быть структуройCONSTANT_Utf8_info(§4.4.7), представляющей строку "NestMembers". - attribute_length
-
Значение элемента
attribute_lengthуказывает длину атрибута, за исключением начальных шести байтов. - number_of_classes
-
Значение элемента
number_of_classesуказывает количество элементов в массивеclasses. - classes[]
-
Каждое значение в массиве
classesдолжно быть допустимым индексом в таблицеconstant_pool. Элементconstant_poolпо этому индексу должен быть структуройCONSTANT_Class_info(§4.4.1), представляющей класс или интерфейс, являющийся членом вложенности, организованной текущим классом или интерфейсом.Массив
classesиспользуется при проверке доступа (§5.4.4). Он должен содержать ссылки на другие классы и интерфейсы, находящиеся в том же пакете среды выполнения и имеющие атрибутыNestHost, которые ссылаются на текущий класс или интерфейс. Элементы массива, которые не удовлетворяют этим условиям, игнорируются при проверке доступа.
Атрибут 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). Элементы массива, представляющие классы или интерфейсы, которые не пытаются напрямую расширить или реализовать текущий класс или интерфейс, игнорируются.
Когда виртуальная машина Java (§5.3) загружает потенциальный файл class, она сначала проверяет, соответствует ли файл основному формату файла 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. Структурные ограничения таковы:
-
Каждая инструкция должна выполняться только с соответствующим типом и количеством аргументов в стеке операндов и массиве локальных переменных, независимо от пути выполнения, который приводит к её вызову.
Как указано в §2.3.4 и §2.11.1, виртуальная машина Java неявно преобразует значения типов
boolean,byte,shortиcharк типуint, позволяя инструкциям, ожидающим значения типаint, работать с ними. -
Если инструкция может выполняться по нескольким различным путям выполнения, стек операндов должен иметь одинаковую глубину (§2.6.2) перед выполнением инструкции, независимо от выбранного пути.
-
В любой момент выполнения стек операндов не может увеличиваться до глубины, большей, чем та, которая подразумевается элементом
max_stack. -
В любой момент выполнения из стека операндов не может быть извлечено больше значений, чем их содержится в нём.
-
В любой момент выполнения порядок локальных переменных, хранящих значение типа
longилиdouble, не может быть изменён или пара разделена. В любой момент локальные переменные такой пары не могут обрабатываться по отдельности. -
К локальной переменной (или паре локальных переменных в случае значения типа
longилиdouble) нельзя обратиться до того, как ей будет присвоено значение. -
Каждая инструкция invokespecial должна указывать на одну из следующих:
-
метод инициализации экземпляра (§2.9.1)
-
метод в текущем классе или интерфейсе
-
метод в суперклассе текущего класса
-
метод в непосредственном суперинтерфейсе текущего класса или интерфейса
-
метод в
Object
Если инструкция invokespecial указывает на метод инициализации экземпляра, то целевой объект в стеке операндов должен быть неинициализированным экземпляром класса. Метод инициализации экземпляра никогда не должен вызываться на инициализированном экземпляре класса. Кроме того:
-
Если целевой объект в стеке операндов - неинициализированный экземпляр класса для текущего класса, то invokespecial должен указывать на метод инициализации экземпляра из текущего класса или его непосредственного суперкласса.
-
Если целевой объект в стеке операндов - экземпляр класса, созданный предыдущей инструкцией new, то invokespecial должен указывать на метод инициализации экземпляра из класса этого экземпляра.
-
В обоих случаях, если инструкция покрыта обработчиком исключений, любая копия целевого объекта, хранящаяся в локальной переменной, должна рассматриваться как неиспользуемая кодом обработчика исключений.
Если инструкция invokespecial указывает на метод, который не является методом инициализации экземпляра, то целевой объект в стеке операндов должен быть экземпляром класса, совместимым по присваиванию с текущим классом (JLS §5.2).
Общее правило для invokespecial состоит в том, что класс или интерфейс, указанный в invokespecial, должны быть "выше" вызывающего класса или интерфейса, а объект-получатель, на который направлен invokespecial, должен быть "на" или "ниже" вызывающего класса или интерфейса. Последнее положение особенно важно: класс или интерфейс может выполнять invokespecial только на своих собственных объектах. См. §invokespecial для объяснения реализации последнего положения в Prolog.
-
-
Каждый метод инициализации экземпляра, за исключением метода инициализации экземпляра, полученного из конструктора класса
Object, должен вызвать другой метод инициализации экземпляра классаthisили метод инициализации экземпляра его непосредственного суперклассаsuperдо того, как будут обращены его экземпляры членов и до возвращения вызывающего метода инициализации экземпляра.Однако поля экземпляров класса
this, объявленные в текущем классе, могут быть присвоены с помощью putfield до вызова любого метода инициализации экземпляра. -
При вызове любого метода экземпляра или при обращении к любой переменной экземпляра экземпляр класса, содержащий метод или переменную экземпляра, должен быть уже инициализирован.
-
При выполнении инструкции 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 24.
Из-за этих потенциальных проблем виртуальной машине 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).
Мы постулируем существование 27 предикат 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>. - 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структуры. - ... (и так далее)
Кадры карты стека представляются в Прологе как список терминов вида:
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, в противном случае это класс, указанный в обработчике.
handlersAreLegal(Environment) :-
exceptionHandlers(Environment, Handlers),
checklist(handlerIsLegal(Environment), Handlers).
handlerIsLegal(Environment, Handler) :-
Handler = handler(Start, End, Target, _),
Start < End,
allInstructions(Environment, Instructions),
member(instruction(Start, _), Instructions),
offsetStackFrame(Environment, Target, _),
instructionsIncludeEnd(Instructions, End),
currentClassLoader(Environment, CurrentLoader),
handlerExceptionClass(Handler, ExceptionClass, CurrentLoader),
isBootstrapLoader(BL),
isAssignable(ExceptionClass, class('java/lang/Throwable', BL)).
instructionsIncludeEnd(Instructions, End) :-
member(instruction(End, _), Instructions).
instructionsIncludeEnd(Instructions, End) :-
member(endOfCode(End), Instructions).
handlerExceptionClass(handler(_, _, _, 0),
class('java/lang/Throwable', BL), _) :-
isBootstrapLoader(BL).
handlerExceptionClass(handler(_, _, _, Name),
class(Name, L), L) :-
Name \= 0.
Теперь перейдём к потоку инструкций и фреймов карты стека.
Объединение инструкций и фреймов карты стека в один поток включает четыре случая:
-
Объединение пустого
StackMapи списка инструкций даёт исходный список инструкций.mergeStackMapAndCode([], CodeList, CodeList).
-
Учитывая список фреймов карты стека, начинающийся со состояния типа для инструкции в
Offset, и список инструкций, начинающийся сOffset, объединённый список — это начало списка фреймов карты стека, за которым следует начало списка инструкций, за которым следует объединение хвостов двух списков.mergeStackMapAndCode([stackMap(Offset, Map) | RestMap], [instruction(Offset, Parse) | RestCode], [stackMap(Offset, Map), instruction(Offset, Parse) | RestMerge]) :- mergeStackMapAndCode(RestMap, RestCode, RestMerge). -
В противном случае, учитывая список фреймов карты стека, начинающийся со состояния типа для инструкции в
OffsetM, и список инструкций, начинающийся сOffsetP, еслиOffsetP < OffsetM, объединённый список состоит из начала списка инструкций, за которым следует объединение списка фреймов карты стека и хвоста списка инструкций.mergeStackMapAndCode([stackMap(OffsetM, Map) | RestMap], [instruction(OffsetP, Parse) | RestCode], [instruction(OffsetP, Parse) | RestMerge]) :- OffsetP < OffsetM, mergeStackMapAndCode([stackMap(OffsetM, Map) | RestMap], RestCode, RestMerge). -
В противном случае, объединение двух списков не определено. Поскольку список инструкций имеет монотонно возрастающие смещения, объединение двух списков не определено, если для каждого смещения фрейма карты стека нет соответствующего смещения инструкции и фреймы карт стека не упорядочены монотонно возрастающим образом.
Чтобы определить, является ли объединённый поток для метода типами корректным, мы сначала выводим начальное состояние типа метода.
Начальное состояние типа метода состоит из пустого стека операндов и типов локальных переменных, полученных из типа this и аргументов, а также соответствующего флага, в зависимости от того, является ли это <init> методом.
methodInitialStackFrame(Class, Method, FrameSize, frame(Locals, [], Flags),
ReturnType):-
methodDescriptor(Method, Descriptor),
parseMethodDescriptor(Descriptor, RawArgs, ReturnType),
expandTypeList(RawArgs, Args),
methodInitialThisType(Class, Method, ThisList),
flags(ThisList, Flags),
append(ThisList, Args, ThisArgs),
expandToLength(ThisArgs, FrameSize, top, Locals).
Учитывая список типов, следующая фраза генерирует список, где каждый тип размера 2 был заменён двумя записями: одна для самого себя, и одна top запись. Результат соответствует представлению списка в 32-битовых словах в виртуальной машине Java.
expandTypeList([], []).
expandTypeList([Item | List], [Item | Result]) :-
sizeOf(Item, 1),
expandTypeList(List, Result).
expandTypeList([Item | List], [Item, top | Result]) :-
sizeOf(Item, 2),
expandTypeList(List, Result).
flags([uninitializedThis], [flagThisUninit]).
flags(X, []) :- X \= [uninitializedThis].
expandToLength(List, Size, _Filler, List) :-
length(List, Size).
expandToLength(List, Size, Filler, Result) :-
length(List, ListLength),
ListLength < Size,
Delta is Size - ListLength,
length(Extra, Delta),
checklist(=(Filler), Extra),
append(List, Extra, Result).
Для начального состояния типа метода экземпляра мы вычисляем тип this и помещаем его в список. Тип this в методе <init> класса Object равен Object; в других <init> методах тип this равен uninitializedThis; в противном случае, тип this в методе экземпляра равен class(N, L), где N — имя класса, содержащего метод, а L — его определяющий загрузчик классов.
Для начального состояния типа статического метода this не имеет значения, поэтому список пуст.
methodInitialThisType(_Class, Method, []) :-
methodAccessFlags(Method, AccessFlags),
member(static, AccessFlags),
methodName(Method, MethodName),
MethodName \= '<init>'.
methodInitialThisType(Class, Method, [This]) :-
methodAccessFlags(Method, AccessFlags),
notMember(static, AccessFlags),
instanceMethodInitialThisType(Class, Method, This).
instanceMethodInitialThisType(Class, Method, class('java/lang/Object', L)) :-
methodName(Method, '<init>'),
classDefiningLoader(Class, L),
isBootstrapLoader(L),
classClassName(Class, 'java/lang/Object').
instanceMethodInitialThisType(Class, Method, uninitializedThis) :-
methodName(Method, '<init>'),
classClassName(Class, ClassName),
classDefiningLoader(Class, CurrentLoader),
superclassChain(ClassName, CurrentLoader, Chain),
Chain \= [].
instanceMethodInitialThisType(Class, Method, class(ClassName, L)) :-
methodName(Method, MethodName),
MethodName \= '<init>',
classDefiningLoader(Class, L),
classClassName(Class, ClassName).
Теперь мы вычисляем, является ли объединённый поток для метода типами корректным, используя начальное состояние типа метода:
-
Если у нас есть фрейм карты стека и входное состояние типа, состояние типа должно быть присваиваемо состоянию типа во фрейме карты стека. Мы можем затем продолжить проверку типа остальной части потока со состоянием типа, указанным во фрейме карты стека.
mergedCodeIsTypeSafe(Environment, [stackMap(Offset, MapFrame) | MoreCode], frame(Locals, OperandStack, Flags)) :- frameIsAssignable(frame(Locals, OperandStack, Flags), MapFrame), mergedCodeIsTypeSafe(Environment, MoreCode, MapFrame). -
Объединённый поток кода является безопасным по типу относительно входного состояния типа
T, если он начинается с инструкцииI, которая является безопасной по типу относительноT, иIудовлетворяет своим обработчикам исключений (см. ниже), и хвост потока является безопасным по типу, учитывая состояние типа после этого выполненияI.NextStackFrameуказывает, что выполнение переходит к следующей инструкции. Для инструкции безусловного перехода он будет иметь специальное значениеafterGoto.ExceptionStackFrameуказывает, что передаётся обработчикам исключений.mergedCodeIsTypeSafe(Environment, [instruction(Offset, Parse) | MoreCode], frame(Locals, OperandStack, Flags)) :- instructionIsTypeSafe(Parse, Environment, Offset, frame(Locals, OperandStack, Flags), NextStackFrame, ExceptionStackFrame), instructionSatisfiesHandlers(Environment, Offset, ExceptionStackFrame), mergedCodeIsTypeSafe(Environment, MoreCode, NextStackFrame). -
После безусловного перехода (указанного входным состоянием типа
afterGoto), если у нас есть фрейм карты стека, предоставляющий состояние типа для следующих инструкций, мы можем продолжить и проверить их на тип, используя состояние типа, предоставленное фреймом карты стека.mergedCodeIsTypeSafe(Environment, [stackMap(Offset, MapFrame) | MoreCode], afterGoto) :- mergedCodeIsTypeSafe(Environment, MoreCode, MapFrame). -
Недопустимо иметь код после безусловного перехода без предоставления для него фрейма карты стека.
mergedCodeIsTypeSafe(_Environment, [instruction(_, _) | _MoreCode], afterGoto) :- write_ln('No stack frame after unconditional branch'), fail. -
Если у нас есть безусловный переход в конце кода, остановитесь.
mergedCodeIsTypeSafe(_Environment, [endOfCode(Offset)], afterGoto).
Переход к целевому адресу является безопасным по типу, если для целевого адреса существует связанный фрейм карты стека, Frame, а текущий фрейм карты стека, StackFrame, присваиваем Frame.
targetIsTypeSafe(Environment, StackFrame, Target) :-
offsetStackFrame(Environment, Target, Frame),
frameIsAssignable(StackFrame, Frame).
Инструкция удовлетворяет своим обработчикам исключений, если она удовлетворяет каждому обработчику исключений, который применим к инструкции.
instructionSatisfiesHandlers(Environment, Offset, ExceptionStackFrame) :-
exceptionHandlers(Environment, Handlers),
sublist(isApplicableHandler(Offset), Handlers, ApplicableHandlers),
checklist(instructionSatisfiesHandler(Environment, ExceptionStackFrame),
ApplicableHandlers).
Обработчик исключений является применимым к инструкции, если смещение инструкции больше или равно началу диапазона обработчика и меньше конца диапазона обработчика.
isApplicableHandler(Offset, handler(Start, End, _Target, _ClassName)) :-
Offset >= Start,
Offset < End.
Инструкция удовлетворяет обработчику исключений, если состояние исходящего типа инструкции равно 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).
Описание каждого правила на английском языке предназначено для удобочитаемости, интуитивности и краткости. Поэтому в описании избегается повторение всех контекстуальных предположений, приведенных выше. В частности:
-
Описание не упоминает явно среду.
-
Когда в описании говорится о стеке операндов или локальных переменных, это относится к компонентам стека операндов и локальных переменных состояния типа: либо к входному состоянию типа, либо к выходному.
-
Состояние типа после внезапного завершения инструкции почти всегда идентично входному состоянию типа. Описание обсуждает состояние типа после внезапного завершения инструкции только в том случае, если это не так.
-
Описание говорит о извлечении и помещении типов на стек операндов, но не обсуждает явно проблемы подпотока или переполнения стека. Описание предполагает, что эти операции могут быть успешно выполнены, но правила Пролога для управления стеком операндов гарантируют, что необходимые проверки выполняются.
-
Описание обсуждает только манипулирование логическими типами. На практике некоторые типы занимают более одного слова. Описание абстрагируется от этих деталей представления, но правила Пролога, которые манипулируют данными, этого не делают.
Любые неоднозначности можно разрешить, обратившись к формальным правилам Пролога.
aaload
Инструкция aaload безопасна с точки зрения типов тогда и только тогда, когда можно корректно заменить типы, соответствующие int и тип массива с типом компонента ComponentType, где ComponentType является подтипом Object, используя ComponentType для получения выходного состояния типа.
instructionIsTypeSafe(aaload, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
nth1OperandStackIs(2, StackFrame, ArrayType),
arrayComponentType(ArrayType, ComponentType),
isBootstrapLoader(BL),
validTypeTransition(Environment,
[int, arrayOf(class('java/lang/Object', BL))],
ComponentType, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Тип компонента массива X - это X. Мы определяем тип компонента null как null.
arrayComponentType(arrayOf(X), X). arrayComponentType(null, null).
aastore
Инструкция aastore безопасна с точки зрения типов тогда и только тогда, когда можно корректно извлечь типы, соответствующие Object, int и массив Object из входного стека операндов, получая выходное состояние типа.
instructionIsTypeSafe(aastore, _Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
isBootstrapLoader(BL),
canPop(StackFrame,
[class('java/lang/Object', BL),
int,
arrayOf(class('java/lang/Object', BL))],
NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
aconst_null
Инструкция aconst_null безопасна с точки зрения типов, если можно корректно поместить тип null на входной стек операндов, получая выходное состояние типа.
instructionIsTypeSafe(aconst_null, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [], null, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
aload, aload_<n>
Инструкция aload с операндом Index безопасна с точки зрения типов и дает выходное состояние типа NextStackFrame, если инструкция загрузки с операндом Index и типом reference безопасна с точки зрения типов и дает выходное состояние типа NextStackFrame.
instructionIsTypeSafe(aload(Index), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
loadIsTypeSafe(Environment, Index, reference, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкции aload_<n>, для 0 ≤ n ≤ 3, безопасны с точки зрения типов тогда и только тогда, когда эквивалентная инструкция aload безопасна с точки зрения типов.
instructionHasEquivalentTypeRule(aload_0, aload(0)). instructionHasEquivalentTypeRule(aload_1, aload(1)). instructionHasEquivalentTypeRule(aload_2, aload(2)). instructionHasEquivalentTypeRule(aload_3, aload(3)).
anewarray
Инструкция anewarray с операндом CP безопасна с точки зрения типов тогда и только тогда, когда CP ссылается на запись константного пула, обозначающую класс, интерфейс или тип массива, и можно законно заменить тип, соответствующий int на входном стеке операндов на массив с типом компонента CP, получая выходное состояние типа.
instructionIsTypeSafe(anewarray(CP), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
(CP = class(_, _) ; CP = arrayOf(_)),
validTypeTransition(Environment, [int], arrayOf(CP),
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
areturn
Инструкция areturn безопасна с точки зрения типов тогда и только тогда, когда объявленный тип возвращаемого значения окружающего метода, ReturnType, является типом reference, и можно корректно извлечь тип, соответствующий ReturnType, из входного стека операндов.
instructionIsTypeSafe(areturn, Environment, _Offset, StackFrame,
afterGoto, ExceptionStackFrame) :-
thisMethodReturnType(Environment, ReturnType),
isAssignable(ReturnType, reference),
canPop(StackFrame, [ReturnType], _PoppedStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
arraylength
Инструкция arraylength безопасна с точки зрения типов тогда и только тогда, когда можно корректно заменить тип массива на входном стеке операндов типом int, получая выходное состояние типа.
instructionIsTypeSafe(arraylength, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
nth1OperandStackIs(1, StackFrame, ArrayType),
arrayComponentType(ArrayType, _),
validTypeTransition(Environment, [top], int, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
astore, astore_<n>
Инструкция astore с операндом Index безопасна с точки зрения типов и дает выходное состояние типа NextStackFrame, если инструкция сохранения с операндом Index и типом reference безопасна с точки зрения типов и дает выходное состояние типа NextStackFrame.
instructionIsTypeSafe(astore(Index), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
storeIsTypeSafe(Environment, Index, reference, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкции astore_<n>, для 0 ≤ n ≤ 3, безопасны с точки зрения типов тогда и только тогда, когда эквивалентная инструкция astore безопасна с точки зрения типов.
instructionHasEquivalentTypeRule(astore_0, astore(0)). instructionHasEquivalentTypeRule(astore_1, astore(1)). instructionHasEquivalentTypeRule(astore_2, astore(2)). instructionHasEquivalentTypeRule(astore_3, astore(3)).
athrow
Инструкция athrow безопасна с точки зрения типов тогда и только тогда, когда вершина стека операндов соответствует Throwable.
instructionIsTypeSafe(athrow, _Environment, _Offset, StackFrame,
afterGoto, ExceptionStackFrame) :-
isBootstrapLoader(BL),
canPop(StackFrame, [class('java/lang/Throwable', BL)], _PoppedStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
baload
Инструкция baload безопасна с точки зрения типов тогда и только тогда, когда можно корректно заменить типы, соответствующие int и тип малого массива на входном стеке операндов на int, получая выходное состояние типа.
instructionIsTypeSafe(baload, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :
nth1OperandStackIs(2, StackFrame, ArrayType),
isSmallArray(ArrayType),
validTypeTransition(Environment, [int, top], int,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Тип массива является типом малого массива, если это массив byte, массив boolean или подтип thereof (null).
isSmallArray(arrayOf(byte)). isSmallArray(arrayOf(boolean)). isSmallArray(null).
bastore
Инструкция bastore безопасна с точки зрения типов тогда и только тогда, когда можно корректно извлечь типы, соответствующие int, int и тип малого массива из входного стека операндов, получая выходное состояние типа.
instructionIsTypeSafe(bastore, _Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
nth1OperandStackIs(3, StackFrame, ArrayType),
isSmallArray(ArrayType),
canPop(StackFrame, [int, int, top], NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
bipush
Инструкция bipush безопасна с точки зрения типов тогда и только тогда, когда эквивалентная инструкция sipush безопасна с точки зрения типов.
instructionHasEquivalentTypeRule(bipush(Value), sipush(Value)).
caload
Инструкция caload безопасна с точки зрения типов тогда и только тогда, когда можно корректно заменить типы, соответствующие int и массив char на входном стеке операндов на int, получая выходное состояние типа.
instructionIsTypeSafe(caload, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [int, arrayOf(char)], int,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
castore
Инструкция castore является безошибочной с типом, если можно корректно извлечь типы, соответствующие int, int и массив char из входного стека операндов, что приведет к состоянию выходного типа.
instructionIsTypeSafe(castore, _Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
canPop(StackFrame, [int, int, arrayOf(char)], NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
checkcast
Инструкция checkcast с операндом CP является безошибочной с типом, если CP ссылается на запись в пуле констант, обозначающую класс или массив, и можно корректно заменить тип Object вверху входного стека операндов типом, обозначенным CP, что приведет к состоянию выходного типа.
instructionIsTypeSafe(checkcast(CP), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
(CP = class(_, _) ; CP = arrayOf(_)),
isBootstrapLoader(BL),
validTypeTransition(Environment, [class('java/lang/Object', BL)], CP,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
d2f, d2i, d2l
Инструкция d2f является безошибочной с типом, если можно корректно извлечь double из входного стека операндов и заменить его на float, что приведет к состоянию выходного типа.
instructionIsTypeSafe(d2f, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [double], float,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция d2i является безошибочной с типом, если можно корректно извлечь double из входного стека операндов и заменить его на int, что приведет к состоянию выходного типа.
instructionIsTypeSafe(d2i, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [double], int,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция d2l является безошибочной с типом, если можно корректно извлечь double из входного стека операндов и заменить его на long, что приведет к состоянию выходного типа.
instructionIsTypeSafe(d2l, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [double], long,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
dadd
Инструкция dadd является безошибочной с типом тогда и только тогда, когда можно корректно заменить типы, соответствующие double и double в входном стеке операндов на double, что приведет к состоянию выходного типа.
instructionIsTypeSafe(dadd, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [double, double], double,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
daload
Инструкция daload является безошибочной с типом тогда и только тогда, когда можно корректно заменить типы, соответствующие int и массиву double в входном стеке операндов на double, что приведет к состоянию выходного типа.
instructionIsTypeSafe(daload, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [int, arrayOf(double)], double,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
dastore
Инструкция dastore является безошибочной с типом тогда и только тогда, когда можно корректно извлечь типы, соответствующие double, int и массиву double из входного стека операндов, что приведет к состоянию выходного типа.
instructionIsTypeSafe(dastore, _Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
canPop(StackFrame, [double, int, arrayOf(double)], NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
dcmp<op>
Инструкция dcmpg является безошибочной с типом тогда и только тогда, когда можно корректно заменить типы, соответствующие double и double в входном стеке операндов на int, что приведет к состоянию выходного типа.
instructionIsTypeSafe(dcmpg, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [double, double], int,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция dcmpl является безошибочной с типом тогда и только тогда, когда эквивалентная инструкция dcmpg является безошибочной с типом.
instructionHasEquivalentTypeRule(dcmpl, dcmpg).
dconst_<d>
Инструкция dconst_0 является безошибочной с типом, если можно корректно поместить тип double в входной стек операндов, что приведет к состоянию выходного типа.
instructionIsTypeSafe(dconst_0, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [], double, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция dconst_1 является безошибочной с типом тогда и только тогда, когда эквивалентная инструкция dconst_0 является безошибочной с типом.
instructionHasEquivalentTypeRule(dconst_1, dconst_0).
ddiv
Инструкция ddiv является безошибочной с типом тогда и только тогда, когда эквивалентная инструкция dadd является безошибочной с типом.
instructionHasEquivalentTypeRule(ddiv, dadd).
dload, dload_<n>
Инструкция dload с операндом Index является безошибочной с типом и приводит к состоянию выходного типа NextStackFrame, если инструкция загрузки с операндом Index и типом double является безошибочной с типом и приводит к состоянию выходного типа NextStackFrame.
instructionIsTypeSafe(dload(Index), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
loadIsTypeSafe(Environment, Index, double, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкции dload_<n>, для 0 ≤ n ≤ 3, безошибочны с типом тогда и только тогда, когда эквивалентная инструкция dload безошибочна с типом.
instructionHasEquivalentTypeRule(dload_0, dload(0)). instructionHasEquivalentTypeRule(dload_1, dload(1)). instructionHasEquivalentTypeRule(dload_2, dload(2)). instructionHasEquivalentTypeRule(dload_3, dload(3)).
dmul
Инструкция dmul является безошибочной с типом тогда и только тогда, когда эквивалентная инструкция dadd является безошибочной с типом.
instructionHasEquivalentTypeRule(dmul, dadd).
dneg
Инструкция dneg является безошибочной с типом тогда и только тогда, когда в входном стеке операндов есть тип, соответствующий double. Инструкция dneg не изменяет состояние типа.
instructionIsTypeSafe(dneg, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [double], double,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
drem
Инструкция drem является безошибочной с типом тогда и только тогда, когда эквивалентная инструкция dadd является безошибочной с типом.
instructionHasEquivalentTypeRule(drem, dadd).
dreturn
Инструкция dreturn является безошибочной с типом, если у объявленного возвращаемого типа окружающего метода тип double, и можно корректно извлечь тип, соответствующий double из входного стека операндов.
instructionIsTypeSafe(dreturn, Environment, _Offset, StackFrame,
afterGoto, ExceptionStackFrame) :-
thisMethodReturnType(Environment, double),
canPop(StackFrame, [double], _PoppedStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
dstore, dstore_<n>
Инструкция dstore с операндом Index является безошибочной с типом и приводит к состоянию выходного типа NextStackFrame, если инструкция сохранения с операндом Index и типом double является безошибочной с типом и приводит к состоянию выходного типа NextStackFrame.
instructionIsTypeSafe(dstore(Index), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
storeIsTypeSafe(Environment, Index, double, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкции dstore_<n>, для 0 ≤ n ≤ 3, безошибочны с типом тогда и только тогда, когда эквивалентная инструкция dstore безошибочна с типом.
instructionHasEquivalentTypeRule(dstore_0, dstore(0)). instructionHasEquivalentTypeRule(dstore_1, dstore(1)). instructionHasEquivalentTypeRule(dstore_2, dstore(2)). instructionHasEquivalentTypeRule(dstore_3, dstore(3)).
dsub
Инструкция dsub является безошибочной с типом тогда и только тогда, когда эквивалентная инструкция dadd является безошибочной с типом.
instructionHasEquivalentTypeRule(dsub, dadd).
dup
Инструкция dup является безошибочной с типом тогда и только тогда, когда можно корректно заменить тип категории 1, Type, на типы Type, Type, что приведет к состоянию выходного типа.
instructionIsTypeSafe(dup, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
StackFrame = frame(Locals, InputOperandStack, Flags),
popCategory1(InputOperandStack, Type, _),
canSafelyPush(Environment, InputOperandStack, Type, OutputOperandStack),
NextStackFrame = frame(Locals, OutputOperandStack, Flags),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
dup_x1
Инструкция dup_x1 является безошибочной с типом тогда и только тогда, когда можно корректно заменить два типа категории 1, Type1 и Type2, в входном стеке операндов на типы Type1, Type2, Type1, что приведет к состоянию выходного типа.
instructionIsTypeSafe(dup_x1, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
StackFrame = frame(Locals, InputOperandStack, Flags),
popCategory1(InputOperandStack, Type1, Stack1),
popCategory1(Stack1, Type2, Rest),
canSafelyPushList(Environment, Rest, [Type1, Type2, Type1],
OutputOperandStack),
NextStackFrame = frame(Locals, OutputOperandStack, Flags),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
dup_x2
Инструкция dup_x2 безопасна по типу, если она является формой, безопасной по типу инструкции dup_x2.
instructionIsTypeSafe(dup_x2, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
StackFrame = frame(Locals, InputOperandStack, Flags),
dup_x2FormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack),
NextStackFrame = frame(Locals, OutputOperandStack, Flags),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция dup_x2 является формой, безопасной по типу инструкции dup_x2, если она является инструкцией формой 1, безопасной по типу dup_x2 или инструкцией формой 2, безопасной по типу dup_x2.
dup_x2FormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
dup_x2Form1IsTypeSafe(Environment, InputOperandStack, OutputOperandStack).
dup_x2FormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
dup_x2Form2IsTypeSafe(Environment, InputOperandStack, OutputOperandStack).
Инструкция dup_x2 является инструкцией формой 1, безопасной по типу dup_x2, если можно корректно заменить три типа категории 1, Type1, Type2, Type3 на стеке входных операндов типами Type1, Type2, Type3, Type1, что соответствует состоянию выходных типов.
dup_x2Form1IsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
popCategory1(InputOperandStack, Type1, Stack1),
popCategory1(Stack1, Type2, Stack2),
popCategory1(Stack2, Type3, Rest),
canSafelyPushList(Environment, Rest, [Type1, Type3, Type2, Type1],
OutputOperandStack).
Инструкция dup_x2 является инструкцией формой 2, безопасной по типу dup_x2, если можно корректно заменить тип категории 1, Type1, и тип категории 2, Type2, на стеке входных операндов типами Type1, Type2, Type1, что соответствует состоянию выходных типов.
dup_x2Form2IsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
popCategory1(InputOperandStack, Type1, Stack1),
popCategory2(Stack1, Type2, Rest),
canSafelyPushList(Environment, Rest, [Type1, Type2, Type1],
OutputOperandStack).
dup2
Инструкция dup2 безопасна по типу, если она является формой, безопасной по типу инструкции dup2.
instructionIsTypeSafe(dup2, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
StackFrame = frame(Locals, InputOperandStack, Flags),
dup2FormIsTypeSafe(Environment,InputOperandStack, OutputOperandStack),
NextStackFrame = frame(Locals, OutputOperandStack, Flags),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция dup2 является формой, безопасной по типу инструкции dup2, если она является инструкцией формой 1, безопасной по типу dup2 или инструкцией формой 2, безопасной по типу dup2.
dup2FormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
dup2Form1IsTypeSafe(Environment,InputOperandStack, OutputOperandStack).
dup2FormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
dup2Form2IsTypeSafe(Environment,InputOperandStack, OutputOperandStack).
Инструкция dup2 является инструкцией формой 1, безопасной по типу dup2, если можно корректно заменить два типа категории 1, Type1 и Type2, на стеке входных операндов типами Type1, Type2, Type1, Type2, что соответствует состоянию выходных типов.
dup2Form1IsTypeSafe(Environment, InputOperandStack, OutputOperandStack):-
popCategory1(InputOperandStack, Type1, TempStack),
popCategory1(TempStack, Type2, _),
canSafelyPushList(Environment, InputOperandStack, [Type2, Type1],
OutputOperandStack).
Инструкция dup2 является инструкцией формой 2, безопасной по типу dup2, если можно корректно заменить тип категории 2, Type, на стеке входных операндов типами Type, Type, что соответствует состоянию выходных типов.
dup2Form2IsTypeSafe(Environment, InputOperandStack, OutputOperandStack):-
popCategory2(InputOperandStack, Type, _),
canSafelyPush(Environment, InputOperandStack, Type, OutputOperandStack).
dup2_x1
Инструкция dup2_x1 безопасна по типу, если она является формой, безопасной по типу инструкции dup2_x1.
instructionIsTypeSafe(dup2_x1, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
StackFrame = frame(Locals, InputOperandStack, Flags),
dup2_x1FormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack),
NextStackFrame = frame(Locals, OutputOperandStack, Flags),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция dup2_x1 является формой, безопасной по типу инструкции dup2_x1, если она является инструкцией формой 1, безопасной по типу dup2_x1 или инструкцией формой 2, безопасной по типу dup_x2.
dup2_x1FormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
dup2_x1Form1IsTypeSafe(Environment, InputOperandStack, OutputOperandStack).
dup2_x1FormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
dup2_x1Form2IsTypeSafe(Environment, InputOperandStack, OutputOperandStack).
Инструкция dup2_x1 является инструкцией формой 1, безопасной по типу dup2_x1, если можно корректно заменить три типа категории 1, Type1, Type2, Type3, на стеке входных операндов типами Type1, Type2, Type3, Type1, Type2, что соответствует состоянию выходных типов.
dup2_x1Form1IsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
popCategory1(InputOperandStack, Type1, Stack1),
popCategory1(Stack1, Type2, Stack2),
popCategory1(Stack2, Type3, Rest),
canSafelyPushList(Environment, Rest, [Type2, Type1, Type3, Type2, Type1],
OutputOperandStack).
Инструкция dup2_x1 является инструкцией формой 2, безопасной по типу dup2_x1, если можно корректно заменить тип категории 2, Type1, и тип категории 1, Type2, на стеке входных операндов типами Type1, Type2, Type1, что соответствует состоянию выходных типов.
dup2_x1Form2IsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
popCategory2(InputOperandStack, Type1, Stack1),
popCategory1(Stack1, Type2, Rest),
canSafelyPushList(Environment, Rest, [Type1, Type2, Type1],
OutputOperandStack).
dup2_x2
Инструкция dup2_x2 безопасна по типу, если она является формой, безопасной по типу инструкции dup2_x2.
instructionIsTypeSafe(dup2_x2, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
StackFrame = frame(Locals, InputOperandStack, Flags),
dup2_x2FormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack),
NextStackFrame = frame(Locals, OutputOperandStack, Flags),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция dup2_x2 является формой, безопасной по типу инструкции dup2_x2, если выполняется одно из следующих условий:
-
это инструкция формой 1, безопасной по типу dup2_x2.
-
это инструкция формой 2, безопасной по типу dup2_x2.
-
это инструкция формой 3, безопасной по типу dup2_x2.
-
это инструкция формой 4, безопасной по типу dup2_x2.
dup2_x2FormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
dup2_x2Form1IsTypeSafe(Environment, InputOperandStack, OutputOperandStack).
dup2_x2FormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
dup2_x2Form2IsTypeSafe(Environment, InputOperandStack, OutputOperandStack).
dup2_x2FormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
dup2_x2Form3IsTypeSafe(Environment, InputOperandStack, OutputOperandStack).
dup2_x2FormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
dup2_x2Form4IsTypeSafe(Environment, InputOperandStack, OutputOperandStack).
Инструкция dup2_x2 является инструкцией формой 1, безопасной по типу dup2_x2, если можно корректно заменить четыре типа категории 1, Type1, Type2, Type3, Type4, на стеке входных операндов типами Type1, Type2, Type3, Type4, Type1, Type2, что соответствует состоянию выходных типов.
dup2_x2Form1IsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
popCategory1(InputOperandStack, Type1, Stack1),
popCategory1(Stack1, Type2, Stack2),
popCategory1(Stack2, Type3, Stack3),
popCategory1(Stack3, Type4, Rest),
canSafelyPushList(Environment, Rest,
[Type2, Type1, Type4, Type3, Type2, Type1],
OutputOperandStack).
Инструкция dup2_x2 является инструкцией формой 2, безопасной по типу dup2_x2, если можно корректно заменить тип категории 2, Type1, и два типа категории 1, Type2, Type3, на стеке входных операндов типами Type1, Type2, Type3, Type1, что соответствует состоянию выходных типов.
dup2_x2Form2IsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
popCategory2(InputOperandStack, Type1, Stack1),
popCategory1(Stack1, Type2, Stack2),
popCategory1(Stack2, Type3, Rest),
canSafelyPushList(Environment, Rest,
[Type1, Type3, Type2, Type1],
OutputOperandStack).
Инструкция dup2_x2 является инструкцией формой 3, безопасной по типу dup2_x2, если можно корректно заменить два типа категории 1, Type1, Type2, и тип категории 2, Type3, на стеке входных операндов типами Type1, Type2, Type3, Type1, Type2, что соответствует состоянию выходных типов.
dup2_x2Form3IsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
popCategory1(InputOperandStack, Type1, Stack1),
popCategory1(Stack1, Type2, Stack2),
popCategory2(Stack2, Type3, Rest),
canSafelyPushList(Environment, Rest,
[Type2, Type1, Type3, Type2, Type1],
OutputOperandStack).
Инструкция dup2_x2 является инструкцией формой 4, безопасной по типу dup2_x2, если можно корректно заменить два типа категории 2, Type1, Type2, на стеке входных операндов типами Type1, Type2, Type1, что соответствует состоянию выходных типов.
dup2_x2Form4IsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
popCategory2(InputOperandStack, Type1, Stack1),
popCategory2(Stack1, Type2, Rest),
canSafelyPushList(Environment, Rest, [Type1, Type2, Type1],
OutputOperandStack).
f2d, f2i, f2l
Инструкция f2d безопасна по типу, если можно корректно удалить float со стека входных операндов и заменить его на double, что соответствует состоянию выходных типов.
instructionIsTypeSafe(f2d, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [float], double,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция f2i безопасна по типу, если можно корректно удалить float со стека входных операндов и заменить его на int, что соответствует состоянию выходных типов.
instructionIsTypeSafe(f2i, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [float], int,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция f2l безопасна по типу, если можно корректно удалить float со стека входных операндов и заменить его на long, что соответствует состоянию выходных типов.
instructionIsTypeSafe(f2l, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [float], long,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
fadd
Инструкция fadd безопасна по типу, если можно корректно заменить типы, соответствующие float и float, на стеке входных операндов на float, что соответствует состоянию выходных типов.
instructionIsTypeSafe(fadd, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [float, float], float,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
faload
Инструкция faload является безопасной с точки зрения типов тогда и только тогда, когда можно корректно заменить типы, соответствующие int и массиву float на стеке входных операндов на тип float, получая состояние выходного типа.
instructionIsTypeSafe(faload, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [int, arrayOf(float)], float,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
fastore
Инструкция fastore является безопасной с точки зрения типов тогда и только тогда, когда можно корректно извлечь типы, соответствующие float, int и массиву float со стека входных операндов, получая состояние выходного типа.
instructionIsTypeSafe(fastore, _Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
canPop(StackFrame, [float, int, arrayOf(float)], NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
fcmp<op>
Инструкция fcmpg является безопасной с точки зрения типов тогда и только тогда, когда можно корректно заменить типы, соответствующие float и float на стеке входных операндов на int, получая состояние выходного типа.
instructionIsTypeSafe(fcmpg, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [float, float], int,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция fcmpl является безопасной с точки зрения типов тогда и только тогда, когда эквивалентная инструкция fcmpg является безопасной с точки зрения типов.
instructionHasEquivalentTypeRule(fcmpl, fcmpg).
fconst_<f>
Инструкция fconst_0 является безопасной с точки зрения типов, если можно корректно поместить тип float на стек входных операндов, получая состояние выходного типа.
instructionIsTypeSafe(fconst_0, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [], float, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Правила для других вариантов инструкции fconst аналогичны.
instructionHasEquivalentTypeRule(fconst_1, fconst_0). instructionHasEquivalentTypeRule(fconst_2, fconst_0).
fdiv
Инструкция fdiv является безопасной с точки зрения типов тогда и только тогда, когда эквивалентная инструкция fadd является безопасной с точки зрения типов.
instructionHasEquivalentTypeRule(fdiv, fadd).
fload, fload_<n>
Инструкция fload с операндом Index безопасна с точки зрения типов и приводит к состоянию выходного типа NextStackFrame, если инструкция загрузки с операндом Index и типом float безопасна с точки зрения типов и приводит к состоянию выходного типа NextStackFrame.
instructionIsTypeSafe(fload(Index), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
loadIsTypeSafe(Environment, Index, float, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкции fload_<n> для 0 ≤ n ≤ 3 безопасны с точки зрения типов тогда и только тогда, когда эквивалентная инструкция fload безопасна с точки зрения типов.
instructionHasEquivalentTypeRule(fload_0, fload(0)). instructionHasEquivalentTypeRule(fload_1, fload(1)). instructionHasEquivalentTypeRule(fload_2, fload(2)). instructionHasEquivalentTypeRule(fload_3, fload(3)).
fmul
Инструкция fmul является безопасной с точки зрения типов тогда и только тогда, когда эквивалентная инструкция fadd является безопасной с точки зрения типов.
instructionHasEquivalentTypeRule(fmul, fadd).
fneg
Инструкция fneg является безопасной с точки зрения типов тогда и только тогда, когда на стеке входных операндов есть тип, соответствующий float. Инструкция fneg не изменяет состояние типа.
instructionIsTypeSafe(fneg, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [float], float,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
frem
Инструкция frem является безопасной с точки зрения типов тогда и только тогда, когда эквивалентная инструкция fadd является безопасной с точки зрения типов.
instructionHasEquivalentTypeRule(frem, fadd).
freturn
Инструкция freturn является безопасной с точки зрения типов, если у окружающего метода тип возвращаемого значения объявлен как float, и можно корректно извлечь тип, соответствующий float со стека входных операндов.
instructionIsTypeSafe(freturn, Environment, _Offset, StackFrame,
afterGoto, ExceptionStackFrame) :-
thisMethodReturnType(Environment, float),
canPop(StackFrame, [float], _PoppedStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
fstore, fstore_<n>
Инструкция fstore с операндом Index безопасна с точки зрения типов и приводит к состоянию выходного типа NextStackFrame, если инструкция сохранения с операндом Index и типом float безопасна с точки зрения типов и приводит к состоянию выходного типа NextStackFrame.
instructionIsTypeSafe(fstore(Index), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
storeIsTypeSafe(Environment, Index, float, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкции fstore_<n> для 0 ≤ n ≤ 3 безопасны с точки зрения типов тогда и только тогда, когда эквивалентная инструкция fstore безопасна с точки зрения типов.
instructionHasEquivalentTypeRule(fstore_0, fstore(0)). instructionHasEquivalentTypeRule(fstore_1, fstore(1)). instructionHasEquivalentTypeRule(fstore_2, fstore(2)). instructionHasEquivalentTypeRule(fstore_3, fstore(3)).
fsub
Инструкция fsub является безопасной с точки зрения типов тогда и только тогда, когда эквивалентная инструкция fadd является безопасной с точки зрения типов.
instructionHasEquivalentTypeRule(fsub, fadd).
getfield
Инструкция getfield с операндом CP безопасна с точки зрения типов тогда и только тогда, когда CP ссылается на запись константного пула, обозначающую поле с объявленным типом FieldType, которое является членом класса FieldClassName, и можно корректно заменить тип, соответствующий FieldClassName, на тип FieldType на стеке входных операндов, получая состояние выходного типа. FieldClassName не должен быть типом массива. Поля protected подвергаются дополнительной проверке (§4.10.1.8).
instructionIsTypeSafe(getfield(CP), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
CP = field(FieldClassName, FieldName, FieldDescriptor),
parseFieldDescriptor(FieldDescriptor, FieldType),
passesProtectedCheck(Environment, FieldClassName, FieldName,
FieldDescriptor, StackFrame),
currentClassLoader(Environment, CurrentLoader),
validTypeTransition(Environment,
[class(FieldClassName, CurrentLoader)], FieldType,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
getstatic
Инструкция getstatic с операндом CP безопасна с точки зрения типов тогда и только тогда, когда CP ссылается на запись константного пула, обозначающую поле с объявленным типом FieldType, и можно корректно поместить FieldType на стек входных операндов, получая состояние выходного типа.
instructionIsTypeSafe(getstatic(CP), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
CP = field(_FieldClassName, _FieldName, FieldDescriptor),
parseFieldDescriptor(FieldDescriptor, FieldType),
validTypeTransition(Environment, [], FieldType,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
goto, goto_w
Инструкция goto является безопасной с точки зрения типов тогда и только тогда, когда её целевой операнд является допустимой точкой ветвления.
instructionIsTypeSafe(goto(Target), Environment, _Offset, StackFrame,
afterGoto, ExceptionStackFrame) :-
targetIsTypeSafe(Environment, StackFrame, Target),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция goto_w является безопасной с точки зрения типов тогда и только тогда, когда эквивалентная инструкция goto является безопасной с точки зрения типов.
instructionHasEquivalentTypeRule(goto_w(Target), goto(Target)).
i2b, i2c, i2d, i2f, i2l, i2s
Инструкция i2b является безопасной с точки зрения типов тогда и только тогда, когда эквивалентная инструкция ineg является безопасной с точки зрения типов.
instructionHasEquivalentTypeRule(i2b, ineg).
Инструкция i2c является безопасной с точки зрения типов тогда и только тогда, когда эквивалентная инструкция ineg является безопасной с точки зрения типов.
instructionHasEquivalentTypeRule(i2c, ineg).
Инструкция i2d является безопасной, если можно корректно извлечь int со стека входных операндов и заменить его на double, получая состояние выходного типа.
instructionIsTypeSafe(i2d, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [int], double,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция i2f является безопасной, если можно корректно извлечь int со стека входных операндов и заменить его на float, получая состояние выходного типа.
instructionIsTypeSafe(i2f, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [int], float,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция i2l является безопасной, если можно корректно извлечь int со стека входных операндов и заменить его на long, получая состояние выходного типа.
instructionIsTypeSafe(i2l, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [int], long,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция i2s является безопасной с точки зрения типов тогда и только тогда, когда эквивалентная инструкция ineg является безопасной с точки зрения типов.
instructionHasEquivalentTypeRule(i2s, ineg).
iadd
Инструкция iadd является безопасной с точки зрения типов тогда и только тогда, когда можно корректно заменить типы, соответствующие int и int на стеке входных операндов на int, получая состояние выходного типа.
instructionIsTypeSafe(iadd, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [int, int], int,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
iaload
Инструкция iaload является безопасной с точки зрения типов тогда и только тогда, когда можно корректно заменить типы, соответствующие int, и массив int на входном стеке операндов на int, получая состояние типов на выходе.
instructionIsTypeSafe(iaload, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [int, arrayOf(int)], int,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
iand
Инструкция iand является безопасной с точки зрения типов тогда и только тогда, когда эквивалентная инструкция iadd является безопасной с точки зрения типов.
instructionHasEquivalentTypeRule(iand, iadd).
iastore
Инструкция iastore является безопасной с точки зрения типов тогда и только тогда, когда можно корректно извлечь типы, соответствующие int, int и массив int с входного стека операндов, получая состояние типов на выходе.
instructionIsTypeSafe(iastore, _Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
canPop(StackFrame, [int, int, arrayOf(int)], NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
iconst_<i>
Инструкция iconst_m1 является безопасной с точки зрения типов, если можно корректно поместить тип int на входной стек операндов, получая состояние типов на выходе.
instructionIsTypeSafe(iconst_m1, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [], int, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Правила для других вариантов инструкции iconst эквивалентны.
instructionHasEquivalentTypeRule(iconst_0, iconst_m1). instructionHasEquivalentTypeRule(iconst_1, iconst_m1). instructionHasEquivalentTypeRule(iconst_2, iconst_m1). instructionHasEquivalentTypeRule(iconst_3, iconst_m1). instructionHasEquivalentTypeRule(iconst_4, iconst_m1). instructionHasEquivalentTypeRule(iconst_5, iconst_m1).
idiv
Инструкция idiv является безопасной с точки зрения типов тогда и только тогда, когда эквивалентная инструкция iadd является безопасной с точки зрения типов.
instructionHasEquivalentTypeRule(idiv, iadd).
if_acmp<cond>
Инструкция if_acmpeq является безопасной с точки зрения типов тогда и только тогда, когда можно корректно извлечь типы, соответствующие reference и reference с входного стека операндов, получая состояние типов на выходе NextStackFrame, и операнд инструкции, Target, является допустимой целью перехода, предполагая входное состояние типов NextStackFrame.
instructionIsTypeSafe(if_acmpeq(Target), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
canPop(StackFrame, [reference, reference], NextStackFrame),
targetIsTypeSafe(Environment, NextStackFrame, Target),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Правило для if_acmpne идентично.
instructionHasEquivalentTypeRule(if_acmpne(Target), if_acmpeq(Target)).
if_icmp<cond>
Инструкция if_icmpeq является безопасной с точки зрения типов тогда и только тогда, когда можно корректно извлечь типы, соответствующие int и int с входного стека операндов, получая состояние типов на выходе NextStackFrame, и операнд инструкции, Target, является допустимой целью перехода, предполагая входное состояние типов NextStackFrame.
instructionIsTypeSafe(if_icmpeq(Target), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
canPop(StackFrame, [int, int], NextStackFrame),
targetIsTypeSafe(Environment, NextStackFrame, Target),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Правила для всех остальных вариантов инструкции if_icmp<cond> идентичны.
instructionHasEquivalentTypeRule(if_icmpge(Target), if_icmpeq(Target)). instructionHasEquivalentTypeRule(if_icmpgt(Target), if_icmpeq(Target)). instructionHasEquivalentTypeRule(if_icmple(Target), if_icmpeq(Target)). instructionHasEquivalentTypeRule(if_icmplt(Target), if_icmpeq(Target)). instructionHasEquivalentTypeRule(if_icmpne(Target), if_icmpeq(Target)).
if<cond>
Инструкция ifeq является безопасной с точки зрения типов тогда и только тогда, когда можно корректно извлечь тип, соответствующий int с входного стека операндов, получая состояние типов на выходе NextStackFrame, и операнд инструкции, Target, является допустимой целью перехода, предполагая входное состояние типов NextStackFrame.
instructionIsTypeSafe(ifeq(Target), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
canPop(StackFrame, [int], NextStackFrame),
targetIsTypeSafe(Environment, NextStackFrame, Target),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Правила для всех остальных вариантов инструкции if<cond> идентичны.
instructionHasEquivalentTypeRule(ifge(Target), ifeq(Target)). instructionHasEquivalentTypeRule(ifgt(Target), ifeq(Target)). instructionHasEquivalentTypeRule(ifle(Target), ifeq(Target)). instructionHasEquivalentTypeRule(iflt(Target), ifeq(Target)). instructionHasEquivalentTypeRule(ifne(Target), ifeq(Target)).
ifnonnull, ifnull
Инструкция ifnonnull является безопасной с точки зрения типов тогда и только тогда, когда можно корректно извлечь тип, соответствующий reference с входного стека операндов, получая состояние типов на выходе NextStackFrame, и операнд инструкции, Target, является допустимой целью перехода, предполагая входное состояние типов NextStackFrame.
instructionIsTypeSafe(ifnonnull(Target), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
canPop(StackFrame, [reference], NextStackFrame),
targetIsTypeSafe(Environment, NextStackFrame, Target),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция ifnull является безопасной с точки зрения типов тогда и только тогда, когда эквивалентная инструкция ifnonnull является безопасной с точки зрения типов.
instructionHasEquivalentTypeRule(ifnull(Target), ifnonnull(Target)).
iinc
Инструкция iinc с первым операндом Index является безопасной с точки зрения типов тогда и только тогда, когда LIndex имеет тип int. Инструкция iinc не изменяет состояние типа.
instructionIsTypeSafe(iinc(Index, _Value), _Environment, _Offset,
StackFrame, StackFrame, ExceptionStackFrame) :-
StackFrame = frame(Locals, _OperandStack, _Flags),
nth0(Index, Locals, int),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
iload, iload_<n>
Инструкция iload с операндом Index является безопасной с точки зрения типов и дает выходное состояние типа NextStackFrame, если инструкция загрузки с операндом Index и типом int является безопасной с точки зрения типов и дает выходное состояние типа NextStackFrame.
instructionIsTypeSafe(iload(Index), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
loadIsTypeSafe(Environment, Index, int, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкции iload_<n>, для 0 ≤ n ≤ 3, безопасны с точки зрения типов тогда и только тогда, когда эквивалентная инструкция iload безопасна с точки зрения типов.
instructionHasEquivalentTypeRule(iload_0, iload(0)). instructionHasEquivalentTypeRule(iload_1, iload(1)). instructionHasEquivalentTypeRule(iload_2, iload(2)). instructionHasEquivalentTypeRule(iload_3, iload(3)).
imul
Инструкция imul является безопасной с точки зрения типов тогда и только тогда, когда эквивалентная инструкция iadd является безопасной с точки зрения типов.
instructionHasEquivalentTypeRule(imul, iadd).
ineg
Инструкция ineg является безопасной с точки зрения типов тогда и только тогда, когда на входном стеке операндов есть тип, соответствующий int. Инструкция ineg не изменяет состояние типа.
instructionIsTypeSafe(ineg, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [int], int, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
instanceof
Инструкция instanceof с операндом CP является безопасной с точки зрения типов тогда и только тогда, когда CP ссылается на запись константного пула, обозначающую либо класс, либо массив, и можно корректно заменить тип Object на вершине входного стека операндов на тип int, получая состояние типов на выходе.
instructionIsTypeSafe(instanceof(CP), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
(CP = class(_, _) ; CP = arrayOf(_)),
isBootstrapLoader(BL),
validTypeTransition(Environment, [class('java/lang/Object', BL)], int,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
invokedynamic
Инструкция invokedynamic является безопасной с точки зрения типов тогда и только тогда, когда все перечисленные ниже утверждения истинны:
-
Её первый операнд,
CP, ссылается на запись константного пула, обозначающую динамическую точку вызова с именемCallSiteNameи дескрипторомDescriptor. -
CallSiteNameне равно<init>. -
CallSiteNameне равно<clinit>. -
Можно корректно заменить типы, соответствующие типам аргументов, указанным в
Descriptor, на входном стеке операндов, типом возвращаемого значения, указанным вDescriptor, получая состояние типов на выходе.
instructionIsTypeSafe(invokedynamic(CP,0,0), Environment, _Offset,
StackFrame, NextStackFrame, ExceptionStackFrame) :-
CP = dmethod(CallSiteName, Descriptor),
CallSiteName \= '<init>',
CallSiteName \= '<clinit>',
parseMethodDescriptor(Descriptor, OperandArgList, ReturnType),
reverse(OperandArgList, StackArgList),
validTypeTransition(Environment, StackArgList, ReturnType,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
invokeinterface
Инструкция invokeinterface является типизированной тогда и только тогда, когда выполняются все следующие условия:
-
Её первый операнд,
CP, ссылается на запись константного пула, обозначающую метод интерфейса, названныйMethodNameс описаниемDescriptor, являющийся членом интерфейсаMethodIntfName. -
MethodNameне является<init>. -
MethodNameне является<clinit>. -
Её второй операнд,
Count, является допустимым операндом счётчика (см. ниже). -
Можно корректно заменить типы, соответствующие типу
MethodIntfNameи типам аргументов, указанным вDescriptorна стеке входных операндов, на тип возврата, указанный вDescriptor, получая состояние выходного типа.
instructionIsTypeSafe(invokeinterface(CP, Count, 0), Environment, _Offset,
StackFrame, NextStackFrame, ExceptionStackFrame) :-
CP = imethod(MethodIntfName, MethodName, Descriptor),
MethodName \= '<init>',
MethodName \= '<clinit>',
parseMethodDescriptor(Descriptor, OperandArgList, ReturnType),
currentClassLoader(Environment, CurrentLoader),
reverse([class(MethodIntfName, CurrentLoader) | OperandArgList],
StackArgList),
canPop(StackFrame, StackArgList, TempFrame),
validTypeTransition(Environment, [], ReturnType,
TempFrame, NextStackFrame),
countIsValid(Count, StackFrame, TempFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Операнд Count инструкции invokeinterface является допустимым, если он равен размеру аргументов инструкции. Это равно разнице между размером InputFrame и OutputFrame.
countIsValid(Count, InputFrame, OutputFrame) :-
InputFrame = frame(_Locals1, OperandStack1, _Flags1),
OutputFrame = frame(_Locals2, OperandStack2, _Flags2),
length(OperandStack1, Length1),
length(OperandStack2, Length2),
Count =:= Length1 - Length2.
invokespecial
Инструкция invokespecial является типизированной тогда и только тогда, когда выполняются все следующие условия:
-
Её первый операнд,
CP, ссылается на запись константного пула, обозначающую метод, названныйMethodNameс описаниемDescriptor, являющийся членом классаMethodClassName. -
Либо:
-
MethodNameне является<init>. -
MethodNameне является<clinit>. -
Можно корректно заменить типы, соответствующие текущему классу и типам аргументов, указанным в
Descriptorна стеке входных операндов, на тип возврата, указанный вDescriptor, получая состояние выходного типа. -
Можно корректно заменить типы, соответствующие классу
MethodClassNameи типам аргументов, указанным вDescriptorна стеке входных операндов, на тип возврата, указанный вDescriptor.
-
instructionIsTypeSafe(invokespecial(CP), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
CP = method(MethodClassName, MethodName, Descriptor),
MethodName \= '<init>',
MethodName \= '<clinit>',
parseMethodDescriptor(Descriptor, OperandArgList, ReturnType),
thisClass(Environment, class(CurrentClassName, CurrentLoader)),
isAssignable(class(CurrentClassName, CurrentLoader),
class(MethodClassName, CurrentLoader)),
reverse([class(CurrentClassName, CurrentLoader) | OperandArgList],
StackArgList),
validTypeTransition(Environment, StackArgList, ReturnType,
StackFrame, NextStackFrame),
reverse([class(MethodClassName, CurrentLoader) | OperandArgList],
StackArgList2),
validTypeTransition(Environment, StackArgList2, ReturnType,
StackFrame, _ResultStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Оператор isAssignable обеспечивает структурное ограничение, что invokespecial, в случае, отличном от метода инициализации экземпляра, должен называть метод в текущем классе/интерфейсе или в родительском классе/интерфейсе.
Первый оператор validTypeTransition обеспечивает структурное ограничение, что invokespecial, в случае, отличном от метода инициализации экземпляра, ориентируется на объект-получатель текущего класса или нижестоящий. Чтобы понять почему, рассмотрим, что StackArgList моделирует список типов на стеке операндов, ожидаемых методом, начиная с текущего класса (класса, выполняющего invokespecial). Фактические типы на стеке операндов находятся в StackFrame. Эффект validTypeTransition заключается в извлечении первого типа из стека операндов в StackFrame и проверке, является ли он подтипом первой составляющей StackArgList, а именно текущего класса. Таким образом, фактический тип получателя совместим с текущим классом.
Внимательный читатель может заметить, что обеспечение этого структурного ограничения заменяет структурное ограничение, касающееся invokespecial метода protected. Таким образом, код Prolog выше не ссылается на passesProtectedCheck (§4.10.1.8), тогда как код Prolog для invokespecial метода инициализации экземпляра использует passesProtectedCheck, чтобы убедиться, что фактический тип получателя совместим с текущим классом, когда некоторые protected методы инициализации экземпляров называются.
Второй оператор validTypeTransition обеспечивает структурное ограничение, что любая инструкция вызова метода должна ориентироваться на объект-получатель, тип которого совместим с типом, указанным в инструкции. Чтобы понять почему, рассмотрим, что StackArgList2 моделирует список типов на стеке операндов, ожидаемых методом, начиная с типа, указанного в инструкции. Опять же, фактические типы на стеке операндов находятся в StackFrame, и эффект validTypeTransition заключается в проверке, совместим ли фактический тип получателя в StackFrame с типом, указанным в инструкции в StackArgList2.
-
Или:
-
ИмяМетода является
<init>. -
Descriptorопределяет тип возвратаvoid. -
Можно корректно извлечь типы, соответствующие типам аргументов, указанным в
Descriptor, и неинициализированному типу,UninitializedArg, со стека входных операндов, получаяOperandStack. -
Состояние выходного типа выводится из состояния входного типа, сначала заменив входной стек операндов на
OperandStack, а затем заменив все экземплярыUninitializedArgна тип инициализируемого экземпляра. -
Если инструкция вызывает метод инициализации экземпляра для экземпляра класса, созданного предшествующей инструкцией new, и метод является
protected, то использование соответствует особым правилам доступа кprotectedчленам (§4.10.1.8).
-
instructionIsTypeSafe(invokespecial(CP), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
CP = method(MethodClassName, '<init>', Descriptor),
parseMethodDescriptor(Descriptor, OperandArgList, void),
reverse(OperandArgList, StackArgList),
canPop(StackFrame, StackArgList, TempFrame),
TempFrame = frame(Locals, [uninitializedThis | OperandStack], Flags),
currentClassLoader(Environment, CurrentLoader),
rewrittenUninitializedType(uninitializedThis, Environment,
class(MethodClassName, CurrentLoader), This),
rewrittenInitializationFlags(uninitializedThis, Flags, NextFlags),
substitute(uninitializedThis, This, OperandStack, NextOperandStack),
substitute(uninitializedThis, This, Locals, NextLocals),
substitute(uninitializedThis, top, Locals, ExceptionLocals),
NextStackFrame = frame(NextLocals, NextOperandStack, NextFlags),
ExceptionStackFrame = frame(ExceptionLocals, [], 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),
substitute(uninitialized(Address), top, Locals, ExceptionLocals),
NextStackFrame = frame(NextLocals, NextOperandStack, NextFlags),
ExceptionStackFrame = frame(ExceptionLocals, [], 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 может завершиться неудачей, оставив объект в частично инициализированном, постоянно непригодном состоянии. Чтобы предотвратить повторные попытки инициализации после того, как объект не смог инициализироваться в первый раз, обработчик исключений должен рассматривать все ссылки на объект, хранящиеся в локальных переменных, как имеющие тип top, а не uninitializedThis или uninitialized(Offset).
В специальном случае инициализации текущего объекта (то есть при вызове <init> для типа uninitializedThis), исходный кадр, как правило, содержит неинициализированный объект в локальной переменной 0 и имеет флаг flagThisUninit. Нормальное завершение invokespecial инициализирует неинициализированный объект и отключает флаг flagThisUninit. Но если вызов invokespecial сгенерирует исключение, кадр исключения содержит повреждённый объект (с типом top) и флаг flagThisUninit (старый флаг). Нет способа выполнить инструкцию возврата при этом состоянии типа, поэтому обработчик должен сгенерировать другое исключение (или зациклиться). Фактически, даже выразить обработчик с таким состоянием типа невозможно, потому что нет способа для кадра стека, как выражено атрибутом StackMapTable (§4.7.4), иметь flagThisUninit без сопутствующего использования типа uninitializedThis.
Если бы не было этих особых ограничений на инициализацию объектов, типы локальных переменных и флаги кадра исключения всегда были бы такими же, как типы локальных переменных и флаги входного кадра.
invokestatic
Инструкция invokestatic является безошибочной с точки зрения типов, если выполняются все следующие условия:
-
Её первый операнд,
CP, ссылается на запись константного пула, обозначающую метод с именемMethodNameи дескрипторомDescriptor. -
MethodNameне является<init>. -
MethodNameне является<clinit>. -
Можно корректно заменить типы, соответствующие типам аргументов, указанным в
Descriptor, на стек операндов, на тип возвращаемого значения, указанный вDescriptor, получив состояние выходных типов.
instructionIsTypeSafe(invokestatic(CP), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
CP = method(_MethodClassName, MethodName, Descriptor),
MethodName \= '<init>',
MethodName \= '<clinit>',
parseMethodDescriptor(Descriptor, OperandArgList, ReturnType),
reverse(OperandArgList, StackArgList),
validTypeTransition(Environment, StackArgList, ReturnType,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
invokevirtual
Инструкция invokevirtual является безошибочной с точки зрения типов, если выполняются все следующие условия:
-
Её первый операнд,
CP, ссылается на запись константного пула, обозначающую метод с именемMethodNameи дескрипторомDescriptor, являющимся членом классаMethodClassName. -
MethodNameне является<init>. -
MethodNameне является<clinit>. -
Можно корректно заменить типы, соответствующие классу
MethodClassNameи типам аргументов, указанным вDescriptor, на стек операндов, на тип возвращаемого значения, указанный вDescriptor, получив состояние выходных типов. -
Если метод является
protected, использование соответствует специальным правилам, регулирующим доступ к членамprotected(§4.10.1.8).
instructionIsTypeSafe(invokevirtual(CP), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
CP = method(MethodClassName, MethodName, Descriptor),
MethodName \= '<init>',
MethodName \= '<clinit>',
parseMethodDescriptor(Descriptor, OperandArgList, ReturnType),
reverse(OperandArgList, ArgList),
currentClassLoader(Environment, 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) при определении типов значений в стеке операндов.
Далее инициализируется анализатор потока данных. Для первой инструкции метода локальные переменные, представляющие параметры, изначально содержат значения типов, указанных в описателе типа метода; стек операндов пуст. Все остальные локальные переменные содержат недопустимое значение. Для других инструкций, которые ещё не были проанализированы, нет никакой информации о стеке операндов или локальных переменных.
Наконец, выполняется анализ потока данных. Для каждой инструкции устанавливается бит «изменён» (changed), указывающий, нужно ли на эту инструкцию обратить внимание. Изначально бит «изменён» установлен только для первой инструкции. Анализатор потока данных выполняет следующий цикл:
-
Выбрать инструкцию виртуальной машины Java, для которой установлен бит «изменён». Если инструкции, для которых установлен бит «изменён», больше нет, метод успешно проверен. В противном случае сбросить бит «изменён» для выбранной инструкции.
-
Смоделировать эффект инструкции на стек операндов и массив локальных переменных, выполнив следующее:
-
Если инструкция использует значения из стека операндов, убедитесь, что в стеке достаточно значений и что верхние значения в стеке имеют соответствующий тип. В противном случае проверка завершается неудачно.
-
Если инструкция использует локальную переменную, убедитесь, что указанная локальная переменная содержит значение соответствующего типа. В противном случае проверка завершается неудачно.
-
Если инструкция помещает значения в стек операндов, убедитесь, что в стеке операндов достаточно места для новых значений. Добавьте указанные типы в верхнюю часть моделируемого стека операндов.
-
Если инструкция изменяет локальную переменную, запишите, что локальная переменная теперь содержит новый тип.
-
-
Определить инструкции, которые могут следовать за текущей инструкцией. Последующие инструкции могут быть следующими:
-
Следующая инструкция, если текущая инструкция не является инструкцией безусловного перехода (например, goto, return или athrow). Проверка завершается неудачно, если есть возможность «выпасть» из последней инструкции метода.
-
Цель(и) условного или безусловного перехода или переключателя.
-
Любые обработчики исключений для этой инструкции.
-
-
Объединить состояние стека операндов и массива локальных переменных в конце выполнения текущей инструкции в каждую из последующих инструкций следующим образом:
-
Если это первый раз, когда посещается последующая инструкция, запишите, что состояние стека операндов и значения локальных переменных, рассчитанные на шаге 2, являются состоянием стека операндов и массива локальных переменных перед выполнением последующей инструкции. Установите бит «изменён» для последующей инструкции.
-
Если последующая инструкция была видна ранее, объедините состояние стека операндов и локальных переменных, рассчитанные на шаге 2, со значениями, которые уже там были. Установите бит «изменён», если произошло какое-либо изменение значений.
В особом случае перехода управления в обработчик исключений:
-
Запишите, что единственный объект, типа исключения, указанного обработчиком исключений, является состоянием стека операндов до выполнения последующей инструкции. Должно быть достаточно места в стеке операндов для этого единственного значения, как если бы инструкция его поместила.
-
Запишите, что значения локальных переменных, сразу перед шагом 2, являются состоянием массива локальных переменных перед выполнением последующей инструкции. Значения локальных переменных, рассчитанные на шаге 2, не имеют значения.
-
-
Продолжить с шага 1.
Для объединения двух стеков операндов количество значений в каждом стеке должно быть одинаковым. Типы значений в стеках также должны быть идентичны, за исключением того, что значения разных типов reference могут появляться в соответствующих местах на двух стеках. В этом случае объединённый стек операндов содержит тип ссылки, представляющий первый общий суперкласс, суперинтерфейс или массив супертипов двух типов. Такой тип reference всегда существует, потому что тип 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 Virtual Machine приведены в §3 (Компиляция для Java Virtual Machine).)
Метод инициализации экземпляра (§2.9.1) для класса myClass видит новый неинициализированный объект в качестве своего аргумента this в локальной переменной 0. Прежде чем этот метод вызовет другой метод инициализации экземпляра класса myClass или его непосредственного суперкласса для this, единственной операцией, которую метод может выполнить над this, является присваивание полей, объявленных в myClass.
При выполнении анализа потока данных для методов экземпляра верификатор инициализирует локальную переменную 0 объектом текущего класса или, для методов инициализации экземпляров, локальная переменная 0 содержит специальный тип, указывающий на неинициализированный объект. После вызова соответствующего метода инициализации экземпляров (из текущего класса или его непосредственного суперкласса) для этого объекта все экземпляры этого специального типа в модели верификатора стека операндов и в массиве локальных переменных заменяются типом текущего класса. Верификатор отклоняет код, который использует новый объект до его инициализации или инициализирует объект более одного раза. Кроме того, он гарантирует, что каждый нормальный возврат метода вызывал метод инициализации экземпляра либо в классе этого метода, либо в непосредственном суперклассе.
Аналогично, специальный тип создается и помещается в модель верификатора стека операндов в результате инструкции Java Virtual Machine new. Специальный тип указывает инструкцию, с помощью которой был создан экземпляр класса, и тип созданного неинициализированного экземпляра класса. Когда вызывается метод инициализации экземпляра, объявленный в классе неинициализированного экземпляра класса, для этого экземпляра класса, все экземпляры специального типа заменяются на целевой тип экземпляра класса. Это изменение типа может распространяться на последующие инструкции по мере выполнения анализа потока данных.
Номер инструкции необходимо хранить как часть специального типа, так как в стеке операндов может одновременно существовать несколько неинициализированных экземпляров одного класса. Например, последовательность инструкций Java Virtual Machine, которая реализует:
new InputStream(new Foo(), new InputStream("foo"))
может иметь два неинициализированных экземпляра InputStream в стеке операндов одновременно. Когда метод инициализации экземпляра вызывается для экземпляра класса, только те экземпляры специального типа в стеке операндов или в массиве локальных переменных, которые являются тем же самым объектом, что и экземпляр класса, заменяются.
Для реализации конструкции finally, компилятор языка программирования Java, генерирующий файлы с номером версии 50.0 или ниже, может использовать средства обработки исключений вместе с двумя специальными инструкциями: jsr («переход к подпрограмме») и ret («возврат из подпрограммы»). Блок finally компилируется как подпрограмма внутри кода метода виртуальной машины Java, очень похоже на код обработчика исключений. При выполнении инструкции jsr, вызывающей подпрограмму, она помещает адрес возврата, адрес инструкции после выполняемой инструкции jsr, на стек операндов как значение типа returnAddress. Код подпрограммы сохраняет адрес возврата в локальной переменной. В конце подпрограммы инструкция ret извлекает адрес возврата из локальной переменной и передает управление инструкции по адресу возврата.
Управление может быть передано блоку finally (подпрограмму finally можно вызвать) несколькими способами. Если блок finally завершается нормально, подпрограмма finally вызывается с помощью инструкции jsr перед вычислением следующего выражения. Оператор break или continue внутри блока finally, который передает управление за пределы блока finally, сначала выполняет jsr в код блока finally. Если блок finally выполняет оператор return, скомпилированный код выполняет следующее:
-
Сохраняет возвращаемое значение (если есть) в локальной переменной.
-
Выполняет jsr в код блока finally.
-
При возврате из блока finally возвращает сохранённое в локальной переменной значение.
Компилятор создаёт специальный обработчик исключений, который перехватывает любые исключения, брошенные блоком finally. Если исключение возникает в блоке finally, этот обработчик выполняет следующее:
-
Сохраняет исключение в локальной переменной.
-
Выполняет jsr в блок finally.
-
При возврате из блока finally повторно выбрасывает исключение.
Дополнительную информацию об реализации конструкции finally, см. §3.13.
Код блока finally представляет собой особую проблему для верификатора. Обычно, если к определённой инструкции можно получить доступ по нескольким путям, и определённая локальная переменная содержит несовместимые значения по этим путям, то локальная переменная становится непригодной для использования. Однако, блок finally может вызываться из нескольких разных мест, что приводит к различным обстоятельствам:
-
Вызов из обработчика исключений может иметь определённую локальную переменную, содержащую исключение.
-
Вызов для реализации return может иметь локальную переменную, содержащую возвращаемое значение.
-
Вызов из нижней части блока finally может иметь неопределённое значение в этой же локальной переменной.
Сам код блока 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.