Глава 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_InvokeDynamic_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.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
- if_acmp<cond>
- if_icmp<cond>
- if<cond>
- ifnonnull
- ifnull
- iinc
- iload, iload_<n>
- imul
- ineg
- instanceof
- invokedynamic
- invokeinterface
- invokespecial
- invokestatic
- invokevirtual
- ior
- irem
- ireturn
- ishl, ishr, iushr
- istore, istore_<n>
- isub
- ixor
- l2d, l2f, l2i
- ladd
- laload
- land
- lastore
- lcmp
-
В этом разделе описан формат файла class виртуальной машины Java. Каждый файл class содержит определение одного класса или интерфейса. Хотя класс или интерфейс не обязательно должен иметь внешнее представление, буквально содержащееся в файле (например, потому что класс генерируется загрузчиком классов), мы будем условно называть любое допустимое представление класса или интерфейса, как находящееся в формате файла class.
Файл class состоит из потока байтов по 8 бит. Все 16-битные, 32-битные и 64-битные величины создаются путём считывания соответственно двух, четырёх и восьми последовательных 8-битных байтов. Многобайтовые данные всегда хранятся в порядке big-endian, где сначала идут старшие байты. В платформе Java SE этот формат поддерживается интерфейсами java.io.DataInput и java.io.DataOutput, и классами, такими как java.io.DataInputStream и java.io.DataOutputStream.
В этом разделе определены собственные типы данных, представляющие данные файла class: типы u1, u2 и u4 представляют собой соответственно беззнаковое одно-, двух- или четырёхбайтовое значение. В платформе Java SE эти типы могут быть прочитаны методами, такими как readUnsignedByte, readUnsignedShort и readInt интерфейса java.io.DataInput.
В этом разделе формат файла class представлен с помощью псевдоструктур, написанных в нотации, подобной C. Для того чтобы избежать путаницы с полями классов и экземпляров классов и т. д., содержимое структур, описывающих формат файла class, называется элементами. Последовательные элементы хранятся в файле class последовательно, без заполнения или выравнивания.
Таблицы, состоящие из нуля или более элементов переменной длины, используются в нескольких структурах файла class. Хотя для ссылки на элементы таблицы используется синтаксис массива C, тот факт, что таблицы являются потоками структур переменной длины, означает, что невозможно напрямую преобразовать индекс таблицы в байтовый смещение в таблице.
Когда мы ссылаемся на структуру данных как на массив, она состоит из нуля или более смежных элементов фиксированной длины и может индексироваться как массив.
Ссылка на ASCII-символ в этом разделе должна интерпретироваться как соответствующий этому символу Юникод-код.
END_OF_DOCUMENT_MARKERФайл class состоит из одной структуры ClassFile:
ClassFile {
u4 magic;
u2 minor_version;
u2 major_version;
u2 constant_pool_count;
cp_info constant_pool[constant_pool_count-1];
u2 access_flags;
u2 this_class;
u2 super_class;
u2 interfaces_count;
u2 interfaces[interfaces_count];
u2 fields_count;
field_info fields[fields_count];
u2 methods_count;
method_info methods[methods_count];
u2 attributes_count;
attribute_info attributes[attributes_count];
}
Элементы структуры ClassFile следующие:
- magic
-
Элемент
magicсодержит магическое число, идентифицирующее формат файлаclass; его значение равно0xCAFEBABE. - minor_version, major_version
-
Значения элементов
minor_versionиmajor_versionпредставляют собой номера младшей и старшей версии этого файлаclass. Вместе номер старшей и младшей версии определяют версию формата файлаclass. Если файлclassимеет номер старшей версии M и номер младшей версии m, версия формата файлаclassобозначается как M.m. Таким образом, версии форматов файловclassмогут быть упорядочены лексикографически, например, 1.5 < 2.0 < 2.1.Реализация Java Virtual Machine может поддерживать формат файла
classверсии v тогда и только тогда, когда v находится в некотором непрерывном диапазоне Mi.0 ≤ v ≤ Mj.m. Уровень релиза платформы Java SE, которому соответствует реализация Java Virtual Machine, отвечает за определение диапазона.Реализация Java Virtual Machine Oracle в JDK версии 1.0.2 поддерживает версии формата файла
classот 45.0 до 45.3 включительно. Релизы JDK 1.1.* поддерживают версии формата файлаclassв диапазоне от 45.0 до 45.65535 включительно. Для k ≥ 2, релиз JDK 1.k поддерживает версии формата файлаclassв диапазоне от 45.0 до 44+k.0 включительно. - constant_pool_count
-
Значение элемента
constant_pool_countравно количеству записей в таблицеconstant_poolплюс один. Индексconstant_poolсчитается допустимым, если он больше нуля и меньшеconstant_pool_count, за исключением констант типаlongиdouble, указанных в §4.4.5. - constant_pool[]
-
constant_poolпредставляет собой таблицу структур (§4.4), представляющих различные строковые константы, имена классов и интерфейсов, имена полей и другие константы, на которые ссылается структураClassFileи её подструктуры. Формат каждой записи таблицыconstant_poolуказан в первом байте «тега».Таблица
constant_poolиндексируется от 1 доconstant_pool_count- 1. - access_flags
-
Значение элемента
access_flagsпредставляет собой маску флагов, используемых для обозначения разрешений доступа к этому классу или интерфейсу и его свойств. Интерпретация каждого флага, если он установлен, указана в таблице 4.1-A.Таблица 4.1-A. Модификаторы доступа к классу и свойствам
Имя флага Значение Интерпретация ACC_PUBLIC0x0001 Объявленный public; может быть доступен извне своего пакета.ACC_FINAL0x0010 Объявленный final; не допускаются подклассы.ACC_SUPER0x0020 Обращается со специальными методами суперкласса при вызове инструкцией invokespecial. ACC_INTERFACE0x0200 Интерфейс, а не класс. ACC_ABSTRACT0x0400 Объявленный abstract; не может быть инстанцирован.ACC_SYNTHETIC0x1000 Синтетический; отсутствует в исходном коде. ACC_ANNOTATION0x2000 Объявлен как тип аннотации. ACC_ENUM0x4000 Объявлен как тип enum.Интерфейс определяется установленным флагом
ACC_INTERFACE. Если флагACC_INTERFACEне установлен, этот файлclassопределяет класс, а не интерфейс.Если флаг
ACC_INTERFACEустановлен, также должен быть установлен флагACC_ABSTRACT, а флагиACC_FINAL,ACC_SUPERиACC_ENUMне должны быть установлены.Если флаг
ACC_INTERFACEне установлен, любой другой флаг в таблице 4.1-A может быть установлен, за исключениемACC_ANNOTATION. Однако, в таком файлеclassне должно быть установлены оба флагаACC_FINALиACC_ABSTRACT(JLS §8.1.1.2).Флаг
ACC_SUPERуказывает, какой из двух альтернативных семантик должен быть выражен инструкцией invokespecial (§invokespecial), если она встречается в этом классе или интерфейсе. Компиляторы в набор команд Java Virtual Machine должны устанавливать флагACC_SUPER. В Java SE 8 и выше Java Virtual Machine считает флагACC_SUPERустановленным в каждом файлеclass, независимо от фактического значения флага в файлеclassи версии файлаclass.Флаг
ACC_SUPERсуществует для обратной совместимости с кодом, скомпилированным более старыми компиляторами для языка программирования Java. В выпусках JDK до 1.0.2 компилятор генерировалaccess_flags, в котором флаг, теперь представляющийACC_SUPER, не имел назначенного значения, и реализация Java Virtual Machine Oracle игнорировала флаг, если он был установлен.Флаг
ACC_SYNTHETICуказывает, что этот класс или интерфейс был сгенерирован компилятором и не отображается в исходном коде.Тип аннотации должен иметь установленный флаг
ACC_ANNOTATION. Если установлен флагACC_ANNOTATION, также должен быть установлен флагACC_INTERFACE.Флаг
ACC_ENUMуказывает, что этот класс или его суперкласс объявлен как тип перечисления.Все биты элемента
access_flags, не назначенные в таблице 4.1-A, зарезервированы для будущего использования. Они должны быть установлены в ноль в сгенерированных файлахclassи должны игнорироваться реализациями Java Virtual Machine. - this_class
-
Значение элемента
this_classдолжно быть допустимым индексом в таблицеconstant_pool. Записьconstant_poolв этом индексе должна быть структуройCONSTANT_Class_info(§4.4.1), представляющей класс или интерфейс, определенный этим файломclass. - super_class
-
Для класса значение элемента
super_classдолжно быть либо нулевым, либо допустимым индексом в таблицеconstant_pool. Если значение элементаsuper_classотлично от нуля, записьconstant_poolв этом индексе должна быть структуройCONSTANT_Class_info, представляющей непосредственный суперкласс класса, определенного этим файломclass. Ни непосредственный суперкласс, ни любой из его суперклассов не могут иметь флагACC_FINAL, установленный в элементеaccess_flagsего структурыClassFile.Если значение элемента
super_classравно нулю, этот файлclassдолжен представлять классObject, единственный класс или интерфейс без непосредственного суперкласса.Для интерфейса значение элемента
super_classвсегда должно быть допустимым индексом в таблицеconstant_pool. Записьconstant_poolв этом индексе должна быть структуройCONSTANT_Class_info, представляющей классObject. - interfaces_count
-
Значение элемента
interfaces_countуказывает количество непосредственных суперинтерфейсов этого класса или интерфейса. - interfaces[]
-
Каждое значение в массиве
interfacesдолжно быть допустимым индексом в таблицеconstant_pool. Записьconstant_poolв каждом значенииinterfaces[i], где 0 ≤ i <interfaces_count, должна быть структуройCONSTANT_Class_info, представляющей интерфейс, являющийся непосредственным суперинтерфейсом этого класса или интерфейса, в порядке следования слева направо, указанном в источнике для типа. - fields_count
-
Значение элемента
fields_countзадает количество структурfield_infoв таблицеfields. Структурыfield_infoпредставляют собой все поля, как переменные класса, так и переменные экземпляра, объявленные этим классом или типом интерфейса. - fields[]
-
Каждое значение в таблице
fieldsдолжно быть структуройfield_info(§4.5), дающей полное описание поля в этом классе или интерфейсе. Таблицаfieldsвключает только те поля, которые объявлены этим классом или интерфейсом. Она не включает элементы, представляющие поля, унаследованные от суперклассов или суперинтерфейсов. - methods_count
-
Значение элемента
methods_countзадает количество структурmethod_infoв таблицеmethods. - methods[]
-
Каждое значение в таблице
methodsдолжно быть структуройmethod_info(§4.6), предоставляющей полное описание метода в этом классе или интерфейсе. Если ни один из флаговACC_NATIVEиACC_ABSTRACTне установлен в элементеaccess_flagsструктурыmethod_info, инструкции Java Virtual Machine для реализации метода также предоставляются.Структуры
method_infoпредставляют все методы, объявленные этим типом класса или интерфейса, включая методы экземпляра, методы класса, методы инициализации экземпляра (§2.9) и любой метод инициализации класса или интерфейса (§2.9). Таблицаmethodsне включает элементы, представляющие методы, унаследованные от суперклассов или суперинтерфейсов. - attributes_count
-
Значение элемента
attributes_countуказывает количество атрибутов в таблицеattributesэтого класса. - attributes[]
-
Каждое значение в таблице
attributesдолжно быть структуройattribute_info(§4.7).Атрибуты, определённые в этом спецификации как присутствующие в таблице
attributesструктурыClassFile, перечислены в таблице 4.7-C.Правила, касающиеся атрибутов, определённых для присутствия в таблице
attributesструктурыClassFile, приведены в §4.7.Правила, касающиеся не предопределённых атрибутов в таблице
attributesструктурыClassFile, приведены в §4.7.1.
Имена классов и интерфейсов, которые появляются в структурах файлов 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 символов < или > (то есть левой угловой скобки или правой угловой скобки).
Обратите внимание, что имя поля или имя метода интерфейса может быть <init> или <clinit>, но ни одна инструкция вызова метода не может ссылаться на <clinit>, и только инструкция invokespecial (§invokespecial) может ссылаться на <init>.
A дескриптор — это строка, представляющая тип поля или метода. Дескрипторы представлены в формате файла class с использованием модифицированных строк UTF-8 (§4.4.7) и поэтому могут быть взяты, если не ограничены, из всего кодового пространства Юникода.
Дескрипторы задаются с помощью грамматики. Грамматика — это набор правил, описывающих, как последовательности символов могут образовывать синтаксически корректные дескрипторы различных типов. Терминальные символы грамматики показаны в шрифте fixed width. Нетерминальные символы показаны в шрифте курсив. Определение нетерминала вводится именем определяемого нетерминала, за которым следует двоеточие. Затем следуют одно или несколько альтернативных определений для нетерминала на последующих строках.
Синтаксис {x} в правой части правила означает ноль или более вхождений x.
Фраза (one of) в правой части правила означает, что каждый из терминальных символов в следующей строке или строках является альтернативным определением.
Дескриптор поля представляет тип класса, экземпляра или локальной переменной.
B C D F I J S Z L ClassName ;
Символы BaseType, L и ; ObjectType, и [ ArrayType — все ASCII-символы.
ClassName представляет имя двоичного класса или интерфейса, закодированное во внутреннем формате (§4.2.1).
Интерпретация дескрипторов полей как типов показана в таблице 4.3-A.
Дескриптор поля, представляющий тип массива, действителен только в том случае, если он представляет тип с 255 или меньше измерениями.
Таблица 4.3-A. Интерпретация дескрипторов полей
| FieldType термин | Тип | Интерпретация |
|---|---|---|
B | byte | знаковое байтовое значение |
C | char | код точки Юникода в основном многоязычном плане, закодированный с помощью UTF-16 |
D | double | значение с плавающей точкой двойной точности |
F | float | значение с плавающей точкой одинарной точности |
I | int | целое число |
J | long | длинное целое число |
L ClassName ;
| reference | экземпляр класса ClassName |
S | short | знаковое короткое целое число |
Z | boolean |
true или false
|
[ | reference | одно измерение массива |
Дескриптор поля для переменной экземпляра типа int просто I.
Дескриптор поля для переменной экземпляра типа Object — Ljava/lang/Object;. Обратите внимание, что используется внутренний формат двоичного имени класса Object.
Дескриптор поля для переменной экземпляра типа многомерного массива double[][][] — [[[D.
Дескриптор метода содержит ноль или более дескрипторов параметров, представляющих типы параметров, которые принимает метод, и дескриптор возвращаемого значения, представляющий тип возвращаемого значения (если оно есть).
VСимвол V указывает, что метод не возвращает значения (его результат — void).
Дескриптор метода для метода:
Object m(int i, double d, Thread t) {...}
равен:
(IDLjava/lang/Thread;)Ljava/lang/Object;
Обратите внимание, что используются внутренние формы двоичных имён Thread и Object.
Дескриптор метода действителен только в том случае, если он представляет параметры метода с общей длиной 255 или меньше, где эта длина включает вклад this в случае вызова метода экземпляра или интерфейса. Общая длина вычисляется путём суммирования вкладов отдельных параметров, где параметр типа long или double вносит два единицы в длину, а параметр любого другого типа — одну единицу.
Дескриптор метода одинаков для метода класса или метода экземпляра. Хотя метод экземпляра получает this, ссылку на объект, к которому вызывается метод, помимо своих аргументов, этот факт не отражён в дескрипторе метода. Ссылка на this передаётся неявно инструкциями Java Virtual Machine, которые вызывают методы экземпляра (§2.6.1, §4.11).
Инструкции виртуальной машины Java не полагаются на расположение в памяти классов, интерфейсов, экземпляров классов или массивов во время выполнения. Вместо этого инструкции ссылаются на символическую информацию в таблице constant_pool.
Все записи в таблице constant_pool имеют следующий общий формат:
cp_info {
u1 tag;
u1 info[];
}
Каждый элемент в таблице constant_pool должен начинаться с 1-байтового тега, указывающего вид записи cp_info. Содержимое массива info изменяется в зависимости от значения tag. Действительные теги и их значения перечислены в таблице 4.4-А. За байтом тега должны следовать два или более байта, содержащие информацию о конкретной константе. Формат дополнительной информации зависит от значения тега.
Таблица 4.4-А. Теги пула констант
| Тип константы | Значение |
|---|---|
CONSTANT_Class | 7 |
CONSTANT_Fieldref | 9 |
CONSTANT_Methodref | 10 |
CONSTANT_InterfaceMethodref | 11 |
CONSTANT_String | 8 |
CONSTANT_Integer | 3 |
CONSTANT_Float | 4 |
CONSTANT_Long | 5 |
CONSTANT_Double | 6 |
CONSTANT_NameAndType | 12 |
CONSTANT_Utf8 | 1 |
CONSTANT_MethodHandle | 15 |
CONSTANT_MethodType | 16 |
CONSTANT_InvokeDynamic | 18 |
Структура 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), представляющей тип класса или интерфейса, содержащего поле или метод.Элемент
class_indexструктурыCONSTANT_Methodref_infoдолжен быть типом класса, а не интерфейса.Элемент
class_indexструктурыCONSTANT_InterfaceMethodref_infoдолжен быть типом интерфейса.Элемент
class_indexструктурыCONSTANT_Fieldref_infoможет быть либо типом класса, либо типом интерфейса. - name_and_type_index
-
Значение элемента
name_and_type_indexдолжно быть допустимым индексом в таблицеconstant_pool. Записьconstant_poolв этом индексе должна быть структуройCONSTANT_NameAndType_info(§4.4.6). Эта запись указывает имя и описатель поля или метода.В структуре
CONSTANT_Fieldref_infoуказанный описатель должен быть описателем поля (§4.3.2). В противном случае, указанный описатель должен быть описателем метода (§4.3.3).Если имя метода в структуре
CONSTANT_Methodref_infoначинается с '<' ('\u003c'), то имя должно быть специальным именем<init>, представляющим метод инициализации экземпляра (§2.9). Тип возвращаемого значения такого метода должен бытьvoid.
Структура CONSTANT_String_info используется для представления постоянных объектов типа String:
CONSTANT_String_info {
u1 tag;
u2 string_index;
}
Элементы структуры CONSTANT_String_info следующие:
- tag
-
Элемент
tagструктурыCONSTANT_String_infoимеет значениеCONSTANT_String(8). - string_index
-
Значение элемента
string_indexдолжно быть допустимым индексом в таблицеconstant_pool. Записьconstant_poolв этом индексе должна быть структуройCONSTANT_Utf8_info(§4.4.7), представляющей последовательность точек кодирования Юникода, для которой должен быть инициализирован объектString.
Структуры CONSTANT_Integer_info и CONSTANT_Float_info представляют числовые константы 4 байта (int и float):
CONSTANT_Integer_info {
u1 tag;
u4 bytes;
}
CONSTANT_Float_info {
u1 tag;
u4 bytes;
}
Элементы этих структур следующие:
- tag
-
Элемент
tagструктурыCONSTANT_Integer_infoимеет значениеCONSTANT_Integer(3).Элемент
tagструктурыCONSTANT_Float_infoимеет значениеCONSTANT_Float(4). - bytes
-
Элемент
bytesструктурыCONSTANT_Integer_infoпредставляет значение константыint. Байты значения хранятся в формате big-endian (старший байт вначале).Элемент
bytesструктурыCONSTANT_Float_infoпредставляет значение константыfloatв формате одинарной точности с плавающей запятой IEEE 754 (§2.3.2). Байты представления одинарной точности хранятся в формате big-endian (старший байт вначале).Значение, представленное структурой
CONSTANT_Float_info, определяется следующим образом. Байты значения сначала преобразуются в целое числоintконстанты bits. Затем:-
Если bits равно
0x7f800000, то значениеfloatбудет положительной бесконечностью. -
Если bits равно
0xff800000, то значениеfloatбудет отрицательной бесконечностью. -
Если bits находится в диапазоне от
0x7f800001до0x7fffffffили от0xff800001до0xffffffff, то значениеfloatбудет NaN. -
Во всех других случаях, пусть
s,eиmбудут тремя значениями, которые могут быть вычислены из bits:int s = ((bits >> 31) == 0) ? 1 : -1; int e = ((bits >> 23) & 0xff); int m = (e == 0) ? (bits & 0x7fffff) << 1 : (bits & 0x7fffff) | 0x800000;
Тогда значение
floatравно результату математического выраженияs · m · 2e-150. -
Структуры CONSTANT_Long_info и CONSTANT_Double_info представляют числовые константы 8 байт (long и double):
CONSTANT_Long_info {
u1 tag;
u4 high_bytes;
u4 low_bytes;
}
CONSTANT_Double_info {
u1 tag;
u4 high_bytes;
u4 low_bytes;
}
Все константы 8 байт занимают две записи в таблице constant_pool файла class. Если структура CONSTANT_Long_info или CONSTANT_Double_info является элементом в таблице constant_pool с индексом n, то следующий используемый элемент в пуле расположен по индексу n+2. Индекс constant_pool n+1 должен быть валиден, но считается неиспользуемым.
В ретроспективе, сделать константы 8 байт занимающими две записи в пуле констант было плохим решением.
Элементы этих структур следующие:
- tag
-
Элемент
tagструктурыCONSTANT_Long_infoимеет значениеCONSTANT_Long(5).Элемент
tagструктурыCONSTANT_Double_infoимеет значениеCONSTANT_Double(6). - high_bytes, low_bytes
-
Незнаковые элементы
high_bytesиlow_bytesструктурыCONSTANT_Long_infoвместе представляют значение константыlong((long) high_bytes << 32) + low_bytesгде байты каждого из
high_bytesиlow_bytesхранятся в формате big-endian (старший байт вначале).Элементы
high_bytesиlow_bytesструктурыCONSTANT_Double_infoвместе представляют значение константыdoubleв формате двойной точности с плавающей запятой IEEE 754 (§2.3.2). Байты каждого элемента хранятся в формате big-endian (старший байт вначале).Значение, представленное структурой
CONSTANT_Double_info, определяется следующим образом. Элементыhigh_bytesиlow_bytesпреобразуются в константу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;
Затем значение с плавающей запятой равно результату математического выражения
doubles · m · 2e-1075. -
Структура CONSTANT_NameAndType_info используется для представления поля или метода, не указывая, к какому типу класса или интерфейса оно относится:
CONSTANT_NameAndType_info {
u1 tag;
u2 name_index;
u2 descriptor_index;
}
Элементы структуры CONSTANT_NameAndType_info следующие:
- tag
-
Элемент
tagструктурыCONSTANT_NameAndType_infoимеет значениеCONSTANT_NameAndType(12). - name_index
-
Значение элемента
name_indexдолжно быть допустимым индексом в таблицеconstant_pool. Запись в этой таблице по этому индексу должна быть структуройCONSTANT_Utf8_info(§4.4.7), представляющей либо специальное имя метода<init>(§2.9), либо допустимое неопределённое имя поля или метода (§4.2.2). - descriptor_index
-
Значение элемента
descriptor_indexдолжно быть допустимым индексом в таблицеconstant_pool. Запись в этой таблице по этому индексу должна быть структуройCONSTANT_Utf8_info(§4.4.7), представляющей допустимый дескриптор поля или метода (§4.3.2, §4.3.3).
Структура CONSTANT_Utf8_info используется для представления константных строковых значений:
CONSTANT_Utf8_info {
u1 tag;
u2 length;
u1 bytes[length];
}
Элементы структуры CONSTANT_Utf8_info следующие:
- tag
-
Элемент
tagструктурыCONSTANT_Utf8_infoимеет значениеCONSTANT_Utf8(1). - length
-
Значение элемента
lengthуказывает количество байтов в массивеbytes(а не длину результирующей строки). - bytes[]
-
Массив
bytesсодержит байты строки.Ни один байт не может иметь значение
(byte)0.Ни один байт не может лежать в диапазоне от
(byte)0xf0до(byte)0xff.
Контент строки закодирован в модифицированной кодировке UTF-8. Модифицированные UTF-8 строки закодированы так, что последовательности кодовых точек, содержащие только не нулевые ASCII-символы, могут быть представлены с использованием только 1 байта на каждую кодовую точку, но все кодовые точки в кодовой странице Unicode могут быть представлены. Модифицированные UTF-8 строки не завершаются нулём. Кодировка следующая:
-
Кодовые точки в диапазоне от '
\u0001' до '\u007F' представлены одним байтом:Таблица 4.4.
0 биты 6-0 7 бит данных в байте дают значение представляемой кодовой точки.
-
Нулевая кодовая точка ('
\u0000') и кодовые точки в диапазоне от '\u0080' до '\u07FF' представлены парой байтовxиy:Таблица 4.5.
x:Таблица 4.6.
1 1 0 биты 10-6
y:Таблица 4.7.
1 0 биты 5-0
Два байта представляют кодовую точку со значением:
((x & 0x1f) << 6) + (y & 0x3f) -
Кодовые точки в диапазоне от '
\u0800' до '\uFFFF' представлены 3 байтамиx,yиz:Таблица 4.8.
x:Таблица 4.9.
1 1 1 0 биты 15-12
y:Таблица 4.10.
1 0 биты 11-6
z:Таблица 4.11.
1 0 биты 5-0
Три байта представляют кодовую точку со значением:
((x & 0xf) << 12) + ((y & 0x3f) << 6) + (z & 0x3f)
-
Символы с кодовыми точками выше U+FFFF (так называемые дополнительные символы) представляются путем отдельного кодирования двух суррогатных кодовых единиц их представления UTF-16. Каждая суррогатная кодовая единица представляется тремя байтами. Это означает, что дополнительные символы представляются шестью байтами,
u,v,w,x,yиz:Таблица 4.12.
u:Таблица 4.13.
1 1 1 0 1 1 0 1
v:Таблица 4.14.
1 0 1 0 (биты 20-16)-1
w:Таблица 4.15.
1 0 биты 15-10
x:Таблица 4.16.
1 1 1 0 1 1 0 1
y:Таблица 4.17.
1 0 1 1 биты 9-6
z:Таблица 4.18.
1 0 биты 5-0
Шесть байтов представляют кодовую точку со значением:
0x10000 + ((v & 0x0f) << 16) + ((w & 0x3f) << 10) + ((y & 0x0f) << 6) + (z & 0x3f)
Байты многобайтовых символов хранятся в файле class в формате big-endian (старший байт впереди).
Существует два отличия между этим форматом и "стандартным" форматом UTF-8. Во-первых, нулевой символ (char)0 кодируется в формате 2 байта, а не 1, таким образом, чтобы модифицированные строки UTF-8 никогда не имели вложенных нулей. Во-вторых, используются только форматы стандартного UTF-8 с 1, 2 и 3 байтами. Виртуальная машина Java не распознает четырехбайтовый формат стандартного UTF-8; она использует собственный формат с двумя и тремя байтами.
Для получения дополнительной информации о стандартном формате UTF-8 см. раздел 3.9 Формы кодирования Unicode в Стандарте Unicode, версия 6.0.0.
Структура CONSTANT_MethodHandle_info используется для представления дескриптора метода:
CONSTANT_MethodHandle_info {
u1 tag;
u1 reference_kind;
u2 reference_index;
}
Элементы структуры CONSTANT_MethodHandle_info следующие:
- tag
-
Элемент
tagструктурыCONSTANT_MethodHandle_infoимеет значениеCONSTANT_MethodHandle(15). - reference_kind
-
Значение элемента
reference_kindдолжно находиться в диапазоне от 1 до 9. Значение обозначает тип этого дескриптора метода, который характеризует его поведение в байткоде (§5.4.3.5). - reference_index
-
Значение элемента
reference_indexдолжно быть допустимым индексом в таблицеconstant_pool. Записьconstant_poolв этой таблице по этому индексу должна быть следующей:-
Если значение элемента
reference_kindравно 1 (REF_getField), 2 (REF_getStatic), 3 (REF_putField) или 4 (REF_putStatic), то записьconstant_poolв таблице по этому индексу должна быть структуройCONSTANT_Fieldref_info(§4.4.2), представляющей поле, для которого должен быть создан дескриптор метода. -
Если значение элемента
reference_kindравно 5 (REF_invokeVirtual) или 8 (REF_newInvokeSpecial), то записьconstant_poolв таблице по этому индексу должна быть структуройCONSTANT_Methodref_info(§4.4.2), представляющей метод или конструктор класса (§2.9), для которого должен быть создан дескриптор метода. -
Если значение элемента
reference_kindравно 6 (REF_invokeStatic) или 7 (REF_invokeSpecial), то, если номер версии файлаclassменьше 52.0, записьconstant_poolв таблице по этому индексу должна быть структуройCONSTANT_Methodref_info, представляющей метод класса; если номер версии файлаclassравен 52.0 или выше, записьconstant_poolв таблице по этому индексу должна быть либо структуройCONSTANT_Methodref_info, либо структуройCONSTANT_InterfaceMethodref_info(§4.4.2), представляющей метод класса или интерфейса, для которого должен быть создан дескриптор метода. -
Если значение элемента
reference_kindравно 9 (REF_invokeInterface), то записьconstant_poolв таблице по этому индексу должна быть структуройCONSTANT_InterfaceMethodref_info, представляющей метод интерфейса, для которого должен быть создан дескриптор метода.
Если значение элемента
reference_kindравно 5 (REF_invokeVirtual), 6 (REF_invokeStatic), 7 (REF_invokeSpecial) или 9 (REF_invokeInterface), имя метода, представленного структуройCONSTANT_Methodref_infoилиCONSTANT_InterfaceMethodref_info, не должно быть<init>или<clinit>.Если значение равно 8 (
REF_newInvokeSpecial), имя метода, представленного структуройCONSTANT_Methodref_info, должно быть<init>. -
Структура CONSTANT_MethodType_info используется для представления типа метода:
CONSTANT_MethodType_info {
u1 tag;
u2 descriptor_index;
}
Элементы структуры CONSTANT_MethodType_info следующие:
- tag
-
Элемент
tagструктурыCONSTANT_MethodType_infoимеет значениеCONSTANT_MethodType(16). - descriptor_index
-
Значение элемента
descriptor_indexдолжно быть корректным индексом в таблицеconstant_pool. Записьconstant_poolв этом индексе должна быть структуройCONSTANT_Utf8_info(§4.4.7), представляющей дескриптор метода (§4.3.3).
Структура CONSTANT_InvokeDynamic_info используется инструкцией invokedynamic (§invokedynamic) для указания метода-загрузчика, имени динамического вызова, типов аргументов и возвращаемого значения вызова и, необязательно, последовательности дополнительных констант, называемых статическими аргументами метода-загрузчика.
CONSTANT_InvokeDynamic_info {
u1 tag;
u2 bootstrap_method_attr_index;
u2 name_and_type_index;
}
Элементы структуры CONSTANT_InvokeDynamic_info следующие:
- tag
-
Элемент
tagструктурыCONSTANT_InvokeDynamic_infoимеет значениеCONSTANT_InvokeDynamic(18). - bootstrap_method_attr_index
-
Значение элемента
bootstrap_method_attr_indexдолжно быть корректным индексом в массивеbootstrap_methodsтаблицы метода-загрузчика (§4.7.23) этого файлаclass. - name_and_type_index
-
Значение элемента
name_and_type_indexдолжно быть корректным индексом в таблицеconstant_pool. Записьconstant_poolв этом индексе должна быть структуройCONSTANT_NameAndType_info(§4.4.6), представляющей имя метода и дескриптор метода (§4.3.3).
Каждое поле описывается структурой 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; доступно только внутри определяющего класса.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указывает, что это поле используется для хранения элемента перечислимого типа.Все биты элемента
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) и метод инициализации класса или интерфейса (§2.9), описывается структурой method_info.
В одном файле class не может быть двух методов с одинаковым именем и дескриптором (§4.3.3).
Структура имеет следующий формат:
method_info {
u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
}
Элементы структуры method_info следующие:
- access_flags
-
Значение элемента
access_flags— маска флагов, используемая для обозначения разрешений доступа и свойств этого метода. Интерпретация каждого флага, если он установлен, указана в Таблице 4.6-A.Таблица 4.6-A. Флаги доступа и свойств методов
Название флага Значение Интерпретация ACC_PUBLIC0x0001 Объявлен public; может быть доступен извне своего пакета.ACC_PRIVATE0x0002 Объявлен private; доступен только внутри определяющего класса.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 Объявлен strictfp; режим чисел с плавающей точкой — FP-strict.ACC_SYNTHETIC0x1000 Объявлен синтетическим; отсутствует в исходном коде. Методы классов могут иметь любые флаги в Таблице 4.6-A. Однако каждый метод класса может иметь не более одного из его флагов
ACC_PUBLIC,ACC_PRIVATEиACC_PROTECTED(JLS §8.4.3).Методы интерфейсов могут иметь любые флаги в Таблице 4.6-A, кроме
ACC_PROTECTED,ACC_FINAL,ACC_SYNCHRONIZEDиACC_NATIVE(JLS §9.4). В файлеclassс номером версии меньше 52.0 каждый метод интерфейса должен иметь установленные флагиACC_PUBLICиACC_ABSTRACT; в файлеclassс номером версии 52.0 или выше каждый метод интерфейса должен иметь ровно один из флаговACC_PUBLICиACC_PRIVATE.Если у метода класса или интерфейса установлен флаг
ACC_ABSTRACT, он не должен иметь установленных флаговACC_PRIVATE,ACC_STATIC,ACC_FINAL,ACC_SYNCHRONIZED,ACC_NATIVEилиACC_STRICT.Каждый метод инициализации экземпляра (§2.9) может иметь не более одного из флагов
ACC_PUBLIC,ACC_PRIVATEиACC_PROTECTED, а также флагиACC_VARARGS,ACC_STRICTиACC_SYNTHETIC, но не должен иметь других флагов в Таблице 4.6-A.Методы инициализации класса и интерфейса вызываются виртуальной машиной Java неявно. Значение их элемента
access_flagsигнорируется, за исключением установки флагаACC_STRICT.Флаг
ACC_BRIDGEиспользуется для указания метода-мостика, сгенерированного компилятором языка программирования Java.Флаг
ACC_VARARGSуказывает, что этот метод принимает переменное количество аргументов на уровне исходного кода. Метод, объявленный с переменным числом аргументов, должен компилироваться с флагомACC_VARARGS, установленным в 1. Все остальные методы должны компилироваться с флагомACC_VARARGS, установленным в 0.Флаг
ACC_SYNTHETICуказывает, что этот метод был сгенерирован компилятором и не отображается в исходном коде, если только это не один из методов, указанных в §4.7.8.Все биты элемента
access_flags, не назначенные в Таблице 4.6-A, зарезервированы для будущего использования. Они должны быть установлены в ноль в сгенерированных файлахclassи должны игнорироваться реализациями виртуальной машины Java. - name_index
-
Значение элемента
name_indexдолжно быть допустимым индексом в таблицеconstant_pool. Элементconstant_poolв этом индексе должен быть структуройCONSTANT_Utf8_info(§4.4.7), представляющей либо одно из специальных имён методов<init>или<clinit>(§2.9), либо допустимое неоквалифицированное имя, обозначающее метод (§4.2.2). - descriptor_index
-
Значение элемента
descriptor_indexдолжно быть допустимым индексом в таблицеconstant_pool. Элементconstant_poolв этом индексе должен быть структуройCONSTANT_Utf8_info, представляющей допустимый дескриптор метода (§4.3.3).В будущей редакции этого спецификации может потребоваться, чтобы последний дескриптор параметра дескриптора метода был типом массива, если флаг
ACC_VARARGSустановлен в элементеaccess_flags. - attributes_count
-
Значение элемента
attributes_countуказывает количество дополнительных атрибутов этого метода. - attributes[]
-
Каждое значение таблицы
attributesдолжно быть структуройattribute_info(§4.7).Метод может иметь любое количество дополнительных атрибутов.
Атрибуты, определённые в этой спецификации как появляющиеся в таблице
attributesструктурыmethod_info, перечислены в Таблице 4.7-C.Правила, касающиеся атрибутов, определённых для появления в таблице
attributesструктурыmethod_info, приведены в §4.7.Правила, касающиеся неопределённых атрибутов в таблице
attributesструктурыmethod_info, приведены в §4.7.1.
Атрибуты используются в структурах ClassFile, field_info, method_info и Code_attribute формата файла class (§4.1, §4.5, §4.6, §4.7.3).
Все атрибуты имеют следующий общий формат:
attribute_info {
u2 attribute_name_index;
u4 attribute_length;
u1 info[attribute_length];
}
Для всех атрибутов attribute_name_index должен быть допустимым 16-битным беззнаковым индексом в пуле констант класса. Элемент пула констант constant_pool по адресу attribute_name_index должен быть структурой CONSTANT_Utf8_info (§4.4.7), представляющей имя атрибута. Значение элемента attribute_length указывает длину последующей информации в байтах. Длина не включает начальные шесть байтов, содержащих элементы attribute_name_index и attribute_length.
Данным спецификацией определены 23 атрибута. Они представлены трижды для удобства навигации:
-
Таблица 4.7-A упорядочена по номерам разделов атрибутов в данной главе. Каждый атрибут сопровождается первой версией формата файла
class, в котором он был определён, и соответствующей версией платформы Java SE. (Для старых версий файловclassиспользуется версия выпуска JDK вместо версии платформы Java SE). -
Таблица 4.7-B упорядочена по первой версии формата файла
class, в котором каждый атрибут был определён. -
Таблица 4.7-C упорядочена по расположению в файле
class, где каждый атрибут должен быть определён.
В контексте их использования в этой спецификации, то есть в таблицах attributes структур файлов class, в которых они появляются, имена этих предварительно определённых атрибутов зарезервированы.
Любые условия наличия предварительно определённого атрибута в таблице attributes указаны явно в разделе, описывающем атрибут. Если условия не указаны, то атрибут может появляться любое количество раз в таблице attributes.
Предварительно определённые атрибуты сгруппированы в три группы в соответствии с их назначением:
-
Пять атрибутов критически важны для корректной интерпретации файла
classвиртуальной машиной Java:-
ConstantValue -
Code -
StackMapTable -
Exceptions -
BootstrapMethods
В файле
classверсии V каждый из этих атрибутов должен распознаваться и корректно считываться реализацией виртуальной машины Java, если реализация распознаёт файлыclassверсии V, и V не меньше версии, в которой атрибут был впервые определён, и атрибут появляется в месте, где он должен появиться. -
-
Двенадцать атрибутов критически важны для корректной интерпретации файла
classбиблиотеками классов платформы Java SE:-
InnerClasses -
EnclosingMethod -
Synthetic -
Signature -
RuntimeVisibleAnnotations -
RuntimeInvisibleAnnotations -
RuntimeVisibleParameterAnnotations -
RuntimeInvisibleParameterAnnotations -
RuntimeVisibleTypeAnnotations -
RuntimeInvisibleTypeAnnotations -
AnnotationDefault -
MethodParameters
Каждый из этих атрибутов в файле
classверсии V должен быть распознан и корректно прочитан реализацией библиотек классов платформы Java SE, если реализация распознает файлыclassверсии V, и V не меньше версии, в которой атрибут был впервые определён, и атрибут появляется в месте, где он должен появиться. -
-
Шесть атрибутов не являются критическими для корректной интерпретации файла
classни виртуальной машиной Java, ни библиотеками классов платформы Java SE, но полезны для инструментов:-
SourceFile -
SourceDebugExtension -
LineNumberTable -
LocalVariableTable -
LocalVariableTypeTable -
Deprecated
Использование этих атрибутов реализацией виртуальной машины Java или библиотеками классов платформы Java SE является необязательным. Реализация может использовать информацию, содержащуюся в этих атрибутах, или же должна игнорировать эти атрибуты без ошибок.
-
Таблица 4.7-A. Предопределённые class атрибуты файлов (по разделам)
| Атрибут | Раздел |
class файл | Java SE |
|---|---|---|---|
ConstantValue | §4.7.2 | 45.3 | 1.0.2 |
Code | §4.7.3 | 45.3 | 1.0.2 |
StackMapTable | §4.7.4 | 50.0 | 6 |
Exceptions | §4.7.5 | 45.3 | 1.0.2 |
InnerClasses | §4.7.6 | 45.3 | 1.1 |
EnclosingMethod | §4.7.7 | 49.0 | 5.0 |
Synthetic | §4.7.8 | 45.3 | 1.1 |
Signature | §4.7.9 | 49.0 | 5.0 |
SourceFile | §4.7.10 | 45.3 | 1.0.2 |
SourceDebugExtension | §4.7.11 | 49.0 | 5.0 |
LineNumberTable | §4.7.12 | 45.3 | 1.0.2 |
LocalVariableTable | §4.7.13 | 45.3 | 1.0.2 |
LocalVariableTypeTable | §4.7.14 | 49.0 | 5.0 |
Deprecated | §4.7.15 | 45.3 | 1.1 |
RuntimeVisibleAnnotations | §4.7.16 | 49.0 | 5.0 |
RuntimeInvisibleAnnotations | §4.7.17 | 49.0 | 5.0 |
RuntimeVisibleParameterAnnotations | §4.7.18 | 49.0 | 5.0 |
RuntimeInvisibleParameterAnnotations | §4.7.19 | 49.0 | 5.0 |
RuntimeVisibleTypeAnnotations | §4.7.20 | 52.0 | 8 |
RuntimeInvisibleTypeAnnotations | §4.7.21 | 52.0 | 8 |
AnnotationDefault | §4.7.22 | 49.0 | 5.0 |
BootstrapMethods | §4.7.23 | 51.0 | 7 |
MethodParameters | §4.7.24 | 52.0 | 8 |
Таблица 4.7-B. Предопределённые class атрибуты файлов (по версии class файла)
| Атрибут |
class файл | Java SE | Раздел |
|---|---|---|---|
ConstantValue | 45.3 | 1.0.2 | §4.7.2 |
Code | 45.3 | 1.0.2 | §4.7.3 |
Exceptions | 45.3 | 1.0.2 | §4.7.5 |
SourceFile | 45.3 | 1.0.2 | §4.7.10 |
LineNumberTable | 45.3 | 1.0.2 | §4.7.12 |
LocalVariableTable | 45.3 | 1.0.2 | §4.7.13 |
InnerClasses | 45.3 | 1.1 | §4.7.6 |
Synthetic | 45.3 | 1.1 | §4.7.8 |
Deprecated | 45.3 | 1.1 | §4.7.15 |
EnclosingMethod | 49.0 | 5.0 | §4.7.7 |
Signature | 49.0 | 5.0 | §4.7.9 |
SourceDebugExtension | 49.0 | 5.0 | §4.7.11 |
LocalVariableTypeTable | 49.0 | 5.0 | §4.7.14 |
RuntimeVisibleAnnotations | 49.0 | 5.0 | §4.7.16 |
RuntimeInvisibleAnnotations | 49.0 | 5.0 | §4.7.17 |
RuntimeVisibleParameterAnnotations | 49.0 | 5.0 | §4.7.18 |
RuntimeInvisibleParameterAnnotations | 49.0 | 5.0 | §4.7.19 |
AnnotationDefault | 49.0 | 5.0 | §4.7.22 |
StackMapTable | 50.0 | 6 | §4.7.4 |
BootstrapMethods | 51.0 | 7 | §4.7.23 |
RuntimeVisibleTypeAnnotations | 52.0 | 8 | §4.7.20 |
RuntimeInvisibleTypeAnnotations | 52.0 | 8 | §4.7.21 |
MethodParameters | 52.0 | 8 | §4.7.24 |
Таблица 4.7-C. Предопределённые class атрибуты файлов (по расположению)
| Атрибут | Расположение |
class файла |
|---|---|---|
SourceFile | ClassFile | 45.3 |
InnerClasses | ClassFile | 45.3 |
EnclosingMethod | ClassFile | 49.0 |
SourceDebugExtension | ClassFile | 49.0 |
BootstrapMethods | ClassFile | 51.0 |
ConstantValue | field_info | 45.3 |
Code | method_info | 45.3 |
Exceptions | method_info | 45.3 |
RuntimeVisibleParameterAnnotations, RuntimeInvisibleParameterAnnotations
| method_info | 49.0 |
AnnotationDefault | method_info | 49.0 |
MethodParameters | method_info | 52.0 |
Synthetic |
ClassFile, field_info, method_info
| 45.3 |
Deprecated |
ClassFile, field_info, method_info
| 45.3 |
Signature |
ClassFile, field_info, method_info
| 49.0 |
RuntimeVisibleAnnotations, RuntimeInvisibleAnnotations
|
ClassFile, field_info, method_info
| 49.0 |
LineNumberTable | Code | 45.3 |
LocalVariableTable | Code | 45.3 |
LocalVariableTypeTable | Code | 49.0 |
StackMapTable | Code | 50.0 |
RuntimeVisibleTypeAnnotations, RuntimeInvisibleTypeAnnotations
|
ClassFile, field_info, method_info, Code
| 52.0 |
Компиляторы разрешается определять и генерировать class файлы, содержащие новые атрибуты в таблицах attributes структур файлов class, структур field_info, структур method_info и атрибутов Code (§4.7.3). Реализации виртуальной машины Java разрешается распознавать и использовать новые атрибуты, найденные в этих таблицах attributes. Однако любой атрибут, не определённый в спецификации файла class, не должен влиять на семантику файла class. Реализации виртуальной машины Java обязаны игнорировать нераспознанные атрибуты.
Например, разрешается определение нового атрибута для поддержки отладки, специфичной для поставщика. Поскольку реализации виртуальных машин Java обязаны игнорировать нераспознанные атрибуты, файлы class, предназначенные для конкретной реализации виртуальной машины Java, будут работоспособны и для других реализаций, даже если эти реализации не могут использовать дополнительную отладочную информацию, содержащуюся в этих файлах class.
Реализации виртуальных машин Java специально запрещается генерировать исключение или иным образом отказываться от использования файлов class просто из-за наличия нового атрибута. Конечно, инструменты, работающие с файлами class, могут работать неправильно, если им предоставляются файлы class, которые не содержат всех необходимых атрибутов.
Два атрибута, которые предполагается различными, но которые оказываются имеющими одинаковое имя атрибута и одинаковую длину, будут конфликтовать в реализациях, распознающих любой из атрибутов. Атрибуты, определённые не в этой спецификации, должны иметь имена, выбранные в соответствии с соглашениями об именовании пакетов, описанных в Спецификации языка Java, издание Java SE 8 (JLS §6.1).
В будущих версиях этой спецификации могут быть определены дополнительные атрибуты.
Атрибут ConstantValue — атрибут фиксированной длины в таблице attributes структуры field_info (§4.5). Атрибут ConstantValue представляет собой значение константного выражения (JLS §15.28) и используется следующим образом:
-
Если флаг
ACC_STATICв элементеaccess_flagsструктурыfield_infoустановлен, то поле, представленное структуройfield_info, получает значение, представленное его атрибутомConstantValue, как часть инициализации класса или интерфейса, объявляющего поле (§5.5). Это происходит до вызова метода инициализации класса или интерфейса этого класса или интерфейса (§2.9). -
В противном случае виртуальная машина Java должна проигнорировать атрибут.
В таблице attributes структуры field_info может быть, максимум, один атрибут ConstantValue.
Атрибут ConstantValue имеет следующий формат:
ConstantValue_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 constantvalue_index;
}
Элементы структуры ConstantValue_attribute следующие:
- attribute_name_index
-
Значение элемента
attribute_name_indexдолжно быть допустимым индексом в таблицеconstant_pool. Элементconstant_poolв этом индексе должен быть структуройCONSTANT_Utf8_info(§4.4.7) представляющей строку "ConstantValue". - attribute_length
-
Значение элемента
attribute_lengthструктурыConstantValue_attributeдолжно быть равно двум. - constantvalue_index
-
Значение элемента
constantvalue_indexдолжно быть допустимым индексом в таблицеconstant_pool. Элементconstant_poolв этом индексе задаёт значение константы, представленной этим атрибутом. Элементconstant_poolдолжен иметь тип, соответствующий полю, как указано в таблице 4.7.2-A.Таблица 4.7.2-A. Типы атрибута константного значения
Тип поля Тип элемента longCONSTANT_LongfloatCONSTANT_FloatdoubleCONSTANT_Doubleint,short,char,byte,booleanCONSTANT_IntegerStringCONSTANT_String
Атрибут Code — это атрибут переменной длины в таблице attributes структуры method_info (§4.6). Атрибут Code содержит инструкции виртуальной машины Java и вспомогательную информацию для метода, включая метод инициализации экземпляра или метод инициализации класса или интерфейса (§2.9).
Если метод является либо native, либо abstract, его структура method_info не должна содержать атрибут Code в своей таблице attributes. В противном случае, его структура method_info должна содержать ровно один атрибут Code в своей таблице attributes.
Атрибут Code имеет следующий формат:
Code_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 max_stack;
u2 max_locals;
u4 code_length;
u1 code[code_length];
u2 exception_table_length;
{ u2 start_pc;
u2 end_pc;
u2 handler_pc;
u2 catch_type;
} exception_table[exception_table_length];
u2 attributes_count;
attribute_info attributes[attributes_count];
}
Элементы структуры Code_attribute следующие:
- attribute_name_index
-
Значение элемента
attribute_name_indexдолжно быть допустимым индексом в таблицеconstant_pool. Записьconstant_poolв этом индексе должна быть структуройCONSTANT_Utf8_info(§4.4.7) представляющей строку "Code". - attribute_length
-
Значение элемента
attribute_lengthуказывает длину атрибута, за исключением начальных шести байтов. - max_stack
-
Значение элемента
max_stackзадаёт максимальную глубину стека операндов этого метода (§2.6.2) в любой момент во время выполнения метода. - max_locals
-
Значение элемента
max_localsзадаёт количество локальных переменных в массиве локальных переменных, выделенных при вызове этого метода (§2.6.1), включая локальные переменные, используемые для передачи параметров в метод при его вызове.Наибольший индекс локальной переменной для значения типа
longилиdouble—max_locals - 2. Наибольший индекс локальной переменной для значения любого другого типа —max_locals - 1. - code_length
-
Значение элемента
code_lengthзадаёт количество байтов в массивеcodeдля этого метода.Значение
code_lengthдолжно быть больше нуля (так как массивcodeне должен быть пустым) и меньше 65536. - code[]
-
Массив
codeсодержит фактические байты кода виртуальной машины Java, реализующие метод.При чтении массива
codeв память на машине с адресацией байтов, если первый байт массива выровнен по границе 4 байта, 32-битные смещения tableswitch и lookupswitch будут выровнены по 4 байтам. (Дополнительную информацию о последствиях выравнивания массиваcodeсм. в описаниях этих инструкций.)Подробные ограничения на содержимое массива
codeобширны и приведены в отдельном разделе (§4.9). - exception_table_length
-
Значение элемента
exception_table_lengthзадаёт количество записей в таблицеexception_table. - exception_table[]
-
Каждая запись в массиве
exception_tableописывает один обработчик исключений в массивеcode. Порядок обработчиков в массивеexception_tableважен (§2.10).Каждая запись
exception_tableсодержит следующие четыре элемента:- start_pc, end_pc
-
Значения двух элементов
start_pcиend_pcуказывают диапазоны в массивеcode, в которых обработчик исключений активен. Значениеstart_pcдолжно быть допустимым индексом в массивеcodeкода инструкции. Значениеend_pcдолжно быть либо допустимым индексом в массивеcodeкода инструкции, либо равноcode_length, длине массиваcode. Значениеstart_pcдолжно быть меньше значенияend_pc.start_pcвключительно иend_pcисключающий; то есть обработчик исключений должен быть активен, пока счётчик команд находится в интервале [start_pc,end_pc).Факт, что
end_pcисключающий, является исторической ошибкой в дизайне виртуальной машины Java: если код виртуальной машины Java для метода имеет ровно 65535 байт и заканчивается инструкцией длиной 1 байт, то эта инструкция не может быть защищена обработчиком исключений. Разработчик компилятора может обойти эту ошибку, ограничив максимальный размер сгенерированного кода виртуальной машины Java для любого метода, метода инициализации экземпляра или статического инициализатора (размер любого массива кода) до 65534 байт. - handler_pc
-
Значение элемента
handler_pcуказывает начало обработчика исключений. Значение элемента должно быть допустимым индексом в массивеcodeи должно быть индексом кода инструкции. - catch_type
-
Если значение элемента
catch_typeненулевое, оно должно быть допустимым индексом в таблицеconstant_pool. Записьconstant_poolв этом индексе должна быть структуройCONSTANT_Class_info(§4.4.1), представляющей класс исключений, для которого этот обработчик исключений предназначен. Обработчик исключений будет вызван только если брошенное исключение является экземпляром данного класса или одного из его подклассов.Верификатор проверяет, что класс является
Throwableили подклассомThrowable(§4.9.2).Если значение элемента
catch_typeравно нулю, этот обработчик исключений вызывается для всех исключений.Это используется для реализации
finally(§3.13).
- attributes_count
-
Значение элемента
attributes_countуказывает количество атрибутов атрибутаCode. - attributes[]
-
Каждое значение в таблице
attributesдолжно быть структуройattribute_info(§4.7).Атрибут
Codeможет иметь любое количество дополнительных атрибутов.Определяемые этой спецификацией атрибуты, которые могут присутствовать в таблице
attributesатрибутаCode, перечислены в таблице 4.7-C.Правила, касающиеся атрибутов, определённых для присутствия в таблице
attributesатрибутаCode, приведены в §4.7.Правила, касающиеся неопределённых атрибутов в таблице
attributesатрибутаCode, приведены в §4.7.1.
Атрибут StackMapTable является атрибутом переменной длины в таблице атрибутов атрибута Code (§4.7.3). Атрибут StackMapTable используется во время процесса проверки типами (§4.10.1).
В таблице атрибутов атрибута Code может находиться не более одного атрибута StackMapTable.
В файле класса, версия которого 50.0 или выше, если атрибут Code метода не содержит атрибут StackMapTable, у него есть неявный атрибут StackMapTable (§4.10.1). Этот неявный атрибут эквивалентен атрибуту StackMapTable с значением number_of_entries равным нулю.
Атрибут StackMapTable имеет следующий формат:
StackMapTable_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 number_of_entries;
stack_map_frame entries[number_of_entries];
}
Элементы структуры StackMapTable таковы:
- attribute_name_index
-
Значение элемента attribute_name_index должно быть корректным индексом в таблице CONSTANT_Utf8_info. Запись в таблице по этому индексу должна быть структурой CONSTANT_Utf8_info (§4.4.7) представляющей строку "StackMapTable".
- attribute_length
-
Значение элемента attribute_length указывает длину атрибута, за исключением первых шести байт.
- number_of_entries
-
Значение элемента number_of_entries задаёт количество записей в таблице stack_map_frames.
- entries[]
-
Каждая запись в таблице stack_map_frames описывает одну кадровую карту стека метода. Порядок кадровых карт стека в таблице stack_map_frames важен.
Кадровая карта стека (stack map frame) указывает (явным или неявным образом) смещение байткода, к которому она относится, и типы проверки локальных переменных и элементов стека операндов для этого смещения.
Каждая кадровая карта стека, описанная в таблице stack_map_frames, использует некоторые семантики предыдущей кадровой карты стека. Первая кадровая карта стека метода неявна и вычисляется из описателя метода проверяющим типом (§4.10.1.6). Структура stack_map_frame на позиции entries[1] поэтому описывает вторую кадровую карту стека метода.
Смещение байткода, к которому относится кадровая карта стека, вычисляется путем взятия значения delta_offset, указанного в кадровой карте стека (явным или неявным образом), и добавления его к смещению байткода предыдущей кадровой карты стека, если предыдущая кадровая карта не является начальной кадровой картой метода. В этом случае смещение байткода, к которому относится кадровая карта стека, - это значение offset, указанное в кадровой карте стека.
Использование смещения delta_offset, а не хранения фактического смещения байткода, гарантирует, что кадровые карты стека отсортированы в правильном порядке. Кроме того, использование формулы delta_offset для всех явных кадровых карт (в отличие от неявной первой кадровой карты) гарантирует отсутствие дубликатов.
Мы говорим, что инструкция в байткоде имеет соответствующую кадровую карту стека, если инструкция начинается со смещения i в массиве instructions атрибута Code, и атрибут Code имеет атрибут StackMapTable, чей массив entries содержит кадровую карту стека, применимую к смещению байткода i.
Тип проверки (verification type) задает тип одной или двух локаций, где локация — это либо одна локальная переменная, либо один элемент стека операндов. Тип проверки представлен объединением типов, verification_type_info, состоящим из одного байтового тега, указывающего используемый элемент объединения, и нуля или более байтов, предоставляющих дополнительную информацию о теге.
union verification_type_info {
Top_variable_info;
Integer_variable_info;
Float_variable_info;
Long_variable_info;
Double_variable_info;
Null_variable_info;
UninitializedThis_variable_info;
Object_variable_info;
Uninitialized_variable_info;
}
Тип проверки, задающий одну локацию в массиве локальных переменных или в стеке операндов, представлен следующими элементами объединения verification_type_info:
-
Элемент TOP указывает, что локальная переменная имеет тип проверки TOP.
Top_variable_info { u1 tag = ITEM_Top; /* 0 */ } -
Элемент INTEGER указывает, что локация имеет тип проверки INTEGER.
Integer_variable_info { u1 tag = ITEM_Integer; /* 1 */ } -
Элемент FLOAT указывает, что локация имеет тип проверки FLOAT.
Float_variable_info { u1 tag = ITEM_Float; /* 2 */ } -
Тип LONG указывает, что локация имеет тип проверки LONG.
Null_variable_info { u1 tag = ITEM_Null; /* 5 */ } -
Элемент DOUBLE указывает, что локация имеет тип проверки DOUBLE.
UninitializedThis_variable_info { u1 tag = ITEM_UninitializedThis; /* 6 */ } -
Элемент CLASS указывает, что локация имеет тип проверки, являющийся классом, представленным структурой CONSTANT_Class_info (§4.4.1), найденной в таблице CONSTANT_Class_info по индексу, указанному элементом class_index.
Object_variable_info { u1 tag = ITEM_Object; /* 7 */ u2 cpool_index; } -
Элемент NEW указывает, что локация имеет тип проверки NEW. Элемент new_index указывает смещение в массиве new_array атрибута Code, содержащем данный атрибут StackMapTable, инструкции new, которая создала объект, хранящийся в локации.
Uninitialized_variable_info { u1 tag = ITEM_Uninitialized; /* 8 */ u2 offset; }
Тип проверки, задающий две локации в массиве локальных переменных или в стеке операндов, представлен следующими элементами объединения verification_type_info:
-
Элемент INTEGER указывает, что первая из двух локаций имеет тип проверки INTEGER.
Long_variable_info { u1 tag = ITEM_Long; /* 4 */ } -
Элемент FLOAT указывает, что первая из двух локаций имеет тип проверки FLOAT.
Double_variable_info { u1 tag = ITEM_Double; /* 3 */ } -
Элементы LONG и DOUBLE указывают тип проверки второй из двух локаций следующим образом:
-
Если первая из двух локаций — локальная переменная, то:
-
Это не локальная переменная с наибольшим индексом.
-
Следующая локальная переменная с более высоким индексом имеет тип проверки LONG.
-
-
Если первая из двух локаций — элемент стека операндов, то:
-
Это не верхний элемент стека операндов.
-
Следующий элемент стека операндов имеет тип проверки DOUBLE.
-
-
Кадровая карта стека представлена объединением типов, stack_map_frame, состоящим из одного байтового тега, указывающего используемый элемент объединения, и нуля или более байтов, предоставляющих дополнительную информацию о теге.
union stack_map_frame {
same_frame;
same_locals_1_stack_item_frame;
same_locals_1_stack_item_frame_extended;
chop_frame;
same_frame_extended;
append_frame;
full_frame;
}
Тег указывает тип кадровой карты стека:
-
Тип кадра
same_frameпредставлен тегами в диапазоне [0-63]. Этот тип кадра указывает, что в кадре точно такие же локальные переменные, как в предыдущем кадре, а стек операндов пуст. Значениеoffset_deltaдля кадра — значение элемента тега,frame_type.same_frame { u1 frame_type = SAME; /* 0-63 */ } -
Тип кадра
same_locals_1_stack_item_frameпредставлен тегами в диапазоне [64, 127]. Этот тип кадра указывает, что в кадре точно такие же локальные переменные, как в предыдущем кадре, а стек операндов содержит один элемент. Значениеoffset_deltaдля кадра определяется формулойframe_type - 64. Тип проверки одного элемента стека следует за типом кадра.same_locals_1_stack_item_frame { u1 frame_type = SAME_LOCALS_1_STACK_ITEM; /* 64-127 */ verification_type_info stack[1]; } -
Теги в диапазоне [128-246] зарезервированы для будущего использования.
-
Тип кадра
same_locals_1_stack_item_frame_extendedпредставлен тегом 247. Этот тип кадра указывает, что в кадре точно такие же локальные переменные, как в предыдущем кадре, а стек операндов содержит один элемент. Значениеoffset_deltaдля кадра задается явно, в отличие от типа кадраsame_locals_1_stack_item_frame. Тип проверки одного элемента стека следует послеoffset_delta.same_locals_1_stack_item_frame_extended { u1 frame_type = SAME_LOCALS_1_STACK_ITEM_EXTENDED; /* 247 */ u2 offset_delta; verification_type_info stack[1]; } -
Тип кадра
chop_frameпредставлен тегами в диапазоне [248-250]. Этот тип кадра указывает, что в кадре локальные переменные такие же, как в предыдущем кадре, за исключением последних k локальных переменных, которые отсутствуют, а стек операндов пуст. Значение k определяется формулой251 - frame_type. Значениеoffset_deltaдля кадра задается явно.Предполагается, что типы проверки локальных переменных в предыдущем кадре задаются
locals, структурированным массивом, как в типе кадраfull_frame. Еслиlocals[M-1]в предыдущем кадре представляло локальную переменную X, аlocals[M]представляло локальную переменную Y, то эффект удаления одной локальной переменной заключается в том, чтоlocals[M-1]в новом кадре представляет локальную переменную X, аlocals[M]не определено.Ошибка, если k больше числа локальных переменных в
localsдля предыдущего кадра, то есть если число локальных переменных в новом кадре будет меньше нуля. -
Тип кадра
same_frame_extendedпредставлен тегом 251. Этот тип кадра указывает, что в кадре точно такие же локальные переменные, как в предыдущем кадре, а стек операндов пуст. Значениеoffset_deltaдля кадра задается явно, в отличие от типа кадраsame_frame.same_frame_extended { u1 frame_type = SAME_FRAME_EXTENDED; /* 251 */ u2 offset_delta; } -
Тип кадра
append_frameпредставлен тегами в диапазоне [252-254]. Этот тип кадра указывает, что в кадре локальные переменные такие же, как в предыдущем кадре, за исключением того, что определены k дополнительных локальных переменных, а стек операндов пуст. Значение k определяется формулойframe_type - 251. Значениеoffset_deltaдля кадра задается явно.append_frame { u1 frame_type = APPEND; /* 252-254 */ u2 offset_delta; verification_type_info locals[frame_type - 251]; }Нулевой элемент в
localsпредставляет тип проверки первой дополнительной локальной переменной. Еслиlocals[M]представляет локальную переменнуюN, то:-
locals[M+1]представляет локальную переменнуюN+1, еслиlocals[M]является одним изTop_variable_info,Integer_variable_info,Float_variable_info,Null_variable_info,UninitializedThis_variable_info,Object_variable_infoилиUninitialized_variable_info; и -
locals[M+1]представляет локальную переменнуюN+2, еслиlocals[M]является либоLong_variable_info, либоDouble_variable_info.
Ошибка, если для любого индекса i,
locals[i]представляет локальную переменную, чей индекс больше максимального числа локальных переменных для метода. -
-
Тип кадра
full_frameпредставлен тегом 255. Значениеoffset_deltaдля кадра задается явно.full_frame { u1 frame_type = FULL_FRAME; /* 255 */ u2 offset_delta; u2 number_of_locals; verification_type_info locals[number_of_locals]; u2 number_of_stack_items; verification_type_info stack[number_of_stack_items]; }Нулевой элемент в
localsпредставляет тип проверки локальной переменной 0. Еслиlocals[M]представляет локальную переменнуюN, то:-
locals[M+1]представляет локальную переменнуюN+1, еслиlocals[M]является одним изTop_variable_info,Integer_variable_info,Float_variable_info,Null_variable_info,UninitializedThis_variable_info,Object_variable_infoилиUninitialized_variable_info; и -
locals[M+1]представляет локальную переменнуюN+2, еслиlocals[M]является либоLong_variable_info, либоDouble_variable_info.
Ошибка, если для любого индекса i,
locals[i]представляет локальную переменную, чей индекс больше максимального числа локальных переменных для метода.Нулевой элемент в
stackпредставляет тип проверки нижнего элемента стека операндов, а последующие элементы вstackпредставляют типы проверки элементов стека, более близких к вершине стека операндов. Мы называем нижний элемент стека операндов элементом стека 0, а последующие элементы стека операндов — элементами стека 1, 2 и т. д. Еслиstack[M]представляет элемент стекаN, то:-
stack[M+1]представляет элемент стекаN+1, еслиstack[M]является одним изTop_variable_info,Integer_variable_info,Float_variable_info,Null_variable_info,UninitializedThis_variable_info,Object_variable_infoилиUninitialized_variable_info; и -
stack[M+1]представляет элемент стекаN+2, еслиstack[M]является либоLong_variable_info, либоDouble_variable_info.
Ошибка, если для любого индекса i,
stack[i]представляет элемент стека, чей индекс больше максимального размера стека операндов для метода. -
Атрибут Exceptions — атрибут переменной длины в таблице attributes структуры method_info (§4.6). Атрибут Exceptions указывает, какие исключения, обрабатываемые по типу `checked`, может генерировать метод.
В таблице attributes структуры method_info может быть не более одного атрибута Exceptions.
Атрибут Exceptions имеет следующий формат:
Exceptions_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 number_of_exceptions;
u2 exception_index_table[number_of_exceptions];
}
Элементы структуры Exceptions_attribute следующие:
- attribute_name_index
-
Значение элемента
attribute_name_indexдолжно быть допустимым индексом в таблицеconstant_pool. Элементconstant_poolв этом индексе должен быть структуройCONSTANT_Utf8_info(§4.4.7), представляющей строку "Exceptions". - attribute_length
-
Значение элемента
attribute_lengthуказывает длину атрибута, за исключением начальных шести байтов. - number_of_exceptions
-
Значение элемента
number_of_exceptionsуказывает количество записей вexception_index_table. - exception_index_table[]
-
Каждое значение в массиве
exception_index_tableдолжно быть допустимым индексом в таблицеconstant_pool. Элементconstant_poolв этом индексе должен быть структуройCONSTANT_Class_info(§4.4.1), представляющей тип класса, который должен выбрасывать этот метод.
Метод должен выбрасывать исключение только в том случае, если выполняется хотя бы одно из трех следующих условий:
-
Исключение является экземпляром
RuntimeExceptionили одного из его подклассов. -
Исключение является экземпляром
Errorили одного из его подклассов. -
Исключение является экземпляром одного из классов исключений, указанных в
exception_index_table, или одним из их подклассов.
Эти требования не проверяются в виртуальной машине Java; они проверяются только на этапе компиляции.
Атрибут InnerClasses — это атрибут переменной длины в таблице attributes структуры ClassFile (§4.1).
Если константный пул класса или интерфейса C содержит хотя бы одну запись CONSTANT_Class_info (§4.4.1), которая представляет класс или интерфейс, не являющийся членом пакета, то в таблице attributes структуры ClassFile для C должен присутствовать ровно один атрибут InnerClasses.
Атрибут InnerClasses имеет следующий формат:
InnerClasses_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 number_of_classes;
{ u2 inner_class_info_index;
u2 outer_class_info_index;
u2 inner_name_index;
u2 inner_class_access_flags;
} classes[number_of_classes];
}
Элементы структуры InnerClasses_attribute таковы:
- attribute_name_index
-
Значение элемента
attribute_name_indexдолжно быть допустимым индексом в таблицеconstant_pool. Записьconstant_poolв этой таблице должна быть структуройCONSTANT_Utf8_info(§4.4.7), представляющей строку "InnerClasses". - attribute_length
-
Значение элемента
attribute_lengthуказывает длину атрибута, не включая начальные шесть байтов. - number_of_classes
-
Значение элемента
number_of_classesуказывает количество элементов в массивеclasses. - classes[]
-
Каждая запись
CONSTANT_Class_infoв таблицеconstant_pool, представляющая класс или интерфейс C, который не является членом пакета, должна иметь ровно одну соответствующую запись в массивеclasses.Если класс или интерфейс имеет члены, которые являются классами или интерфейсами, его таблица
constant_pool(и, следовательно, его атрибутInnerClasses) должен ссылаться на каждого такого члена (JLS §13.1), даже если этот член не упоминается в классе иначе.Кроме того, таблица
constant_poolкаждого вложенного класса и вложенного интерфейса должна ссылаться на его внешний класс, поэтому все вложенные классы и вложенные интерфейсы будут иметь информациюInnerClassesдля каждого внешнего класса и для каждого из своих вложенных классов и интерфейсов.Каждая запись в массиве
classesсодержит следующие четыре элемента:- inner_class_info_index
-
Значение элемента
inner_class_info_indexдолжно быть допустимым индексом в таблицеconstant_pool. Записьconstant_poolв этом индексе должна быть структуройCONSTANT_Class_info, представляющей C. Остальные элементы в записи массиваclassesсодержат информацию о C. - outer_class_info_index
-
Если C не является членом класса или интерфейса (то есть, если C — это верхнеуровневый класс или интерфейс (JLS §7.6) или локальный класс (JLS §14.3) или анонимный класс (JLS §15.9.5)), значение элемента
outer_class_info_indexдолжно быть равно нулю.В противном случае значение элемента
outer_class_info_indexдолжно быть допустимым индексом в таблицеconstant_pool, а запись в этом индексе должна быть структуройCONSTANT_Class_info, представляющей класс или интерфейс, членом которого является C. - inner_name_index
-
Если C анонимный (JLS §15.9.5), значение элемента
inner_name_indexдолжно быть равно нулю.В противном случае значение элемента
inner_name_indexдолжно быть допустимым индексом в таблицеconstant_pool, а запись в этом индексе должна быть структуройCONSTANT_Utf8_info(§4.4.7), представляющей исходное простое имя C, как указано в исходном коде, из которого был скомпилирован этот файлclass. - inner_class_access_flags
-
Значение элемента
inner_class_access_flags— это маска флагов, используемых для обозначения разрешений доступа к классу или интерфейсу C и его свойств, как объявлено в исходном коде, из которого был скомпилирован этот файлclass. Он используется компилятором для восстановления исходной информации, когда исходный код недоступен. Флаги указаны в таблице 4.7.6-A.Таблица 4.7.6-A. Флаги доступа и свойств вложенных классов
Имя флага Значение Толкование ACC_PUBLIC0x0001 Отмечено или неявно publicв исходном коде.ACC_PRIVATE0x0002 Отмечено privateв исходном коде.ACC_PROTECTED0x0004 Отмечено protectedв исходном коде.ACC_STATIC0x0008 Отмечено или неявно staticв исходном коде.ACC_FINAL0x0010 Отмечено finalв исходном коде.ACC_INTERFACE0x0200 Было interfaceв исходном коде.ACC_ABSTRACT0x0400 Отмечено или неявно abstractв исходном коде.ACC_SYNTHETIC0x1000 Объявлен синтетическим; отсутствует в исходном коде. ACC_ANNOTATION0x2000 Объявлен как тип аннотации. ACC_ENUM0x4000 Объявлен как тип enum.Все биты элемента
inner_class_access_flags, не назначенные в таблице 4.7.6-A, зарезервированы для будущего использования. Они должны быть установлены в ноль в сгенерированных файлахclassи должны игнорироваться реализациями Java Virtual Machine.
Если файл
classимеет номер версии 51.0 или выше и содержит атрибутInnerClassesв своей таблицеattributes, то для всех записей в массивеclassesатрибутаInnerClassesзначение элементаouter_class_info_indexдолжно быть равно нулю, если значение элементаinner_name_indexравно нулю.
Реализация Oracle Java Virtual Machine не проверяет согласованность атрибута 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 должен быть установлен. Исключение составляют методы, сгенерированные компилятором, которые не считаются артефактами реализации, а именно метод инициализации экземпляра, представляющий собой конструктор по умолчанию языка программирования Java (§2.9), метод инициализации класса (§2.9), а также методы Enum.values() и Enum.valueOf().
Атрибут Synthetic был введён в JDK 1.1 для поддержки вложенных классов и интерфейсов.
Атрибут Synthetic имеет следующий формат:
Synthetic_attribute {
u2 attribute_name_index;
u4 attribute_length;
}
Элементы структуры Synthetic_attribute следующие:
- attribute_name_index
-
Значение элемента
attribute_name_indexдолжно быть допустимым индексом в таблицеconstant_pool. Записьconstant_poolв этой позиции должна быть структуройCONSTANT_Utf8_info(§4.4.7), представляющей строку "Synthetic". - attribute_length
-
Значение элемента
attribute_lengthдолжно быть нулём.
Атрибут Signature — атрибут фиксированной длины в таблице attributes структуры ClassFile, field_info или method_info (§4.1, §4.5, §4.6). Атрибут Signature записывает сигнатуру (§4.7.9.1) для класса, интерфейса, конструктора, метода или поля, объявление которого в языке программирования Java использует переменные типов или параметризованные типы. Подробности об этих типах см. в Спецификации языка Java, издание Java SE 8.
Атрибут Signature имеет следующий формат:
Signature_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 signature_index;
}
Элементы структуры Signature_attribute следующие:
- attribute_name_index
-
Значение элемента
attribute_name_indexдолжно быть допустимым индексом в таблицеconstant_pool. Записьconstant_poolв этой позиции должна быть структуройCONSTANT_Utf8_info(§4.4.7), представляющей строку "Signature". - attribute_length
-
Значение элемента
attribute_lengthструктурыSignature_attributeдолжно быть равно двум. - signature_index
-
Значение элемента
signature_indexдолжно быть допустимым индексом в таблицеconstant_pool. Записьconstant_poolв этой позиции должна быть структуройCONSTANT_Utf8_info(§4.4.7), представляющей сигнатуру класса, если этот атрибутSignatureявляется атрибутом структурыClassFile; сигнатуру метода, если этот атрибутSignatureявляется атрибутом структурыmethod_info; или сигнатуру поля в противном случае.
Реализация виртуальной машины Java от Oracle не проверяет корректность атрибутов Signature во время загрузки или связывания классов. Вместо этого атрибуты Signature проверяются методами библиотек классов платформы Java SE, которые предоставляют обобщённые сигнатуры классов, интерфейсов, конструкторов, методов и полей. Примеры включают getGenericSuperclass в Class и toGenericString в java.lang.reflect.Executable.
Подписи кодируют объявления, написанные на языке программирования Java, которые используют типы за пределами системы типов Java Virtual Machine. Они поддерживают рефлексию и отладку, а также компиляцию, когда доступны только файлы class.
Компилятор Java должен генерировать подпись для любого класса, интерфейса, конструктора, метода или поля, объявление которого использует переменные типа или параметризованные типы. Конкретно, компилятор Java должен генерировать:
-
Подпись класса для любого объявления класса или интерфейса, который является обобщенным, или имеет параметризованный тип в качестве суперкласса или суперинтерфейса, или оба.
-
Подпись метода для любого объявления метода или конструктора, который является обобщенным, или имеет переменную типа или параметризованный тип в качестве возвращаемого типа или типа формального параметра, или имеет переменную типа в
throws-описании, или любое сочетание этих условий.Если
throws-описание объявления метода или конструктора не включает переменные типа, то компилятор может рассматривать объявление как не имеющееthrows-описания для целей генерации подписи метода. -
Подпись поля для любого объявления поля, формального параметра или локальной переменной, тип которого использует переменную типа или параметризованный тип.
Подписи задаются с использованием грамматики, которая следует обозначениям §4.3.1. В дополнение к этим обозначениям:
-
Синтаксис [x] в правой части продукции обозначает ноль или одно вхождение x. То есть, x является необязательным символом. Альтернатива, содержащая необязательный символ, фактически определяет две альтернативы: одну, которая опускает необязательный символ, и одну, которая его включает.
-
Очень длинная правая часть может быть продолжена на второй строке, четко отступая вторую строку.
Грамматика включает терминальный символ Идентификатор для обозначения имени типа, поля, метода, формального параметра, локальной переменной или переменной типа, как сгенерировано компилятором Java. Такое имя не должно содержать ни одного из ASCII-символов . ; [ / < > : (т.е. символы, запрещённые в именах методов (§4.2.2) и также двоеточие), но может содержать символы, которые не должны появляться в идентификаторе на языке программирования Java (JLS §3.8).
Подписи основаны на иерархии нетерминалов, известных как подписи типов:
-
Подпись типа Java представляет либо ссылочный тип, либо примитивный тип языка программирования Java.
JavaTypeSignature:Следующая продукция из §4.3.2 приведена здесь для удобства:
BaseType:(одно из)
BCDFIJSZ -
Подпись ссылочного типа представляет ссылочный тип языка программирования Java, то есть тип класса или интерфейса, переменную типа или тип массива.
Подпись типа класса представляет (возможно параметризованный) тип класса или интерфейса. Подпись типа класса должна быть сформулирована таким образом, чтобы её можно было надёжно сопоставить с бинарным именем обозначаемого класса путём стирания всех аргументов типа и преобразования каждого символа
.в символ$.Подпись переменной типа представляет переменную типа.
Подпись типа массива представляет одну размерность типа массива.
ReferenceTypeSignature:ClassTypeSignature:PackageSpecifier:Identifier/{PackageSpecifier}SimpleClassTypeSignature:Identifier [TypeArguments]TypeArguments:TypeArgument:WildcardIndicator:+
-ClassTypeSignatureSuffix:TypeVariableSignature:TIdentifier;ArrayTypeSignature:
Подпись класса кодирует информацию о типе объявления класса (возможно, обобщенного). Она описывает любые параметры типа класса и перечисляет его (возможно параметризованные) непосредственные суперклассы и непосредственные суперинтерфейсы, если они есть. Параметр типа описывается его именем, за которым следуют любые ограничения класса и интерфейса.
Подпись метода кодирует информацию о типе объявления метода (возможно, обобщенного). Она описывает любые параметры типа метода; (возможно параметризованные) типы формальных параметров; (возможно параметризованный) возвращаемый тип, если таковой имеется; и типы любых исключений, объявленных в throws-описании метода.
Следующая конструкция из §4.3.3 приведена здесь для удобства:
VИз-за артефактов, сгенерированных компилятором, подпись метода может не совпадать точно с описанием метода (§4.3.3). В частности, количество типов формальных параметров в подписи метода может быть меньше, чем количество описаний параметров в описании метода.
Подпись поля кодирует тип поля, формального параметра или объявления локальной переменной (возможно, с параметрами).
Атрибут SourceFile — это необязательный атрибут фиксированной длины в таблице attributes структуры ClassFile (§4.1).
В таблице attributes структуры ClassFile может быть не более одного атрибута SourceFile.
Атрибут SourceFile имеет следующий формат:
SourceFile_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 sourcefile_index;
}
Элементы структуры SourceFile_attribute следующие:
- attribute_name_index
-
Значение элемента
attribute_name_indexдолжно быть допустимым индексом в таблицеconstant_pool. Элементconstant_poolв этом индексе должен быть структуройCONSTANT_Utf8_info(§4.4.7), представляющей строку "SourceFile". - attribute_length
-
Значение элемента
attribute_lengthструктурыSourceFile_attributeдолжно быть два. - sourcefile_index
-
Значение элемента
sourcefile_indexдолжно быть допустимым индексом в таблицеconstant_pool. Элементconstant_poolв этом индексе должен быть структуройCONSTANT_Utf8_info(§4.4.7), представляющей строку.Строка, на которую ссылается элемент
sourcefile_index, будет интерпретироваться как имя файла исходного кода, из которого был скомпилирован этот файлclass. Она не будет интерпретироваться как имя каталога, содержащего файл, или абсолютный путь к файлу; такая платформенно-специфичная дополнительная информация должна быть предоставлена интерпретатором во время выполнения или инструментом разработки в момент фактического использования имени файла.
Атрибут SourceDebugExtension — это необязательный атрибут в таблице attributes структуры ClassFile (§4.1).
В таблице attributes структуры ClassFile может быть не более одного атрибута SourceDebugExtension.
Атрибут SourceDebugExtension имеет следующий формат:
SourceDebugExtension_attribute {
u2 attribute_name_index;
u4 attribute_length;
u1 debug_extension[attribute_length];
}
Элементы структуры SourceDebugExtension_attribute следующие:
- attribute_name_index
-
Значение элемента
attribute_name_indexдолжно быть допустимым индексом в таблицеconstant_pool. Элементconstant_poolв этом индексе должен быть структуройCONSTANT_Utf8_info(§4.4.7), представляющей строку "SourceDebugExtension". - attribute_length
-
Значение элемента
attribute_lengthуказывает длину атрибута, исключая начальные шесть байт. - debug_extension[]
-
Массив
debug_extensionсодержит расширенную отладочную информацию, которая не имеет семантического влияния на виртуальную машину Java. Информация представлена с помощью модифицированной строки UTF-8 (§4.4.7) без завершающего нулевого байта.Обратите внимание, что массив
debug_extensionможет обозначать строку, длина которой больше, чем может быть представлена экземпляром классаString.
Атрибут LineNumberTable — это необязательный атрибут переменной длины в таблице attributes атрибута Code (§4.7.3). Он может использоваться отладчиками для определения того, какая часть массива code соответствует заданному номеру строки в исходном файле.
Если в таблице attributes атрибута Code присутствуют несколько атрибутов LineNumberTable, то они могут появляться в любом порядке.
Может быть более одного атрибута LineNumberTable на одну строку исходного файла в таблице attributes атрибута Code. То есть атрибуты LineNumberTable вместе могут представлять одну строку исходного файла, и они не обязательно должны быть взаимно однозначными строкам исходного кода.
Атрибут LineNumberTable имеет следующий формат:
LineNumberTable_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 line_number_table_length;
{ u2 start_pc;
u2 line_number;
} line_number_table[line_number_table_length];
}
Элементы структуры LineNumberTable_attribute следующие:
- attribute_name_index
-
Значение элемента
attribute_name_indexдолжно быть допустимым индексом в таблицеconstant_pool. Элементconstant_poolв этом индексе должен быть структуройCONSTANT_Utf8_info(§4.4.7), представляющей строку "LineNumberTable". - attribute_length
-
Значение элемента
attribute_lengthуказывает длину атрибута, исключая начальные шесть байт. - line_number_table_length
-
Значение элемента
line_number_table_lengthуказывает количество записей в массивеline_number_table. - line_number_table[]
-
Каждая запись в массиве
line_number_tableуказывает, что номер строки в исходном файле изменяется в заданной точке массиваcode. Каждая записьline_number_tableдолжна содержать следующие два элемента:- start_pc
-
Значение элемента
start_pcдолжно указывать индекс в массивеcode, в котором начинается код новой строки в исходном файле.Значение
start_pcдолжно быть меньше значения элементаcode_lengthатрибутаCode, в котором этот атрибутLineNumberTableсодержится. - line_number
-
Значение элемента
line_numberдолжно указать соответствующий номер строки в исходном файле.
Атрибут таблицы локальных переменных является необязательным атрибутом переменной длины в таблице атрибутов кода (§4.7.3). Он может использоваться отладчиками для определения значения заданной локальной переменной во время выполнения метода.
Если несколько атрибутов таблиц локальных переменных присутствуют в таблице атрибутов кода, то они могут располагаться в любом порядке.
Может быть не более одного атрибута таблицы локальных переменных на каждую локальную переменную в таблице атрибутов кода.
Атрибут таблицы локальных переменных имеет следующий формат:
LocalVariableTable_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 local_variable_table_length;
{ u2 start_pc;
u2 length;
u2 name_index;
u2 descriptor_index;
u2 index;
} local_variable_table[local_variable_table_length];
}
Элементы структуры атрибута таблицы локальных переменных:
- attribute_name_index
-
Значение этого элемента должно быть допустимым индексом в таблице имен констант. Элемент таблицы имен констант с указанным индексом должен быть структурой §4.4.7 представляющей строку "LocalVariableTable".
- attribute_length
-
Значение этого элемента указывает длину атрибута, за исключением первых шести байтов.
- local_variable_table_length
-
Значение этого элемента указывает количество элементов в массиве local_variable_table.
- local_variable_table[]
-
Каждый элемент массива local_variable_table указывает диапазон смещений в массиве инструкций, в котором локальная переменная имеет значение. Он также указывает индекс в массиве локальных переменных текущей рамки, где находится эта локальная переменная. Каждый элемент должен содержать пять элементов:
- start_pc, length
-
Указанная локальная переменная должна иметь значение в диапазоне индексов в массиве инструкций [
start_pc,start_pc + length), то есть отstart_pcвключительно доstart_pc + lengthне включительно.Значение
start_pcдолжно быть допустимым индексом в массиве инструкций этого атрибута и должно быть индексом оператора инструкции.Значение
start_pc + lengthдолжно быть либо допустимым индексом в массиве инструкций этого атрибута и быть индексом оператора инструкции, либо это должно быть первый индекс за пределами массива инструкций. - name_index
-
Значение этого элемента должно быть допустимым индексом в таблице имен констант. Элемент таблицы имен констант с указанным индексом должен содержать структуру §4.4.7 , представляющую допустимое неопределенное имя, обозначающее локальную переменную (§4.2.2).
- descriptor_index
-
Значение этого элемента должно быть допустимым индексом в таблице имен констант. Элемент таблицы имен констант с указанным индексом должен содержать структуру §4.4.7 , представляющую описание поля, которое кодирует тип локальной переменной в исходной программе (§4.3.2).
- index
-
Указанная локальная переменная должна находиться на позиции
indexв массиве локальных переменных текущей рамки.Если локальная переменная на позиции
indexимеет типdoubleилиlong, она занимает места как наindex, так и наindex + 1.
Атрибут таблицы типов локальных переменных является необязательным атрибутом переменной длины в таблице атрибутов кода (§4.7.3). Он может использоваться отладчиками для определения значения заданной локальной переменной во время выполнения метода.
Если несколько атрибутов таблицы типов локальных переменных присутствуют в таблице атрибутов кода, то они могут располагаться в любом порядке.
Может быть не более одного атрибута таблицы типов локальных переменных на каждую локальную переменную в таблице атрибутов кода.
Атрибут таблицы типов локальных переменных отличается от атрибута таблицы локальных переменных (§4.7.13) тем, что он предоставляет информацию о сигнатуре, а не о описании. Это различие существенно только для переменных, тип которых использует тип переменной или параметризованный тип. Такие переменные будут присутствовать в обеих таблицах, в то время как переменные других типов будут присутствовать только в таблице типов.
Атрибут таблицы типов локальных переменных имеет следующий формат:
LocalVariableTypeTable_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 local_variable_type_table_length;
{ u2 start_pc;
u2 length;
u2 name_index;
u2 signature_index;
u2 index;
} local_variable_type_table[local_variable_type_table_length];
}
Элементы структуры атрибута таблицы типов локальных переменных:
- attribute_name_index
-
Значение этого элемента должно быть допустимым индексом в таблице имен констант. Элемент таблицы имен констант с указанным индексом должен быть структурой §4.4.7 , представляющей строку "LocalVariableTypeTable".
- attribute_length
-
Значение этого элемента указывает длину атрибута, за исключением первых шести байтов.
- local_variable_type_table_length
-
Значение этого элемента указывает количество элементов в массиве local_variable_type_table.
- local_variable_type_table[]
-
Каждый элемент массива local_variable_type_table указывает диапазон смещений в массиве инструкций, в котором локальная переменная имеет значение. Он также указывает индекс в массиве локальных переменных текущей рамки, где находится эта локальная переменная. Каждый элемент должен содержать пять элементов:
- start_pc, length
-
Указанная локальная переменная должна иметь значение в диапазоне индексов в массиве инструкций [
start_pc,start_pc + length), то есть отstart_pcвключительно доstart_pc + lengthне включительно.Значение
start_pcдолжно быть допустимым индексом в массиве инструкций этого атрибута и должно быть индексом оператора инструкции.Значение
start_pc + lengthдолжно быть либо допустимым индексом в массиве инструкций этого атрибута и быть индексом оператора инструкции, либо это должно быть первый индекс за пределами массива инструкций. - name_index
-
Значение этого элемента должно быть допустимым индексом в таблице имен констант. Элемент таблицы имен констант с указанным индексом должен содержать структуру §4.4.7 , представляющую допустимое неопределенное имя, обозначающее локальную переменную (§4.2.2).
- signature_index
-
Значение этого элемента должно быть допустимым индексом в таблице имен констант. Элемент таблицы имен констант с указанным индексом должен содержать структуру §4.4.7 , представляющую подпись поля, которая кодирует тип локальной переменной в исходной программе (§4.7.9.1).
- index
-
Указанная локальная переменная должна находиться на позиции
indexв массиве локальных переменных текущей рамки.Если локальная переменная на позиции
indexимеет типdoubleилиlong, она занимает места как наindex, так и наindex + 1.
Атрибут Deprecated — это необязательный атрибут фиксированной длины в таблице attributes структуры ClassFile, field_info или method_info (§4.1, §4.5, §4.6). Класс, интерфейс, метод или поле могут быть помечены атрибутом Deprecated, чтобы указать, что класс, интерфейс, метод или поле устарели.
Интерпретатор или инструмент выполнения, читающие формат файла class, например, компилятор, могут использовать эту метку, чтобы предупредить пользователя о том, что используется устаревший класс, интерфейс, метод или поле. Наличие атрибута Deprecated не изменяет семантики класса или интерфейса.
Атрибут Deprecated имеет следующий формат:
Deprecated_attribute {
u2 attribute_name_index;
u4 attribute_length;
}
Элементы структуры Deprecated_attribute следующие:
- attribute_name_index
-
Значение элемента
attribute_name_indexдолжно быть допустимым индексом в таблицеconstant_pool. Записьconstant_poolв этом индексе должна быть структуройCONSTANT_Utf8_info(§4.4.7), представляющей строку "Deprecated". - attribute_length
-
Значение элемента
attribute_lengthдолжно быть равно нулю.
Атрибут RuntimeVisibleAnnotations — это атрибут переменной длины в таблице attributes структуры ClassFile, field_info или method_info (§4.1, §4.5, §4.6). Атрибут RuntimeVisibleAnnotations записывает аннотации, видимые во время выполнения, при объявлении соответствующего класса, поля или метода. Виртуальная машина Java должна обеспечить доступ к этим аннотациям, чтобы они могли быть возвращены соответствующими рефлексивными API.
В таблице attributes структуры ClassFile, field_info или method_info может быть не более одного атрибута RuntimeVisibleAnnotations.
Атрибут RuntimeVisibleAnnotations имеет следующий формат:
RuntimeVisibleAnnotations_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 num_annotations;
annotation annotations[num_annotations];
}
Элементы структуры RuntimeVisibleAnnotations_attribute следующие:
- attribute_name_index
-
Значение элемента
attribute_name_indexдолжно быть допустимым индексом в таблицеconstant_pool. Записьconstant_poolв этом индексе должна быть структуройCONSTANT_Utf8_info(§4.4.7), представляющей строку "RuntimeVisibleAnnotations". - attribute_length
-
Значение элемента
attribute_lengthуказывает длину атрибута, за исключением начальных шести байтов. - num_annotations
-
Значение элемента
num_annotationsзадаёт количество аннотаций, видимых во время выполнения, представленных структурой. - annotations[]
-
Каждая запись в таблице
annotationsпредставляет отдельную аннотацию, видимую во время выполнения, в объявлении. Структураannotationимеет следующий формат:annotation { u2 type_index; u2 num_element_value_pairs; { u2 element_name_index; element_value value; } element_value_pairs[num_element_value_pairs]; }Элементы структуры
annotationследующие:- type_index
-
Значение элемента
type_indexдолжно быть допустимым индексом в таблицеconstant_pool. Записьconstant_poolв этом индексе должна быть структуройCONSTANT_Utf8_info(§4.4.7), представляющей описатель поля (§4.3.2). Описатель поля обозначает тип аннотации, представленной этой структуройannotation. - num_element_value_pairs
-
Значение элемента
num_element_value_pairsзадаёт количество пар имя-значение для аннотации, представленной этой структуройannotation. - element_value_pairs[]
-
Каждое значение таблицы
element_value_pairsпредставляет одну пару имя-значение в аннотации, представленной этой структуройannotation. Каждая записьelement_value_pairsсодержит следующие два элемента:- element_name_index
-
Значение элемента
element_name_indexдолжно быть допустимым индексом в таблицеconstant_pool. Записьconstant_poolв этом индексе должна быть структуройCONSTANT_Utf8_info(§4.4.7). Записьconstant_poolобозначает имя элемента пары имя-значение, представленной этой записьюelement_value_pairs.Другими словами, запись обозначает элемент типа аннотации, указанного в
type_index. - value
-
Значение элемента
valueпредставляет значение пары имя-значение, представленной этой записьюelement_value_pairs.
Структура element_value — это разновидность, представляющая значение пары "элемент-значение". Её формат следующий:
element_value {
u1 tag;
union {
u2 const_value_index;
{ u2 type_name_index;
u2 const_name_index;
} enum_const_value;
u2 class_info_index;
annotation annotation_value;
{ u2 num_values;
element_value values[num_values];
} array_value;
} value;
}
Элемент tag использует один символ ASCII для указания типа значения пары "элемент-значение". Это определяет, какой элемент объединения value используется. Таблица 4.7.16.1-A показывает допустимые символы для элемента tag, тип, указываемый каждым символом, и элемент, используемый в объединении value для каждого символа. Четвёртый столбец таблицы используется в описании одного элемента объединения value.
Таблица 4.7.16.1-A. Интерпретация значений тегов как типов
tag Элемент | Тип |
value Элемент | Тип константы |
|---|---|---|---|
B | byte | const_value_index | CONSTANT_Integer |
C | char | const_value_index | CONSTANT_Integer |
D | double | const_value_index | CONSTANT_Double |
F | float | const_value_index | CONSTANT_Float |
I | int | const_value_index | CONSTANT_Integer |
J | long | const_value_index | CONSTANT_Long |
S | short | const_value_index | CONSTANT_Integer |
Z | boolean | const_value_index | CONSTANT_Integer |
s | Тип перечисления | enum_const_value | Не применимо |
c | Class | class_info_index | Не применимо |
@ | Тип аннотации | annotation_value | Не применимо |
[ | Тип массива | array_value | Не применимо |
Элемент value представляет значение пары "элемент-значение". Элемент является объединением, чьи собственные элементы следующие:
- const_value_index
-
Элемент
const_value_indexобозначает либо примитивное постоянное значение, либоStringлитерал в качестве значения этой пары "элемент-значение".Значение элемента
const_value_indexдолжно быть корректным индексом в таблицеconstant_pool. Записьconstant_poolв этом индексе должна соответствовать типу элементаtag, как указано в четвёртом столбце таблицы 4.7.16.1-A. - enum_const_value
-
Элемент
enum_const_valueобозначает константу перечисления как значение этой пары "элемент-значение".Элемент
enum_const_valueсостоит из следующих двух элементов:- type_name_index
-
Значение элемента
type_name_indexдолжно быть корректным индексом в таблицеconstant_pool. Записьconstant_poolв этом индексе должна быть структуройCONSTANT_Utf8_info(§4.4.7), представляющей дескриптор поля (§4.3.2). Записьconstant_poolпредоставляет внутреннюю форму двоичного имени типа константы перечисления, представленной этой структуройelement_value(§4.2.1). - const_name_index
-
Значение элемента
const_name_indexдолжно быть корректным индексом в таблицеconstant_pool. Записьconstant_poolв этом индексе должна быть структуройCONSTANT_Utf8_info(§4.4.7). Записьconstant_poolпредоставляет простое имя константы перечисления, представленной этой структуройelement_value.
- class_info_index
-
Элемент
class_info_indexобозначает литерал класса как значение этой пары "элемент-значение".Элемент
class_info_indexдолжен быть корректным индексом в таблицеconstant_pool. Записьconstant_poolв этом индексе должна быть структуройCONSTANT_Utf8_info(§4.4.7), представляющей дескриптор возвращаемого значения (§4.3.3). Дескриптор возвращаемого значения даёт тип, соответствующий литералу класса, представленному этой структуройelement_value. Соответствие типов литералам классов:-
Для литерала класса C
.class, где C — имя класса, интерфейса или массива, соответствующим типом является C. Дескриптор возвращаемого значения вconstant_poolбудет ObjectType или ArrayType. -
Для литерала класса p
.class, где p — имя примитивного типа, соответствующим типом является p. Дескриптор возвращаемого значения вconstant_poolбудет символом BaseType. -
Для литерала класса
void.class, соответствующим типом являетсяvoid. Дескриптор возвращаемого значения вconstant_poolбудет V.
Например, литерал класса
Object.classсоответствует типуObject, поэтому записьconstant_poolравнаLjava/lang/Object;, тогда как литерал классаint.classсоответствует типуint, поэтому записьconstant_poolравнаI.Литерал класса
void.classсоответствует типуvoid, поэтому записьconstant_poolравна V, тогда как литерал классаVoid.classсоответствует типуVoid, поэтому записьconstant_poolравнаLjava/lang/Void;. -
- annotation_value
-
Элемент
annotation_valueобозначает "вложенную" аннотацию как значение этой пары "элемент-значение".Значение элемента
annotation_value— это структураannotation(§4.7.16), которая задаёт аннотацию, представленную этой структуройelement_value. - array_value
-
Элемент
array_valueобозначает массив как значение этой пары "элемент-значение".Элемент
array_valueсостоит из следующих двух элементов:- num_values
-
Значение элемента
num_valuesдаёт количество элементов в массиве, представленном этой структуройelement_value. - values[]
-
Каждое значение в таблице
valuesдаёт соответствующий элемент массива, представленного этой структуройelement_value.
Атрибут RuntimeInvisibleAnnotations — это атрибут переменной длины в таблице attributes структуры ClassFile, field_info или method_info (§4.1, §4.5, §4.6). Атрибут RuntimeInvisibleAnnotations записывает невидимые во время выполнения аннотации в объявлении соответствующего класса, метода или поля.
В таблице attributes структуры ClassFile, field_info или method_info может быть, но не обязательно, присутствовать не более одного атрибута RuntimeInvisibleAnnotations.
Атрибут RuntimeInvisibleAnnotations аналогичен атрибуту RuntimeVisibleAnnotations (§4.7.16), за исключением того, что аннотации, представленные атрибутом RuntimeInvisibleAnnotations, не должны быть доступны для возврата отражающими API, если виртуальная машина Java не получила инструкцию сохранить эти аннотации с помощью какого-либо механизма, специфичного для реализации, такого как флаг командной строки. В противном случае виртуальная машина Java игнорирует этот атрибут.
Атрибут RuntimeInvisibleAnnotations имеет следующий формат:
RuntimeInvisibleAnnotations_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 num_annotations;
annotation annotations[num_annotations];
}
Элементы структуры RuntimeInvisibleAnnotations_attribute следующие:
- attribute_name_index
-
Значение элемента
attribute_name_indexдолжно быть допустимым индексом в таблицеconstant_pool. Записьconstant_poolв этом индексе должна быть структуройCONSTANT_Utf8_info(§4.4.7), представляющей строку "RuntimeInvisibleAnnotations". - attribute_length
-
Значение элемента
attribute_lengthуказывает длину атрибута, за исключением начальных шести байтов. - num_annotations
-
Значение элемента
num_annotationsопределяет количество аннотаций, невидимых во время выполнения, представленных структурой. - annotations[]
-
Каждая запись в таблице
annotationsпредставляет собой отдельную аннотацию, невидимую во время выполнения, в объявлении. Структураannotationописана в §4.7.16.
Атрибут RuntimeVisibleParameterAnnotations — это атрибут переменной длины в таблице attributes структуры method_info (§4.6). Атрибут RuntimeVisibleParameterAnnotations записывает аннотации, видимые во время выполнения, в объявлениях формальных параметров соответствующего метода. Виртуальная машина Java должна сделать эти аннотации доступными для возврата соответствующими отражающими API.
В таблице attributes структуры method_info может быть не более одного атрибута RuntimeVisibleParameterAnnotations.
Атрибут RuntimeVisibleParameterAnnotations имеет следующий формат:
RuntimeVisibleParameterAnnotations_attribute {
u2 attribute_name_index;
u4 attribute_length;
u1 num_parameters;
{ u2 num_annotations;
annotation annotations[num_annotations];
} parameter_annotations[num_parameters];
}
Элементы структуры RuntimeVisibleParameterAnnotations_attribute следующие:
- attribute_name_index
-
Значение элемента
attribute_name_indexдолжно быть допустимым индексом в таблицеconstant_pool. Записьconstant_poolв этом индексе должна быть структуройCONSTANT_Utf8_info(§4.4.7), представляющей строку "RuntimeVisibleParameterAnnotations". - attribute_length
-
Значение элемента
attribute_lengthуказывает длину атрибута, за исключением начальных шести байтов. - num_parameters
-
Значение элемента
num_parametersзадает количество формальных параметров метода, представленного структуройmethod_info, к которому применяется аннотация.Эта информация дублируется и может быть извлечена из описания метода.
- parameter_annotations[]
-
Каждая запись в таблице
parameter_annotationsпредставляет все аннотации, видимые во время выполнения, в объявлении отдельного формального параметра. i-ая запись в таблице соответствует i-му формальному параметру в описании метода (§4.3.3). Каждая записьparameter_annotationsсодержит следующие два элемента:- num_annotations
-
Значение элемента
num_annotationsуказывает количество аннотаций, видимых во время выполнения, в объявлении формального параметра, соответствующего записиparameter_annotations. - annotations[]
-
Каждая запись в таблице
annotationsпредставляет собой отдельную аннотацию, видимую во время выполнения, в объявлении формального параметра, соответствующего записиparameter_annotations. Структураannotationописана в §4.7.16.
Атрибут RuntimeInvisibleParameterAnnotations — это атрибут переменной длины в таблице attributes структуры method_info (§4.6). Атрибут RuntimeInvisibleParameterAnnotations записывает невидимые во время выполнения аннотации в объявлениях формальных параметров соответствующего метода.
В таблице attributes структуры method_info может быть не более одного атрибута RuntimeInvisibleParameterAnnotations.
Атрибут RuntimeInvisibleParameterAnnotations аналогичен атрибуту RuntimeVisibleParameterAnnotations (§4.7.18), за исключением того, что аннотации, представленные атрибутом RuntimeInvisibleParameterAnnotations, не должны быть доступны для возврата отражающими API, если виртуальная машина Java специально не получила инструкцию сохранить эти аннотации с помощью какого-либо механизма, специфичного для реализации, такого как флаг командной строки. В противном случае виртуальная машина Java игнорирует этот атрибут.
Атрибут RuntimeInvisibleParameterAnnotations имеет следующий формат:
RuntimeInvisibleParameterAnnotations_attribute {
u2 attribute_name_index;
u4 attribute_length;
u1 num_parameters;
{ u2 num_annotations;
annotation annotations[num_annotations];
} parameter_annotations[num_parameters];
}
Элементы структуры RuntimeInvisibleParameterAnnotations_attribute следующие:
- attribute_name_index
-
Значение элемента
attribute_name_indexдолжно быть допустимым индексом в таблицеconstant_pool. Записьconstant_poolв этом индексе должна быть структуройCONSTANT_Utf8_info(§4.4.7), представляющей строку "RuntimeInvisibleParameterAnnotations". - attribute_length
-
Значение элемента
attribute_lengthуказывает длину атрибута, за исключением начальных шести байтов. - num_parameters
-
Значение элемента
num_parametersзадает количество формальных параметров метода, представленного структуройmethod_info, к которому применяется аннотация.Эта информация дублируется и может быть извлечена из описания метода.
- parameter_annotations[]
-
Каждая запись в таблице
parameter_annotationsпредставляет все невидимые во время выполнения аннотации в объявлении отдельного формального параметра. i-ая запись в таблице соответствует i-му формальному параметру в описании метода (§4.3.3). Каждая записьparameter_annotationsсодержит следующие два элемента:- num_annotations
-
Значение элемента
num_annotationsуказывает количество аннотаций, невидимых во время выполнения, в объявлении формального параметра, соответствующего записиparameter_annotations. - annotations[]
-
Каждая запись в таблице
annotationsпредставляет собой отдельную аннотацию, невидимую во время выполнения, в объявлении формального параметра, соответствующего записиparameter_annotations. Структураannotationописана в §4.7.16.
Атрибут RuntimeVisibleTypeAnnotations — это атрибут переменной длины в таблице attributes структуры ClassFile, field_info или method_info, или атрибуте Code (§4.1, §4.5, §4.6, §4.7.3). Атрибут RuntimeVisibleTypeAnnotations записывает видимые во время выполнения аннотации типов, используемых в объявлении соответствующего класса, поля или метода, или в выражении в теле соответствующего метода. Атрибут RuntimeVisibleTypeAnnotations также записывает видимые во время выполнения аннотации объявлений параметра типа для обобщенных классов, интерфейсов, методов и конструкторов. Виртуальная машина Java должна обеспечить доступность этих аннотаций, чтобы их можно было получить с помощью соответствующих рефлексивных API.
В таблице attributes структуры ClassFile, field_info или method_info, или атрибуте Code может быть не более одного атрибута RuntimeVisibleTypeAnnotations.
Таблица attributes содержит атрибут RuntimeVisibleTypeAnnotations только в том случае, если типы аннотированы в типах объявлений или выражений, соответствующих родительской структуре или атрибуту таблицы attributes.
Например, все аннотации типов в пункте implements объявления класса записываются в атрибут RuntimeVisibleTypeAnnotations структуры ClassFile класса. В то же время, все аннотации типа в объявлении поля записываются в атрибут RuntimeVisibleTypeAnnotations структуры field_info поля.
Атрибут RuntimeVisibleTypeAnnotations имеет следующий формат:
RuntimeVisibleTypeAnnotations_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 num_annotations;
type_annotation annotations[num_annotations];
}
Элементы структуры RuntimeVisibleTypeAnnotations_attribute следующие:
- attribute_name_index
-
Значение элемента
attribute_name_indexдолжно быть допустимым индексом в таблицеconstant_pool. Элементconstant_poolв этом индексе должен быть структуройCONSTANT_Utf8_info, представляющей строку "RuntimeVisibleTypeAnnotations". - attribute_length
-
Значение элемента
attribute_lengthуказывает длину атрибута, за исключением начальных шести байтов. - num_annotations
-
Значение элемента
num_annotationsзадаёт количество видимых во время выполнения аннотаций типов, представленных структурой. - annotations[]
-
Каждый элемент таблицы
annotationsпредставляет собой отдельную видимую во время выполнения аннотацию типа, используемого в объявлении или выражении. Структураtype_annotationимеет следующий формат:type_annotation { u1 target_type; union { type_parameter_target; supertype_target; type_parameter_bound_target; empty_target; method_formal_parameter_target; throws_target; localvar_target; catch_target; offset_target; type_argument_target; } target_info; type_path target_path; u2 type_index; u2 num_element_value_pairs; { u2 element_name_index; element_value value; } element_value_pairs[num_element_value_pairs]; }Первые три элемента -
target_type,target_infoиtarget_path- указывают точное местоположение аннотированного типа. Последние три элемента -type_index,num_element_value_pairsиelement_value_pairs[]- указывают тип самой аннотации и пары значений элементов.Элементы структуры
type_annotationследующие:- target_type
-
Значение элемента
target_typeобозначает вид целевого объекта, на котором появляется аннотация. Различные виды целевых объектов соответствуют контекстам типов языка программирования Java, где типы используются в объявлениях и выражениях (JLS §4.11).Допустимые значения
target_typeуказаны в таблице 4.7.20-A и таблице 4.7.20-B. Каждое значение — однобайтовый тег, указывающий, какой элемент объединенияtarget_infoследует за элементомtarget_type, чтобы предоставить дополнительную информацию о целевом объекте.Виды целевых объектов в таблице 4.7.20-A и таблице 4.7.20-B соответствуют контекстам типов в JLS §4.11. А именно, значения
target_type0x10-0x17 и 0x40-0x42 соответствуют контекстам типов 1-10, а значенияtarget_type0x43-0x4B соответствуют контекстам типов 11-16.Значение элемента
target_typeопределяет, появляется ли структураtype_annotationв атрибутеRuntimeVisibleTypeAnnotationsв структуреClassFile, структуреfield_info, структуреmethod_infoили атрибутеCode. Таблица 4.7.20-C указывает расположение атрибутаRuntimeVisibleTypeAnnotationsдля структурыtype_annotationс каждым допустимым значениемtarget_type. - target_info
-
Значение элемента
target_infoточно указывает, какой тип в объявлении или выражении аннотирован.Элементы объединения
target_infoуказаны в §4.7.20.1. - target_path
-
Значение элемента
target_pathточно указывает, какая часть типа, указанногоtarget_info, аннотирована.Формат структуры
type_pathуказан в §4.7.20.2. - type_index, num_element_value_pairs, element_value_pairs[]
-
Значение этих элементов в структуре
type_annotationтакое же, как и их значение в структуреannotation(§4.7.16).
Таблица 4.7.20-A. Интерпретация значений target_type (Часть 1)
| Значение | Вид целевого объекта |
target_info элемент |
|---|---|---|
| 0x00 | объявление параметра типа обобщенного класса или интерфейса | type_parameter_target |
| 0x01 | объявление параметра типа обобщенного метода или конструктора | type_parameter_target |
| 0x10 | тип в пункте extends или implements объявления класса (включая непосредственного суперкласса или непосредственного суперинтерфейса объявления анонимного класса) или в пункте extends объявления интерфейса | supertype_target |
| 0x11 | тип в ограничении объявления параметра типа обобщенного класса или интерфейса | type_parameter_bound_target |
| 0x12 | тип в ограничении объявления параметра типа обобщенного метода или конструктора | type_parameter_bound_target |
| 0x13 | тип в объявлении поля | empty_target |
| 0x14 | тип возвращаемого значения метода или тип вновь созданного объекта | empty_target |
| 0x15 | тип получателя метода или конструктора | empty_target |
| 0x16 | тип в объявлении формального параметра метода, конструктора или лямбда-выражения | formal_parameter_target |
| 0x17 | тип в пункте throws метода или конструктора | throws_target |
Таблица 4.7.20-B. Интерпретация значений target_type (Часть 2)
| Значение | Вид целевого объекта |
target_info элемент |
|---|---|---|
| 0x40 | тип в объявлении локальной переменной | localvar_target |
| 0x41 | тип в объявлении переменной ресурса | localvar_target |
| 0x42 | тип в объявлении параметра исключения | catch_target |
| 0x43 | тип в выражении instanceof | offset_target |
| 0x44 | тип в выражении new | offset_target |
| 0x45 | тип в выражении ссылки на метод, использующем ::new
| offset_target |
| 0x46 | тип в выражении ссылки на метод, использующем ::Identifier
| offset_target |
| 0x47 | тип в выражении приведения типа | type_argument_target |
| 0x48 | аргумент типа для обобщенного конструктора в выражении new или явном операторе вызова конструктора | type_argument_target |
| 0x49 | аргумент типа для обобщенного метода в выражении вызова метода | type_argument_target |
| 0x4A | аргумент типа для обобщенного конструктора в выражении ссылки на метод, использующем ::new
| type_argument_target |
| 0x4B | аргумент типа для обобщенного метода в выражении ссылки на метод, использующем ::Identifier
| type_argument_target |
Таблица 4.7.20-C. Позиция атрибута, окружающего значения target_type
| Значение | Тип целевого объекта | Позиция |
|---|---|---|
| 0x00 | объявление параметра типа обобщенного класса или интерфейса | ClassFile |
| 0x01 | объявление параметра типа обобщенного метода или конструктора | method_info |
| 0x10 | тип в части extends объявления класса или интерфейса или в части implements объявления интерфейса | ClassFile |
| 0x11 | тип в ограничении объявления параметра типа обобщенного класса или интерфейса | ClassFile |
| 0x12 | тип в ограничении объявления параметра типа обобщенного метода или конструктора | method_info |
| 0x13 | тип в объявлении поля | field_info |
| 0x14 | тип возвращаемого значения метода или конструктора | method_info |
| 0x15 | тип получателя метода или конструктора | method_info |
| 0x16 | тип в объявлении формального параметра метода, конструктора или лямбда-выражения | method_info |
| 0x17 | тип в части throws объявления метода или конструктора | method_info |
| 0x40-0x4B | типы в объявлениях локальных переменных, переменных ресурсов, параметрах исключений, выражениях | Code |
Элементы объединения target_info (кроме первого) точно указывают, какой тип в объявлении или выражении аннотирован. Первый элемент указывает не какой тип, а какое объявление параметра типа аннотировано. Элементы следующие:
-
Элемент
type_parameter_targetуказывает, что аннотация появляется на объявлении i-го параметра типа обобщённого класса, обобщённого интерфейса, обобщённого метода или обобщённого конструктора.type_parameter_target { u1 type_parameter_index; }Значение элемента
type_parameter_indexуказывает, какое объявление параметра типа аннотировано. Значениеtype_parameter_index, равное0, указывает первое объявление параметра типа. -
Элемент
supertype_targetуказывает, что аннотация появляется на типе в разделеextendsилиimplementsобъявления класса или интерфейса.supertype_target { u2 supertype_index; }Значение
supertype_index, равное 65535, указывает, что аннотация появляется на суперклассе в разделеextendsобъявления класса.Любое другое значение
supertype_index— это индекс в массивеinterfaces, содержащемся в обозначающей структуреClassFile, и указывает, что аннотация появляется на соответствующем суперинтерфейсе в разделеimplementsобъявления класса или разделеextendsобъявления интерфейса. -
Элемент
type_parameter_bound_targetуказывает, что аннотация появляется на i-м ограничении j-го объявления параметра типа обобщённого класса, интерфейса, метода или конструктора.type_parameter_bound_target { u1 type_parameter_index; u1 bound_index; }Значение элемента
type_parameter_indexуказывает, какое объявление параметра типа имеет аннотированное ограничение. Значениеtype_parameter_index, равное0, указывает первое объявление параметра типа.Значение элемента
bound_indexуказывает, какое ограничение объявления параметра типа, указанного элементомtype_parameter_index, аннотировано. Значениеbound_index, равное0, указывает первое ограничение объявления параметра типа.Элемент
type_parameter_bound_targetзаписывает, что ограничение аннотировано, но не записывает сам тип, являющийся ограничением. Тип можно найти, проанализировав сигнатуру класса или метода, хранящуюся в соответствующем атрибутеSignature. -
Элемент
empty_targetуказывает, что аннотация появляется на типе в объявлении поля, типе возвращаемого значения метода, типе вновь созданного объекта или типе получателя метода или конструктора.empty_target { }В каждом из этих мест появляется только один тип, поэтому в объединении
target_infoнет информации о каждом типе. -
Элемент
formal_parameter_targetуказывает, что аннотация появляется на типе в объявлении формального параметра метода, конструктора или лямбда-выражения.formal_parameter_target { u1 formal_parameter_index; }Значение элемента
formal_parameter_indexуказывает, объявление какого формального параметра имеет аннотированный тип. Значениеformal_parameter_index, равное0, указывает первое объявление формального параметра.Элемент
formal_parameter_targetзаписывает, что тип формального параметра аннотирован, но сам тип не записывает. Тип можно найти, проанализировав дескриптор метода (§4.3.3) структурыmethod_info, содержащей атрибутRuntimeVisibleTypeAnnotations. Значениеformal_parameter_index, равное0, указывает на первый дескриптор параметра в дескрипторе метода. -
Элемент
throws_targetуказывает, что аннотация появляется на i-м типе в разделеthrowsобъявления метода или конструктора.throws_target { u2 throws_type_index; }Значение элемента
throws_type_index— это индекс в массивеexception_index_tableатрибутаExceptionsструктурыmethod_info, содержащей атрибутRuntimeVisibleTypeAnnotations. -
Элемент
localvar_targetуказывает, что аннотация появляется на типе в объявлении локальной переменной, включая переменную, объявленную как ресурс в оператореtry-с-ресурсами.localvar_target { u2 table_length; { u2 start_pc; u2 length; u2 index; } table[table_length]; }Значение элемента
table_lengthзадаёт количество элементов в массивеtable. Каждый элемент указывает диапазон смещений в массивеcode, в котором локальная переменная имеет значение. Он также указывает индекс в массиве локальных переменных текущей рамки, в котором находится эта локальная переменная. Каждый элемент содержит следующие три значения:- start_pc, length
-
Указанная локальная переменная имеет значение в индексах в массиве
codeв интервале [start_pc,start_pc + length), то есть междуstart_pcвключительно иstart_pc + lengthисключая. - index
-
Указанная локальная переменная должна находиться в позиции
indexв массиве локальных переменных текущей рамки.Если локальная переменная в позиции
indexимеет типdoubleилиlong, она занимает позицииindexиindex + 1.
Таблица необходима для полного указания локальной переменной, тип которой аннотирован, потому что одна локальная переменная может быть представлена различными индексами локальных переменных в течение нескольких активных диапазонов. Элементы
start_pc,lengthиindexв каждом элементе таблицы указывают ту же информацию, что и атрибутLocalVariableTable.Элемент
localvar_targetзаписывает, что тип локальной переменной аннотирован, но сам тип не записывает. Тип можно найти, проанализировав соответствующий атрибутLocalVariableTable. -
Элемент
catch_targetуказывает, что аннотация появляется на i-м типе в объявлении параметра исключения.catch_target { u2 exception_table_index; }Значение элемента
exception_table_index— это индекс в массивеexception_tableатрибутаCode, содержащего атрибутRuntimeVisibleTypeAnnotations.Возможность более одного типа в объявлении параметра исключения возникает из многократного раздела
catchоператораtry, где тип параметра исключения является объединением типов (JLS §14.20). Компилятор обычно создаёт один элементexception_tableдля каждого типа в объединении, что позволяет элементуcatch_targetих различать. Это сохраняет соответствие между типом и его аннотациями. -
Элемент
offset_targetуказывает, что аннотация появляется на типе в выражении instanceof или выражении new, или типе перед::в выражении ссылки на метод.offset_target { u2 offset; }Значение элемента
offsetуказывает смещение в массивеcodeлибо байткода инструкции instanceof, соответствующего выражению instanceof, либо байткода инструкции new, соответствующего выражению new, либо байткода инструкции, соответствующей выражению ссылки на метод. -
Элемент
type_argument_targetуказывает, что аннотация появляется либо на i-м типе в выражении приведения типа, либо на i-м аргументе типа в списке явных аргументов типа для следующих элементов: выражение new, явное обращение к конструктору, выражение вызова метода или выражение ссылки на метод.type_argument_target { u2 offset; u1 type_argument_index; }Значение элемента
offsetуказывает смещение в массивеcodeлибо байткода инструкции, соответствующей выражению приведения типа, байткода инструкции new, соответствующего выражению new, байткода инструкции, соответствующей явным вызовом конструктора, байткода инструкции, соответствующей выражению вызова метода, или байткода инструкции, соответствующей выражению ссылки на метод.Для выражения приведения типа значение элемента
type_argument_indexуказывает, какой тип в операторе приведения типа аннотирован. Значениеtype_argument_index, равное0, указывает первый (или единственный) тип в операторе приведения типа.Возможность более одного типа в выражении приведения типа возникает при приведении к пересечению типов.
Для списка явных аргументов типа значение элемента
type_argument_indexуказывает, какой аргумент типа аннотирован. Значениеtype_argument_index, равное0, указывает первый аргумент типа.
Всякий раз, когда тип используется в объявлении или выражении, структура type_path определяет, какая часть типа аннотирована. Аннотация может появляться в самом типе, но если тип является ссылочным типом, то есть дополнительные места, где может появиться аннотация:
-
Если тип массива T
[]используется в объявлении или выражении, то аннотация может появляться в любом типе компонента типа массива, включая тип элемента. -
Если в объявлении или выражении используется вложенный тип T1
.T2, то аннотация может появиться в имени типа верхнего уровня или любого типа-члена. -
Если в объявлении или выражении используется параметризованный тип T
<A>или T<? extends A>или T<? super A>, то аннотация может появиться в любом аргументе типа или в границе любого аргумента типа с подстановочным знаком.
Например, рассмотрим различные части String[][], которые аннотированы в:
@Foo String[][] // Annotates the class type String String @Foo [][] // Annotates the array type String[][] String[] @Foo [] // Annotates the array type String[]
или различные части вложенного типа Outer.Middle.Inner, которые аннотированы в:
@Foo Outer.Middle.Inner Outer.@Foo Middle.Inner Outer.Middle.@Foo Inner
или различные части параметризованных типов Map<String,Object> и List<...>, которые аннотированы в:
@Foo Map<String,Object> Map<@Foo String,Object> Map<String,@Foo Object> List<@Foo ? extends String> List<? extends @Foo String>
Структура type_path имеет следующий формат:
type_path {
u1 path_length;
{ u1 type_path_kind;
u1 type_argument_index;
} path[path_length];
}
Значение элемента path_length дает количество записей в массиве path:
-
Если значение
path_lengthравно0, то аннотация появляется непосредственно в самом типе. -
Если значение
path_lengthненулевое, то каждая запись в массивеpathпредставляет собой итеративный, слева направо шаг к точному местоположению аннотации в типе массива, вложенном типе или параметризованном типе. (В типе массива итерация посещает сам тип массива, затем его тип компонента, затем тип компонента этого типа компонента и так далее, пока не будет достигнут тип элемента.) Каждая запись содержит следующие два элемента:- type_path_kind
-
Допустимые значения для элемента
type_path_kindперечислены в Таблица 4.7.20.2-A.Table 4.7.20.2-A. Interpretation of
type_path_kindvaluesЗначение Интерпретация 0Аннотация глубже в типе массива 1Аннотация глубже во вложенном типе 2Аннотация находится в границе аргумента типа с подстановочным знаком параметризованного типа 3Аннотация находится в аргументе типа параметризованного типа
- type_argument_index
-
Если значение элемента
type_path_kindравно0,1или2, то значение элементаtype_argument_indexравно0.Если значение элемента
type_path_kindравно3, то значение элементаtype_argument_indexуказывает, какой аргумент типа параметризованного типа аннотирован, где0указывает на первый аргумент типа параметризованного типа.
Table 4.7.20.2-B. type_path structures for @A Map<@B ? extends @C String, @D List<@E
Object>>
| Annotation | path_length | path |
|---|---|---|
@A | 0 | [] |
@B | 1 | [{type_path_kind: 3; type_argument_index: 0}] |
@C | 2 | [{type_path_kind: 3; type_argument_index: 0}, {type_path_kind: 2; type_argument_index: 0}] |
@D | 1 | [{type_path_kind: 3; type_argument_index: 1}] |
@E | 2 | [{type_path_kind: 3; type_argument_index: 1}, {type_path_kind: 3; type_argument_index: 0}] |
Table 4.7.20.2-C. type_path structures for @I String @F [] @G [] @H []
| Annotation | path_length | path |
|---|---|---|
@F | 0 | [] |
@G | 1 | [{type_path_kind: 0; type_argument_index: 0}] |
@H | 2 | [{type_path_kind: 0; type_argument_index: 0}, {type_path_kind: 0; type_argument_index: 0}] |
@I | 3 | [{type_path_kind: 0; type_argument_index: 0}, {type_path_kind: 0; type_argument_index: 0}, {type_path_kind: 0; type_argument_index:
0}] |
Table 4.7.20.2-D. type_path structures for @A List<@B Comparable<@F Object @C [] @D [] @E
[]>>
| Annotation | path_length | path |
|---|---|---|
@A | 0 | [] |
@B | 1 | [{type_path_kind: 3; type_argument_index: 0}] |
@C | 2 | [{type_path_kind: 3; type_argument_index: 0}, {type_path_kind: 3; type_argument_index: 0}] |
@D | 3 | [{type_path_kind: 3; type_argument_index: 0}, {type_path_kind: 3; type_argument_index: 0}, {type_path_kind: 0; type_argument_index:
0}] |
@E | 4 | [{type_path_kind: 3; type_argument_index: 0}, {type_path_kind: 3; type_argument_index: 0}, {type_path_kind: 0; type_argument_index:
0}, {type_path_kind: 0; type_argument_index: 0}] |
@F | 5 | [{type_path_kind: 3; type_argument_index: 0}, {type_path_kind: 3; type_argument_index: 0}, {type_path_kind: 0; type_argument_index:
0}, {type_path_kind: 0; type_argument_index: 0}, {type_path_kind: 0; type_argument_index: 0}] |
Table 4.7.20.2-E. type_path structures for @C Outer . @B Middle . @A Inner
| Annotation | path_length | path |
|---|---|---|
@A | 2 | [{type_path_kind: 1; type_argument_index: 0}, {type_path_kind: 1; type_argument_index: 0}] |
@B | 1 | [{type_path_kind: 1; type_argument_index: 0}] |
@C | 0 | [] |
Table 4.7.20.2-F. type_path structures for Outer . Middle<@D Foo . @C Bar> . Inner<@B
String @A []>
| Annotation | path_length | path |
|---|---|---|
@A | 3 | [{type_path_kind: 1; type_argument_index: 0}, {type_path_kind: 1; type_argument_index: 0}, {type_path_kind: 3; type_argument_index:
0}] |
@B | 4 | [{type_path_kind: 1; type_argument_index: 0}, {type_path_kind: 1; type_argument_index: 0}, {type_path_kind: 3; type_argument_index:
0}, {type_path_kind: 0; type_argument_index: 0}] |
@C | 3 | [{type_path_kind: 1; type_argument_index: 0}, {type_path_kind: 3; type_argument_index: 0}, {type_path_kind: 1; type_argument_index:
0}] |
@D | 2 | [{type_path_kind: 1; type_argument_index: 0}, {type_path_kind: 3; type_argument_index: 0}] |
Атрибут RuntimeInvisibleTypeAnnotations — атрибут переменной длины в таблице attributes структуры ClassFile, field_info или method_info, или атрибута Code (§4.1, §4.5, §4.6, §4.7.3). Атрибут RuntimeInvisibleTypeAnnotations записывает невидимые во время выполнения аннотации на типы, используемые в соответствующем объявлении класса, поля или метода, или в выражении в соответствующем теле метода. Атрибут RuntimeInvisibleTypeAnnotations также записывает аннотации на объявления параметров типа обобщённых классов, интерфейсов, методов и конструкторов.
В таблице attributes структуры ClassFile, field_info или method_info, или атрибута Code может быть не более одного атрибута RuntimeInvisibleTypeAnnotations.
Таблица attributes содержит атрибут RuntimeInvisibleTypeAnnotations только в том случае, если типы аннотированы в видах объявления или выражения, соответствующих родительской структуре или атрибуту таблицы attributes.
Атрибут RuntimeInvisibleTypeAnnotations имеет следующий формат:
RuntimeInvisibleTypeAnnotations_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 num_annotations;
type_annotation annotations[num_annotations];
}
Элементы структуры RuntimeInvisibleTypeAnnotations_attribute следующие:
- attribute_name_index
-
Значение элемента
attribute_name_indexдолжно быть допустимым индексом в таблицеconstant_pool. Элементconstant_poolв этой таблице должен быть структуройCONSTANT_Utf8_info, представляющей строку "RuntimeInvisibleTypeAnnotations". - attribute_length
-
Значение элемента
attribute_lengthуказывает длину атрибута, за исключением первых шести байтов. - num_annotations
-
Значение элемента
num_annotationsзадаёт количество аннотаций типа, невидимых во время выполнения, представленных структурой. - annotations[]
-
Каждый элемент таблицы
annotationsпредставляет собой одну отдельную аннотацию типа, невидимую во время выполнения, используемую в объявлении или выражении. Структураtype_annotationзадана в §4.7.20.
Атрибут AnnotationDefault — атрибут переменной длины в таблице attributes некоторых структур method_info (§4.6), а именно тех, которые представляют элементы типов аннотаций (JLS §9.6.1). Атрибут AnnotationDefault записывает значение по умолчанию (JLS §9.6.2) для элемента, представленного структурой method_info. Виртуальная машина Java должна сделать это значение по умолчанию доступным, чтобы его можно было применить соответствующими рефлексивными API.
В таблице attributes структуры method_info, представляющей элемент типа аннотации, может быть не более одного атрибута AnnotationDefault.
Атрибут AnnotationDefault имеет следующий формат:
AnnotationDefault_attribute {
u2 attribute_name_index;
u4 attribute_length;
element_value default_value;
}
Элементы структуры AnnotationDefault_attribute следующие:
- attribute_name_index
-
Значение элемента
attribute_name_indexдолжно быть допустимым индексом в таблицеconstant_pool. Элементconstant_poolв этой таблице должен быть структуройCONSTANT_Utf8_info(§4.4.7), представляющей строку "AnnotationDefault". - attribute_length
-
Значение элемента
attribute_lengthуказывает длину атрибута, за исключением первых шести байтов. - default_value
-
Элемент
default_valueпредставляет значение по умолчанию элемента типа аннотации, представленного структуройmethod_info, содержащей этот атрибутAnnotationDefault.
Атрибут BootstrapMethods — атрибут переменной длины в таблице attributes структуры ClassFile (§4.1). Атрибут BootstrapMethods записывает спецификаторы методов инициализации, на которые ссылаются инструкции invokedynamic (§invokedynamic).
В таблице attributes структуры ClassFile должен быть ровно один атрибут BootstrapMethods, если таблица ClassFile структуры constant_pool содержит по крайней мере один элемент CONSTANT_InvokeDynamic_info (§4.4.10).
В таблице attributes структуры ClassFile может быть не более одного атрибута BootstrapMethods.
Атрибут BootstrapMethods имеет следующий формат:
BootstrapMethods_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 num_bootstrap_methods;
{ u2 bootstrap_method_ref;
u2 num_bootstrap_arguments;
u2 bootstrap_arguments[num_bootstrap_arguments];
} bootstrap_methods[num_bootstrap_methods];
}
Элементы структуры BootstrapMethods_attribute следующие:
- attribute_name_index
-
Значение элемента
attribute_name_indexдолжно быть допустимым индексом в таблицеconstant_pool. Элементconstant_poolв этой таблице должен быть структуройCONSTANT_Utf8_info(§4.4.7), представляющей строку "BootstrapMethods". - attribute_length
-
Значение элемента
attribute_lengthуказывает длину атрибута, за исключением первых шести байтов.Значение элемента
attribute_lengthзависит от количества инструкций invokedynamic в данной структуреClassFile. - num_bootstrap_methods
-
Значение элемента
num_bootstrap_methodsопределяет количество спецификаторов методов инициализации в массивеbootstrap_methods. - bootstrap_methods[]
-
Каждый элемент таблицы
bootstrap_methodsсодержит индекс к структуреCONSTANT_MethodHandle_info(§4.4.8), которая определяет метод инициализации, и последовательность (возможно, пустую) индексов к статическим аргументам для метода инициализации.Каждый элемент
bootstrap_methodsдолжен содержать следующие три элемента:- bootstrap_method_ref
-
Значение элемента
bootstrap_method_refдолжно быть допустимым индексом в таблицеconstant_pool. Элементconstant_poolв этой таблице должен быть структуройCONSTANT_MethodHandle_info(§4.4.8).Форма дескриптора метода определяется продолжающимся разрешением спецификатора места вызова в §invokedynamic, где выполнение
invokeвjava.lang.invoke.MethodHandleтребует, чтобы дескриптор метода инициализации подстраивался под фактические передаваемые аргументы, как если бы он был вызванjava.lang.invoke.MethodHandle.asType. Соответственно, элементreference_kindструктурыCONSTANT_MethodHandle_infoдолжен иметь значение 6 или 8 (§5.4.3.5), а элементreference_indexдолжен указывать на статический метод или конструктор, принимающий три аргумента типаjava.lang.invoke.MethodHandles.Lookup,Stringиjava.lang.invoke.MethodTypeв указанном порядке. В противном случае вызов метода инициализации во время разрешения спецификатора места вызова завершится неожиданно. - num_bootstrap_arguments
-
Значение элемента
num_bootstrap_argumentsопределяет количество элементов в массивеbootstrap_arguments. - bootstrap_arguments[]
-
Каждый элемент массива
bootstrap_argumentsдолжен быть допустимым индексом в таблицеconstant_pool. Элементconstant_poolв этой таблице должен быть структуройCONSTANT_String_info,CONSTANT_Class_info,CONSTANT_Integer_info,CONSTANT_Long_info,CONSTANT_Float_info,CONSTANT_Double_info,CONSTANT_MethodHandle_infoилиCONSTANT_MethodType_info(§4.4.3, §4.4.1, §4.4.4, §4.4.5, §4.4.8, §4.4.9).
Атрибут MethodParameters — атрибут переменной длины в таблице attributes структуры method_info (§4.6). Атрибут MethodParameters записывает информацию о формальных параметрах метода, например, их названия.
В таблице attributes структуры method_info может быть не более одного атрибута MethodParameters.
Атрибут MethodParameters имеет следующий формат:
MethodParameters_attribute {
u2 attribute_name_index;
u4 attribute_length;
u1 parameters_count;
{ u2 name_index;
u2 access_flags;
} parameters[parameters_count];
}
Элементы структуры MethodParameters_attribute следующие:
- attribute_name_index
-
Значение элемента
attribute_name_indexдолжно быть корректным индексом в таблицеconstant_pool. Элементconstant_poolв этом индексе должен быть структуройCONSTANT_Utf8_info, представляющей строку "MethodParameters". - attribute_length
-
Значение элемента
attribute_lengthуказывает длину атрибута, исключая первые шесть байтов. - parameters_count
-
Значение элемента
parameters_countуказывает количество описателей параметров в описателе метода (§4.3.3), на который ссылается элементdescriptor_indexокружающей структурыmethod_infoатрибута.Это не является ограничением, которое должна проверять реализация виртуальной машины Java во время проверки формата (§4.8). Задача сопоставления описателей параметров в описателе метода с элементами в массиве
parametersниже выполняется библиотеками рефлексии платформы Java SE. - parameters[]
-
Каждый элемент массива
parametersсодержит следующую пару элементов:- name_index
-
Значение элемента
name_indexдолжно быть либо нулем, либо корректным индексом в таблицеconstant_pool.Если значение элемента
name_indexравно нулю, то этот элементparametersуказывает формальный параметр без имени.Если значение элемента
name_indexне равно нулю, то элементconstant_poolв этом индексе должен быть структуройCONSTANT_Utf8_info, представляющей допустимое неопределенное имя, обозначающее формальный параметр (§4.2.2). - access_flags
-
Значение элемента
access_flagsследующее:- 0x0010 (
ACC_FINAL) -
Указывает, что формальный параметр был объявлен
final. - 0x1000 (
ACC_SYNTHETIC) -
Указывает, что формальный параметр не был явно или неявно объявлен в исходном коде в соответствии со спецификацией языка, на котором был написан исходный код (JLS §13.1). (Формальный параметр — это артефакт реализации компилятора, который создал этот файл
class.) - 0x8000 (
ACC_MANDATED) -
Указывает, что формальный параметр был неявно объявлен в исходном коде в соответствии со спецификацией языка, на котором был написан исходный код (JLS §13.1). (Формальный параметр предписывается спецификацией языка, поэтому все компиляторы языка должны его генерировать.)
- 0x0010 (
Элемент с индексом i в массиве parameters соответствует i-му описателю параметра в описателе метода. (Элемент parameters_count имеет один байт, так как описание метода ограничено 255 параметрами.) Эффективно, это означает, что массив parameters хранит информацию обо всех параметрах метода. Можно представить и другие схемы, где элементы в массиве parameters указывают на соответствующие описатели параметров, но это сделало бы атрибут MethodParameters излишне сложным.
Элемент с индексом i в массиве parameters может или не может соответствовать i-му типу в атрибуте Signature окружающего метода (если он присутствует), или i-й аннотации в аннотациях параметров окружающего метода.
Когда виртуальная машина Java загружает потенциальный файл class (§5.3), она сначала проверяет, что файл имеет основной формат файла class (§4.1). Этот процесс называется проверкой формата. Проверки выполняются следующим образом:
-
Первые четыре байта должны содержать правильное магическое число.
-
Все распознанные атрибуты должны иметь правильную длину.
-
Файл
classне должен быть усеченным или иметь лишние байты в конце. -
Константный пул должен удовлетворять ограничениям, описанным в §4.4.
Например, каждая структура
CONSTANT_Class_infoв константном пуле должна содержать в своем элементеname_indexкорректный индекс константного пула для структурыCONSTANT_Utf8_info. -
Все ссылки на поля и методы в константном пуле должны иметь допустимые имена, допустимые классы и допустимые описатели (§4.3).
Проверка формата не гарантирует, что данное поле или метод фактически существуют в данном классе, ни то, что указанные описатели относятся к реальным классам. Проверка формата гарантирует только, что эти элементы имеют правильный формат. Более подробная проверка выполняется при верификации самих байткодов и при разрешении.
Эти проверки целостности основного файла class необходимы для любого толкования содержимого файла class. Проверка формата отличается от верификации байткодов, хотя исторически они были связаны, поскольку оба являются формой проверки целостности.
Код метода, метода инициализации экземпляра или метода инициализации класса или интерфейса (§2.9) хранится в массиве 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 не могут появляться в массивеcode. -
Код операции первой инструкции в массиве
codeначинается с индекса0. -
Для каждой инструкции в массиве
code, кроме последней, индекс кода операции следующей инструкции равен индексу кода операции текущей инструкции плюс длина этой инструкции, включая все ее операнды.Инструкция wide обрабатывается как любая другая инструкция в этих целях; код операции, указывающий операцию, которую инструкция wide должна изменить, обрабатывается как один из операндов этой инструкции wide. Этот код операции никогда не должен быть непосредственно доступен для вычислений.
-
Последний байт последней инструкции в массиве
codeдолжен быть байтом с индексомcode_length - 1.
Статические ограничения на операнды инструкций в массиве code следующие:
-
Цель каждой инструкции перехода и ветвления (jsr, jsr_w, goto, goto_w, ifeq, ifne, ifle, iflt, ifge, ifgt, ifnull, ifnonnull, if_icmpeq, if_icmpne, if_icmple, if_icmplt, if_icmpge, if_icmpgt, if_acmpeq, if_acmpne) должна быть операцией инструкции в этом методе.
Цель инструкции перехода или ветвления никогда не должна быть операцией, используемой для определения операции, которая будет изменена инструкцией wide; цель перехода или ветвления может быть самой инструкцией wide.
-
Каждая цель, включая значение по умолчанию, каждой инструкции tableswitch должна быть операцией инструкции в этом методе.
Каждая инструкция tableswitch должна иметь количество записей в своей таблице переходов, соответствующее значению ее операндов таблицы переходов low и high, и ее значение low должно быть меньше или равно ее значению high.
Никакая цель инструкции tableswitch не может быть операцией, используемой для определения операции, которая будет изменена инструкцией wide; цель tableswitch может быть самой инструкцией wide.
-
Каждая цель, включая значение по умолчанию, каждой инструкции lookupswitch должна быть операцией инструкции в этом методе.
Каждая инструкция lookupswitch должна иметь количество пар match-offset, соответствующее значению ее операнда npairs. Пары match-offset должны быть отсортированы в порядке возрастания по числовому значению со знаком.
Никакая цель инструкции lookupswitch не может быть операцией, используемой для определения операции, которая будет изменена инструкцией wide; цель lookupswitch может быть самой инструкцией wide.
-
Операнд каждой инструкции ldc и каждой инструкции ldc_w должен быть допустимым индексом в таблице
constant_pool. Запись пула констант, на которую ссылается этот индекс, должна иметь тип:-
CONSTANT_Integer,CONSTANT_FloatилиCONSTANT_String, если номер версии файлаclassменьше 49.0. -
CONSTANT_Integer,CONSTANT_Float,CONSTANT_StringилиCONSTANT_Class, если номер версии файлаclassравен 49.0 или 50.0. -
CONSTANT_Integer,CONSTANT_Float,CONSTANT_String,CONSTANT_Class,CONSTANT_MethodTypeилиCONSTANT_MethodHandle, если номер версии файлаclassравен 51.0 или выше.
-
-
Операнды каждой инструкции ldc2_w должны представлять допустимый индекс в таблице
constant_pool. Запись пула констант, на которую ссылается этот индекс, должна иметь типCONSTANT_LongилиCONSTANT_Double.Последующий индекс пула констант также должен быть допустимым индексом в пуле констант, и запись пула констант по этому индексу не должна использоваться.
-
Операнды каждой инструкции getfield, putfield, getstatic и putstatic должны представлять допустимый индекс в таблице
constant_pool. Запись пула констант, на которую ссылается этот индекс, должна иметь типCONSTANT_Fieldref. -
Операнды indexbyte каждой инструкции invokevirtual должны представлять допустимый индекс в таблице
constant_pool. Запись пула констант, на которую ссылается этот индекс, должна иметь типCONSTANT_Methodref. -
Операнды indexbyte каждой инструкции invokespecial и invokestatic должны представлять допустимый индекс в таблице
constant_pool. Если номер версии файлаclassменьше 52.0, запись пула констант, на которую ссылается этот индекс, должна иметь типCONSTANT_Methodref; если номер версии файлаclassравен 52.0 или выше, запись пула констант, на которую ссылается этот индекс, должна иметь типCONSTANT_MethodrefилиCONSTANT_InterfaceMethodref. -
Операнды indexbyte каждой инструкции invokeinterface должны представлять допустимый индекс в таблице
constant_pool. Запись пула констант, на которую ссылается этот индекс, должна иметь типCONSTANT_InterfaceMethodref.Значение операнда count каждой инструкции invokeinterface должно отражать количество локальных переменных, необходимых для хранения аргументов, которые будут переданы методу интерфейса, как подразумевается дескриптором структуры
CONSTANT_NameAndType_info, на которую ссылается запись пула константCONSTANT_InterfaceMethodref.Четвертый байт операнда каждой инструкции invokeinterface должен иметь значение нуль.
-
Операнды indexbyte каждой инструкции invokedynamic должны представлять допустимый индекс в таблице
constant_pool. Запись пула констант, на которую ссылается этот индекс, должна иметь типCONSTANT_InvokeDynamic.Третий и четвертый байты операнда каждой инструкции invokedynamic должны иметь значение нуль.
-
Только инструкция invokespecial может вызывать метод инициализации экземпляра (§2.9).
Никакой другой метод, имя которого начинается с символа '
<' ('\u003c'), не может быть вызван инструкциями вызова метода. В частности, метод инициализации класса или интерфейса со специальным именем<clinit>никогда не вызывается явно из инструкций Java Virtual Machine, а только неявно самой Java Virtual Machine. -
Операнды каждой инструкции instanceof, checkcast, new и anewarray, а также операнды indexbyte каждой инструкции multianewarray, должны представлять допустимый индекс в таблице
constant_pool. Запись пула констант, на которую ссылается этот индекс, должна иметь типCONSTANT_Class. -
Никакая инструкция new не может ссылаться на запись пула констант типа
CONSTANT_Class, представляющую тип массива (§4.3.2). Инструкция new не может использоваться для создания массива. -
Никакая инструкция anewarray не может использоваться для создания массива более чем с 255 измерениями.
-
Инструкция multianewarray должна использоваться только для создания массива типа, имеющего по крайней мере столько же измерений, сколько значение ее операнда dimensions. То есть, хотя инструкция multianewarray не обязана создавать все измерения типа массива, на который ссылаются ее операнды indexbyte, она не должна пытаться создать больше измерений, чем имеется в типе массива.
Операнд dimensions каждой инструкции multianewarray не должен быть равен нулю.
-
Операнд atype каждой инструкции newarray должен принимать одно из значений
T_BOOLEAN(4),T_CHAR(5),T_FLOAT(6),T_DOUBLE(7),T_BYTE(8),T_SHORT(9),T_INT(10) илиT_LONG(11).
-
Операнд index каждой инструкции iload, fload, aload, istore, fstore, astore, iinc и ret должен быть целым неотрицательным числом, не превышающим
max_locals - 1.Неявный индекс каждой инструкции iload_<n>, fload_<n>, aload_<n>, istore_<n>, fstore_<n> и astore_<n> не должен превышать
max_locals - 1. -
Операнд index каждой инструкции lload, dload, lstore и dstore должен быть не больше
max_locals - 2.Неявный индекс каждой инструкции lload_<n>, dload_<n>, lstore_<n> и dstore_<n> не должен превышать
max_locals - 2. -
Операды indexbyte каждой инструкции wide, изменяющей инструкцию iload, fload, aload, istore, fstore, astore, iinc или ret, должны представлять неотрицательное целое число, не превышающее
max_locals - 1.Операды indexbyte каждой инструкции wide, изменяющей инструкцию lload, dload, lstore или dstore, должны представлять неотрицательное целое число, не превышающее
max_locals - 2.
Структурные ограничения на массив code определяют ограничения на взаимосвязи инструкций виртуальной машины Java. Структурные ограничения следующие:
-
Каждая инструкция должна выполняться только с соответствующим типом и количеством аргументов в стеке операндов и массиве локальных переменных, независимо от пути выполнения, который приводит к ее вызову.
Инструкция, работающая с значениями типа
int, также может работать со значениями типаboolean,byte,charиshort.Как указано в §2.3.4 и §2.11.1, виртуальная машина Java внутренне преобразует значения типов
boolean,byte,shortиcharв типint.) -
Если инструкция может быть выполнена по нескольким различным путям выполнения, стек операндов должен иметь одинаковую глубину (§2.6.2) перед выполнением инструкции, независимо от выбранного пути.
-
В любой момент выполнения стек операндов не может увеличиться до глубины, большей, чем та, которая подразумевается элементом
max_stack. -
В любой момент выполнения не может быть извлечено больше значений из стека операндов, чем их содержится в нём.
-
В любой момент выполнения порядок локальной пары переменных, содержащей значение типа
longилиdouble, не может быть изменён или пара разделена. В любой момент локальные переменные такой пары не могут быть обработаны по отдельности. -
К локальной переменной (или паре локальных переменных в случае значения типа
longилиdouble) нельзя получить доступ до её инициализации. -
Каждая инструкция invokespecial должна называть метод инициализации экземпляра (§2.9), метод в текущем классе или интерфейсе, метод в суперклассе текущего класса, метод в прямом суперинтерфейсе текущего класса или интерфейса, или метод в
Object.При вызове метода инициализации экземпляра неинициализированный экземпляр класса должен находиться в соответствующей позиции в стеке операндов. Метод инициализации экземпляра никогда не должен вызываться для инициализированного экземпляра класса.
Если инструкция invokespecial называет метод инициализации экземпляра, а целевая ссылка в стеке операндов — это неинициализированный экземпляр класса для текущего класса, тогда invokespecial должен называть метод инициализации экземпляра из текущего класса или его непосредственного суперкласса.
Если инструкция invokespecial называет метод инициализации экземпляра, а целевая ссылка в стеке операндов — это экземпляр класса, созданный ранее инструкцией new, тогда invokespecial должен называть метод инициализации экземпляра из класса этого экземпляра класса.
Если инструкция invokespecial называет метод, который не является методом инициализации экземпляра, то тип целевой ссылки в стеке операндов должен быть совместим с присваиванием для текущего класса (JLS §5.2).
-
Каждый метод инициализации экземпляра, кроме метода инициализации экземпляра, полученного от конструктора класса
Object, должен вызвать другой метод инициализации экземпляраthisили метод инициализации экземпляра своего непосредственного суперклассаsuperперед доступом к его экземплярам членов.Однако, поля экземпляра
this, которые объявлены в текущем классе, могут быть присвоены до вызова любого метода инициализации экземпляра. -
При вызове любого метода экземпляра или доступе к любой переменной экземпляра экземпляр класса, содержащий метод экземпляра или переменную экземпляра, должен быть уже инициализирован.
-
Если в локальной переменной есть неинициализированный экземпляр класса в коде, защищённом обработчиком исключений, то i) если обработчик находится внутри метода
<init>, обработчик должен либо сгенерировать исключение, либо зациклиться, и ii) если обработчик не находится внутри метода<init>, неинициализированный экземпляр класса должен остаться неинициализированным. -
Ни при каких обстоятельствах не должно быть неинициализированного экземпляра класса в стеке операндов или в локальной переменной при выполнении инструкций jsr или jsr_w.
-
Тип каждого экземпляра класса, являющегося целью инструкции вызова метода, должен быть совместим с присваиванием для типа класса или интерфейса, указанного в инструкции (JLS §5.2).
-
Типы аргументов каждого вызова метода должны быть совместимы с вызовом метода с описанием метода (JLS §5.3, §4.3.3).
-
Каждая инструкция возврата должна соответствовать типу возвращаемого значения метода:
-
Если метод возвращает
boolean,byte,char,shortилиint, может быть использована только инструкция ireturn. -
Если метод возвращает
float,longилиdouble, соответственно, может быть использована только инструкция freturn, lreturn или dreturn. -
Если метод возвращает тип
reference, может быть использована только инструкция areturn, и тип возвращаемого значения должен быть совместим с присваиванием для описания возвращаемого значения метода (JLS §5.2, §4.3.3). -
Все методы инициализации экземпляров, методы инициализации класса или интерфейса и методы, объявленные как возвращающие
void, должны использовать только инструкцию return.
-
-
Тип каждого экземпляра класса, к которому обращается инструкция getfield, или который модифицируется инструкцией putfield, должен быть совместим с присваиванием для типа класса, указанного в инструкции (JLS §5.2).
-
Тип каждого значения, сохранённого инструкцией putfield или putstatic, должен быть совместим с описанием поля (§4.3.2) экземпляра класса или класса, в который оно сохраняется:
-
Если тип описания —
boolean,byte,char,shortилиint, то значение должно бытьint. -
Если тип описания —
float,longилиdouble, то значение должно бытьfloat,longилиdoubleсоответственно. -
Если тип описания — тип
reference, то значение должно быть типа, совместимого с присваиванием для типа описания (JLS §5.2).
-
-
Тип каждого значения, сохранённого в массиве инструкцией aastore, должен быть типа
reference.Компонентный тип массива, в который сохраняется значение инструкцией aastore, также должен быть типа
reference. -
Каждая инструкция athrow должна выбрасывать только значения, являющиеся экземплярами класса
Throwableили подклассовThrowable.Каждый класс, упомянутый в элементе
catch_typeмассиваexception_tableструктурыCode_attributeметода, должен бытьThrowableили подклассомThrowable. -
Если используется getfield или putfield для доступа к полю
protected, объявленному в суперклассе, который является членом другой среды выполнения пакета, отличной от текущего класса, то тип экземпляра класса, к которому осуществляется доступ, должен быть таким же, как текущий класс, или подклассом текущего класса.Если используется invokevirtual или invokespecial для доступа к методу
protected, объявленному в суперклассе, который является членом другой среды выполнения пакета, отличной от текущего класса, то тип экземпляра класса, к которому осуществляется доступ, должен быть таким же, как текущий класс, или подклассом текущего класса. -
Выполнение никогда не выходит за пределы массива
code. -
Адрес возврата (значение типа
returnAddress) не может быть загружен из локальной переменной. -
Инструкция, следующая за каждой инструкцией jsr или jsr_w, может быть возвращена только одной инструкцией ret.
-
Инструкции jsr или jsr_w, возвращающиеся в точку, не могут использоваться для рекурсивного вызова подпрограммы, если эта подпрограмма уже присутствует в цепочке вызова подпрограмм. (Подпрограммы могут быть вложены при использовании конструкций
try-finallyиз блокаfinally.) -
Каждый экземпляр типа
returnAddressможет быть возвращён не более одного раза.Если инструкция ret возвращается в точку в цепочке вызова подпрограмм выше инструкции ret, соответствующей данному экземпляру типа
returnAddress, то этот экземпляр никогда не может использоваться как адрес возврата.
Несмотря на то, что компилятор языка программирования Java должен генерировать только файлы class, удовлетворяющие всем статическим и структурным ограничениям в предыдущих разделах, виртуальная машина Java не гарантирует, что любой загружаемый файл был сгенерирован этим компилятором или имеет правильную структуру. Такие приложения, как веб-браузеры, не загружают исходный код, который затем компилируют; эти приложения загружают уже скомпилированные файлы class. Браузеру необходимо определить, был ли файл class создан надёжным компилятором или злоумышленником, пытающимся использовать виртуальную машину Java.
Дополнительной проблемой проверки на этапе компиляции является различие версий. Пользователь может успешно скомпилировать класс, например, PurchaseStockOptions, в качестве подкласса TradingClass. Но определение TradingClass может измениться с момента компиляции класса таким образом, что это несовместимо с существующими бинарными файлами. Методы могут быть удалены или изменены их возвращаемые типы или модификаторы. Поля могут измениться по типу или перейти из экземпляров переменных в переменные класса. Модификаторы доступа к методу или переменной могут измениться с public на private. Обсуждение этих вопросов см. в главе 13 «Бинарная совместимость» в Спецификации языка Java, издание Java SE 8.
Из-за этих потенциальных проблем виртуальной машине Java необходимо самостоятельно проверить, что требуемые ограничения соблюдаются файлами class, которые она пытается включить. Реализация виртуальной машины Java проверяет, что каждый файл class удовлетворяет необходимым ограничениям на этапе компоновки (§5.4).
Верификация на этапе компоновки повышает производительность интерпретатора во время выполнения. Дорогостоящие проверки, которые в противном случае должны были выполняться для проверки ограничений во время выполнения для каждой интерпретируемой инструкции, могут быть исключены. Виртуальная машина Java может предположить, что эти проверки уже выполнены. Например, виртуальная машина Java уже будет знать следующее:
-
Нет переполнения или недополнения стека операндов.
-
Все обращения и записи локальных переменных допустимы.
-
Аргументы всех инструкций виртуальной машины Java имеют допустимые типы.
Существуют две стратегии, которые могут быть использованы реализациями виртуальных машин Java для верификации:
-
Верификация с помощью проверки типов должна использоваться для проверки файлов
class, номер версии которых больше или равен 50.0. -
Верификация с помощью вывода типов должна поддерживаться всеми реализациями виртуальных машин Java, за исключением тех, которые соответствуют профилям Java ME CLDC и Java Card, для верификации файлов
class, номер версии которых меньше 50.0.Верификация в реализациях виртуальных машин Java, поддерживающих профили Java ME CLDC и Java Card, регулируется их соответствующими спецификациями.
В обеих стратегиях верификация в основном связана с соблюдением статических и структурных ограничений из §4.9 для массива code атрибута Code (§4.7.3). Однако есть три дополнительных проверки вне атрибута Code, которые должны быть выполнены во время верификации:
-
Обеспечение того, что классы
finalне являются подклассами. -
Обеспечение того, что методы
finalне переопределяются (§5.4.5). -
Проверка того, что каждый класс (кроме
Object) имеет непосредственный суперкласс.
Файл класса, у которого номер версии равен 50.0 или выше (§4.1), должен быть проверен с использованием правил проверки по типу, приведённых в данном разделе.
Если и только если номер версии файла класса равен 50.0, то в случае неудачи проверки по типу реализация виртуальной машины Java может выбрать попытку проверки по типу вывода (§4.10.2).
Это прагматическая корректировка, разработанная для облегчения перехода к новой дисциплине проверки. Многие инструменты, которые манипулируют файлами класса, могут изменять байткоды метода таким образом, что потребует корректировки кадров карты стека метода. Если инструмент не выполняет необходимые корректировки кадров карты стека, проверка по типу может завершиться неудачей, даже если байткод принципиально корректен (и, следовательно, прошёл бы проверку по старой схеме вывода типа). Чтобы предоставить реализаторам время для адаптации своих инструментов, реализации виртуальных машин Java могут перейти к старой дисциплине проверки, но только на ограниченное время.
В тех случаях, когда проверка по типу завершается неудачей, но вызов проверки по типу вывода успешен, ожидается определённая потеря производительности. Такая потеря неизбежна. Она также должна послужить сигналом поставщикам инструментов о необходимости корректировки их вывода и предоставляет поставщикам дополнительный стимул для внесения этих корректировок.
В итоге, переключение на проверку по типу вывода поддерживает как постепенное добавление кадров карты стека в платформу Java SE (если они отсутствуют в файле класса версии 50.0, переключение разрешается), так и постепенное удаление инструкций jsr и jsr_w из платформы Java SE (если они присутствуют в файле класса версии 50.0, переключение разрешается).
Если реализация виртуальной машины Java когда-либо пытается выполнить проверку по типу вывода для файлов класса версии 50.0, она должна сделать это во всех случаях, когда проверка по типу завершается неудачей.
Это означает, что реализация виртуальной машины Java не может выбирать прибегать к типу вывода в одном случае и не делать этого в другом. Она должна либо отклонять файлы класса, которые не проходят проверку по типу, либо последовательно переключаться на проверку по типу вывода всякий раз, когда проверка по типу завершается неудачей.
Проверяющий тип применяет правила типа, которые определены с помощью предложений Prolog. Текст на английском языке используется для описания правил типа неформальным образом, а предложения Prolog обеспечивают формальное описание.
Проверяющий тип требует список кадров карты стека для каждого метода с атрибутом Code (§4.7.3). Список кадров карты стека задаётся атрибутом StackMapTable (§4.7.4) атрибута. Цель заключается в том, чтобы кадр карты стека должен появляться в начале каждого базового блока в методе. Кадр карты стека определяет тип проверки каждого элемента стека операндов и каждой локальной переменной в начале каждого базового блока. Проверяющий тип считывает кадры карты стека для каждого метода с атрибутом Code и использует эти карты для генерации доказательства типовой безопасности инструкций в атрибуте Code.
Класс является типобезопасным, если все его методы типобезопасны, и он не является подклассом класса, промежуточного класса.
classIsTypeSafe(Class) :-
classClassName(Class, Name),
classDefiningLoader(Class, L),
superclassChain(Name, L, Chain),
Chain \= [],
classSuperClassName(Class, SuperclassName),
loadedClass(SuperclassName, L, Superclass),
classIsNotFinal(Superclass),
classMethods(Class, Methods),
checklist(methodIsTypeSafe(Class), Methods).
classIsTypeSafe(Class) :-
classClassName(Class, 'java/lang/Object'),
classDefiningLoader(Class, L),
isBootstrapLoader(L),
classMethods(Class, Methods),
checklist(methodIsTypeSafe(Class), Methods).
Предикат Prolog load_class предполагает, что ClassTerm является термином Prolog, представляющим бинарный класс, который был успешно проанализирован и загружен. Данное описание не предписывает точную структуру этого термина, но требует, чтобы определенные предикаты были определены на нём.
Например, мы предполагаем предикат get_methods, который, получив термин, представляющий класс, как описано выше, в качестве первого аргумента, связывает его второй аргумент со списком, включающим все методы класса, представленные в удобной форме, описанной позже.
Если предикат is_class_valid неверен, проверяющий тип должен сгенерировать исключение InvalidClassFileException для того, чтобы указать, что файл класса некорректен. В противном случае, файл класса успешно прошёл проверку по типу, и проверка байткодов завершилась успешно.
Остальная часть этого раздела подробно описывает процесс проверки по типу:
-
Во-первых, мы даём предикаты Prolog для основных артефактов виртуальной машины Java, таких как классы и методы (§4.10.1.1).
-
Во-вторых, мы определяем систему типов, известную проверяющему типу (§4.10.1.2).
-
В-третьих, мы определяем представление Prolog для инструкций и кадров карты стека (§4.10.1.3, §4.10.1.4).
-
В-четвёртых, мы определяем, как проверяется тип метода для методов без кода (§4.10.1.5) и методов с кодом (§4.10.1.6).
-
В-пятых, мы обсуждаем проблемы проверки типа, общие для всех инструкций загрузки и сохранения (§4.10.1.7), а также вопросы доступа к защищённым членам (§4.10.1.8).
-
Наконец, мы определяем правила проверки типа для каждой инструкции (§4.10.1.9).
Мы постулируем существование 28 предикат Prolog ("доступы"), которые имеют определенное ожидаемое поведение, но чьи формальные определения не даны в этом документе.
- classClassName(Class, ClassName)
-
Извлекает имя,
ClassName, классаClass. - classIsInterface(Class)
-
Истина тогда и только тогда, когда класс,
Class, является интерфейсом. - classIsNotFinal(Class)
-
Истина тогда и только тогда, когда класс,
Class, не является классомfinal. - classSuperClassName(Class, SuperClassName)
-
Извлекает имя,
SuperClassName, суперкласса классаClass. - classInterfaces(Class, Interfaces)
-
Извлекает список,
Interfaces, прямых суперинтерфейсов классаClass. - classMethods(Class, Methods)
-
Извлекает список,
Methods, методов, объявленных в классеClass. - classAttributes(Class, Attributes)
-
Извлекает список,
Attributes, атрибутов классаClass.Каждый атрибут представлен как применение предикатов в формате
attribute(AttributeName, AttributeContents), гдеAttributeName- имя атрибута. Формат содержимого атрибута не определен. - classDefiningLoader(Class, Loader)
-
Извлекает определяющий класс-загрузчик,
Loader, классаClass. - isBootstrapLoader(Loader)
-
Истина тогда и только тогда, когда класс-загрузчик
Loaderявляется загрузчиком bootstrap. - loadedClass(Name, InitiatingLoader, ClassDefinition)
-
Истина тогда и только тогда, когда существует класс с именем
Name, чье представление (в соответствии с этим документом) при загрузке классом-загрузчикомInitiatingLoaderестьClassDefinition. - methodName(Method, Name)
-
Извлекает имя,
Name, методаMethod. - methodAccessFlags(Method, AccessFlags)
-
Извлекает флаги доступа,
AccessFlags, методаMethod. - methodDescriptor(Method, Descriptor)
-
Извлекает дескриптор,
Descriptor, методаMethod. - methodAttributes(Method, Attributes)
-
Извлекает список,
Attributes, атрибутов методаMethod. - isInit(Method)
-
Истина тогда и только тогда, когда
Method(независимо от класса) является<init>. - isNotInit(Method)
-
Истина тогда и только тогда, когда
Method(независимо от класса) не является<init>. - isNotFinal(Method, Class)
-
Истина тогда и только тогда, когда
Methodв классеClassне являетсяfinal. - isStatic(Method, Class)
-
Истина тогда и только тогда, когда
Methodв классеClassявляетсяstatic. - isNotStatic(Method, Class)
-
Истина тогда и только тогда, когда
Methodв классеClassне являетсяstatic. - isPrivate(Method, Class)
-
Истина тогда и только тогда, когда
Methodв классеClassявляетсяprivate. - isNotPrivate(Method, Class)
-
Истина тогда и только тогда, когда
Methodв классеClassне являетсяprivate. - isProtected(MemberClass, MemberName, MemberDescriptor)
-
Истина тогда и только тогда, когда существует член с именем
MemberNameи дескрипторомMemberDescriptorв классеMemberClass, и он являетсяprotected. - isNotProtected(MemberClass, MemberName, MemberDescriptor)
-
Истина тогда и только тогда, когда существует член с именем
MemberNameи дескрипторомMemberDescriptorв классеMemberClass, и он не являетсяprotected. - parseFieldDescriptor(Descriptor, Type)
-
Преобразует дескриптор поля,
Descriptor, в соответствующий тип проверкиType(§4.10.1.2). - parseMethodDescriptor(Descriptor, ArgTypeList, ReturnType)
-
Преобразует дескриптор метода,
Descriptor, в список типов проверки,ArgTypeList, соответствующих типам аргументов метода, и тип проверки,ReturnType, соответствующий типу возвращаемого значения. - parseCodeAttribute(Class, Method, FrameSize, MaxStack, ParsedCode, Handlers, StackMap)
-
Извлекает поток инструкций,
ParsedCode, методаMethodвClass, а также максимальный размер стека операндов,MaxStack, максимальное число локальных переменных,FrameSize, обработчики исключений,Handlers, и карту стекаStackMap.Представление потока инструкций и атрибута карты стека должно соответствовать указаниям в §4.10.1.3 и §4.10.1.4.
- samePackageName(Class1, Class2)
-
Истина тогда и только тогда, когда имена пакетов
Class1иClass2одинаковы. - differentPackageName(Class1, Class2)
-
Истина тогда и только тогда, когда имена пакетов
Class1иClass2различны.
При проверке типа тела метода удобно обращаться к информации о методе. Для этой цели мы определяем окружение - шестерку, состоящую из:
-
класс
-
метод
-
объявленный тип возвращаемого значения метода
-
инструкции в методе
-
максимальный размер стека операндов
-
список обработчиков исключений
Мы определяем доступы для извлечения информации из окружения.
allInstructions(Environment, Instructions) :-
Environment = environment(_Class, _Method, _ReturnType,
Instructions, _, _).
exceptionHandlers(Environment, Handlers) :-
Environment = environment(_Class, _Method, _ReturnType,
_Instructions, _, Handlers).
maxOperandStackLength(Environment, MaxStack) :-
Environment = environment(_Class, _Method, _ReturnType,
_Instructions, MaxStack, _Handlers).
thisClass(Environment, class(ClassName, L)) :-
Environment = environment(Class, _Method, _ReturnType,
_Instructions, _, _),
classDefiningLoader(Class, L),
classClassName(Class, ClassName).
thisMethodReturnType(Environment, ReturnType) :-
Environment = environment(_Class, _Method, ReturnType,
_Instructions, _, _).
Мы определяем дополнительные предикаты для извлечения информации более высокого уровня из окружения.
offsetStackFrame(Environment, Offset, StackFrame) :-
allInstructions(Environment, Instructions),
member(stackMap(Offset, StackFrame), Instructions).
currentClassLoader(Environment, Loader) :-
thisClass(Environment, class(_, Loader)).
И наконец, мы определяем общий предикат, используемый во всех правилах типов:
notMember(_, []). notMember(X, [A | More]) :- X \= A, notMember(X, More).
Принципом, руководящим определением того, какие доступы постулируются, а какие полностью специфицируются, является то, что мы не хотим чрезмерно специфицировать представление файла class. Предоставление конкретных доступов к термину Class или Method заставит нас полностью специфицировать формат для терма Prolog, представляющего файл class.
Проверяющий типов использует систему типов, основанную на иерархии типов проверки, проиллюстрированной ниже.
Verification type hierarchy:
top
____________/\____________
/ \
/ \
oneWord twoWord
/ | \ / \
/ | \ / \
int float reference long double
/ \
/ \_____________
/ \
/ \
uninitialized +------------------+
/ \ | Java reference |
/ \ | type hierarchy |
uninitializedThis uninitialized(Offset) +------------------+
|
|
null
Большинство типов проверки напрямую соответствуют примитивным и ссылочным типам, представленным описателями полей в таблице 4.3-A:
-
Примитивные типы
double,float,intиlong(описатели полейD,F,I,J) соответствуют типу проверки с тем же именем. -
Примитивные типы
byte,char,shortиboolean(описатели полейB,C,S,Z) соответствуют типу проверкиint. -
Типы классов и интерфейсов соответствуют типам проверки, использующим функтор
class. Тип проверкиclass(N, L)представляет класс с двоичным именемN, загруженный загрузчикомL. Обратите внимание, чтоLявляется инициализирующим загрузчиком (§5.3) класса, представленногоclass(N, L), и может, или не может, быть загрузчиком, определяющим класс.Например, тип класса
Objectбудет представлен какclass('java/lang/Object', BL), гдеBL— загрузчик-бутстрап. -
Массивы соответствуют типам проверки, использующим функтор
arrayOf. Тип проверкиarrayOf(T)представляет тип массива, компонентом которого является тип проверкиT.Например, типы
int[]иObject[]будут представлены какarrayOf(int)иarrayOf(class('java/lang/Object', BL))соответственно.
Тип проверки uninitialized(Offset) представляется применением функтора uninitialized к аргументу, представляющему числовое значение Offset.
Другие типы проверки представлены в Prolog как атомы, имена которых обозначают тип проверки.
Правила подтипизации для типов проверки таковы.
Подтипизация рефлексивна.
isAssignable(X, X).
Типы проверки, которые не являются ссылочными типами в языке программирования Java, имеют правила подтипизации вида:
isAssignable(v, X) :- isAssignable(the_direct_supertype_of_v, X).
То есть, v является подтипом X, если непосредственным супертипом v является подтип X. Правила:
isAssignable(oneWord, top).
isAssignable(twoWord, top).
isAssignable(int, X) :- isAssignable(oneWord, X).
isAssignable(float, X) :- isAssignable(oneWord, X).
isAssignable(long, X) :- isAssignable(twoWord, X).
isAssignable(double, X) :- isAssignable(twoWord, X).
isAssignable(reference, X) :- isAssignable(oneWord, X).
isAssignable(class(_, _), X) :- isAssignable(reference, X).
isAssignable(arrayOf(_), X) :- isAssignable(reference, X).
isAssignable(uninitialized, X) :- isAssignable(reference, X).
isAssignable(uninitializedThis, X) :- isAssignable(uninitialized, X).
isAssignable(uninitialized(_), X) :- isAssignable(uninitialized, X).
isAssignable(null, class(_, _)).
isAssignable(null, arrayOf(_)).
isAssignable(null, X) :- isAssignable(class('java/lang/Object', BL), X),
isBootstrapLoader(BL).
Эти правила подтипизации не являются, возможно, наиболее очевидной формулировкой подтипизации. Существует четкое разделение между правилами подтипизации для ссылочных типов в языке программирования Java и правилами для остальных типов проверки. Разделение позволяет нам сформулировать общие отношения подтипизации между ссылочными типами языка программирования Java и другими типами проверки. Эти отношения справедливы независимо от позиции ссылочного типа Java в иерархии типов и помогают предотвратить излишнюю загрузку классов реализацией виртуальной машины Java. Например, мы не хотим подниматься по иерархии суперклассов Java в ответ на запрос вида class(foo, L) <:
twoWord.
Также у нас есть правило, гласящее, что подтипизация рефлексивна, так что вместе эти правила охватывают большинство типов проверки, которые не являются ссылочными типами в языке программирования Java.
Правила подтипизации для ссылочных типов в языке программирования Java задаются рекурсивно с помощью isJavaAssignable.
isAssignable(class(X, Lx), class(Y, Ly)) :-
isJavaAssignable(class(X, Lx), class(Y, Ly)).
isAssignable(arrayOf(X), class(Y, L)) :-
isJavaAssignable(arrayOf(X), class(Y, L)).
isAssignable(arrayOf(X), arrayOf(Y)) :-
isJavaAssignable(arrayOf(X), arrayOf(Y)).
Для присваиваний интерфейсы рассматриваются как Object.
isJavaAssignable(class(_, _), class(To, L)) :-
loadedClass(To, L, ToClass),
classIsInterface(ToClass).
isJavaAssignable(From, To) :-
isJavaSubclassOf(From, To).
Типы массивов являются подтипами Object. Также предполагается, что типы массивов являются подтипами Cloneable и java.io.Serializable.
isJavaAssignable(arrayOf(_), class('java/lang/Object', BL)) :-
isBootstrapLoader(BL).
isJavaAssignable(arrayOf(_), X) :-
isArrayInterface(X).
isArrayInterface(class('java/lang/Cloneable', BL)) :-
isBootstrapLoader(BL).
isArrayInterface(class('java/io/Serializable', BL)) :-
isBootstrapLoader(BL).
Подтипизация между массивами примитивного типа — это тождественное отношение.
isJavaAssignable(arrayOf(X), arrayOf(Y)) :-
atom(X),
atom(Y),
X = Y.
Подтипизация между массивами ссылочного типа ковариантна.
isJavaAssignable(arrayOf(X), arrayOf(Y)) :-
compound(X), compound(Y), isJavaAssignable(X, Y).
Наследование рефлексивно.
isJavaSubclassOf(class(SubclassName, L), class(SubclassName, L)).
isJavaSubclassOf(class(SubclassName, LSub), class(SuperclassName, LSuper)) :-
superclassChain(SubclassName, LSub, Chain),
member(class(SuperclassName, L), Chain),
loadedClass(SuperclassName, L, Sup),
loadedClass(SuperclassName, LSuper, Sup).
superclassChain(ClassName, L, [class(SuperclassName, Ls) | Rest]) :-
loadedClass(ClassName, L, Class),
classSuperClassName(Class, SuperclassName),
classDefiningLoader(Class, Ls),
superclassChain(SuperclassName, Ls, Rest).
superclassChain('java/lang/Object', L, []) :-
loadedClass('java/lang/Object', L, Class),
classDefiningLoader(Class, BL),
isBootstrapLoader(BL).
Индивидуальные инструкции байткода представлены в Prolog как термины, функтором которых является имя инструкции, а аргументами — её распарсенные операнды.
Например, инструкция aload представлена термином aload(N), который включает индекс N, являющийся операндом инструкции.
Инструкции в целом представлены как список терминов вида:
instruction(Offset, AnInstruction)
Например, instruction(21,
aload(1)).
Порядок инструкций в этом списке должен совпадать с порядком в файле class.
Несколько инструкций имеют операнды, являющиеся записями пула констант, представляющими поля, методы и динамические вызовы. В пуле констант поле представлено структурой CONSTANT_Fieldref_info, метод — структурой CONSTANT_InterfaceMethodref_info (для метода интерфейса) или структурой CONSTANT_Methodref_info (для метода класса), а динамический вызов — структурой CONSTANT_InvokeDynamic_info (§4.4.2, §4.4.10). Такие структуры представлены как применения функторов вида:
-
field(FieldClassName, FieldName, FieldDescriptor)для поля, гдеFieldClassName— имя класса, на который ссылается элементclass_indexв структуреCONSTANT_Fieldref_info, аFieldNameиFieldDescriptorсоответствуют имени и описателю поля, на которые ссылается элементname_and_type_indexструктурыCONSTANT_Fieldref_info. -
imethod(MethodIntfName, MethodName, MethodDescriptor)для метода интерфейса, гдеMethodIntfName— имя интерфейса, на который ссылается элементclass_indexструктурыCONSTANT_InterfaceMethodref_info, аMethodNameиMethodDescriptorсоответствуют имени и описателю метода, на которые ссылается элементname_and_type_indexструктурыCONSTANT_InterfaceMethodref_info; -
method(MethodClassName, MethodName, MethodDescriptor)для метода класса, гдеMethodClassName— имя класса, на который ссылается элементclass_indexструктурыCONSTANT_Methodref_info, аMethodNameиMethodDescriptorсоответствуют имени и описателю метода, на которые ссылается элементname_and_type_indexструктурыCONSTANT_Methodref_info; и -
dmethod(CallSiteName, MethodDescriptor)для динамического вызова, гдеCallSiteNameиMethodDescriptorсоответствуют имени и описателю метода, на которые ссылается элементname_and_type_indexструктурыCONSTANT_InvokeDynamic_info.
Для ясности предполагается, что описатели полей и методов (§4.3.2, §4.3.3) отображаются в более читаемые имена: ведущие L и заключительные ; опускаются из имён классов, а символы BaseType, используемые для примитивных типов, отображаются в имена этих типов.
Например, инструкция getfield, операндом которой был индекс в пуле констант, который ссылается на поле foo типа F в классе Bar, будет представлена как getfield(field('Bar', 'foo', 'F')).
Записи пула констант, которые ссылаются на константные значения, такие как CONSTANT_String, CONSTANT_Integer, CONSTANT_Float, CONSTANT_Long, CONSTANT_Double и CONSTANT_Class, кодируются с помощью функторов с именами string, int, float, long, double и classConstant соответственно.
Например, инструкция ldc для загрузки целого числа 91 будет закодирована как ldc(int(91)).
Кадры карты стека представляются в Prolog как список термов вида:
stackMap(Offset, TypeState)
где:
-
Offset— целое число, указывающее смещение байткода, к которому применяется кадр карты стека (§4.7.4).Порядок смещений байткода в этом списке должен совпадать с порядком в файле
class. -
TypeState— ожидаемое состояние типа входных данных для инструкции в позицииOffset.
Состояние типа — это отображение локаций в стеке операндов и локальных переменных метода на типы проверки. Оно имеет вид:
frame(Locals, OperandStack, Flags)
где:
-
Locals— список типов проверки, где i-й элемент списка (с нумерацией с 0) представляет тип локальной переменной i. -
OperandStack— список типов проверки, где первый элемент списка представляет тип вершины стека операндов, а типы элементов стека под вершиной следуют в списке в соответствующем порядке.Типы размером 2 (
longиdouble) представляются двумя элементами стека, где первый элемент —top, а второй — сам тип.Например, стек с значением
double, значениемintи значениемlongв состоянии типа представляется стеком с пятью элементами:topиdoubleдля значенияdouble,intдля значенияintиtopиlongдля значенияlong. Соответственно,OperandStack— это список[top, double, int, top, long]. -
Flags— список, который может быть пустым или содержать единственный элементflagThisUninit.Если какая-либо локальная переменная в
Localsимеет типuninitializedThis, тоFlagsсодержит единственный элементflagThisUninit, иначеFlags— пустой список.flagThisUninitиспользуется в конструкторах для обозначения состояний типа, где инициализацияthisеще не завершена. В таких состояниях метод не может возвращать значение.
Подтипизация типов проверки распространяется точечно на состояния типов.
Массив локальных переменных метода имеет фиксированную длину по построению (см. methodInitialStackFrame в §4.10.1.6), в то время как стек операндов растёт и уменьшается. Поэтому требуется явная проверка длины стеков операндов, применимость которых требуется.
frameIsAssignable(frame(Locals1, StackMap1, Flags1),
frame(Locals2, StackMap2, Flags2)) :-
length(StackMap1, StackMapLength),
length(StackMap2, StackMapLength),
maplist(isAssignable, Locals1, Locals2),
maplist(isAssignable, StackMap1, StackMap2),
subset(Flags1, Flags2).
Длина стека операндов не должна превышать объявленной максимальной длины стека.
operandStackHasLegalLength(Environment, OperandStack) :-
length(OperandStack, Length),
maxOperandStackLength(Environment, MaxStack),
Length =< MaxStack.
Некоторые инструкции для массивов (§aaload, §arraylength, §baload, §bastore) просматривают типы значений в стеке операндов, чтобы проверить, являются ли они типами массивов. Следующая клауза обращается к i-му элементу стека операндов из состояния типа.
nth1OperandStackIs(i, frame(_Locals, OperandStack, _Flags), Element) :- nth1(i, OperandStack, Element).
Обработка стека операндов инструкциями загрузки и сохранения (§4.10.1.7) усложняется тем, что некоторые типы занимают два элемента в стеке. Приведённые ниже предикаты учитывают это, позволяя остальной части спецификации абстрагироваться от этой проблемы.
Извлечь список типов из стека.
canPop(frame(Locals, OperandStack, Flags), Types,
frame(Locals, PoppedOperandStack, Flags)) :-
popMatchingList(OperandStack, Types, PoppedOperandStack).
popMatchingList(OperandStack, [], OperandStack).
popMatchingList(OperandStack, [P | Rest], NewOperandStack) :-
popMatchingType(OperandStack, P, TempOperandStack, _ActualType),
popMatchingList(TempOperandStack, Rest, NewOperandStack).
Извлечь отдельный тип из стека. Точнее, если логическая вершина стека — это подтип указанного типа, Type, то извлечь его. Если тип занимает два элемента стека, то логическая вершина стека — это на самом деле тип, расположенный непосредственно под вершиной, а вершина стека — это недоступный тип top.
popMatchingType([ActualType | OperandStack],
Type, OperandStack, ActualType) :-
sizeOf(Type, 1),
isAssignable(ActualType, Type).
popMatchingType([top, ActualType | OperandStack],
Type, OperandStack, ActualType) :-
sizeOf(Type, 2),
isAssignable(ActualType, Type).
sizeOf(X, 2) :- isAssignable(X, twoWord).
sizeOf(X, 1) :- isAssignable(X, oneWord).
sizeOf(top, 1).
Поместить логический тип в стек. Точное поведение зависит от размера типа. Если помещаемый тип имеет размер 1, то он просто помещается в стек. Если помещаемый тип имеет размер 2, то он помещается, а затем помещается top.
pushOperandStack(OperandStack, 'void', OperandStack).
pushOperandStack(OperandStack, Type, [Type | OperandStack]) :-
sizeOf(Type, 1).
pushOperandStack(OperandStack, Type, [top, Type | OperandStack]) :-
sizeOf(Type, 2).
Поместить список типов в стек, если есть место.
canSafelyPush(Environment, InputOperandStack, Type, OutputOperandStack) :-
pushOperandStack(InputOperandStack, Type, OutputOperandStack),
operandStackHasLegalLength(Environment, OutputOperandStack).
canSafelyPushList(Environment, InputOperandStack, Types,
OutputOperandStack) :-
canPushList(InputOperandStack, Types, OutputOperandStack),
operandStackHasLegalLength(Environment, OutputOperandStack).
canPushList(InputOperandStack, [], InputOperandStack).
canPushList(InputOperandStack, [Type | Rest], OutputOperandStack) :-
pushOperandStack(InputOperandStack, Type, InterimOperandStack),
canPushList(InterimOperandStack, Rest, OutputOperandStack).
Обработка стека операндов инструкциями dup полностью описывается в терминах категории типов значений в стеке (§2.11.1).
Типы категории 1 занимают один элемент стека. Извлечение логического типа категории 1, Type, из стека возможно, если вершина стека — это Type, и Type не является top (в противном случае это могло бы означать верхнюю половину типа категории 2). Результатом является входной стек с удалённым верхним элементом.
popCategory1([Type | Rest], Type, Rest) :-
Type \= top,
sizeOf(Type, 1).
Типы категории 2 занимают два элемента стека. Извлечение логического типа категории 2, Type, из стека возможно, если вершина стека — это тип top, и элемент непосредственно под ним — это Type. Результатом является входной стек с удалёнными двумя верхними элементами.
popCategory2([top, Type | Rest], Type, Rest) :-
sizeOf(Type, 2).
Большинство правил типов для отдельных инструкций (§4.10.1.9) основаны на понятии допустимого перехода типов. Переход типов является допустимым, если можно извлечь список ожидаемых типов из стека операндов входного состояния типа и заменить их ожидаемым типом результата, получив новое допустимое состояние типа. В частности, размер стека операндов в новом состоянии типа не должен превышать его объявленного максимального размера.
validTypeTransition(Environment, ExpectedTypesOnStack, ResultType,
frame(Locals, InputOperandStack, Flags),
frame(Locals, NextOperandStack, Flags)) :-
popMatchingList(InputOperandStack, ExpectedTypesOnStack,
InterimOperandStack),
pushOperandStack(InterimOperandStack, ResultType, NextOperandStack),
operandStackHasLegalLength(Environment, NextOperandStack).
Методы abstract и методы native считаются безопасными с точки зрения типов, если они не переопределяют метод final.
methodIsTypeSafe(Class, Method) :-
doesNotOverrideFinalMethod(Class, Method),
methodAccessFlags(Method, AccessFlags),
member(abstract, AccessFlags).
methodIsTypeSafe(Class, Method) :-
doesNotOverrideFinalMethod(Class, Method),
methodAccessFlags(Method, AccessFlags),
member(native, AccessFlags).
Методы private и методы static ортогональны динамическому диспетчеризации методов, поэтому они никогда не переопределяют другие методы (§5.4.5).
doesNotOverrideFinalMethod(class('java/lang/Object', L), Method) :-
isBootstrapLoader(L).
doesNotOverrideFinalMethod(Class, Method) :-
isPrivate(Method, Class).
doesNotOverrideFinalMethod(Class, Method) :-
isStatic(Method, Class).
doesNotOverrideFinalMethod(Class, Method) :-
isNotPrivate(Method, Class),
isNotStatic(Method, Class),
doesNotOverrideFinalMethodOfSuperclass(Class, Method).
doesNotOverrideFinalMethodOfSuperclass(Class, Method) :-
classSuperClassName(Class, SuperclassName),
classDefiningLoader(Class, L),
loadedClass(SuperclassName, L, Superclass),
classMethods(Superclass, SuperMethodList),
finalMethodNotOverridden(Method, Superclass, SuperMethodList).
Методы final, которые являются private и/или static, являются необычными, так как методы private и методы static не могут быть переопределены напрямую. Таким образом, если найден метод final private или метод final static, он логически не был переопределён другим методом.
finalMethodNotOverridden(Method, Superclass, SuperMethodList) :-
methodName(Method, Name),
methodDescriptor(Method, Descriptor),
member(method(_, Name, Descriptor), SuperMethodList),
isFinal(Method, Superclass),
isPrivate(Method, Superclass).
finalMethodNotOverridden(Method, Superclass, SuperMethodList) :-
methodName(Method, Name),
methodDescriptor(Method, Descriptor),
member(method(_, Name, Descriptor), SuperMethodList),
isFinal(Method, Superclass),
isStatic(Method, Superclass).
Если найден не-final private метод или не-final static метод, пропустите его, так как он ортогонален переопределению.
finalMethodNotOverridden(Method, Superclass, SuperMethodList) :-
methodName(Method, Name),
methodDescriptor(Method, Descriptor),
member(method(_, Name, Descriptor), SuperMethodList),
isNotFinal(Method, Superclass),
isPrivate(Method, Superclass),
doesNotOverrideFinalMethodOfSuperclass(Superclass, Method).
finalMethodNotOverridden(Method, Superclass, SuperMethodList) :-
methodName(Method, Name),
methodDescriptor(Method, Descriptor),
member(method(_, Name, Descriptor), SuperMethodList),
isNotFinal(Method, Superclass),
isStatic(Method, Superclass),
doesNotOverrideFinalMethodOfSuperclass(Superclass, Method).
Если найден метод, который не является final, не является private и не является static методом, то действительно метод final не был переопределён. В противном случае вернитесь к верхнему уровню.
finalMethodNotOverridden(Method, Superclass, SuperMethodList) :-
methodName(Method, Name),
methodDescriptor(Method, Descriptor),
member(method(_, Name, Descriptor), SuperMethodList),
isNotFinal(Method, Superclass),
isNotStatic(Method, Superclass),
isNotPrivate(Method, Superclass).
finalMethodNotOverridden(Method, Superclass, SuperMethodList) :-
methodName(Method, Name),
methodDescriptor(Method, Descriptor),
notMember(method(_, Name, Descriptor), SuperMethodList),
doesNotOverrideFinalMethodOfSuperclass(Superclass, Method).
Методы, не являющиеся abstract, не-native, являются корректными по типу, если у них есть код, и этот код корректен по типу.
methodIsTypeSafe(Class, Method) :-
doesNotOverrideFinalMethod(Class, Method),
methodAccessFlags(Method, AccessFlags),
methodAttributes(Method, Attributes),
notMember(native, AccessFlags),
notMember(abstract, AccessFlags),
member(attribute('Code', _), Attributes),
methodWithCodeIsTypeSafe(Class, Method).
Метод с кодом безопасен по типу, если возможно объединить код и кадры карты стека в один поток так, чтобы каждый кадр карты стека предшествовал инструкции, к которой он относится, и объединённый поток был корректен по типу. Обработчики исключений метода, если таковые имеются, также должны быть допустимыми.
methodWithCodeIsTypeSafe(Class, Method) :-
parseCodeAttribute(Class, Method, FrameSize, MaxStack,
ParsedCode, Handlers, StackMap),
mergeStackMapAndCode(StackMap, ParsedCode, MergedCode),
methodInitialStackFrame(Class, Method, FrameSize, StackFrame, ReturnType),
Environment = environment(Class, Method, ReturnType, MergedCode,
MaxStack, Handlers),
handlersAreLegal(Environment),
mergedCodeIsTypeSafe(Environment, MergedCode, StackFrame).
Рассмотрим сначала обработчики исключений.
Обработчик исключений представляется применением функтора в форме:
handler(Start, End, Target, ClassName)
где аргументы соответственно представляют собой начало и конец диапазона инструкций, охватываемого обработчиком, первую инструкцию кода обработчика и имя класса исключения, для обработки которого предназначен этот обработчик.
Обработчик исключений является допустимым, если его начало (Start) меньше его конца (End), существует инструкция, смещение которой равно Start, существует инструкция, смещение которой равно End, и класс исключения обработчика присваиваем классу Throwable. Класс исключения обработчика равен Throwable, если запись класса обработчика равна 0, в противном случае это класс, указанный в обработчике.
Дополнительное требование существует для обработчика внутри метода <init>, если одна из инструкций, охватываемых обработчиком, является invokespecial метода <init>. В этом случае тот факт, что обработчик работает, означает, что объект, который строится, вероятно, повреждён, поэтому важно, чтобы обработчик не заглатывал исключение и не позволял окружающему методу <init> возвратиться к вызывающей стороне. Соответственно, обработчик должен либо прерваться, сгенерировав исключение для вызывающей стороны окружающего метода <init>, либо зациклиться.
handlersAreLegal(Environment) :-
exceptionHandlers(Environment, Handlers),
checklist(handlerIsLegal(Environment), Handlers).
handlerIsLegal(Environment, Handler) :-
Handler = handler(Start, End, Target, _),
Start < End,
allInstructions(Environment, Instructions),
member(instruction(Start, _), Instructions),
offsetStackFrame(Environment, Target, _),
instructionsIncludeEnd(Instructions, End),
currentClassLoader(Environment, CurrentLoader),
handlerExceptionClass(Handler, ExceptionClass, CurrentLoader),
isBootstrapLoader(BL),
isAssignable(ExceptionClass, class('java/lang/Throwable', BL)),
initHandlerIsLegal(Environment, Handler).
instructionsIncludeEnd(Instructions, End) :-
member(instruction(End, _), Instructions).
instructionsIncludeEnd(Instructions, End) :-
member(endOfCode(End), Instructions).
handlerExceptionClass(handler(_, _, _, 0),
class('java/lang/Throwable', BL), _) :-
isBootstrapLoader(BL).
handlerExceptionClass(handler(_, _, _, Name),
class(Name, L), L) :-
Name \= 0.
initHandlerIsLegal(Environment, Handler) :-
notInitHandler(Environment, Handler).
notInitHandler(Environment, Handler) :-
Environment = environment(_Class, Method, _, Instructions, _, _),
isNotInit(Method).
notInitHandler(Environment, Handler) :-
Environment = environment(_Class, Method, _, Instructions, _, _),
isInit(Method),
member(instruction(_, invokespecial(CP)), Instructions),
CP = method(MethodClassName, MethodName, Descriptor),
MethodName \= '<init>'.
initHandlerIsLegal(Environment, Handler) :-
isInitHandler(Environment, Handler),
sublist(isApplicableInstruction(Target), Instructions,
HandlerInstructions),
noAttemptToReturnNormally(HandlerInstructions).
isInitHandler(Environment, Handler) :-
Environment = environment(_Class, Method, _, Instructions, _, _),
isInit(Method).
member(instruction(_, invokespecial(CP)), Instructions),
CP = method(MethodClassName, '<init>', Descriptor).
isApplicableInstruction(HandlerStart, instruction(Offset, _)) :-
Offset >= HandlerStart.
noAttemptToReturnNormally(Instructions) :-
notMember(instruction(_, return), Instructions).
noAttemptToReturnNormally(Instructions) :-
member(instruction(_, athrow), Instructions).
Теперь перейдём к потоку инструкций и кадрам карты стека.
Объединение инструкций и кадров карты стека в один поток включает в себя четыре случая:
-
Объединение пустого
StackMapи списка инструкций даёт исходный список инструкций.mergeStackMapAndCode([], CodeList, CodeList).
-
Учитывая список кадров карты стека, начинающийся со состояния типа для инструкции в позиции
Offset, и список инструкций, начинающийся в позицииOffset, объединённый список представляет собой голову списка кадров карты стека, за которой следует голова списка инструкций, за которой следует объединение хвостов двух списков.mergeStackMapAndCode([stackMap(Offset, Map) | RestMap], [instruction(Offset, Parse) | RestCode], [stackMap(Offset, Map), instruction(Offset, Parse) | RestMerge]) :- mergeStackMapAndCode(RestMap, RestCode, RestMerge). -
В противном случае, учитывая список кадров карты стека, начинающийся со состояния типа для инструкции в позиции
OffsetM, и список инструкций, начинающийся в позицииOffsetP, тогда, еслиOffsetP < OffsetM, объединённый список состоит из головы списка инструкций, за которой следует объединение списка кадров карты стека и хвоста списка инструкций.mergeStackMapAndCode([stackMap(OffsetM, Map) | RestMap], [instruction(OffsetP, Parse) | RestCode], [instruction(OffsetP, Parse) | RestMerge]) :- OffsetP < OffsetM, mergeStackMapAndCode([stackMap(OffsetM, Map) | RestMap], RestCode, RestMerge). -
В противном случае объединение двух списков не определено. Поскольку список инструкций имеет монотонно возрастающие смещения, объединение двух списков не определено, если у каждого смещения кадра карты стека нет соответствующего смещения инструкции, и кадры карты стека не находятся в монотонно возрастающем порядке.
Чтобы определить, является ли объединённый поток для метода корректным по типу, мы сначала выводим начальное состояние типа метода.
Начальное состояние типа метода состоит из пустого стека операндов и типов локальных переменных, полученных из типа this и аргументов, а также соответствующего флага, в зависимости от того, является ли это методом <init>.
methodInitialStackFrame(Class, Method, FrameSize, frame(Locals, [], Flags),
ReturnType):-
methodDescriptor(Method, Descriptor),
parseMethodDescriptor(Descriptor, RawArgs, ReturnType),
expandTypeList(RawArgs, Args),
methodInitialThisType(Class, Method, ThisList),
flags(ThisList, Flags),
append(ThisList, Args, ThisArgs),
expandToLength(ThisArgs, FrameSize, top, Locals).
Учитывая список типов, следующий оператор генерирует список, где каждый тип размера 2 был заменён двумя записями: одной для себя и одной top записью. Результат затем соответствует представлению списка в виде 32-битовых слов в виртуальной машине Java.
expandTypeList([], []).
expandTypeList([Item | List], [Item | Result]) :-
sizeOf(Item, 1),
expandTypeList(List, Result).
expandTypeList([Item | List], [Item, top | Result]) :-
sizeOf(Item, 2),
expandTypeList(List, Result).
flags([uninitializedThis], [flagThisUninit]).
flags(X, []) :- X \= [uninitializedThis].
expandToLength(List, Size, _Filler, List) :-
length(List, Size).
expandToLength(List, Size, Filler, Result) :-
length(List, ListLength),
ListLength < Size,
Delta is Size - ListLength,
length(Extra, Delta),
checklist(=(Filler), Extra),
append(List, Extra, Result).
Для начального состояния типа метода экземпляра мы вычисляем тип this и помещаем его в список. Тип this в методе <init> класса Object равен Object; в других методах <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).
Описание каждого правила на английском языке предназначено для удобочитаемости, интуитивности и краткости. В связи с этим описание избегает повторения всех контекстных предположений, указанных выше. В частности:
-
Описание не упоминает явно окружение.
-
Когда в дальнейшем описание говорит о стеке операндов или локальных переменных, оно относится к компонентам стека операндов и локальных переменных состояния типа: либо к входному состоянию типа, либо к выходному.
-
Состояние типа после внезапного завершения выполнения инструкции почти всегда идентично входному состоянию типа. Описание рассматривает состояние типа после внезапного завершения инструкции только в том случае, когда это не так.
-
Описание говорит о выталкивании и загрузке типов в стек операндов и не рассматривает явно проблемы подпотока или переполнения стека. Описание предполагает, что эти операции могут быть выполнены успешно, но предложения Prolog для управления стеком операндов гарантируют, что необходимые проверки выполняются.
-
Описание рассматривает только обработку логических типов. На практике некоторые типы занимают более одного слова. Описание абстрагируется от этих деталей представления, но предложения Prolog, которые обрабатывают данные, этого не делают.
Любые неоднозначности можно разрешить, обратившись к формальным предложениям Prolog.
aaload
Инструкция aaload безопасна с точки зрения типов, если можно корректно заменить типы, соответствующие int и типу массива с типом компонента ComponentType, где ComponentType является подтипом Object, причём ComponentType даёт выходное состояние типа.
instructionIsTypeSafe(aaload, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
nth1OperandStackIs(2, StackFrame, ArrayType),
arrayComponentType(ArrayType, ComponentType),
isBootstrapLoader(BL),
validTypeTransition(Environment,
[int, arrayOf(class('java/lang/Object', BL))],
ComponentType, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Тип компонента массива X равен X. Мы определяем тип компонента null как null.
arrayComponentType(arrayOf(X), X). arrayComponentType(null, null).
aastore
Инструкция aastore безопасна с точки зрения типов, если можно корректно извлечь типы, соответствующие Object, int и типу массива Object из входного стека операндов, получив выходное состояние типа.
instructionIsTypeSafe(aastore, _Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
isBootstrapLoader(BL),
canPop(StackFrame,
[class('java/lang/Object', BL),
int,
arrayOf(class('java/lang/Object', BL))],
NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
aconst_null
Инструкция aconst_null безопасна с точки зрения типов, если можно корректно поместить тип null в входной стек операндов, получив выходное состояние типа.
instructionIsTypeSafe(aconst_null, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [], null, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
aload, aload_<n>
Инструкция aload с операндом Index безопасна с точки зрения типов и даёт выходное состояние типа NextStackFrame, если инструкция загрузки с операндом Index и типом reference безопасна с точки зрения типов и даёт выходное состояние типа NextStackFrame.
instructionIsTypeSafe(aload(Index), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
loadIsTypeSafe(Environment, Index, reference, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкции aload_<n> для 0 ≤ n ≤ 3 безопасны с точки зрения типов тогда и только тогда, когда эквивалентная инструкция aload безопасна с точки зрения типов.
instructionHasEquivalentTypeRule(aload_0, aload(0)). instructionHasEquivalentTypeRule(aload_1, aload(1)). instructionHasEquivalentTypeRule(aload_2, aload(2)). instructionHasEquivalentTypeRule(aload_3, aload(3)).
anewarray
Инструкция anewarray с операндом CP безопасна с точки зрения типов, если CP ссылается на запись константного пула, обозначающую либо тип класса, либо тип массива, и можно законно заменить тип, соответствующий int в входном стеке операндов, на массив с типом компонента CP, получив выходное состояние типа.
instructionIsTypeSafe(anewarray(CP), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
(CP = class(_, _) ; CP = arrayOf(_)),
validTypeTransition(Environment, [int], arrayOf(CP),
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
areturn
Инструкция areturn безопасна с точки зрения типов тогда и только тогда, когда объявленный тип возврата ReturnType закрывающего метода является типом reference, и можно корректно извлечь тип, соответствующий ReturnType, из входного стека операндов.
instructionIsTypeSafe(areturn, Environment, _Offset, StackFrame,
afterGoto, ExceptionStackFrame) :-
thisMethodReturnType(Environment, ReturnType),
isAssignable(ReturnType, reference),
canPop(StackFrame, [ReturnType], _PoppedStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
arraylength
Инструкция arraylength безопасна с точки зрения типов, если можно корректно заменить тип массива в входном стеке операндов типом int, получив выходное состояние типа.
instructionIsTypeSafe(arraylength, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
nth1OperandStackIs(1, StackFrame, ArrayType),
arrayComponentType(ArrayType, _),
validTypeTransition(Environment, [top], int, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
astore, astore_<n>
Инструкция astore с операндом Index безопасна с точки зрения типов и даёт выходное состояние типа NextStackFrame, если инструкция сохранения с операндом Index и типом reference безопасна с точки зрения типов и даёт выходное состояние типа NextStackFrame.
instructionIsTypeSafe(astore(Index), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
storeIsTypeSafe(Environment, Index, reference, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкции astore_<n> для 0 ≤ n ≤ 3 безопасны с точки зрения типов тогда и только тогда, когда эквивалентная инструкция astore безопасна с точки зрения типов.
instructionHasEquivalentTypeRule(astore_0, astore(0)). instructionHasEquivalentTypeRule(astore_1, astore(1)). instructionHasEquivalentTypeRule(astore_2, astore(2)). instructionHasEquivalentTypeRule(astore_3, astore(3)).
athrow
Инструкция athrow безопасна с точки зрения типов, если верхняя часть стека операндов соответствует Throwable.
instructionIsTypeSafe(athrow, _Environment, _Offset, StackFrame,
afterGoto, ExceptionStackFrame) :-
isBootstrapLoader(BL),
canPop(StackFrame, [class('java/lang/Throwable', BL)], _PoppedStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
baload
Инструкция baload безопасна с точки зрения типов, если можно корректно заменить типы, соответствующие int и типу малого массива в входном стеке операндов, на int, получив выходное состояние типа.
instructionIsTypeSafe(baload, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :
nth1OperandStackIs(2, StackFrame, ArrayType),
isSmallArray(ArrayType),
validTypeTransition(Environment, [int, top], int,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Тип массива является типом малого массива, если это массив byte, массив boolean или подтип thereof (null).
isSmallArray(arrayOf(byte)). isSmallArray(arrayOf(boolean)). isSmallArray(null).
bastore
Инструкция bastore безопасна с точки зрения типов, если можно корректно извлечь типы, соответствующие int, int и типу малого массива из входного стека операндов, получив выходное состояние типа.
instructionIsTypeSafe(bastore, _Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
nth1OperandStackIs(3, StackFrame, ArrayType),
isSmallArray(ArrayType),
canPop(StackFrame, [int, int, top], NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
bipush
Инструкция bipush безопасна с точки зрения типов, если эквивалентная инструкция sipush безопасна с точки зрения типов.
instructionHasEquivalentTypeRule(bipush(Value), sipush(Value)).
caload
Инструкция caload безопасна с точки зрения типов, если можно корректно заменить типы, соответствующие int и массиву char в входном стеке операндов, на int, получив выходное состояние типа.
instructionIsTypeSafe(caload, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [int, arrayOf(char)], int,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
castore
Инструкция castore безопасна с точки зрения типов тогда и только тогда, когда можно корректно извлечь типы, соответствующие int, int и массиву char со входного стека операндов, что приводит к состоянию выходного типа.
instructionIsTypeSafe(castore, _Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
canPop(StackFrame, [int, int, arrayOf(char)], NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
checkcast
Инструкция checkcast с операндом CP безопасна с точки зрения типов тогда и только тогда, когда CP ссылается на запись константного пула, обозначающую класс или массив, и можно корректно заменить тип Object в верхней части входного стека операндов типом, обозначенным CP, что приводит к состоянию выходного типа.
instructionIsTypeSafe(checkcast(CP), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
(CP = class(_, _) ; CP = arrayOf(_)),
isBootstrapLoader(BL),
validTypeTransition(Environment, [class('java/lang/Object', BL)], CP,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
d2f, d2i, d2l
Инструкция d2f безопасна с точки зрения типов, если можно корректно извлечь double со входного стека операндов и заменить его на float, что приводит к состоянию выходного типа.
instructionIsTypeSafe(d2f, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [double], float,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция d2i безопасна с точки зрения типов, если можно корректно извлечь double со входного стека операндов и заменить его на int, что приводит к состоянию выходного типа.
instructionIsTypeSafe(d2i, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [double], int,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция d2l безопасна с точки зрения типов, если можно корректно извлечь double со входного стека операндов и заменить его на long, что приводит к состоянию выходного типа.
instructionIsTypeSafe(d2l, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [double], long,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
dadd
Инструкция dadd безопасна с точки зрения типов тогда и только тогда, когда можно корректно заменить типы, соответствующие double и double на входном стеке операндов типом double, что приводит к состоянию выходного типа.
instructionIsTypeSafe(dadd, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [double, double], double,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
daload
Инструкция daload безопасна с точки зрения типов тогда и только тогда, когда можно корректно заменить типы, соответствующие int и массиву double на входном стеке операндов типом double, что приводит к состоянию выходного типа.
instructionIsTypeSafe(daload, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [int, arrayOf(double)], double,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
dastore
Инструкция dastore безопасна с точки зрения типов тогда и только тогда, когда можно корректно извлечь типы, соответствующие double, int и массиву double со входного стека операндов, что приводит к состоянию выходного типа.
instructionIsTypeSafe(dastore, _Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
canPop(StackFrame, [double, int, arrayOf(double)], NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
dcmp<op>
Инструкция dcmpg безопасна с точки зрения типов тогда и только тогда, когда можно корректно заменить типы, соответствующие double и double на входном стеке операндов типом int, что приводит к состоянию выходного типа.
instructionIsTypeSafe(dcmpg, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [double, double], int,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция dcmpl безопасна с точки зрения типов тогда и только тогда, когда эквивалентная инструкция dcmpg безопасна.
instructionHasEquivalentTypeRule(dcmpl, dcmpg).
dconst_<d>
Инструкция dconst_0 безопасна с точки зрения типов, если можно корректно поместить тип double на входной стек операндов, что приводит к состоянию выходного типа.
instructionIsTypeSafe(dconst_0, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [], double, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция dconst_1 безопасна с точки зрения типов, если эквивалентная инструкция dconst_0 безопасна.
instructionHasEquivalentTypeRule(dconst_1, dconst_0).
ddiv
Инструкция ddiv безопасна с точки зрения типов, если эквивалентная инструкция dadd безопасна.
instructionHasEquivalentTypeRule(ddiv, dadd).
dload, dload_<n>
Инструкция dload с операндом Index безопасна с точки зрения типов и приводит к состоянию выходного типа NextStackFrame, если инструкция загрузки с операндом Index и типом double безопасна с точки зрения типов и приводит к состоянию выходного типа NextStackFrame.
instructionIsTypeSafe(dload(Index), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
loadIsTypeSafe(Environment, Index, double, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкции dload_<n> для 0 ≤ n ≤ 3 безопасны с точки зрения типов, если эквивалентная инструкция dload безопасна.
instructionHasEquivalentTypeRule(dload_0, dload(0)). instructionHasEquivalentTypeRule(dload_1, dload(1)). instructionHasEquivalentTypeRule(dload_2, dload(2)). instructionHasEquivalentTypeRule(dload_3, dload(3)).
dmul
Инструкция dmul безопасна с точки зрения типов, если эквивалентная инструкция dadd безопасна.
instructionHasEquivalentTypeRule(dmul, dadd).
dneg
Инструкция dneg безопасна с точки зрения типов тогда и только тогда, когда на входном стеке операндов есть тип, соответствующий double. Инструкция dneg не изменяет состояние типа.
instructionIsTypeSafe(dneg, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [double], double,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
drem
Инструкция drem безопасна с точки зрения типов, если эквивалентная инструкция dadd безопасна.
instructionHasEquivalentTypeRule(drem, dadd).
dreturn
Инструкция dreturn безопасна с точки зрения типов, если объявленный тип возвращаемого значения в методе равно double, и можно корректно извлечь тип, соответствующий double со входного стека операндов.
instructionIsTypeSafe(dreturn, Environment, _Offset, StackFrame,
afterGoto, ExceptionStackFrame) :-
thisMethodReturnType(Environment, double),
canPop(StackFrame, [double], _PoppedStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
dstore, dstore_<n>
Инструкция dstore с операндом Index безопасна с точки зрения типов и приводит к состоянию выходного типа NextStackFrame, если инструкция сохранения с операндом Index и типом double безопасна с точки зрения типов и приводит к состоянию выходного типа NextStackFrame.
instructionIsTypeSafe(dstore(Index), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
storeIsTypeSafe(Environment, Index, double, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкции dstore_<n> для 0 ≤ n ≤ 3 безопасны с точки зрения типов, если эквивалентная инструкция dstore безопасна.
instructionHasEquivalentTypeRule(dstore_0, dstore(0)). instructionHasEquivalentTypeRule(dstore_1, dstore(1)). instructionHasEquivalentTypeRule(dstore_2, dstore(2)). instructionHasEquivalentTypeRule(dstore_3, dstore(3)).
dsub
Инструкция dsub безопасна с точки зрения типов, если эквивалентная инструкция dadd безопасна.
instructionHasEquivalentTypeRule(dsub, dadd).
dup
Инструкция dup безопасна с точки зрения типов тогда и только тогда, когда можно корректно заменить категорию 1 тип, Type, на типы Type, Type, что приводит к состоянию выходного типа.
instructionIsTypeSafe(dup, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
StackFrame = frame(Locals, InputOperandStack, Flags),
popCategory1(InputOperandStack, Type, _),
canSafelyPush(Environment, InputOperandStack, Type, OutputOperandStack),
NextStackFrame = frame(Locals, OutputOperandStack, Flags),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
dup_x1
Инструкция dup_x1 безопасна с точки зрения типов тогда и только тогда, когда можно корректно заменить два типа категории 1, Type1 и Type2, на входном стеке операндов на типы Type1, Type2, Type1, что приводит к состоянию выходного типа.
instructionIsTypeSafe(dup_x1, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
StackFrame = frame(Locals, InputOperandStack, Flags),
popCategory1(InputOperandStack, Type1, Stack1),
popCategory1(Stack1, Type2, Rest),
canSafelyPushList(Environment, Rest, [Type1, Type2, Type1],
OutputOperandStack),
NextStackFrame = frame(Locals, OutputOperandStack, Flags),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
dup_x2
Инструкция dup_x2 безопасна по типу, если она является безопасной формой по типу инструкции dup_x2.
instructionIsTypeSafe(dup_x2, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
StackFrame = frame(Locals, InputOperandStack, Flags),
dup_x2FormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack),
NextStackFrame = frame(Locals, OutputOperandStack, Flags),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция dup_x2 является безопасной формой по типу инструкции dup_x2, если она является инструкцией безопасная форма 1 dup_x2 или инструкцией безопасная форма 2 dup_x2.
dup_x2FormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
dup_x2Form1IsTypeSafe(Environment, InputOperandStack, OutputOperandStack).
dup_x2FormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
dup_x2Form2IsTypeSafe(Environment, InputOperandStack, OutputOperandStack).
Инструкция dup_x2 является инструкцией безопасная форма 1 dup_x2, если можно корректно заменить три типа категории 1, Type1, Type2, Type3 на стеке операндов входного типа на типы Type1, Type2, Type3, Type1, что соответствует выходному состоянию типа.
dup_x2Form1IsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
popCategory1(InputOperandStack, Type1, Stack1),
popCategory1(Stack1, Type2, Stack2),
popCategory1(Stack2, Type3, Rest),
canSafelyPushList(Environment, Rest, [Type1, Type3, Type2, Type1],
OutputOperandStack).
Инструкция dup_x2 является инструкцией безопасная форма 2 dup_x2, если можно корректно заменить тип категории 1, Type1, и тип категории 2, Type2, на стеке операндов входного типа на типы Type1, Type2, Type1, что соответствует выходному состоянию типа.
dup_x2Form2IsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
popCategory1(InputOperandStack, Type1, Stack1),
popCategory2(Stack1, Type2, Rest),
canSafelyPushList(Environment, Rest, [Type1, Type2, Type1],
OutputOperandStack).
dup2
Инструкция dup2 безопасна по типу, если она является безопасной формой по типу инструкции dup2.
instructionIsTypeSafe(dup2, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
StackFrame = frame(Locals, InputOperandStack, Flags),
dup2FormIsTypeSafe(Environment,InputOperandStack, OutputOperandStack),
NextStackFrame = frame(Locals, OutputOperandStack, Flags),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция dup2 является безопасной формой по типу инструкции dup2, если она является инструкцией безопасная форма 1 dup2 или инструкцией безопасная форма 2 dup2.
dup2FormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
dup2Form1IsTypeSafe(Environment,InputOperandStack, OutputOperandStack).
dup2FormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
dup2Form2IsTypeSafe(Environment,InputOperandStack, OutputOperandStack).
Инструкция dup2 является инструкцией безопасная форма 1 dup2, если можно корректно заменить два типа категории 1, Type1 и Type2 на стеке операндов входного типа на типы Type1, Type2, Type1, Type2, что соответствует выходному состоянию типа.
dup2Form1IsTypeSafe(Environment, InputOperandStack, OutputOperandStack):-
popCategory1(InputOperandStack, Type1, TempStack),
popCategory1(TempStack, Type2, _),
canSafelyPushList(Environment, InputOperandStack, [Type1, Type2],
OutputOperandStack).
Инструкция dup2 является инструкцией безопасная форма 2 dup2, если можно корректно заменить тип категории 2, Type, на стеке операндов входного типа на типы Type, Type, что соответствует выходному состоянию типа.
dup2Form2IsTypeSafe(Environment, InputOperandStack, OutputOperandStack):-
popCategory2(InputOperandStack, Type, _),
canSafelyPush(Environment, InputOperandStack, Type, OutputOperandStack).
dup2_x1
Инструкция dup2_x1 безопасна по типу, если она является безопасной формой по типу инструкции dup2_x1.
instructionIsTypeSafe(dup2_x1, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
StackFrame = frame(Locals, InputOperandStack, Flags),
dup2_x1FormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack),
NextStackFrame = frame(Locals, OutputOperandStack, Flags),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция dup2_x1 является безопасной формой по типу инструкции dup2_x1, если она является инструкцией безопасная форма 1 dup2_x1 или инструкцией безопасная форма 2 dup_x2.
dup2_x1FormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
dup2_x1Form1IsTypeSafe(Environment, InputOperandStack, OutputOperandStack).
dup2_x1FormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
dup2_x1Form2IsTypeSafe(Environment, InputOperandStack, OutputOperandStack).
Инструкция dup2_x1 является инструкцией безопасная форма 1 dup2_x1, если можно корректно заменить три типа категории 1, Type1, Type2, Type3 на стеке операндов входного типа на типы Type1, Type2, Type3, Type1, Type2, что соответствует выходному состоянию типа.
dup2_x1Form1IsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
popCategory1(InputOperandStack, Type1, Stack1),
popCategory1(Stack1, Type2, Stack2),
popCategory1(Stack2, Type3, Rest),
canSafelyPushList(Environment, Rest, [Type2, Type1, Type3, Type2, Type1],
OutputOperandStack).
Инструкция dup2_x1 является инструкцией безопасная форма 2 dup2_x1, если можно корректно заменить тип категории 2, Type1, и тип категории 1, Type2, на стеке операндов входного типа на типы Type1, Type2, Type1, что соответствует выходному состоянию типа.
dup2_x1Form2IsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
popCategory2(InputOperandStack, Type1, Stack1),
popCategory1(Stack1, Type2, Rest),
canSafelyPushList(Environment, Rest, [Type1, Type2, Type1],
OutputOperandStack).
dup2_x2
Инструкция dup2_x2 безопасна по типу, если она является безопасной формой по типу инструкции dup2_x2.
instructionIsTypeSafe(dup2_x2, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
StackFrame = frame(Locals, InputOperandStack, Flags),
dup2_x2FormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack),
NextStackFrame = frame(Locals, OutputOperandStack, Flags),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция dup2_x2 является безопасной формой по типу инструкции dup2_x2, если выполняется одно из следующих условий:
-
она является инструкцией безопасная форма 1 dup2_x2.
-
она является инструкцией безопасная форма 2 dup2_x2.
-
она является инструкцией безопасная форма 3 dup2_x2.
-
она является инструкцией безопасная форма 4 dup2_x2.
dup2_x2FormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
dup2_x2Form1IsTypeSafe(Environment, InputOperandStack, OutputOperandStack).
dup2_x2FormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
dup2_x2Form2IsTypeSafe(Environment, InputOperandStack, OutputOperandStack).
dup2_x2FormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
dup2_x2Form3IsTypeSafe(Environment, InputOperandStack, OutputOperandStack).
dup2_x2FormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
dup2_x2Form4IsTypeSafe(Environment, InputOperandStack, OutputOperandStack).
Инструкция dup2_x2 является инструкцией безопасная форма 1 dup2_x2, если можно корректно заменить четыре типа категории 1, Type1, Type2, Type3, Type4 на стеке операндов входного типа на типы Type1, Type2, Type3, Type4, Type1, Type2, что соответствует выходному состоянию типа.
dup2_x2Form1IsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
popCategory1(InputOperandStack, Type1, Stack1),
popCategory1(Stack1, Type2, Stack2),
popCategory1(Stack2, Type3, Stack3),
popCategory1(Stack3, Type4, Rest),
canSafelyPushList(Environment, Rest,
[Type2, Type1, Type4, Type3, Type2, Type1],
OutputOperandStack).
Инструкция dup2_x2 является инструкцией безопасная форма 2 dup2_x2, если можно корректно заменить тип категории 2, Type1, и два типа категории 1, Type2, Type3, на стеке операндов входного типа на типы Type1, Type2, Type3, Type1, что соответствует выходному состоянию типа.
dup2_x2Form2IsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
popCategory2(InputOperandStack, Type1, Stack1),
popCategory1(Stack1, Type2, Stack2),
popCategory1(Stack2, Type3, Rest),
canSafelyPushList(Environment, Rest,
[Type1, Type3, Type2, Type1],
OutputOperandStack).
Инструкция dup2_x2 является инструкцией безопасная форма 3 dup2_x2, если можно корректно заменить два типа категории 1, Type1, Type2, и тип категории 2, Type3, на стеке операндов входного типа на типы Type1, Type2, Type3, Type1, Type2, что соответствует выходному состоянию типа.
dup2_x2Form3IsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
popCategory1(InputOperandStack, Type1, Stack1),
popCategory1(Stack1, Type2, Stack2),
popCategory2(Stack2, Type3, Rest),
canSafelyPushList(Environment, Rest,
[Type2, Type1, Type3, Type2, Type1],
OutputOperandStack).
Инструкция dup2_x2 является инструкцией безопасная форма 4 dup2_x2, если можно корректно заменить два типа категории 2, Type1, Type2, на стеке операндов входного типа на типы Type1, Type2, Type1, что соответствует выходному состоянию типа.
dup2_x2Form4IsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
popCategory2(InputOperandStack, Type1, Stack1),
popCategory2(Stack1, Type2, Rest),
canSafelyPushList(Environment, Rest, [Type1, Type2, Type1],
OutputOperandStack).
f2d, f2i, f2l
Инструкция f2d безопасна по типу, если можно корректно извлечь float со стека операндов входного типа и заменить его на double, что соответствует выходному состоянию типа.
instructionIsTypeSafe(f2d, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [float], double,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция f2i безопасна по типу, если можно корректно извлечь float со стека операндов входного типа и заменить его на int, что соответствует выходному состоянию типа.
instructionIsTypeSafe(f2i, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [float], int,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция f2l безопасна по типу, если можно корректно извлечь float со стека операндов входного типа и заменить его на long, что соответствует выходному состоянию типа.
instructionIsTypeSafe(f2l, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [float], long,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
fadd
Инструкция fadd безопасна по типу, если можно корректно заменить типы, соответствующие float и float, на стеке операндов входного типа на float, что соответствует выходному состоянию типа.
instructionIsTypeSafe(fadd, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [float, float], float,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
faload
Инструкция faload является безопасной по типу тогда и только тогда, когда можно корректно заменить типы, соответствующие int и массив float на входном стеке операндов на float, что приводит к состоянию типа на выходе.
instructionIsTypeSafe(faload, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [int, arrayOf(float)], float,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
fastore
Инструкция fastore является безопасной по типу тогда и только тогда, когда можно корректно извлечь типы, соответствующие float, int и массив float с входного стека операндов, что приводит к состоянию типа на выходе.
instructionIsTypeSafe(fastore, _Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
canPop(StackFrame, [float, int, arrayOf(float)], NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
fcmp<op>
Инструкция fcmpg является безопасной по типу тогда и только тогда, когда можно корректно заменить типы, соответствующие float и float на входном стеке операндов на int, что приводит к состоянию типа на выходе.
instructionIsTypeSafe(fcmpg, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [float, float], int,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция fcmpl является безопасной по типу тогда и только тогда, когда эквивалентная инструкция fcmpg является безопасной по типу.
instructionHasEquivalentTypeRule(fcmpl, fcmpg).
fconst_<f>
Инструкция fconst_0 безопасна по типу, если можно корректно поместить тип float на входной стек операндов, что приводит к состоянию типа на выходе.
instructionIsTypeSafe(fconst_0, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [], float, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Правила для других вариантов fconst аналогичны.
instructionHasEquivalentTypeRule(fconst_1, fconst_0). instructionHasEquivalentTypeRule(fconst_2, fconst_0).
fdiv
Инструкция fdiv безопасна по типу тогда и только тогда, когда эквивалентная инструкция fadd безопасна по типу.
instructionHasEquivalentTypeRule(fdiv, fadd).
fload, fload_<n>
Инструкция fload с операндом Index безопасна по типу и приводит к состоянию типа на выходе NextStackFrame, если инструкция загрузки с операндом Index и типом float безопасна по типу и приводит к состоянию типа на выходе NextStackFrame.
instructionIsTypeSafe(fload(Index), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
loadIsTypeSafe(Environment, Index, float, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкции fload_<n>, для 0 ≤ n ≤ 3, безопасны по типу тогда и только тогда, когда эквивалентная инструкция fload безопасна по типу.
instructionHasEquivalentTypeRule(fload_0, fload(0)). instructionHasEquivalentTypeRule(fload_1, fload(1)). instructionHasEquivalentTypeRule(fload_2, fload(2)). instructionHasEquivalentTypeRule(fload_3, fload(3)).
fmul
Инструкция fmul безопасна по типу тогда и только тогда, когда эквивалентная инструкция fadd безопасна по типу.
instructionHasEquivalentTypeRule(fmul, fadd).
fneg
Инструкция fneg безопасна по типу тогда и только тогда, когда на входном стеке операндов есть тип, соответствующий float. Инструкция fneg не изменяет состояние типа.
instructionIsTypeSafe(fneg, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [float], float,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
frem
Инструкция frem безопасна по типу тогда и только тогда, когда эквивалентная инструкция fadd безопасна по типу.
instructionHasEquivalentTypeRule(frem, fadd).
freturn
Инструкция freturn безопасна по типу, если у окружающего метода тип возвращаемого значения указан как float, и можно корректно извлечь тип, соответствующий float с входного стека операндов.
instructionIsTypeSafe(freturn, Environment, _Offset, StackFrame,
afterGoto, ExceptionStackFrame) :-
thisMethodReturnType(Environment, float),
canPop(StackFrame, [float], _PoppedStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
fstore, fstore_<n>
Инструкция fstore с операндом Index безопасна по типу и приводит к состоянию типа на выходе NextStackFrame, если инструкция сохранения с операндом Index и типом float безопасна по типу и приводит к состоянию типа на выходе NextStackFrame.
instructionIsTypeSafe(fstore(Index), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
storeIsTypeSafe(Environment, Index, float, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкции fstore_<n>, для 0 ≤ n ≤ 3, безопасны по типу тогда и только тогда, когда эквивалентная инструкция fstore безопасна по типу.
instructionHasEquivalentTypeRule(fstore_0, fstore(0)). instructionHasEquivalentTypeRule(fstore_1, fstore(1)). instructionHasEquivalentTypeRule(fstore_2, fstore(2)). instructionHasEquivalentTypeRule(fstore_3, fstore(3)).
fsub
Инструкция fsub безопасна по типу тогда и только тогда, когда эквивалентная инструкция fadd безопасна по типу.
instructionHasEquivalentTypeRule(fsub, fadd).
getfield
Инструкция getfield с операндом CP безопасна по типу тогда и только тогда, когда CP ссылается на запись константного пула, обозначающую поле с объявленным типом FieldType, объявленным в классе FieldClass, и можно корректно заменить тип, соответствующий FieldClass, на тип FieldType на входном стеке операндов, что приводит к состоянию типа на выходе. FieldClass не должен быть массивом. Для полей protected применяются дополнительные проверки (§4.10.1.8).
instructionIsTypeSafe(getfield(CP), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
CP = field(FieldClass, FieldName, FieldDescriptor),
parseFieldDescriptor(FieldDescriptor, FieldType),
passesProtectedCheck(Environment, FieldClass, FieldName,
FieldDescriptor, StackFrame),
validTypeTransition(Environment, [class(FieldClass)], FieldType,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
getstatic
Инструкция getstatic с операндом CP безопасна по типу тогда и только тогда, когда CP ссылается на запись константного пула, обозначающую поле с объявленным типом FieldType, и можно корректно поместить FieldType на входной стек операндов, что приводит к состоянию типа на выходе.
instructionIsTypeSafe(getstatic(CP), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
CP = field(_FieldClass, _FieldName, FieldDescriptor),
parseFieldDescriptor(FieldDescriptor, FieldType),
validTypeTransition(Environment, [], FieldType,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
goto, goto_w
Инструкция goto безопасна по типу тогда и только тогда, когда ее целевой операнд является допустимой точкой ветвления.
instructionIsTypeSafe(goto(Target), Environment, _Offset, StackFrame,
afterGoto, ExceptionStackFrame) :-
targetIsTypeSafe(Environment, StackFrame, Target),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция goto_w безопасна по типу тогда и только тогда, когда эквивалентная инструкция goto безопасна по типу.
instructionHasEquivalentTypeRule(goto_w(Target), goto(Target)).
i2b, i2c, i2d, i2f, i2l, i2s
Инструкция i2b безопасна по типу тогда и только тогда, когда эквивалентная инструкция ineg безопасна по типу.
instructionHasEquivalentTypeRule(i2b, ineg).
Инструкция i2c безопасна по типу тогда и только тогда, когда эквивалентная инструкция ineg безопасна по типу.
instructionHasEquivalentTypeRule(i2c, ineg).
Инструкция i2d безопасна по типу, если можно корректно извлечь int с входного стека операндов и заменить его на double, что приводит к состоянию типа на выходе.
instructionIsTypeSafe(i2d, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [int], double,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция i2f безопасна по типу, если можно корректно извлечь int с входного стека операндов и заменить его на float, что приводит к состоянию типа на выходе.
instructionIsTypeSafe(i2f, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [int], float,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция i2l безопасна по типу, если можно корректно извлечь int с входного стека операндов и заменить его на long, что приводит к состоянию типа на выходе.
instructionIsTypeSafe(i2l, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [int], long,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция i2s безопасна по типу тогда и только тогда, когда эквивалентная инструкция ineg безопасна по типу.
instructionHasEquivalentTypeRule(i2s, ineg).
iadd
Инструкция iadd безопасна по типу тогда и только тогда, когда можно корректно заменить типы, соответствующие int и int в стеке операндов на входном состоянии на int, что задаёт выходное состояние типа.
instructionIsTypeSafe(iadd, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [int, int], int,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
iaload
Инструкция iaload безопасна по типу тогда и только тогда, когда можно корректно заменить типы, соответствующие int и массиву int в стеке операндов на входном состоянии на int, что задаёт выходное состояние типа.
instructionIsTypeSafe(iaload, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [int, arrayOf(int)], int,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
iand
Инструкция iand безопасна по типу тогда и только тогда, когда эквивалентная инструкция iadd безопасна по типу.
instructionHasEquivalentTypeRule(iand, iadd).
iastore
Инструкция iastore безопасна по типу тогда и только тогда, когда можно корректно извлечь типы, соответствующие int, int и массиву int из входного стека операндов, что задаёт выходное состояние типа.
instructionIsTypeSafe(iastore, _Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
canPop(StackFrame, [int, int, arrayOf(int)], NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
if_acmp<cond>
Инструкция if_acmpeq безопасна по типу тогда и только тогда, когда можно корректно извлечь типы, соответствующие reference и reference из входного стека операндов, что задаёт выходное состояние типа NextStackFrame, и операнд инструкции, Target, является допустимой точкой ветвления, предполагая входное состояние типа NextStackFrame.
instructionIsTypeSafe(if_acmpeq(Target), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
canPop(StackFrame, [reference, reference], NextStackFrame),
targetIsTypeSafe(Environment, NextStackFrame, Target),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Правило для if_acmpne идентично.
instructionHasEquivalentTypeRule(if_acmpne(Target), if_acmpeq(Target)).
if_icmp<cond>
Инструкция if_icmpeq безопасна по типу тогда и только тогда, когда можно корректно извлечь типы, соответствующие int и int из входного стека операндов, что задаёт выходное состояние типа NextStackFrame, и операнд инструкции, Target, является допустимой точкой ветвления, предполагая входное состояние типа NextStackFrame.
instructionIsTypeSafe(if_icmpeq(Target), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
canPop(StackFrame, [int, int], NextStackFrame),
targetIsTypeSafe(Environment, NextStackFrame, Target),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Правила для всех остальных вариантов инструкции if_icmp<cond> идентичны.
instructionHasEquivalentTypeRule(if_icmpge(Target), if_icmpeq(Target)). instructionHasEquivalentTypeRule(if_icmpgt(Target), if_icmpeq(Target)). instructionHasEquivalentTypeRule(if_icmple(Target), if_icmpeq(Target)). instructionHasEquivalentTypeRule(if_icmplt(Target), if_icmpeq(Target)). instructionHasEquivalentTypeRule(if_icmpne(Target), if_icmpeq(Target)).
if<cond>
Инструкция ifeq безопасна по типу тогда и только тогда, когда можно корректно извлечь тип, соответствующий int из входного стека операндов, что задаёт выходное состояние типа NextStackFrame, и операнд инструкции, Target, является допустимой точкой ветвления, предполагая входное состояние типа NextStackFrame.
instructionIsTypeSafe(ifeq(Target), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
canPop(StackFrame, [int], NextStackFrame),
targetIsTypeSafe(Environment, NextStackFrame, Target),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Правила для всех других вариантов инструкции if<cond> идентичны.
instructionHasEquivalentTypeRule(ifge(Target), ifeq(Target)). instructionHasEquivalentTypeRule(ifgt(Target), ifeq(Target)). instructionHasEquivalentTypeRule(ifle(Target), ifeq(Target)). instructionHasEquivalentTypeRule(iflt(Target), ifeq(Target)). instructionHasEquivalentTypeRule(ifne(Target), ifeq(Target)).
ifnonnull
Инструкция ifnonnull безопасна по типу тогда и только тогда, когда можно корректно извлечь тип, соответствующий reference из входного стека операндов, что задаёт выходное состояние типа NextStackFrame, и операнд инструкции, Target, является допустимой точкой ветвления, предполагая входное состояние типа NextStackFrame.
instructionIsTypeSafe(ifnonnull(Target), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
canPop(StackFrame, [reference], NextStackFrame),
targetIsTypeSafe(Environment, NextStackFrame, Target),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
ifnull
Инструкция ifnull безопасна по типу тогда и только тогда, когда эквивалентная инструкция ifnonnull безопасна по типу.
instructionHasEquivalentTypeRule(ifnull(Target), ifnonnull(Target)).
iinc
Инструкция iinc с первым операндом Index безопасна по типу тогда и только тогда, когда LIndex имеет тип int. Инструкция iinc не изменяет состояние типа.
instructionIsTypeSafe(iinc(Index, _Value), _Environment, _Offset,
StackFrame, StackFrame, ExceptionStackFrame) :-
StackFrame = frame(Locals, _OperandStack, _Flags),
nth0(Index, Locals, int),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
iload, iload_<n>
Инструкция iload с операндом Index безопасна по типу и даёт выходное состояние типа NextStackFrame, если инструкция загрузки с операндом Index и типом int безопасна по типу и даёт выходное состояние типа NextStackFrame.
instructionIsTypeSafe(iload(Index), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
loadIsTypeSafe(Environment, Index, int, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкции iload_<n>, для 0 ≤ n ≤ 3, безопасны по типу тогда и только тогда, когда эквивалентная инструкция iload безопасна по типу.
instructionHasEquivalentTypeRule(iload_0, iload(0)). instructionHasEquivalentTypeRule(iload_1, iload(1)). instructionHasEquivalentTypeRule(iload_2, iload(2)). instructionHasEquivalentTypeRule(iload_3, iload(3)).
imul
Инструкция imul безопасна по типу тогда и только тогда, когда эквивалентная инструкция iadd безопасна по типу.
instructionHasEquivalentTypeRule(imul, iadd).
ineg
Инструкция ineg безопасна по типу тогда и только тогда, когда в входном стеке операндов есть тип, соответствующий int. Инструкция ineg не изменяет состояние типа.
instructionIsTypeSafe(ineg, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [int], int, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
instanceof
Инструкция instanceof с операндом CP безопасна по типу тогда и только тогда, когда CP ссылается на запись константного пула, обозначающую класс или массив, и можно корректно заменить тип Object вверху входного стека операндов на тип int, что задаёт выходное состояние типа.
instructionIsTypeSafe(instanceof(CP), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
(CP = class(_, _) ; CP = arrayOf(_)),
isBootstrapLoader(BL),
validTypeTransition(Environment, [class('java/lang/Object', BL)], int,
StackFrame,NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
invokedynamic
Инструкция invokedynamic безопасна по типу тогда и только тогда, когда выполняются все следующие условия:
-
Её первый операнд,
CP, ссылается на запись константного пула, обозначающую динамическую точку вызова с именемCallSiteNameи дескрипторомDescriptor. -
CallSiteNameне равно<init>. -
CallSiteNameне равно<clinit>. -
Можно корректно заменить типы, соответствующие типам аргументов, указанным в
Descriptor, в входном стеке операндов на возвращаемый тип, указанный вDescriptor, что задаёт выходное состояние типа.
instructionIsTypeSafe(invokedynamic(CP,0,0), Environment, _Offset,
StackFrame, NextStackFrame, ExceptionStackFrame) :-
CP = dmethod(CallSiteName, Descriptor),
CallSiteName \= '<init>',
CallSiteName \= '<clinit>',
parseMethodDescriptor(Descriptor, OperandArgList, ReturnType),
reverse(OperandArgList, StackArgList),
validTypeTransition(Environment, StackArgList, ReturnType,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
invokeinterface
Инструкция invokeinterface является безопасной с точки зрения типов, если все перечисленные ниже условия выполняются:
-
Её первый операнд,
CP, ссылается на запись в пуле констант, обозначающую интерфейсный метод под названиемMethodNameс описаниемDescriptor, который является членом интерфейсаMethodIntfName. -
MethodNameне является<init>. -
MethodNameне является<clinit>. -
Её второй операнд,
Count, является допустимым операндом-счётчиком (см. ниже). -
Можно корректно заменить типы, соответствующие типу
MethodIntfNameи типам аргументов, указанным вDescriptor, на тип возврата, указанный вDescriptor, что приведёт к состоянию исходящих типов.
instructionIsTypeSafe(invokeinterface(CP, Count, 0), Environment, _Offset,
StackFrame, NextStackFrame, ExceptionStackFrame) :-
CP = imethod(MethodIntfName, MethodName, Descriptor),
MethodName \= '<init>',
MethodName \= '<clinit>',
parseMethodDescriptor(Descriptor, OperandArgList, ReturnType),
currentClassLoader(Environment, CurrentLoader),
reverse([class(MethodIntfName, CurrentLoader) | OperandArgList],
StackArgList),
canPop(StackFrame, StackArgList, TempFrame),
validTypeTransition(Environment, [], ReturnType, TempFrame, NextStackFrame),
countIsValid(Count, StackFrame, TempFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Операнд Count инструкции invokeinterface является допустимым, если он равен размеру аргументов инструкции. Это равно разнице между размером InputFrame и OutputFrame.
countIsValid(Count, InputFrame, OutputFrame) :-
InputFrame = frame(_Locals1, OperandStack1, _Flags1),
OutputFrame = frame(_Locals2, OperandStack2, _Flags2),
length(OperandStack1, Length1),
length(OperandStack2, Length2),
Count =:= Length1 - Length2.
invokespecial
Инструкция invokespecial является безопасной с точки зрения типов, если все перечисленные ниже условия выполняются:
-
Её первый операнд,
CP, ссылается на запись в пуле констант, обозначающую метод под названиемMethodNameс описаниемDescriptor, который является членом классаMethodClassName. -
Либо:
-
MethodNameне является<init>. -
MethodNameне является<clinit>. -
Можно корректно заменить типы, соответствующие текущему классу и типам аргументов, указанным в
Descriptor, на тип возврата, указанный вDescriptor, что приведёт к состоянию исходящих типов. -
Можно корректно заменить типы, соответствующие классу
MethodClassNameи типам аргументов, указанным вDescriptor, на тип возврата, указанный вDescriptor.
-
instructionIsTypeSafe(invokespecial(CP), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
CP = method(MethodClassName, MethodName, Descriptor),
MethodName \= '<init>',
MethodName \= '<clinit>',
parseMethodDescriptor(Descriptor, OperandArgList, ReturnType),
thisClass(Environment, class(CurrentClassName, CurrentLoader)),
reverse([class(CurrentClassName, CurrentLoader) | OperandArgList],
StackArgList),
validTypeTransition(Environment, StackArgList, ReturnType,
StackFrame, NextStackFrame),
reverse([class(MethodClassName, CurrentLoader) | OperandArgList],
StackArgList2),
validTypeTransition(Environment, StackArgList2, ReturnType,
StackFrame, _ResultStackFrame),
isAssignable(class(CurrentClassName, CurrentLoader),
class(MethodClassName, CurrentLoader)).
exceptionStackFrame(StackFrame, ExceptionStackFrame).
-
Или:
-
ИмяМетода равно
<init>. -
Descriptorзадаёт тип возвратаvoid. -
Можно корректно извлечь типы, соответствующие типам аргументов, указанным в
Descriptor, и типу неинициализированной переменнойUninitializedArg, из входного стека операндов, что приведёт к результатуOperandStack. -
Состояние исходящих типов выводится из состояния входных типов, сначала заменив входной стек операндов на
OperandStack, а затем заменив все экземплярыUninitializedArgтипом инициализируемого экземпляра.
-
instructionIsTypeSafe(invokespecial(CP), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
CP = method(MethodClassName, '<init>', Descriptor),
parseMethodDescriptor(Descriptor, OperandArgList, void),
reverse(OperandArgList, StackArgList),
canPop(StackFrame, StackArgList, TempFrame),
TempFrame = frame(Locals, FullOperandStack, Flags),
FullOperandStack = [UninitializedArg | OperandStack],
currentClassLoader(Environment, CurrentLoader),
rewrittenUninitializedType(UninitializedArg, Environment,
class(MethodClassName, CurrentLoader), This),
rewrittenInitializationFlags(UninitializedArg, Flags, NextFlags),
substitute(UninitializedArg, This, OperandStack, NextOperandStack),
substitute(UninitializedArg, This, Locals, NextLocals),
NextStackFrame = frame(NextLocals, NextOperandStack, NextFlags),
ExceptionStackFrame = frame(Locals, [], Flags),
passesProtectedCheck(Environment, MethodClassName, '<init>',
Descriptor, NextStackFrame).
Для вычисления того, на какой тип должен быть переписан тип неинициализированного аргумента, существуют два случая:
-
Если мы инициализируем объект внутри его конструктора, его тип изначально равен
uninitializedThis. Этот тип будет переписан на тип класса метода<init>. -
Второй случай возникает при инициализации объекта, созданного инструкцией new. Тип неинициализированного аргумента переписывается на
MethodClass, тип метода-владельца<init>. Мы проверяем, есть ли действительно инструкция new по адресуAddress.
rewrittenUninitializedType(uninitializedThis, Environment,
MethodClass, MethodClass) :-
MethodClass = class(MethodClassName, CurrentLoader),
thisClass(Environment, MethodClass).
rewrittenUninitializedType(uninitializedThis, Environment,
MethodClass, MethodClass) :-
MethodClass = class(MethodClassName, CurrentLoader),
thisClass(Environment, class(thisClassName, thisLoader)),
superclassChain(thisClassName, thisLoader, [MethodClass | Rest]).
rewrittenUninitializedType(uninitialized(Address), Environment,
MethodClass, MethodClass) :-
allInstructions(Environment, Instructions),
member(instruction(Address, new(MethodClass)), Instructions).
rewrittenInitializationFlags(uninitializedThis, _Flags, []).
rewrittenInitializationFlags(uninitialized(_), Flags, Flags).
substitute(_Old, _New, [], []).
substitute(Old, New, [Old | FromRest], [New | ToRest]) :-
substitute(Old, New, FromRest, ToRest).
substitute(Old, New, [From1 | FromRest], [From1 | ToRest]) :-
From1 \= Old,
substitute(Old, New, FromRest, ToRest).
Правило для invokespecial метода <init> является единственной мотивацией для передачи отдельной рамки стека исключений. Вопрос в том, что при инициализации объекта внутри его конструктора invokespecial может вызвать метод суперкласса <init>, и этот вызов может завершиться ошибкой, оставив this неинициализированным. Эта ситуация не может быть создана с помощью исходного кода языка программирования Java, но может быть создана, программируя непосредственно в байткоде.
В этой ситуации исходная рамка содержит неинициализированный объект в локальной переменной 0 и имеет флаг flagThisUninit. Нормальное завершение invokespecial инициализирует неинициализированный объект и отключает флаг flagThisUninit. Но если вызов метода <init> вызывает исключение, неинициализированный объект может остаться в частично инициализированном состоянии и должен быть навсегда непригодным для использования. Это представлено рамкой исключения, содержащей сломанный объект (новое значение локальной переменной) и флаг flagThisUninit (старый флаг). Нет способа перейти от объекта, который кажется инициализированным, с флагом flagThisUninit, к должным образом инициализированному объекту, поэтому объект навсегда непригоден для использования.
Если бы не было этой ситуации, флаги рамки стека исключений всегда были бы такими же, как флаги входной рамки.
invokestatic
Инструкция invokestatic является безопасной с точки зрения типов, если все перечисленные ниже условия выполняются:
-
Её первый операнд,
CP, ссылается на запись в пуле констант, обозначающую метод под названиемMethodNameс описаниемDescriptor. -
MethodNameне является<init>. -
MethodNameне является<clinit>. -
Можно корректно заменить типы, соответствующие типам аргументов, указанным в
Descriptor, на тип возврата, указанный вDescriptor, что приведёт к состоянию исходящих типов.
instructionIsTypeSafe(invokestatic(CP), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
CP = method(_MethodClassName, MethodName, Descriptor),
MethodName \= '<init>',
MethodName \= '<clinit>',
parseMethodDescriptor(Descriptor, OperandArgList, ReturnType),
reverse(OperandArgList, StackArgList),
validTypeTransition(Environment, StackArgList, ReturnType,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
invokevirtual
Инструкция invokevirtual является безопасной с точки зрения типов, если все перечисленные ниже условия выполняются:
-
Её первый операнд,
CP, ссылается на запись в пуле констант, обозначающую метод под названиемMethodNameс описаниемDescriptor, который является членом классаMethodClassName. -
MethodNameне является<init>. -
MethodNameне является<clinit>. -
Можно корректно заменить типы, соответствующие классу
MethodClassNameи типам аргументов, указанным вDescriptor, на тип возврата, указанный вDescriptor, что приведёт к состоянию исходящих типов. -
Если метод является
protected, использование соответствует особым правилам доступа к членамprotected(§4.10.1.8).
instructionIsTypeSafe(invokevirtual(CP), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
CP = method(MethodClassName, MethodName, Descriptor),
MethodName \= '<init>',
MethodName \= '<clinit>',
parseMethodDescriptor(Descriptor, OperandArgList, ReturnType),
reverse(OperandArgList, ArgList),
currentClassLoader(Environment, CurrentLoader),
reverse([class(MethodClassName, CurrentLoader) | OperandArgList],
StackArgList),
validTypeTransition(Environment, StackArgList, ReturnType,
StackFrame, NextStackFrame),
canPop(StackFrame, ArgList, PoppedFrame),
passesProtectedCheck(Environment, MethodClassName, MethodName,
Descriptor, PoppedFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
ior
Инструкция ior является безопасной с точки зрения типов, если эквивалентная инструкция iadd является безопасной с точки зрения типов.
instructionHasEquivalentTypeRule(ior, iadd).
irem
Инструкция irem является безопасной с точки зрения типов, если эквивалентная инструкция iadd является безопасной с точки зрения типов.
instructionHasEquivalentTypeRule(irem, iadd).
ireturn
Инструкция ireturn является безопасной с точки зрения типов, если у окружающего метода тип возврата объявлен как int, и можно корректно извлечь тип, соответствующий типу int, из входного стека операндов.
instructionIsTypeSafe(ireturn, Environment, _Offset, StackFrame,
afterGoto, ExceptionStackFrame) :-
thisMethodReturnType(Environment, int),
canPop(StackFrame, [int], _PoppedStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
ishl, ishr, iushr
Инструкция ishl безопасна с точки зрения типов тогда и только тогда, когда эквивалентная инструкция iadd безопасна с точки зрения типов.
instructionHasEquivalentTypeRule(ishl, iadd).
Инструкция ishr безопасна с точки зрения типов тогда и только тогда, когда эквивалентная инструкция iadd безопасна с точки зрения типов.
instructionHasEquivalentTypeRule(ishr, iadd).
Инструкция iushr безопасна с точки зрения типов тогда и только тогда, когда эквивалентная инструкция iadd безопасна с точки зрения типов.
instructionHasEquivalentTypeRule(iushr, iadd).
istore, istore_<n>
Инструкция istore с операндом Index безопасна с точки зрения типов и приводит к состоянию исходящего типа NextStackFrame, если инструкция сохранения с операндом Index и типом int безопасна с точки зрения типов и приводит к состоянию исходящего типа NextStackFrame.
instructionIsTypeSafe(istore(Index), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
storeIsTypeSafe(Environment, Index, int, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкции istore_<n> для 0 ≤ n ≤ 3 безопасны с точки зрения типов тогда и только тогда, когда эквивалентная инструкция istore безопасна с точки зрения типов.
instructionHasEquivalentTypeRule(istore_0, istore(0)). instructionHasEquivalentTypeRule(istore_1, istore(1)). instructionHasEquivalentTypeRule(istore_2, istore(2)). instructionHasEquivalentTypeRule(istore_3, istore(3)).
isub
Инструкция isub безопасна с точки зрения типов тогда и только тогда, когда эквивалентная инструкция iadd безопасна с точки зрения типов.
instructionHasEquivalentTypeRule(isub, iadd).
ixor
Инструкция ixor безопасна с точки зрения типов тогда и только тогда, когда эквивалентная инструкция iadd безопасна с точки зрения типов.
instructionHasEquivalentTypeRule(ixor, iadd).
l2d, l2f, l2i
Инструкция l2d безопасна с точки зрения типов, если можно корректно извлечь long из входного стека операндов и заменить его на double, что приводит к состоянию исходящего типа.
instructionIsTypeSafe(l2d, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [long], double,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция l2f безопасна с точки зрения типов, если можно корректно извлечь long из входного стека операндов и заменить его на float, что приводит к состоянию исходящего типа.
instructionIsTypeSafe(l2f, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [long], float,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция l2i безопасна с точки зрения типов, если можно корректно извлечь long из входного стека операндов и заменить его на int, что приводит к состоянию исходящего типа.
instructionIsTypeSafe(l2i, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [long], int,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
ladd
Инструкция ladd безопасна с точки зрения типов тогда и только тогда, когда можно корректно заменить типы, соответствующие long и long на входном стеке операндов на long, что приводит к состоянию исходящего типа.
instructionIsTypeSafe(ladd, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [long, long], long,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
laload
Инструкция laload безопасна с точки зрения типов тогда и только тогда, когда можно корректно заменить типы, соответствующие int и массиву long на входном стеке операндов на long, что приводит к состоянию исходящего типа.
instructionIsTypeSafe(laload, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [int, arrayOf(long)], long,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
land
Инструкция land безопасна с точки зрения типов тогда и только тогда, когда эквивалентная инструкция ladd безопасна с точки зрения типов.
instructionHasEquivalentTypeRule(land, ladd).
lastore
Инструкция lastore безопасна с точки зрения типов тогда и только тогда, когда можно корректно извлечь типы, соответствующие long, int и массиву long из входного стека операндов, что приводит к состоянию исходящего типа.
instructionIsTypeSafe(lastore, _Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
canPop(StackFrame, [long, int, arrayOf(long)], NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
lcmp
Инструкция lcmp безопасна с точки зрения типов тогда и только тогда, когда можно корректно заменить типы, соответствующие long и long на входном стеке операндов на int, что приводит к состоянию исходящего типа.
instructionIsTypeSafe(lcmp, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [long, long], int,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
lconst_<l>
Инструкция lconst_0 безопасна с точки зрения типов, если можно корректно поместить тип long на входной стек операндов, что приводит к состоянию исходящего типа.
instructionIsTypeSafe(lconst_0, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [], long, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция lconst_1 безопасна с точки зрения типов тогда и только тогда, когда эквивалентная инструкция lconst_0 безопасна с точки зрения типов.
instructionHasEquivalentTypeRule(lconst_1, lconst_0).
ldc, ldc_w, ldc2_w
Инструкция ldc с операндом CP безопасна с точки зрения типов тогда и только тогда, когда CP относится к записи пула констант, обозначающей сущность типа Type, где Type — это либо int, либо float, либо String, либо Class, либо java.lang.invoke.MethodType, либо java.lang.invoke.MethodHandle, и можно корректно поместить Type на входной стек операндов, что приводит к состоянию исходящего типа.
instructionIsTypeSafe(ldc(CP), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
functor(CP, Tag, _),
isBootstrapLoader(BL),
member([Tag, Type], [
[int, int],
[float, float],
[string, class('java/lang/String', BL)],
[classConst, class('java/lang/Class', BL)],
[methodTypeConst, class('java/lang/invoke/MethodType', BL)],
[methodHandleConst, class('java/lang/invoke/MethodHandle', BL)],
]),
validTypeTransition(Environment, [], Type, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция ldc_w безопасна с точки зрения типов тогда и только тогда, когда эквивалентная инструкция ldc безопасна с точки зрения типов.
instructionHasEquivalentTypeRule(ldc_w(CP), ldc(CP))
Инструкция ldc2_w с операндом CP безопасна с точки зрения типов тогда и только тогда, когда CP относится к записи пула констант, обозначающей сущность типа Tag, где Tag — это либо long, либо double, и можно корректно поместить Tag на входной стек операндов, что приводит к состоянию исходящего типа.
instructionIsTypeSafe(ldc2_w(CP), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
functor(CP, Tag, _),
member(Tag, [long, double]),
validTypeTransition(Environment, [], Tag, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
ldiv
Инструкция ldiv безопасна с точки зрения типов тогда и только тогда, когда эквивалентная инструкция ladd безопасна с точки зрения типов.
instructionHasEquivalentTypeRule(ldiv, ladd).
lload, lload_<n>
Инструкция lload с операндом Index безопасна с точки зрения типов и приводит к состоянию исходящего типа NextStackFrame, если инструкция загрузки с операндом Index и типом long безопасна с точки зрения типов и приводит к состоянию исходящего типа NextStackFrame.
instructionIsTypeSafe(lload(Index), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
loadIsTypeSafe(Environment, Index, long, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкции lload_<n> для 0 ≤ n ≤ 3 безопасны с точки зрения типов тогда и только тогда, когда эквивалентная инструкция lload безопасна с точки зрения типов.
instructionHasEquivalentTypeRule(lload_0, lload(0)). instructionHasEquivalentTypeRule(lload_1, lload(1)). instructionHasEquivalentTypeRule(lload_2, lload(2)). instructionHasEquivalentTypeRule(lload_3, lload(3)).
lmul
Инструкция lmul безопасна с точки зрения типов тогда и только тогда, когда эквивалентная инструкция ladd безопасна с точки зрения типов.
instructionHasEquivalentTypeRule(lmul, ladd).
lneg
Инструкция lneg безопасна с точки зрения типов тогда и только тогда, когда на входном стеке операндов есть тип, соответствующий long. Инструкция lneg не изменяет состояние типа.
instructionIsTypeSafe(lneg, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [long], long,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
lookupswitch
Инструкция lookupswitch является безопасной с точки зрения типов, если её ключи отсортированы, можно корректно извлечь int из входного стека операндов, получив новое состояние типа BranchStackFrame, и все целевые метки инструкции являются допустимыми целевыми метками перехода, предполагая BranchStackFrame как их входное состояние типа.
instructionIsTypeSafe(lookupswitch(Targets, Keys), Environment, _, StackFrame,
afterGoto, ExceptionStackFrame) :-
sort(Keys, Keys),
canPop(StackFrame, [int], BranchStackFrame),
checklist(targetIsTypeSafe(Environment, BranchStackFrame), Targets),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
lor
Инструкция lor является безопасной с точки зрения типов тогда и только тогда, когда эквивалентная инструкция ladd является безопасной с точки зрения типов.
instructionHasEquivalentTypeRule(lor, ladd).
lrem
Инструкция lrem является безопасной с точки зрения типов тогда и только тогда, когда эквивалентная инструкция ladd является безопасной с точки зрения типов.
instructionHasEquivalentTypeRule(lrem, ladd).
lreturn
Инструкция lreturn является безопасной с точки зрения типов, если у объявленного метода тип возвращаемого значения long, и можно корректно извлечь тип, соответствующий long, из входного стека операндов.
instructionIsTypeSafe(lreturn, Environment, _Offset, StackFrame,
afterGoto, ExceptionStackFrame) :-
thisMethodReturnType(Environment, long),
canPop(StackFrame, [long], _PoppedStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
lshl, lshr, lushr
Инструкция lshl является безопасной с точки зрения типов, если можно корректно заменить типы int и long на входном стеке операндов типом long, получив выходное состояние типа.
instructionIsTypeSafe(lshl, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [int, long], long,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция lshr является безопасной с точки зрения типов тогда и только тогда, когда эквивалентная инструкция lshl является безопасной с точки зрения типов.
instructionHasEquivalentTypeRule(lshr, lshl).
Инструкция lushr является безопасной с точки зрения типов тогда и только тогда, когда эквивалентная инструкция lshl является безопасной с точки зрения типов.
instructionHasEquivalentTypeRule(lushr, lshl).
lstore, lstore_<n>
Инструкция lstore с операндом Index является безопасной с точки зрения типов и приводит к выходному состоянию типа NextStackFrame, если инструкция сохранения с операндом Index и типом long является безопасной с точки зрения типов и приводит к выходному состоянию типа NextStackFrame.
instructionIsTypeSafe(lstore(Index), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
storeIsTypeSafe(Environment, Index, long, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкции lstore_<n>, для 0 ≤ n ≤ 3, являются безопасными с точки зрения типов тогда и только тогда, когда эквивалентная инструкция lstore является безопасной с точки зрения типов.
instructionHasEquivalentTypeRule(lstore_0, lstore(0)). instructionHasEquivalentTypeRule(lstore_1, lstore(1)). instructionHasEquivalentTypeRule(lstore_2, lstore(2)). instructionHasEquivalentTypeRule(lstore_3, lstore(3)).
lsub
Инструкция lsub является безопасной с точки зрения типов тогда и только тогда, когда эквивалентная инструкция ladd является безопасной с точки зрения типов.
instructionHasEquivalentTypeRule(lsub, ladd).
lxor
Инструкция lxor является безопасной с точки зрения типов тогда и только тогда, когда эквивалентная инструкция ladd является безопасной с точки зрения типов.
instructionHasEquivalentTypeRule(lxor, ladd).
monitorenter
Инструкция monitorenter является безопасной с точки зрения типов тогда и только тогда, когда можно корректно извлечь тип, соответствующий reference, из входного стека операндов, получив выходное состояние типа.
instructionIsTypeSafe(monitorenter, _Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
canPop(StackFrame, [reference], NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
monitorexit
Инструкция monitorexit является безопасной с точки зрения типов тогда и только тогда, когда эквивалентная инструкция monitorenter является безопасной с точки зрения типов.
instructionHasEquivalentTypeRule(monitorexit, monitorenter).
multianewarray
Инструкция multianewarray с операндами CP и Dim является безопасной с точки зрения типов тогда и только тогда, когда CP ссылается на запись в пуле констант, обозначающую тип массива, размерность которого больше или равна Dim, Dim строго положительно, и можно корректно заменить Dim int типов на входном стеке операндов типом, обозначаемым CP, получив выходное состояние типа.
instructionIsTypeSafe(multianewarray(CP, Dim), Environment, _Offset,
StackFrame, NextStackFrame, ExceptionStackFrame) :-
CP = arrayOf(_),
classDimension(CP, Dimension),
Dimension >= Dim,
Dim > 0,
/* Make a list of Dim ints */
findall(int, between(1, Dim, _), IntList),
validTypeTransition(Environment, IntList, CP,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Размерность типа массива, элемент которого также является типом массива, на единицу больше, чем размерность его компонента.
classDimension(arrayOf(X), Dimension) :-
classDimension(X, Dimension1),
Dimension is Dimension1 + 1.
classDimension(_, Dimension) :-
Dimension = 0.
new
Инструкция new с операндом CP в смещении Offset является безопасной с точки зрения типов тогда и только тогда, когда CP ссылается на запись в пуле констант, обозначающую тип класса, тип uninitialized(Offset) не появляется во входном стеке операндов, и можно корректно поместить uninitialized(Offset) в входной стек операндов и заменить uninitialized(Offset) на top в входных локальных переменных, получив выходное состояние типа.
instructionIsTypeSafe(new(CP), Environment, Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
StackFrame = frame(Locals, OperandStack, Flags),
CP = class(_, _),
NewItem = uninitialized(Offset),
notMember(NewItem, OperandStack),
substitute(NewItem, top, Locals, NewLocals),
validTypeTransition(Environment, [], NewItem,
frame(NewLocals, OperandStack, Flags),
NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Предикат substitute определяется в правиле для invokespecial (§invokespecial).
newarray
Инструкция newarray с операндом TypeCode является безопасной с точки зрения типов тогда и только тогда, когда TypeCode соответствует примитивному типу ElementType, и можно корректно заменить тип int на входном стеке операндов типом 'массив из ElementType', получив выходное состояние типа.
instructionIsTypeSafe(newarray(TypeCode), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
primitiveArrayInfo(TypeCode, _TypeChar, ElementType, _VerifierType),
validTypeTransition(Environment, [int], arrayOf(ElementType),
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Соответствие между кодами типов и примитивными типами задаётся следующим предикатом:
primitiveArrayInfo(4, 0'Z, boolean, int). primitiveArrayInfo(5, 0'C, char, int). primitiveArrayInfo(6, 0'F, float, float). primitiveArrayInfo(7, 0'D, double, double). primitiveArrayInfo(8, 0'B, byte, int). primitiveArrayInfo(9, 0'S, short, int). primitiveArrayInfo(10, 0'I, int, int). primitiveArrayInfo(11, 0'J, long, long).
nop
Инструкция nop всегда безопасна с точки зрения типов. Инструкция nop не влияет на состояние типа.
instructionIsTypeSafe(nop, _Environment, _Offset, StackFrame,
StackFrame, ExceptionStackFrame) :-
exceptionStackFrame(StackFrame, ExceptionStackFrame).
pop, pop2
Инструкция pop является безопасной с точки зрения типов тогда и только тогда, когда можно корректно извлечь тип категории 1 из входного стека операндов, получив выходное состояние типа.
instructionIsTypeSafe(pop, _Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
StackFrame = frame(Locals, [Type | Rest], Flags),
Type \= top,
sizeOf(Type, 1),
NextStackFrame = frame(Locals, Rest, Flags),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция pop2 является безопасной с точки зрения типов тогда и только тогда, когда она является безопасной формой инструкции pop2.
instructionIsTypeSafe(pop2, _Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
StackFrame = frame(Locals, InputOperandStack, Flags),
pop2SomeFormIsTypeSafe(InputOperandStack, OutputOperandStack),
NextStackFrame = frame(Locals, OutputOperandStack, Flags),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
Инструкция pop2 является безопасной формой инструкции pop2 тогда и только тогда, когда она является безопасной формой 1 инструкции pop2 или безопасной формой 2 инструкции pop2.
pop2SomeFormIsTypeSafe(InputOperandStack, OutputOperandStack) :-
pop2Form1IsTypeSafe(InputOperandStack, OutputOperandStack).
pop2SomeFormIsTypeSafe(InputOperandStack, OutputOperandStack) :-
pop2Form2IsTypeSafe(InputOperandStack, OutputOperandStack).
Инструкция pop2 является безопасной формой 1 инструкции pop2 тогда и только тогда, когда можно корректно извлечь два типа размера 1 из входного стека операндов, получив выходное состояние типа.
pop2Form1IsTypeSafe([Type1, Type2 | Rest], Rest) :-
sizeOf(Type1, 1),
sizeOf(Type2, 1).
Инструкция pop2 является безопасной формой 2 инструкции pop2 тогда и только тогда, когда можно корректно извлечь тип размера 2 из входного стека операндов, получив выходное состояние типа.
pop2Form2IsTypeSafe([top, Type | Rest], Rest) :- sizeOf(Type, 2).
putfield
Инструкция putfield с операндом CP является безопасной с точки зрения типов тогда и только тогда, когда CP ссылается на запись в пуле констант, обозначающую поле, объявленный тип которого FieldType, объявленное в классе FieldClass, и можно корректно извлечь типы, соответствующие FieldType и FieldClass, из входного стека операндов, получив выходное состояние типа.
instructionIsTypeSafe(putfield(CP), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
CP = field(FieldClass, FieldName, FieldDescriptor),
parseFieldDescriptor(FieldDescriptor, FieldType),
canPop(StackFrame, [FieldType], PoppedFrame),
passesProtectedCheck(Environment, FieldClass, FieldName,
FieldDescriptor, PoppedFrame),
currentClassLoader(Environment, CurrentLoader),
canPop(StackFrame, [FieldType, class(FieldClass, CurrentLoader)],
NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
putstatic
Инструкция putstatic с операндом CP безопасна с точки зрения типов, если CP ссылается на запись константного пула, обозначающую поле, тип которого объявлен как FieldType, и можно корректно извлечь тип, соответствующий FieldType, из входного стека операндов, получая выходное состояние типа.
instructionIsTypeSafe(putstatic(CP), _Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
CP = field(_FieldClass, _FieldName, FieldDescriptor),
parseFieldDescriptor(FieldDescriptor, FieldType),
canPop(StackFrame, [FieldType], NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
return
Инструкция return безопасна с точки зрения типов, если окружающий метод объявляет тип возврата void, и выполняется одно из следующих условий:
-
Окружающий метод не является методом
<init>, или -
thisуже полностью инициализировано в момент выполнения инструкции.
instructionIsTypeSafe(return, Environment, _Offset, StackFrame,
afterGoto, ExceptionStackFrame) :-
thisMethodReturnType(Environment, void),
StackFrame = frame(_Locals, _OperandStack, Flags),
notMember(flagThisUninit, Flags),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
saload
Инструкция saload безопасна с точки зрения типов, если можно корректно заменить типы, соответствующие int и массиву short, на входном стеке операндов на int, получая выходное состояние типа.
instructionIsTypeSafe(saload, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [int, arrayOf(short)], int,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
sastore
Инструкция sastore безопасна с точки зрения типов, если можно корректно извлечь типы, соответствующие int, int и массиву short, из входного стека операндов, получая выходное состояние типа.
instructionIsTypeSafe(sastore, _Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
canPop(StackFrame, [int, int, arrayOf(short)], NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
sipush
Инструкция sipush безопасна с точки зрения типов, если можно корректно поместить тип int в входной стек операндов, получая выходное состояние типа.
instructionIsTypeSafe(sipush(_Value), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [], int, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
swap
Инструкция swap безопасна с точки зрения типов, если можно корректно заменить два типа категории 1, Type1 и Type2, на входном стеке операндов на типы Type2 и Type1, получая выходное состояние типа.
instructionIsTypeSafe(swap, _Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
StackFrame = frame(_Locals, [Type1, Type2 | Rest], _Flags),
sizeOf(Type1, 1),
sizeOf(Type2, 1),
NextStackFrame = frame(_Locals, [Type2, Type1 | Rest], _Flags),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
tableswitch
Инструкция tableswitch безопасна с точки зрения типов, если её ключи отсортированы, можно корректно извлечь int из входного стека операндов, получив новое состояние типа BranchStackFrame, и все целевые адреса инструкции являются допустимыми целевыми адресами ветвления, предполагая BranchStackFrame в качестве их входного состояния типа.
instructionIsTypeSafe(tableswitch(Targets, Keys), Environment, _Offset,
StackFrame, afterGoto, ExceptionStackFrame) :-
sort(Keys, Keys),
canPop(StackFrame, [int], BranchStackFrame),
checklist(targetIsTypeSafe(Environment, BranchStackFrame), Targets),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
wide
Инструкции wide следуют тем же правилам, что и инструкции, которые они расширяют.
instructionHasEquivalentTypeRule(wide(WidenedInstruction),
WidenedInstruction).
Файл class, не содержащий атрибут StackMapTable (который обязательно имеет номер версии 49.0 или ниже), должен проверяться с помощью вывода типов.
Во время компоновки верификатор проверяет массив code атрибута Code для каждого метода файла class, выполняя анализ потока данных для каждого метода. Верификатор гарантирует, что в любой момент программы, независимо от пути кода, ведущего к этому моменту, выполняются следующие условия:
-
Стек операндов всегда имеет одинаковый размер и содержит одинаковые типы значений.
-
К локальной переменной обращаются только в том случае, если известно, что она содержит значение соответствующего типа.
-
Методы вызываются с соответствующими аргументами.
-
Поля присваиваются только значениями соответствующих типов.
-
Все операционные коды имеют аргументы соответствующего типа в стеке операндов и в массиве локальных переменных.
В целях повышения эффективности определённые тесты, которые в принципе мог бы выполнить верификатор, откладываются до первого вызова кода метода. Таким образом, верификатор избегает загрузки файлов class, если это не требуется.
Например, если метод вызывает другой метод, который возвращает экземпляр класса A, и этот экземпляр присваивается только полю того же типа, верификатор не тратит время на проверку существования класса A. Однако, если он присваивается полю типа B, определения обоих классов A и B должны быть загружены, чтобы убедиться, что A является подклассом B.
Код каждого метода проверяется независимо. Сначала байты, составляющие код, разбиваются на последовательность инструкций, а индекс в массиве code начала каждой инструкции помещается в массив. Затем верификатор проходит по коду второй раз и анализирует инструкции. Во время этого прохода строится структура данных для хранения информации о каждой инструкции виртуальной машины Java в методе. Операнды каждой инструкции, если таковые имеются, проверяются на корректность. Например:
-
Ветвления должны находиться в пределах границ массива
codeдля метода. -
Цели всех инструкций управления потоком — это начало каждой инструкции. В случае инструкции wide, оператор wide считается началом инструкции, а оператор, определяющий операцию, модифицированную инструкцией wide, не считается началом инструкции. Ветвления в середину инструкции запрещены.
-
Ни одна инструкция не может получить доступ к локальной переменной или изменить её по индексу, большему или равному количеству локальных переменных, которое указывает метод для её выделения.
-
Все ссылки на пул констант должны соответствовать типу записи. (Например, инструкция getfield должна ссылаться на поле.)
-
Код не завершается в середине инструкции.
-
Выполнение не может выйти за пределы кода.
-
Для каждого обработчика исключений начальная и конечная точки кода, защищенного обработчиком, должны находиться в начале инструкции или, в случае конечной точки, сразу за концом кода. Начальная точка должна быть перед конечной. Код обработчика исключений должен начинаться с допустимой инструкции, и он не должен начинаться с оператора, модифицируемого инструкцией wide.
Для каждой инструкции метода верификатор записывает содержимое стека операндов и содержимое массива локальных переменных перед выполнением этой инструкции. Для стека операндов ему нужно знать высоту стека и тип каждого значения в нём. Для каждой локальной переменной ему нужно знать либо тип содержимого этой локальной переменной, либо то, что локальная переменная содержит недопустимое или неизвестное значение (она может быть неинициализирована). Верификатор байткода не должен различать целочисленные типы (например, byte, short, char) при определении типов значений в стеке операндов.
Затем инициализируется анализатор потока данных. Для первой инструкции метода локальные переменные, представляющие параметры, изначально содержат значения типов, указанных в описателе типа метода; стек операндов пустой. Все остальные локальные переменные содержат недопустимое значение. Для других инструкций, которые ещё не были просмотрены, никакая информация о стеке операндов или локальных переменных недоступна.
Наконец, выполняется анализатор потока данных. Для каждой инструкции бит «изменён» указывает, нужно ли рассматривать эту инструкцию. Изначально бит «изменён» установлен только для первой инструкции. Анализатор потока данных выполняет следующий цикл:
-
Выберите инструкцию виртуальной машины Java, для которой установлен бит «изменён». Если больше нет инструкций, для которых бит «изменён» установлен, метод успешно проверен. В противном случае снимите бит «изменён» для выбранной инструкции.
-
Моделируйте влияние инструкции на стек операндов и массив локальных переменных следующим образом:
-
Если инструкция использует значения из стека операндов, убедитесь, что на стеке достаточно значений и что верхние значения на стеке имеют соответствующий тип. В противном случае проверка завершается неудачно.
-
Если инструкция использует локальную переменную, убедитесь, что указанная локальная переменная содержит значение соответствующего типа. В противном случае проверка завершается неудачно.
-
Если инструкция помещает значения в стек операндов, убедитесь, что в стеке операндов достаточно места для новых значений. Добавьте указанные типы в начало моделируемого стека операндов.
-
Если инструкция изменяет локальную переменную, запишите, что локальная переменная теперь содержит новый тип.
-
-
Определите инструкции, которые могут следовать за текущей инструкцией. Последующие инструкции могут быть следующими:
-
Следующая инструкция, если текущая инструкция не является инструкцией безусловного передачи управления (например, goto, return или athrow). Проверка завершается неудачно, если можно «выпасть» из последней инструкции метода.
-
Цель(и) условного или безусловного ветвления или переключения.
-
Любые обработчики исключений для этой инструкции.
-
-
Объедините состояние стека операндов и массива локальных переменных в конце выполнения текущей инструкции в каждую из последующих инструкций.
В специальном случае передачи управления обработчику исключений стек операндов устанавливается так, чтобы содержать один объект типа исключения, указанного в информации об обработчике исключений. Должно быть достаточно места в стеке операндов для этого единственного значения, как если бы инструкция его поместила.
-
Если эта последующая инструкция посещалась впервые, запишите, что значения стека операндов и локальных переменных, вычисленные на шагах 2 и 3, являются состоянием стека операндов и массива локальных переменных до выполнения последующей инструкции. Установите бит «изменён» для последующей инструкции.
-
Если последующая инструкция уже была видна, объедините значения стека операндов и локальных переменных, вычисленные на шагах 2 и 3, со значениями, которые уже там есть. Установите бит «изменён», если произошли какие-либо изменения значений.
-
-
Продолжить с шага 1.
Для объединения двух стеков операндов количество значений в каждом стеке должно быть одинаковым. Затем соответствующие значения в двух стеках сравниваются, и вычисляется значение в объединённом стеке следующим образом:
-
Если одно значение — это примитивный тип, то соответствующее значение должно быть тем же примитивным типом. Объединённое значение — это примитивный тип.
-
Если одно значение — это тип ссылки, не являющейся массивом, то соответствующее значение должно быть типом ссылки (массивом или не массивом). Объединённое значение — ссылка на экземпляр первого общего предка двух типов ссылок. (Такой тип ссылки всегда существует, потому что тип
Objectявляется предком всех типов классов, интерфейсов и массивов.)Например,
ObjectиStringможно объединить; результатом являетсяObject. Аналогично,ObjectиString[]можно объединить; результатом снова являетсяObject. ДажеObjectиint[]можно объединить илиStringиint[]; результатом в обоих случаях являетсяObject. -
Если соответствующие значения являются типами ссылок на массивы, то проверяются их размерности. Если типы массивов имеют одинаковые размерности, то объединённое значение — ссылка на экземпляр типа массива, который является первым общим предком обоих типов массивов. (Если у одного или обоих типов массивов есть примитивный тип элемента, то
Objectиспользуется в качестве типа элемента вместо него.) Если типы массивов имеют разные размерности, то объединённое значение — ссылка на экземпляр типа массива, размерность которого меньше из двух; тип элемента — этоCloneableилиjava.io.Serializable, если тип массива меньше былCloneableилиjava.io.Serializable, иObjectв противном случае.Например,
Object[]иString[]можно объединить; результатом являетсяObject[].Cloneable[]иString[]можно объединить, илиjava.io.Serializable[]иString[]; результатом являетсяCloneable[]иjava.io.Serializable[]соответственно. Дажеint[]иString[]можно объединить; результатом являетсяObject[], потому чтоObjectиспользуется вместоintпри вычислении первого общего предка.Поскольку типы массивов могут иметь разные размерности,
Object[]иString[][]могут быть объединены, илиObject[][]иString[]; в обоих случаях результатом являетсяObject[].Cloneable[]иString[][]могут быть объединены; результатом являетсяCloneable[]. Наконец,Cloneable[][]иString[]могут быть объединены; результатом являетсяObject[].
Если стеки операндов нельзя объединить, проверка метода завершается неудачей.
Для объединения двух состояний массивов локальных переменных сравниваются соответствующие пары локальных переменных. Значение объединённой локальной переменной вычисляется по вышеуказанным правилам, за исключением того, что соответствующие значения могут быть различными примитивными типами. В этом случае верификатор записывает, что объединённая локальная переменная содержит неприменимое значение.
Если анализатор потока данных выполняется над методом без сообщения об ошибке проверки, то метод успешно проверен верификатором файла class.
Некоторые инструкции и типы данных усложняют работу анализатора потока данных. Теперь рассмотрим каждый из них подробнее.
Значения типов long и double обрабатываются верификатором особым образом.
Всякий раз, когда значение типа long или double перемещается в локальную переменную с индексом n, индекс n+1 помечается специальным образом, чтобы указать, что он зарезервирован значением с индексом n и не может использоваться как индекс локальной переменной. Любое предыдущее значение с индексом n+1 становится неприменимым.
Всякий раз, когда значение перемещается в локальную переменную с индексом n, проверяется индекс n-1, чтобы определить, является ли он индексом значения типа long или double. Если это так, то локальная переменная с индексом n-1 изменяется, чтобы указать, что она теперь содержит неприменимое значение. Поскольку локальная переменная с индексом n перезаписана, локальная переменная с индексом n-1 не может представлять значение типа long или double.
Обработка значений типов long или double в стеке операндов проще; верификатор обрабатывает их как отдельные значения в стеке. Например, код верификации для операции dadd (сложение двух значений типа double) проверяет, что две верхние позиции в стеке — оба значения типа double. При вычислении длины стека операндов значения типа long и double имеют длину два.
Бестиповые инструкции, которые манипулируют стеком операндов, должны рассматривать значения типа long и double как атомарные (неделимые). Например, верификатор сообщает об ошибке, если верхнее значение в стеке — double, и он встречает инструкцию, такую как pop или dup. Вместо этого должны использоваться инструкции pop2 или dup2.
Создание нового экземпляра класса — многоступенчатый процесс. Выражение:
... new myClass(i, j, k); ...
может быть реализовано следующим образом:
...
new #1 // Allocate uninitialized space for myClass
dup // Duplicate object on the operand stack
iload_1 // Push i
iload_2 // Push j
iload_3 // Push k
invokespecial #5 // Invoke myClass.<init>
...
Эта последовательность инструкций оставляет вновь созданный и инициализированный объект вверху стека операндов. (Дополнительные примеры компиляции в набор инструкций Java Virtual Machine приведены в §3 (Компиляция для Java Virtual Machine).)
Метод инициализации экземпляра (§2.9) для класса myClass видит новый неинициализированный объект в качестве аргумента this в локальной переменной 0. Перед тем, как этот метод вызовет другой метод инициализации экземпляра класса myClass или его непосредственного суперкласса для this, единственной операцией, которую метод может выполнить над this, является присвоение полей, объявленных в myClass.
При анализе потока данных в методах экземпляров верификатор инициализирует локальную переменную 0 объектом текущего класса, а для методов инициализации экземпляров локальная переменная 0 содержит специальный тип, указывающий на неинициализированный объект. После вызова соответствующего метода инициализации экземпляра (из текущего класса или его непосредственного суперкласса) для этого объекта все экземпляры этого специального типа в модели верификатора стека операндов и в массиве локальных переменных заменяются типом текущего класса. Верификатор отклоняет код, использующий новый объект до его инициализации или инициализирующий объект более одного раза. Кроме того, он гарантирует, что каждый нормальный возврат метода вызвал метод инициализации экземпляра либо в классе этого метода, либо в непосредственном суперклассе.
Аналогично, специальный тип создаётся и помещается в модель стека операндов верификатора в результате инструкции Java Virtual Machine new. Специальный тип указывает инструкцию, с помощью которой был создан экземпляр класса, и тип созданного неинициализированного экземпляра класса. Когда вызывается метод инициализации экземпляра, объявленный в классе неинициализированного экземпляра класса, для этого экземпляра класса, все экземпляры специального типа заменяются целевым типом экземпляра класса. Это изменение типа может распространяться на последующие инструкции по мере выполнения анализа потока данных.
Номер инструкции необходимо хранить в составе специального типа, так как может быть несколько неинициализированных экземпляров класса, существующих одновременно в стеке операндов. Например, последовательность инструкций Java Virtual Machine, реализующая:
new InputStream(new Foo(), new InputStream("foo"))
может иметь два неинициализированных экземпляра InputStream в стеке операндов одновременно. Когда метод инициализации экземпляра вызывается для экземпляра класса, только те экземпляры специального типа в стеке операндов или в массиве локальных переменных, которые являются тем же объектом, что и экземпляр класса, заменяются.
Для реализации конструкции 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.