classファイル形式
目次
ClassFile構造CONSTANT_Class_info構造CONSTANT_Fieldref_info、CONSTANT_Methodref_infoおよびCONSTANT_InterfaceMethodref_info構造CONSTANT_String_info構造CONSTANT_Integer_infoおよびCONSTANT_Float_info構造CONSTANT_Long_infoおよびCONSTANT_Double_info構造CONSTANT_NameAndType_info構造CONSTANT_Utf8_info構造CONSTANT_MethodHandle_info構造CONSTANT_MethodType_info構造CONSTANT_Dynamic_infoおよびCONSTANT_InvokeDynamic_info構造CONSTANT_Module_info構造CONSTANT_Package_info構造ConstantValue属性Code属性StackMapTable属性Exceptions属性InnerClasses属性EnclosingMethod属性Synthetic属性Signature属性SourceFile属性SourceDebugExtension属性LineNumberTable属性LocalVariableTable属性LocalVariableTypeTable属性Deprecated属性RuntimeVisibleAnnotations属性RuntimeInvisibleAnnotations属性RuntimeVisibleParameterAnnotations属性RuntimeInvisibleParameterAnnotations属性RuntimeVisibleTypeAnnotations属性RuntimeInvisibleTypeAnnotations属性AnnotationDefault属性BootstrapMethods属性MethodParameters属性Module属性ModulePackages属性ModuleMainClass属性NestHost属性NestMembers属性Record属性PermittedSubclasses属性classファイルの検証protectedメンバーの型チェック
この章では、Java Virtual Machineのclassファイル形式について説明します。 各classファイルには、単一のクラス、インタフェースまたはモジュールの定義が含まれます。 クラス、インタフェースまたはモジュールは、文字通りファイルに格納される外部表現を持つ必要はありませんが(たとえば、クラスがクラス・ローダーによって生成されるため)、クラス、インタフェースまたはモジュールの任意の有効な表現をclassファイル形式であると同時に参照します。
classファイルは、8ビット・バイトのストリームで構成されます。16ビットと32ビットの数量は、それぞれ2バイトと4バイトの連続する8ビット・バイトを読み取ることによって構成されます。 マルチバイト・データ項目は常にビッグ・エンディアンの順序で格納され、高いバイトが最初に格納されます。 この章では、符号なしの1バイト、2バイトまたは4バイトの数量を表すデータ型u1、u2およびu4をそれぞれ定義します。
Java SE Platform APIでは、classファイル形式は、インタフェースjava.io.DataInputおよびjava.io.DataOutputと、java.io.DataInputStreamおよびjava.io.DataOutputStreamなどのクラスでサポートされています。 たとえば、u1、u2およびu4型の値は、インタフェースjava.io.DataInputのreadUnsignedByte、readUnsignedShortおよびreadIntなどのメソッドによって読み取ることができます。
この章では、Cに似た構造表記で記述された擬似構造を使用したclassファイル形式について説明します。 クラスやクラス・インスタンスなどのフィールドと混同しないように、classファイル形式を記述する構造体の内容をアイテムと呼びます。 連続した項目は、パディングや配置なしでclassファイルに順番に格納されます。
表は、0個以上の可変サイズの項目から構成され、複数のclassファイル構造で使用されます。 C形式の配列構文を使用してテーブル項目を参照しますが、テーブルが可変サイズの構造のストリームであるという事実は、テーブルインデックスをバイトオフセットにテーブルに直接変換できないことを意味します。
データ構造体を配列と呼びますが、これはゼロ個以上の連続した固定サイズの項目で構成され、配列のように索引付けできます。
この章のASCII文字への参照は、ASCII文字に対応するUnicodeコード・ポイントを意味するように解釈する必要があります。
ClassFile構造
classファイルは、単一のClassFile構造で構成されます。
ClassFile {
u4 magic;
u2 minor_version;
u2 major_version;
u2 constant_pool_count;
cp_info constant_pool[constant_pool_count-1];
u2 access_flags;
u2 this_class;
u2 super_class;
u2 interfaces_count;
u2 interfaces[interfaces_count];
u2 fields_count;
field_info fields[fields_count];
u2 methods_count;
method_info methods[methods_count];
u2 attributes_count;
attribute_info attributes[attributes_count];
}
ClassFile構造内の項目は次のとおりです。
magicアイテムは、classファイル形式を識別するマジック番号を提供します。値は0xCAFEBABEです。
minor_versionおよびmajor_versionアイテムの値は、このclassファイルのマイナーおよびメジャー・バージョン番号です。 メジャーおよびマイナー・バージョン番号を組み合せて、classファイル形式のバージョンを決定します。 classファイルのメジャー・バージョン番号がMでマイナー・バージョン番号がmの場合は、そのclassファイル形式のバージョンをM.mと示します。
major_version項目は、45から70の範囲の値である必要があります。 Java SE 26に準拠するJava Virtual Machine実装は、まさにこれらの主要なバージョン番号をサポートしています。
major_versionが56以上のclassファイルの場合、minor_versionは0または65535である必要があります。
major_versionが45から55(両端を含む)のclassファイルの場合、minor_versionは任意の値になります。
バージョン番号70.65535のclassファイルは、Java SE 26 (§1.5.1)のプレビュー機能によって異なります。 このようなclassファイルをロードできるのは、ユーザーがホスト・システムを介してプレビュー機能が有効になっている場合のみです。
バージョン番号がM.65535のclassファイル(56≤ M < 70)は、古いリリースのJava SEプラットフォームのプレビュー機能によって異なります。 このようなclassファイルは、プレビュー機能が有効化されているかどうかに関係なくロードできません。 かわりに、classファイルは、古いリリースに準拠するJava Virtual Machine実装によってのみロードできます。
他のバージョン番号を持つclassファイルは、プレビュー機能に依存せず、プレビュー機能が有効かどうかに関係なくロードできます。
classファイルminor_versionをJDKで使用する場合、過去の観点が保証されます。 JDK 1.0.2では、45.0から45.3までのバージョンがサポートされています。 JDK 1.1では、45.0から45.65535までのバージョンがサポートされています。 JDK 1.2でメジャー・バージョン46のサポートが導入されたとき、そのメジャー・バージョンでサポートされているマイナー・バージョンは0のみでした。 その後、JDKsは、新しいメジャー・バージョン(47、48など)のサポートを導入する演習を続けましたが、新しいメジャー・バージョンではマイナー・バージョン0のみをサポートしています。 最後に、Java SE 12でのプレビュー機能(§1.5)の導入により、マイナー・バージョンのclassファイル形式の標準ロールが動機付けされたため、JDK 12ではマイナー・バージョンの0および65535がメジャー・バージョン56でサポートされました。 後続のJDKでは、N.0およびN.65535のサポートが導入されています。ここで、Nは、実装されたJava SEプラットフォームの対応するメジャー・バージョンです。 たとえば、JDK 13は57.0および57.65535をサポートしています。
constant_pool_countアイテムの値は、constant_pool表のエントリ数に1を加えた数と同じです。 constant_pool索引は、0より大きくconstant_pool_countより小さい場合に有効とみなされます。ただし、§4.4.5に記載されているlong型およびdouble型の定数は例外です。
constant_poolは、様々な文字列定数、クラスとインタフェース名、フィールド名、およびClassFile構造体とそのサブ構造体内で参照されるその他の定数を表す構造体(§4.4)の表です。 各constant_pool表エントリの形式は、最初のタグ・バイトで示されます。
constant_pool表は、1からconstant_pool_count - 1に索引付けされます。
access_flagsアイテムの値は、このクラスまたはインタフェースに対するアクセス権限およびプロパティを示すために使用されるフラグのマスクです。 表4.1-Bに、各フラグの解釈(設定されている場合)を示します。
表4.1-B. クラスアクセスおよびプロパティー修飾子
| フラグ名 | 値 | 解釈 |
|---|---|---|
ACC_PUBLIC |
0x0001 | publicを宣言しました。パッケージの外部からアクセスできます。
|
ACC_FINAL |
0x0010 | finalを宣言しました。サブクラスは許可されません。
|
ACC_SUPER |
0x0020 | invokespecial命令によって呼び出されたときに、スーパークラス・メソッドを特別に処理します。 |
ACC_INTERFACE |
0x0200 | クラスではなくインタフェースです。 |
ACC_ABSTRACT |
0x0400 | abstractを宣言しました。インスタンス化しないでください。
|
ACC_SYNTHETIC |
0x1000 | 統合が宣言されました。ソース・コードに存在しません。 |
ACC_ANNOTATION |
0x2000 | 注釈インタフェースとして宣言されます。 |
ACC_ENUM |
0x4000 | enumクラスとして宣言されます。
|
|
|
0x8000 | クラスまたはインタフェースではなく、モジュールです。 |
ACC_MODULEフラグは、このclassファイルがクラスまたはインタフェースではなくモジュールを定義することを示します。 ACC_MODULEフラグが設定されている場合は、この項の最後に指定されているclassファイルに特別なルールが適用されます。 ACC_MODULEフラグが設定されていない場合、現在の段落の直下のルールがclassファイルに適用されます。
インタフェースは、設定されているACC_INTERFACEフラグによって区別されます。 ACC_INTERFACEフラグが設定されていない場合、このclassファイルは、インタフェースまたはモジュールではなくクラスを定義します。
ACC_INTERFACEフラグを設定する場合は、ACC_ABSTRACTフラグも設定する必要があり、ACC_FINAL、ACC_SUPER、ACC_ENUMおよびACC_MODULEフラグを設定しないでください。
ACC_INTERFACEフラグが設定されていない場合、表4.1-Bのその他のフラグは、ACC_ANNOTATIONおよびACC_MODULE以外に設定できます。 ただし、このようなclassファイルには、ACC_FINALフラグとACC_ABSTRACTフラグの両方を設定しないでください(JLS§8.1.1.2)。
ACC_SUPERフラグは、このクラスまたはインタフェースに表示される場合に、invokespecial命令(§invokespecial)によって表される2つの代替セマンティクスのうちどれかを示します。 Java Virtual Machineの命令セットへのコンパイラは、ACC_SUPERフラグを設定する必要があります。 Java SE 8以上では、Java Virtual Machineは、classファイルのフラグの実際の値およびclassファイルのバージョンに関係なく、すべてのclassファイルでACC_SUPERフラグが設定されていると見なします。
ACC_SUPERフラグは、Javaプログラミング言語用の古いコンパイラによってコンパイルされたコードとの下位互換性のために存在します。 JDK 1.0.2より前は、コンパイラによってaccess_flagsが生成され、ACC_SUPERを表すフラグには意味が割り当てられず、OracleのJava Virtual Machine実装ではフラグが設定されている場合は無視されていました。
ACC_SYNTHETICフラグは、このクラスまたはインタフェースがコンパイラによって生成され、ソース・コードに表示されないことを示します。
注釈インタフェース(JLS§9.6)には、ACC_ANNOTATIONフラグが設定されている必要があります。 ACC_ANNOTATIONフラグが設定されている場合は、ACC_INTERFACEフラグも設定する必要があります。
ACC_ENUMフラグは、このクラスまたはそのスーパークラスがenumクラス(JLS§8.9)として宣言されていることを示します。
表4.1-Bで割り当てられていないaccess_flagsアイテムのすべてのビットは、将来使用するために予約されています。 これらは、生成されたclassファイルでゼロに設定する必要があり、Java Virtual Machineの実装では無視する必要があります。
this_classアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、このclassファイルで定義されたクラスまたはインタフェースを表すCONSTANT_Class_info構造体(§4.4.1)である必要があります。
クラスの場合、super_classアイテムの値はゼロであるか、constant_pool表への有効な索引である必要があります。 super_class項目の値が0以外の場合、その索引のconstant_poolエントリは、このclassファイルで定義されたクラスの直接スーパークラスを表すCONSTANT_Class_info構造である必要があります。 直接スーパークラスもそのスーパークラスのいずれも、ClassFile構造のaccess_flags項目にACC_FINALフラグを設定できません。
super_class項目の値がゼロの場合、このclassファイルは、直接スーパークラスを持たない唯一のクラスまたはインタフェースであるObjectクラスを表す必要があります。
インタフェースの場合、super_classアイテムの値は、常にconstant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、クラスObjectを表すCONSTANT_Class_info構造である必要があります。
interfaces_countアイテムの値は、このクラスまたはインタフェース・タイプの直接スーパーインタフェースの数を示します。
interfaces配列の各値は、constant_pool表への有効な索引である必要があります。 interfaces[i]の各値(0 ≤ i < interfaces_count)のconstant_poolエントリは、このクラスまたはインタフェース・タイプの直接スーパーインタフェースであるインタフェースを表すCONSTANT_Class_info構造体で、その型のソースに指定された左から右の順序である必要があります。
fields_countアイテムの値は、fields表のfield_info構造の数を示します。 field_info構造は、このクラスまたはインタフェース・タイプによって宣言されたすべてのフィールド(クラス変数とインスタンス変数の両方)を表します。
fields表の各値は、このクラスまたはインタフェースのフィールドの詳細な説明を示すfield_info構造体(§4.5)である必要があります。 fields表には、このクラスまたはインタフェースによって宣言されたフィールドのみが含まれます。 スーパークラスまたはスーパーインタフェースから継承されたフィールドを表す項目は含まれません。
methods_countアイテムの値は、methods表のmethod_info構造の数を示します。
methods表の各値は、このクラスまたはインタフェースのメソッドの完全な説明を示すmethod_info構造体(§4.6)である必要があります。 method_info構造のaccess_flags項目にACC_NATIVEフラグとACC_ABSTRACTフラグのいずれも設定されていない場合、メソッドを実装するJava Virtual Machine命令も提供されます。
method_info構造は、インスタンス・メソッド、クラス・メソッド、インスタンス初期化メソッド(§2.9.1)、クラスまたはインタフェースの初期化メソッド(§2.9.2)など、このクラスまたはインタフェース・タイプによって宣言されたすべてのメソッドを表します。 methods表には、スーパークラスまたはスーパーインタフェースから継承されるメソッドを表す項目は含まれません。
attributes_countアイテムの値は、このクラスのattributes表の属性の数を示します。
attributes表の各値は、attribute_info構造(§4.7)である必要があります。
ClassFile構造のattributes表に表示されるように、この仕様で定義される属性を表4.7-Cにリストします。
ClassFile構造のattributes表に表示されるように定義された属性に関するルールは、§4.7に示されています。
ClassFile構造のattributes表の事前定義されていない属性に関するルールは、§4.7.1に示されています。
access_flagsアイテムにACC_MODULEフラグが設定されている場合、access_flagsアイテムに他のフラグは設定されず、次のルールがClassFile構造のリストに適用されます。
major_version、minor_version: ≥ 53.0 (すなわち、Java SE 9以上)
this_class: module-info
super_class、interfaces_count、fields_count、methods_count: ゼロ
attributes: 1つのModule属性が存在する必要があります。 Module、ModulePackages、ModuleMainClass、InnerClasses、SourceFile、SourceDebugExtension、RuntimeVisibleAnnotationsおよびRuntimeInvisibleAnnotationsを除き、事前定義属性(§4.7)はいずれも指定できません。
classファイル構造体に出現するクラス名およびインタフェース名は、常にバイナリ名(JLS§13.1)と呼ばれる完全修飾形式で表されます。 このような名前は、常にCONSTANT_Utf8_info構造体(§4.4.7)として表されるため、Unicodeコードスペース全体からさらに制約されないかぎり、描画できます。 クラスおよびインタフェース名は、記述子(§4.3)の一部としてこのような名前を持つCONSTANT_NameAndType_info構造体(§4.4.6)およびすべてのCONSTANT_Class_info構造体(§4.4.1)から参照されます。
過去の理由から、classファイル構造に現れるバイナリ名の構文は、JLS§13.1に記載されているバイナリ名の構文とは異なります。 この内部形式では、通常バイナリ名を構成する識別子を区切るASCIIピリオド(.)は、ASCIIフォワード・スラッシュ(/)に置き換えられます。 識別子自体は修飾されていない名前でなければなりません(§4.2.2)。
たとえば、クラスThreadの通常のバイナリ名はjava.lang.Threadです。 classファイル形式の記述子で使用される内部形式では、クラスThreadの名前への参照は、文字列java/lang/Threadを表すCONSTANT_Utf8_info構造を使用して実装されます。
メソッド、フィールド、ローカル変数および仮パラメータの名前は、非修飾名として格納されます。 修飾されていない名前には、Unicodeコード・ポイントを少なくとも1つ含める必要があり、ASCII文字. ; [ / (ピリオド、セミコロン、左角カッコまたはスラッシュ)を含めることはできません。
メソッド名はさらに制約されるため、特殊なメソッド名<init>および<clinit> (§2.9)を除き、ASCII文字<または> (つまり、左山カッコまたは右山カッコ)を含めることはできません。
<clinit>を参照できるメソッド呼出し命令はなく、invokespecial命令(§invokespecial)のみが<init>を参照できることに注意してください。
Module属性から参照されるモジュール名は、定数プールのCONSTANT_Module_info構造体に格納されます(§4.4.11)。 CONSTANT_Module_info構造は、モジュール名を示すCONSTANT_Utf8_info構造をラップします。 モジュール名は、クラス名やインタフェース名などの内部形式ではエンコードされません。つまり、モジュール名の識別子を区切るASCIIピリオド(.)は、ASCIIフォワード・スラッシュ(/)に置き換えられません。
モジュール名は、次の制約に従って、Unicodeコードスペース全体から描画できます。
モジュール名には、'\\u0000'から'\\u001F'までの範囲のコード・ポイントを含めることはできません。
ASCIIバックスラッシュ(\\)は、モジュール名でエスケープ文字として使用するために予約されています。 モジュール名の後にASCIIバックスラッシュ、ASCIIコロン(:)またはASCIIアットマーク(@)が続かないかぎり、モジュール名に表示できません。 ASCII文字シーケンス\\\\は、モジュール名でバックスラッシュをエンコードするために使用できます。
ASCIIコロン(:)とアットマーク(@)は、モジュール名で将来使用するために予約されています。 エスケープしないかぎり、モジュール名に表示しないでください。 ASCII文字シーケンス\\:および\\@を使用して、モジュール名にコロンとアットマークをエンコードできます。
Module属性から参照されるパッケージ名は、定数プールのCONSTANT_Package_info構造体に格納されます(§4.4.12)。 CONSTANT_Package_info構造は、内部形式でエンコードされたパッケージ名を表すCONSTANT_Utf8_info構造をラップします。
ディスクリプタは、フィールドまたはメソッドのタイプを表す文字列です。 ディスクリプタは、変更されたUTF-8文字列(§4.4.7)を使用してclassファイル形式で表されるため、Unicodeコード・スペース全体からさらに制約を受けずに描画できます。
ディスクリプタは、文法を使用して指定します。 文法は、文字のシーケンスが様々な種類の構文的に正しい記述子を形成する方法を説明する一連のプロダクションです。 文法の末尾記号はfixed widthフォントで表示され、ASCII文字として解釈される必要があります。 非終端記号は、イタリック型で表示されます。 非終端の定義は、定義される非終端の名前とコロンによって導入されます。 非終端の代替定義が1つ以上続きます。
本番環境の右側の構文 {x}は、ゼロ個以上の xを表します。
生産の右側にあるフレーズ(one of)は、次の行または行の各終端記号が代替定義であることを示します。
フィールド記述子は、フィールド、パラメータ、ローカル変数または値のタイプを表します。
B C D F I J S Z
Lクラス名 ;ClassNameは、内部形式でエンコードされたバイナリ・クラスまたはインタフェース名を表します(§4.2.1)。
フィールド記述子は、記述子にClassNameと表示されている場合は、クラス名またはインタフェース名を記述します。 これには、ArrayTypeのComponentTypeにネストされたClassNameが含まれます。
表4.3-Aに、フィールド記述子を型として解釈します。 これらの型の意味については、§2.2、§2.3、および §2.4を参照してください。
配列型を表すフィールド記述子は、ディメンションが255以下の型を表す場合にのみ有効です。
表4.3-A. フィールド記述子の解釈
| FieldType用語 | 型 |
|---|---|
B |
byte |
C |
char |
D |
double |
F |
float |
I |
int |
J |
long |
L ClassName ; |
名前付きクラスまたはインタフェース・タイプ |
S |
short |
Z |
boolean |
|
|
指定されたコンポーネント・タイプの配列 |
int型のインスタンス変数のフィールド記述子は、単純にIです。
Object型のインスタンス変数のフィールド記述子は、Ljava/lang/Object;です。 クラスObjectのバイナリ名の内部形式が使用されることに注意してください。
マルチディメンション配列型double[][][]のインスタンス変数のフィールド記述子は、[[[Dです。
メソッド記述子には、メソッドが取得するパラメータのタイプを表す0個以上のパラメータ記述子と、メソッドが返す値のタイプ(ある場合)を表す戻り記述子が含まれます。
V文字Vは、メソッドが値を返さないことを示します(結果はvoidです)。
パラメータ記述子または戻り記述子のFieldTypeで名前がClassNameとして表示される場合、メソッド記述子はクラス名またはインタフェース名をメンションします。
メソッドのメソッド記述子:
Object m(int i, double d, Thread t) {...}
が:
(IDLjava/lang/Thread;)Ljava/lang/Object;
ThreadおよびObjectのバイナリ名の内部形式が使用されることに注意してください。
メソッド・ディスクリプタは、合計長が255以下のメソッド・パラメータを表す場合にのみ有効です。この長さには、インスタンス・メソッドまたはインタフェース・メソッド呼出しの場合のthisのコントリビューションが含まれます。 合計長は、個々のパラメータのコントリビューションを合計して計算されます。この場合、long型またはdouble型のパラメータは長さに2つの単位を、他の型のパラメータは1つの単位を寄与します。
メソッド記述子は、記述するメソッドがクラス・メソッドであるかインスタンス・メソッドであるかに関係なく同じです。 インスタンス・メソッドはthisに渡されますが、そのメソッドが呼び出されるオブジェクトへの参照は、意図した引数に加えて、そのファクトはメソッド記述子に反映されません。 thisへの参照は、インスタンス・メソッドを起動するJava Virtual Machine命令(§2.6.1、§4.11)によって暗黙的に渡されます。
Java Virtual Machineの命令は、クラス、インタフェース、クラス・インスタンスまたは配列のランタイム・レイアウトに依存しません。 かわりに、指示はconstant_pool表のシンボリック情報を参照します。
すべてのconstant_pool表エントリの一般的な形式は次のとおりです。
cp_info {
u1 tag;
u1 info[];
}
constant_pool表の各エントリは、エントリで示される定数の種類を示す1バイトのタグで始まる必要があります。 表4.4-Aに、対応するタグとともに17種類の定数をリストし、この章のセクション番号で順序付けします。 各タグバイトのあとに、特定の定数に関する情報を提供する2バイト以上のバイトが続く必要があります。 追加情報の形式はタグ・バイトによって異なります。つまり、info配列の内容はtagの値によって変わります。
表4.4-A. 定数プール・タグ(セクション別)
| 定数種類 | タグ | セクション |
|---|---|---|
CONSTANT_Class |
7 | §4.4.1 |
CONSTANT_Fieldref |
9 | §4.4.2 |
CONSTANT_Methodref |
10 | §4.4.2 |
CONSTANT_InterfaceMethodref |
11 | §4.4.2 |
CONSTANT_String |
8 | §4.4.3 |
CONSTANT_Integer |
3 | §4.4.4 |
CONSTANT_Float |
4 | §4.4.4 |
CONSTANT_Long |
5 | §4.4.5 |
CONSTANT_Double |
6 | §4.4.5 |
CONSTANT_NameAndType |
12 | §4.4.6 |
CONSTANT_Utf8 |
1 | §4.4.7 |
CONSTANT_MethodHandle |
15 | §4.4.8 |
CONSTANT_MethodType |
16 | §4.4.9 |
CONSTANT_Dynamic |
17 | §4.4.10 |
CONSTANT_InvokeDynamic |
18 | §4.4.10 |
CONSTANT_Module |
19 | §4.4.11 |
CONSTANT_Package |
20 | §4.4.12 |
バージョン番号がvのclassファイルでは、constant_pool表の各エントリに、classファイル形式(§4.1)のバージョンv以前で定義されているタグが必要です。 つまり、各エントリは、classファイルでの使用が承認されている定数を示す必要があります。 表4.4-Bに、各タグが定義されたclassファイル形式の最初のバージョンを示します。 また、そのバージョンのclassファイル形式を導入したJava SEプラットフォームのバージョンも示されています。
表4.4-B. 定数プール・タグ(タグ別)
| 定数種類 | タグ | classファイル形式
|
Java SE |
|---|---|---|---|
CONSTANT_Utf8 |
1 | 45.3 | 1.0.2 |
CONSTANT_Integer |
3 | 45.3 | 1.0.2 |
CONSTANT_Float |
4 | 45.3 | 1.0.2 |
CONSTANT_Long |
5 | 45.3 | 1.0.2 |
CONSTANT_Double |
6 | 45.3 | 1.0.2 |
CONSTANT_Class |
7 | 45.3 | 1.0.2 |
CONSTANT_String |
8 | 45.3 | 1.0.2 |
CONSTANT_Fieldref |
9 | 45.3 | 1.0.2 |
CONSTANT_Methodref |
10 | 45.3 | 1.0.2 |
CONSTANT_InterfaceMethodref |
11 | 45.3 | 1.0.2 |
CONSTANT_NameAndType |
12 | 45.3 | 1.0.2 |
CONSTANT_MethodHandle |
15 | 51.0 | 7 |
CONSTANT_MethodType |
16 | 51.0 | 7 |
CONSTANT_Dynamic |
17 | 55.0 | 11 |
CONSTANT_InvokeDynamic |
18 | 51.0 | 7 |
CONSTANT_Module |
19 | 53.0 | 9 |
CONSTANT_Package |
20 | 53.0 | 9 |
constant_pool表の一部のエントリはロード可能です。これは、追加の計算を可能にするために実行時にスタックにプッシュできるエンティティを表しているためです。 バージョン番号がvのclassファイルでは、constant_pool表のエントリがロード可能になるのは、最初にバージョンv以前のclassファイル形式でロード可能であるとみなされたタグがある場合です。 表4.4-Cに、各タグをロード可能とみなされた最初のバージョンのclassファイル形式を示します。 また、そのバージョンのclassファイル形式を導入したJava SEプラットフォームのバージョンも示されています。
CONSTANT_Classを除くすべてのケースで、タグは、最初にタグを定義した同じバージョンのclassファイル形式でロード可能であるとみなされました。
表4.4-C. ロード可能な定数プール・タグ
| 定数種類 | タグ | classファイル形式
|
Java SE |
|---|---|---|---|
CONSTANT_Integer |
3 | 45.3 | 1.0.2 |
CONSTANT_Float |
4 | 45.3 | 1.0.2 |
CONSTANT_Long |
5 | 45.3 | 1.0.2 |
CONSTANT_Double |
6 | 45.3 | 1.0.2 |
CONSTANT_Class |
7 | 49.0 | 5.0 |
CONSTANT_String |
8 | 45.3 | 1.0.2 |
CONSTANT_MethodHandle |
15 | 51.0 | 7 |
CONSTANT_MethodType |
16 | 51.0 | 7 |
CONSTANT_Dynamic |
17 | 55.0 | 11 |
CONSTANT_Class_info構造
CONSTANT_Class_info構造は、クラスまたはインタフェースを表すために使用されます。
CONSTANT_Class_info {
u1 tag;
u2 name_index;
}
CONSTANT_Class_info構造の項目は次のとおりです。
配列はオブジェクトであるため、opcode anewarrayおよびmultianewarrayは(ただし、opcode newは)constant_pool表のCONSTANT_Class_info構造体を介して配列「クラス」を参照することはできません。 このような配列クラスの場合、クラスの名前は配列型の記述子になります(§4.3.2)。
たとえば、2次元配列型int[][]を表すクラス名は[[Iで、Thread[]型を表すクラス名は[Ljava/lang/Thread;です。
配列型記述子は、255以下のディメンションを表す場合にのみ有効です。
CONSTANT_Fieldref_info、CONSTANT_Methodref_infoおよびCONSTANT_InterfaceMethodref_info構造
フィールド、メソッドおよびインタフェース・メソッドは、次のような構造で表されます。
CONSTANT_Fieldref_info {
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
CONSTANT_Methodref_info {
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
CONSTANT_InterfaceMethodref_info {
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
これらの構造の項目は次のとおりです。
CONSTANT_Fieldref_info構造のtag項目の値はCONSTANT_Fieldref (9)です。
CONSTANT_Methodref_info構造のtagアイテムの値は、CONSTANT_Methodref (10)です。
CONSTANT_InterfaceMethodref_info構造のtagアイテムの値は、CONSTANT_InterfaceMethodref (11)です。
class_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、フィールドまたはメソッドをメンバーとして持つクラスまたはインタフェース・タイプを表すCONSTANT_Class_info構造体(§4.4.1)である必要があります。
CONSTANT_Fieldref_info構造では、class_index項目はクラス・タイプまたはインタフェース・タイプのいずれかです。
CONSTANT_Methodref_info構造体では、class_index項目はインタフェース型ではなくクラス型である必要があります。
CONSTANT_InterfaceMethodref_info構造体では、class_index項目はクラス型ではなくインタフェース型である必要があります。
name_and_type_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、CONSTANT_NameAndType_info構造体(§4.4.6)である必要があります。 このconstant_poolエントリは、フィールドまたはメソッドの名前と記述子を示します。
CONSTANT_Fieldref_info構造体では、指定された記述子はフィールド記述子(§4.3.2)である必要があります。 それ以外の場合、示された記述子はメソッド記述子(§4.3.3)である必要があります。
CONSTANT_Methodref_info構造内のメソッドの名前が'<'('\\u003c')で始まる場合、その名前は、インスタンス初期化メソッド(§2.9.1)を表す特別な名前<init>である必要があります。 このようなメソッドの戻り型は、voidである必要があります。
CONSTANT_String_info構造
CONSTANT_String_info構造は、String型の定数オブジェクトを表すために使用されます。
CONSTANT_String_info {
u1 tag;
u2 string_index;
}
CONSTANT_String_info構造の項目は次のとおりです。
tagアイテムの値はCONSTANT_String (8)です。
string_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、Stringオブジェクトを初期化するUnicodeコード・ポイントのシーケンスを表すCONSTANT_Utf8_info構造体(§4.4.7)である必要があります。
CONSTANT_Integer_infoおよびCONSTANT_Float_info構造
CONSTANT_Integer_infoおよびCONSTANT_Float_info構造は、4バイトの数値(intおよびfloat)定数を表します。
CONSTANT_Integer_info {
u1 tag;
u4 bytes;
}
CONSTANT_Float_info {
u1 tag;
u4 bytes;
}
これらの構造の項目は次のとおりです。
CONSTANT_Integer_info構造のtagアイテムの値はCONSTANT_Integer (3)です。
CONSTANT_Float_info構造のtagアイテムの値は、CONSTANT_Float (4)です。
CONSTANT_Integer_info構造のbytes項目は、int定数の値を表します。 値のバイトは、ビッグエンディアン(ハイバイト順)で格納されます。
CONSTANT_Float_info構造のbytes項目は、IEEE 754 binary32浮動小数点形式のfloat定数の値を表します(§2.3.2)。 アイテムのバイトは、ビッグ・エンディアン(ハイ・バイト・ファースト)の順序で格納されます。
CONSTANT_Float_info構造体で表される値は、次のように決定されます。 値のバイトは、最初にint定数のビットに変換されます。 次に、
bitsが0x7f800000の場合、float値は正の無限大になります。
bitsが0xff800000の場合、float値は負の無限大になります。
bitsが0x7f800001から0x7fffffffの範囲、または0xff800001から0xffffffffの範囲にある場合、float値はNaNになります。
それ以外の場合は、s、eおよびmを、ビットから計算できる3つの値にしてください。
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構造
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バイトのすべての定数は、classファイルのconstant_pool表の2つのエントリを取ります。 CONSTANT_Long_infoまたはCONSTANT_Double_info構造がconstant_pool表の索引nのエントリである場合、表内の次の使用可能なエントリは索引n+2にあります。 constant_pool索引n+1は有効である必要がありますが、使用不可とみなされます。
振り返ってみると、8バイトの定数のプールエントリを2つ取得することは、不適切な選択でした。
これらの構造の項目は次のとおりです。
CONSTANT_Long_info構造のtagアイテムの値は、CONSTANT_Long (5)です。
CONSTANT_Double_info構造のtag項目の値はCONSTANT_Double (6)です。
CONSTANT_Long_info構造の符号なしhigh_bytesおよびlow_bytes項目は、long定数の値を表します。
((long) high_bytes << 32) + low_bytes
ここで、high_bytesおよびlow_bytesの各バイトは、ビッグ・エンディアン(ハイ・バイト・ファースト)の順序で格納されます。
CONSTANT_Double_info構造のhigh_bytes項目とlow_bytes項目は、IEEE 754 binary64浮動小数点形式のdouble値を表します(§2.3.2)。 各項目のバイトは、ビッグエンディアン(ハイバイト順)で格納されます。
CONSTANT_Double_info構造体で表される値は、次のように決定されます。 high_bytesおよびlow_bytesアイテムは、次と等しいlong定数ビットに変換されます。
((long) high_bytes << 32) + low_bytes
次に、
bitsが0x7ff0000000000000Lの場合、double値は正の無限大になります。
bitsが0xfff0000000000000Lの場合、double値は負の無限大になります。
ビットが0x7ff0000000000001Lから0x7fffffffffffffffLの範囲、または0xfff0000000000001Lから0xffffffffffffffffLの範囲にある場合、double値はNaNになります。
それ以外の場合は、s、eおよびmを、ビットから計算できる3つの値にしてください。
int s = ((bits >> 63) == 0) ? 1 : -1; int e = (int)((bits >> 52) & 0x7ffL); long m = (e == 0) ? (bits & 0xfffffffffffffL) << 1 : (bits & 0xfffffffffffffL) | 0x10000000000000L;
次に、浮動小数点値は、算術式s · m · 2e-1075のdouble値と等しくなります。
CONSTANT_NameAndType_info構造
CONSTANT_NameAndType_info構造は、フィールドまたはメソッドを表すために使用され、どのクラスまたはインタフェース・タイプに属するかは示されません。
CONSTANT_NameAndType_info {
u1 tag;
u2 name_index;
u2 descriptor_index;
}
CONSTANT_NameAndType_info構造の項目は次のとおりです。
tagアイテムの値はCONSTANT_NameAndType (12)です。
name_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、フィールドまたはメソッドを示す有効な非修飾名(§4.2.2)を表すCONSTANT_Utf8_info構造体(§4.4.7)、または特殊なメソッド名<init>(§2.9.1)である必要があります。
descriptor_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、有効なフィールド記述子またはメソッド記述子(§4.3.2、§4.3.3)を表すCONSTANT_Utf8_info構造体(§4.4.7)である必要があります。
CONSTANT_Utf8_info構造
CONSTANT_Utf8_info構造は、定数文字列値を表すために使用されます。
CONSTANT_Utf8_info {
u1 tag;
u2 length;
u1 bytes[length];
}
CONSTANT_Utf8_info構造の項目は次のとおりです。
tagアイテムの値はCONSTANT_Utf8 (1)です。
length項目の値は、bytes配列のバイト数(結果の文字列の長さではない)を示します。
bytes配列には、文字列のバイトが含まれます。
値(byte)0を持つバイトはありません。
バイトは、(byte)0xf0から(byte)0xffの範囲内にありません。
文字列の内容は、変更されたUTF-8でエンコードされます。 変更されたUTF-8文字列はエンコードされるため、NULL以外のASCII文字のみを含むコード・ポイント・シーケンスは、コード・ポイントごとに1バイトのみを使用して表現できますが、Unicodeコード・スペース内のすべてのコード・ポイントを表現できます。 変更されたUTF-8文字列はNULLで終了しません。 エンコーディングは次のとおりです。
'\\u0001'から'\\u007F'の範囲のコード・ポイントは、次の1バイトで表されます。
表4.6.
| 0 | ビット6-0 | ||||||
バイト内の7ビットのデータは、表現されるコードポイントの値を与えます。
NULLコード・ポイント('\\u0000')および'\\u0080'から'\\u07FF'の範囲のコード・ポイントは、バイトxとyのペアで表されます。
表4.7.
x:
|
表4.8.
|
||||||||
y:
|
表4.9.
|
||||||||
2バイトは、値を持つコード・ポイントを表します。
((x & 0x1f) << 6) + (y & 0x3f)
'\\u0800'から'\\uFFFF'の範囲のコード・ポイントは、3バイトのx、yおよびzで表されます。
表4.10.
x:
|
表4.11.
|
||||||||
y:
|
表4.12.
|
||||||||
z:
|
表4.13.
|
||||||||
3バイトは、次の値を持つコード・ポイントを表します。
((x & 0xf) << 12) + ((y & 0x3f) << 6) + (z & 0x3f)
U+FFFF (補助文字と呼ばれる)を超えるコード・ポイントを持つ文字は、UTF-16表現の2つのサロゲート・コード単位を個別にエンコードすることによって表されます。 代理コード単位はそれぞれ3バイトで表現されます。 つまり、補助文字は、u、v、w、x、yおよびzの6バイトで表されます。
表4.14.
u:
|
表4.15.
|
||||||||
v:
|
表4.16.
|
||||||||
w:
|
表4.17.
|
||||||||
x:
|
表4.18.
|
||||||||
y:
|
表4.19.
|
||||||||
z:
|
表4.20.
|
||||||||
6バイトは、次の値を持つコード・ポイントを表します。
0x10000 + ((v & 0x0f) << 16) + ((w & 0x3f) << 10) +
((y & 0x0f) << 6) + (z & 0x3f)
マルチバイト文字のバイトは、classファイルにビッグ・エンディアン(ハイ・バイト・ファースト)の順序で格納されます。
この形式と標準UTF-8形式には2つの違いがあります。 まず、NULL文字(char)0は、1バイト形式ではなく2バイト形式を使用してエンコードされるため、変更されたUTF-8文字列にはNULLが埋め込まれません。 次に、標準 UTF-8の1バイト、2バイト、および3バイト形式のみが使用されます。 Java Virtual Machineは、標準UTF-8の4バイト形式を認識しません。かわりに、独自の2倍3バイト形式を使用します。
標準のUTF-8形式の詳細は、The Unicode Standard、 Version 17.0.0のセクション3.9のUnicodeエンコーディング・フォームを参照してください。
CONSTANT_MethodHandle_info構造
CONSTANT_MethodHandle_info構造は、メソッド・ハンドルを表すために使用されます。
CONSTANT_MethodHandle_info {
u1 tag;
u1 reference_kind;
u2 reference_index;
}
CONSTANT_MethodHandle_info構造の項目は次のとおりです。
tagアイテムの値はCONSTANT_MethodHandle (15)です。
reference_kindアイテムの値は、1から9の範囲内である必要があります。 この値は、このメソッド・ハンドルの種類を示し、バイトコード動作を特徴付けます(§5.4.3.5)。
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エントリは、メソッド・ハンドルが作成されるクラスのメソッドまたはコンストラクタ(§2.9.1)を表すCONSTANT_Methodref_info構造(§4.4.2)である必要があります。
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構造は、メソッド・タイプを表すために使用されます。
CONSTANT_MethodType_info {
u1 tag;
u2 descriptor_index;
}
CONSTANT_MethodType_info構造の項目は次のとおりです。
CONSTANT_Dynamic_infoおよびCONSTANT_InvokeDynamic_info構造
constant_pool表のほとんどの構造は、表に静的に記録された名前、記述子および値を組み合せることで、エンティティを直接表します。 これに対して、CONSTANT_Dynamic_infoおよびCONSTANT_InvokeDynamic_info構造は、エンティティを動的に計算するコードを指すことによって、エンティティを間接的に表します。 ブートストラップ・メソッドと呼ばれるコードは、これらの構造から導出されたシンボリック参照の解決中にJava Virtual Machineによって呼び出されます(§5.1、§5.4.3.6)。 各構造体は、ブートストラップ・メソッド、および計算対象のエンティティを特徴付ける補助名とタイプを指定します。 詳細は次のとおりです。
CONSTANT_Dynamic_info構造は、動的に計算される定数、つまりldc命令(§ldc)の過程でブートストラップ・メソッドを呼び出すことによって生成される任意の値を表すために使用されます。 構造体によって指定された補助型は、動的に計算される定数の型を制約します。
CONSTANT_InvokeDynamic_info構造は、動的計算コール・サイトを表すために使用されます。これは、invokedynamic命令(§invokedynamic)の過程でブートストラップ・メソッドの呼出しによって生成されるjava.lang.invoke.CallSiteのインスタンスです。 構造体によって指定された補助型は、動的に計算されるコールサイトのメソッド型を制約します。
CONSTANT_Dynamic_info {
u1 tag;
u2 bootstrap_method_attr_index;
u2 name_and_type_index;
}
CONSTANT_InvokeDynamic_info {
u1 tag;
u2 bootstrap_method_attr_index;
u2 name_and_type_index;
}
これらの構造の項目は次のとおりです。
CONSTANT_Dynamic_info構造のtagアイテムの値は、CONSTANT_Dynamic (17)です。
CONSTANT_InvokeDynamic_info構造のtag項目の値はCONSTANT_InvokeDynamic (18)です。
bootstrap_method_attr_index項目の値は、このclassファイルのブートストラップ・メソッド表のbootstrap_methods配列への有効な索引である必要があります(§4.7.23)。
CONSTANT_Dynamic_info構造は、構文上、ブートストラップ・メソッド表を介して自身を参照できるという点で一意です。 クラスがロードされたときにそのようなサイクルが検出されることを強制するのではなく(コストがかかる可能性があるチェック)、最初にサイクルを許可しますが、解決時に失敗を義務付けます(§5.4.3.6)。
name_and_type_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、CONSTANT_NameAndType_info構造体(§4.4.6)である必要があります。 このconstant_poolエントリは、名前と記述子を示します。
CONSTANT_Dynamic_info構造体では、指定された記述子はフィールド記述子(§4.3.2)である必要があります。
CONSTANT_InvokeDynamic_info構造体では、示された記述子はメソッド記述子(§4.3.3)である必要があります。
CONSTANT_Module_info構造
CONSTANT_Module_info構造は、モジュールを表すために使用されます。
CONSTANT_Module_info {
u1 tag;
u2 name_index;
}
CONSTANT_Module_info構造の項目は次のとおりです。
CONSTANT_Module_info構造は、モジュールを宣言するclassファイルの定数プール、つまり、access_flagsアイテムにACC_MODULEフラグが設定されているClassFile構造体でのみ許可されます。 他のすべてのclassファイルでは、CONSTANT_Module_info構造は不正です。
CONSTANT_Package_info構造
CONSTANT_Package_info構造は、モジュールによってエクスポートまたはオープンされるパッケージを表すために使用されます。
CONSTANT_Package_info {
u1 tag;
u2 name_index;
}
CONSTANT_Package_info構造の項目は次のとおりです。
CONSTANT_Package_info構造は、モジュールを宣言するclassファイルの定数プール、つまり、access_flagsアイテムにACC_MODULEフラグが設定されているClassFile構造体でのみ許可されます。 他のすべてのclassファイルでは、CONSTANT_Package_info構造は不正です。
各フィールドは、field_info構造体によって記述されます。
1つのclassファイルの2つのフィールドに、同じ名前と記述子を指定することはできません(§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アイテムの値は、このフィールドに対するアクセス権限およびプロパティを示すために使用されるフラグのマスクです。 表4.5-Aに、各フラグの解釈(設定されている場合)を示します。
表4.5-A. フィールドアクセスおよびプロパティーフラグ
| フラグ名 | 値 | 解釈 |
|---|---|---|
ACC_PUBLIC |
0x0001 | publicを宣言しました。パッケージの外部からアクセスできます。
|
ACC_PRIVATE |
0x0002 |
宣言された |
ACC_PROTECTED |
0x0004 | 宣言されたprotected。サブクラス内でアクセスできます。
|
ACC_STATIC |
0x0008 | staticを宣言しました。
|
ACC_FINAL |
0x0010 | finalと宣言され、オブジェクトの構築後に直接割り当てられることはありません(JLS§17.5)。
|
ACC_VOLATILE |
0x0040 | volatileを宣言しました。キャッシュできません。
|
ACC_TRANSIENT |
0x0080 | transientを宣言しました。永続オブジェクト・マネージャによる書込みまたは読取りは行われません。
|
ACC_SYNTHETIC |
0x1000 | 統合が宣言されました。ソース・コードに存在しません。 |
ACC_ENUM |
0x4000 | enumクラスの要素として宣言されます。
|
クラスのフィールドでは、表4.5-Aのいずれかのフラグを設定できます。 ただし、クラスの各フィールドには、ACC_PUBLIC、ACC_PRIVATEおよびACC_PROTECTEDフラグの1つ(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フラグは、このフィールドがenumクラスの要素の保持に使用されることを示します(JLS§8.9)。
表4.5-Aに割り当てられていないaccess_flagsアイテムのすべてのビットは、将来使用するために予約されています。 これらは、生成されたclassファイルでゼロに設定する必要があり、Java Virtual Machineの実装では無視する必要があります。
name_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、フィールドを示す有効な非修飾名を表すCONSTANT_Utf8_info構造体(§4.4.7)である必要があります(§4.2.2)。
descriptor_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、有効なフィールド記述子(§4.3.2)を表すCONSTANT_Utf8_info構造体(§4.4.7)である必要があります。
attributes_countアイテムの値は、このフィールドの追加属性の数を示します。
attributes表の各値は、attribute_info構造(§4.7)である必要があります。
フィールドには、任意の数のオプション属性を関連付けることができます。
field_info構造のattributes表に表示されるように、この仕様で定義される属性を表4.7-Cにリストします。
field_info構造のattributes表に表示されるように定義された属性に関するルールは、§4.7に示されています。
field_info構造のattributes表の事前定義されていない属性に関するルールは、§4.7.1に示されています。
各インスタンス初期化メソッド(§2.9.1)およびクラスまたはインタフェース初期化メソッド(§2.9.2)を含む各メソッドは、method_info構造体によって記述されます。
1つのclassファイルの2つのメソッドで、同じ名前と記述子を持つことはできません(§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アイテムの値は、このメソッドに対するアクセス権限およびプロパティを示すために使用されるフラグのマスクです。 表4.6-Aに、各フラグの解釈(設定されている場合)を示します。
表4.6-A. メソッド・アクセスおよびプロパティ・フラグ
| フラグ名 | 値 | 解釈 |
|---|---|---|
ACC_PUBLIC |
0x0001 | publicを宣言しました。パッケージの外部からアクセスできます。
|
ACC_PRIVATE |
0x0002 |
宣言された |
ACC_PROTECTED |
0x0004 | 宣言されたprotected。サブクラス内でアクセスできます。
|
ACC_STATIC |
0x0008 | staticを宣言しました。
|
ACC_FINAL |
0x0010 | finalを宣言し、上書きしてはならない(§5.4.5)。
|
ACC_SYNCHRONIZED |
0x0020 | synchronizedを宣言します。起動はモニターの使用によってラップされます。
|
ACC_BRIDGE |
0x0040 | コンパイラによって生成されるブリッジ・メソッド。 |
ACC_VARARGS |
0x0080 | 引数の変数数で宣言されます。 |
ACC_NATIVE |
0x0100 | nativeを宣言し、Javaプログラミング言語以外の言語で実装します。
|
ACC_ABSTRACT |
0x0400 | abstractと宣言され、実装は提供されません。
|
ACC_STRICT |
0x0800 |
メジャー・バージョン番号が46以上60以下の |
ACC_SYNTHETIC |
0x1000 | 統合が宣言されました。ソース・コードに存在しません。 |
値0x0800は、メジャー・バージョン番号が46以上60以下のclassファイルでのみ、ACC_STRICTフラグとして解釈されます。 このようなclassファイル内のメソッドの場合、次のルールは、ACC_STRICTフラグを他のフラグと組み合せて設定できるかどうかを判断します。 (ACC_STRICTフラグを設定すると、メソッドの浮動小数点命令がJava SE 1.2から16に制約されます(§2.8)。) メジャー・バージョン番号が46未満または60より大きいclassファイル内のメソッドの場合、値0x0800はACC_STRICTフラグとして解釈されず、割当て解除されます。このようなclassファイルでACC_STRICTフラグを設定することは意味がありません。
クラスのメソッドには、表4.6-Aのフラグを設定できます。 ただし、クラスの各メソッドには、ACC_PUBLIC、ACC_PRIVATEおよびACC_PROTECTEDフラグの1つしか設定できません(JLS§8.4.3)。
インタフェースのメソッドには、ACC_PROTECTED、ACC_FINAL、ACC_SYNCHRONIZEDおよびACC_NATIVE (JLS§9.4)を除く表4.6-Aのフラグが設定されている場合があります。 バージョン番号が52.0より小さいclassファイルでは、インタフェースの各メソッドにACC_PUBLICフラグとACC_ABSTRACTフラグが設定されている必要があります。バージョン番号が52.0以上のclassファイルでは、インタフェースの各メソッドにACC_PUBLICフラグとACC_PRIVATEフラグが1つのみ設定されている必要があります。
クラスまたはインタフェースのメソッドにACC_ABSTRACTフラグが設定されている場合、ACC_PRIVATE、ACC_STATIC、ACC_FINAL、ACC_SYNCHRONIZEDまたはACC_NATIVEフラグが設定されてはいけません。また、(メジャー・バージョン番号が46以上60以下のclassファイルで)ACC_STRICTフラグが設定されてはいけません。
インスタンス初期化メソッド(§2.9.1)には、ACC_PUBLIC、ACC_PRIVATEおよびACC_PROTECTEDフラグの1つしか設定されず、ACC_VARARGSおよびACC_SYNTHETICフラグも設定され、(メジャー・バージョン番号が46以上、最大60のclassファイル内)にもACC_STRICTフラグが設定される場合がありますが、表4.6-Aの他のフラグは設定できません。
バージョン番号が51.0以上のclassファイルでは、名前が<clinit>のメソッドにACC_STATICフラグが設定されている必要があります。
クラスまたはインタフェース初期化メソッド(§2.9.2)は、Java Virtual Machineによって暗黙的に呼び出されます。 ACC_STATICフラグの設定、および(メジャー・バージョン番号が46以上60以下のclassファイル)ACC_STRICTフラグの設定を除き、そのaccess_flagsアイテムの値は無視され、このメソッドはフラグの有効な組合せに関する前述のルールから免除されます。
ACC_BRIDGEフラグは、コンパイラによってJavaプログラミング言語用に生成されたブリッジ・メソッドを示すために使用されます。
ACC_VARARGSフラグは、このメソッドがソース・コード・レベルで可変数の引数を取ることを示します。 可変数の引数を取るように宣言されたメソッドは、ACC_VARARGSフラグを1に設定してコンパイルする必要があります。 他のすべてのメソッドは、ACC_VARARGSフラグを0に設定してコンパイルする必要があります。
ACC_SYNTHETICフラグは、このメソッドがコンパイラによって生成されたことを示しており、§4.7.8で指定したメソッドの1つでないかぎり、ソース・コードには表示されません。
表4.6-Aに割り当てられていないaccess_flagsアイテムのすべてのビットは、将来使用するために予約されています。 (これには、メジャー・バージョン番号が46未満または60より大きいclassファイルの0x0800に対応するビットが含まれます。) これらは、生成されたclassファイルでゼロに設定する必要があり、Java Virtual Machineの実装では無視する必要があります。
name_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、メソッドを示す有効な非修飾名(§4.4.7)を表すCONSTANT_Utf8_info構造体(§4.2.2)または(このメソッドがインタフェースではなくクラス内にある場合)、特別なメソッド名<init>または特別なメソッド名<clinit>である必要があります。
descriptor_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、有効なメソッド記述子を表すCONSTANT_Utf8_info構造体である必要があります(§4.3.3)。 さらに:
このメソッドがインタフェースではなくクラス内にあり、メソッドの名前が<init>の場合、記述子はvoidメソッドを示す必要があります。
メソッドの名前が<clinit>の場合、記述子はvoidメソッドを示し、バージョン番号が51.0以上のclassファイルで引数をとらないメソッドを示す必要があります。
この仕様の将来のエディションでは、access_flags項目にACC_VARARGSフラグが設定されている場合に、メソッド記述子の最後のパラメータ記述子が配列型であることが必要になる場合があります。
attributes_countアイテムの値は、このメソッドの追加属性の数を示します。
attributes表の各値は、attribute_info構造(§4.7)である必要があります。
メソッドには、任意の数のオプション属性を関連付けることができます。
method_info構造のattributes表に表示されるように、この仕様で定義される属性を表4.7-Cにリストします。
method_info構造のattributes表に表示されるように定義された属性に関するルールは、§4.7に示されています。
method_info構造のattributes表の事前定義されていない属性に関するルールは、§4.7.1に示されています。
属性は、classファイル形式のClassFile、field_info、method_info、Code_attributeおよびrecord_component_info構造体(§4.1、§4.5、§4.6、§4.7.3、§4.7.30)で使用されます。
すべての属性は、次の一般形式を持っています:
attribute_info {
u2 attribute_name_index;
u4 attribute_length;
u1 info[attribute_length];
}
すべての属性について、attribute_name_index項目はクラスの定数プールへの有効な符号なし16ビット索引である必要があります。 attribute_name_indexのconstant_poolエントリは、属性の名前を表すCONSTANT_Utf8_info構造体(§4.4.7)である必要があります。 attribute_length項目の値は、後続情報の長さをバイト単位で示します。 長さには、attribute_name_indexおよびattribute_length項目を含む最初の6バイトは含まれません。
30の属性がこの仕様によって事前に定義されています。 ナビゲーションしやすいように、3回リストされます:
この仕様で使用されるコンテキスト内、つまり、それらが出現するclassファイル構造のattributes表では、これらの事前定義済属性の名前が予約されています。
attributes表に事前定義済属性が存在することに関する条件は、その属性を説明するセクションで明示的に指定されます。 条件が指定されていない場合、この属性はattributes表に何回でも出現する可能性があります。
事前定義済の属性は、目的に応じて3つのグループに分類されます:
Java Virtual Machineによるclassファイルの解釈を訂正するには、次の7つの属性が重要です。
ConstantValue
Code
StackMapTable
BootstrapMethods
NestHost
NestMembers
PermittedSubclasses
バージョン番号がvのclassファイルでは、実装でバージョンvがサポートされている場合、これらの各属性がJava Virtual Machineの実装によって認識され、正しく読み取られる必要があります。classファイル・フォーマットのうち、属性が最初にclassファイル・フォーマットのバージョンv以前で定義され、属性が表示されるように定義された場所に属性が表示されます。
10個の属性は、Java Virtual Machineによるclassファイルの正しい解釈には重要ではありませんが、Java SE Platformのクラス・ライブラリによるclassファイルの解釈を訂正するために重要であるか、ツールに有用です(この場合、属性を指定するセクションは、それを「オプション」と記述します)。
Exceptions
InnerClasses
EnclosingMethod
Synthetic
Signature
Record
SourceFile
LineNumberTable
LocalVariableTable
LocalVariableTypeTable
バージョン番号がvのclassファイルでは、実装でバージョンvがサポートされている場合、これらの各属性がJava Virtual Machineの実装によって認識され、正しく読み取られる必要があります。classファイル・フォーマットのうち、属性が最初にclassファイル・フォーマットのバージョンv以前で定義され、属性が表示されるように定義された場所に属性が表示されます。
Java Virtual Machineによるclassファイルの正しい解釈には、13の属性は重要ではありませんが、Java SEプラットフォームのクラス・ライブラリによって公開されるか、ツールによって使用可能になるclassファイルに関するメタデータが含まれています(この場合、属性を指定するセクションは、それを「オプション」と記述します)。
SourceDebugExtension
Deprecated
RuntimeVisibleAnnotations
RuntimeInvisibleAnnotations
RuntimeVisibleParameterAnnotations
RuntimeInvisibleParameterAnnotations
RuntimeVisibleTypeAnnotations
RuntimeInvisibleTypeAnnotations
AnnotationDefault
MethodParameters
Module
ModulePackages
ModuleMainClass
Java Virtual Machineの実装では、これらの属性に含まれる情報を使用する場合もあれば、これらの属性を警告なしで無視する必要がある場合もあります。
表4.7-A. 事前定義済のclassファイル属性(セクション別)
| 属性 | セクション | classファイル
|
Java SE |
|---|---|---|---|
ConstantValue |
§4.7.2 | 45.3 | 1.0.2 |
Code |
§4.7.3 | 45.3 | 1.0.2 |
StackMapTable |
§4.7.4 | 50.0 | 6 |
Exceptions |
§4.7.5 | 45.3 | 1.0.2 |
InnerClasses |
§4.7.6 | 45.3 | 1.1 |
EnclosingMethod |
§4.7.7 | 49.0 | 5.0 |
Synthetic |
§4.7.8 | 45.3 | 1.1 |
Signature |
§4.7.9 | 49.0 | 5.0 |
SourceFile |
§4.7.10 | 45.3 | 1.0.2 |
SourceDebugExtension |
§4.7.11 | 49.0 | 5.0 |
LineNumberTable |
§4.7.12 | 45.3 | 1.0.2 |
LocalVariableTable |
§4.7.13 | 45.3 | 1.0.2 |
LocalVariableTypeTable |
§4.7.14 | 49.0 | 5.0 |
Deprecated |
§4.7.15 | 45.3 | 1.1 |
RuntimeVisibleAnnotations |
§4.7.16 | 49.0 | 5.0 |
RuntimeInvisibleAnnotations |
§4.7.17 | 49.0 | 5.0 |
RuntimeVisibleParameterAnnotations |
§4.7.18 | 49.0 | 5.0 |
RuntimeInvisibleParameterAnnotations |
§4.7.19 | 49.0 | 5.0 |
RuntimeVisibleTypeAnnotations |
§4.7.20 | 52.0 | 8 |
RuntimeInvisibleTypeAnnotations |
§4.7.21 | 52.0 | 8 |
AnnotationDefault |
§4.7.22 | 49.0 | 5.0 |
BootstrapMethods |
§4.7.23 | 51.0 | 7 |
MethodParameters |
§4.7.24 | 52.0 | 8 |
|
|
§4.7.25 | 53.0 | 9 |
|
|
§4.7.26 | 53.0 | 9 |
|
|
§4.7.27 | 53.0 | 9 |
|
|
§4.7.28 | 55.0 | 11 |
|
|
§4.7.29 | 55.0 | 11 |
|
|
§4.7.30 | 60.0 | 16 |
|
|
§4.7.31 | 61.0 | 17 |
表4.7-B. 事前定義済のclassファイル属性(classファイル形式)
| 属性 | classファイル
|
Java SE | セクション |
|---|---|---|---|
ConstantValue |
45.3 | 1.0.2 | §4.7.2 |
Code |
45.3 | 1.0.2 | §4.7.3 |
Exceptions |
45.3 | 1.0.2 | §4.7.5 |
SourceFile |
45.3 | 1.0.2 | §4.7.10 |
LineNumberTable |
45.3 | 1.0.2 | §4.7.12 |
LocalVariableTable |
45.3 | 1.0.2 | §4.7.13 |
InnerClasses |
45.3 | 1.1 | §4.7.6 |
Synthetic |
45.3 | 1.1 | §4.7.8 |
Deprecated |
45.3 | 1.1 | §4.7.15 |
EnclosingMethod |
49.0 | 5.0 | §4.7.7 |
Signature |
49.0 | 5.0 | §4.7.9 |
SourceDebugExtension |
49.0 | 5.0 | §4.7.11 |
LocalVariableTypeTable |
49.0 | 5.0 | §4.7.14 |
RuntimeVisibleAnnotations |
49.0 | 5.0 | §4.7.16 |
RuntimeInvisibleAnnotations |
49.0 | 5.0 | §4.7.17 |
RuntimeVisibleParameterAnnotations |
49.0 | 5.0 | §4.7.18 |
RuntimeInvisibleParameterAnnotations |
49.0 | 5.0 | §4.7.19 |
AnnotationDefault |
49.0 | 5.0 | §4.7.22 |
StackMapTable |
50.0 | 6 | §4.7.4 |
BootstrapMethods |
51.0 | 7 | §4.7.23 |
RuntimeVisibleTypeAnnotations |
52.0 | 8 | §4.7.20 |
RuntimeInvisibleTypeAnnotations |
52.0 | 8 | §4.7.21 |
MethodParameters |
52.0 | 8 | §4.7.24 |
|
|
53.0 | 9 | §4.7.25 |
|
|
53.0 | 9 | §4.7.26 |
|
|
53.0 | 9 | §4.7.27 |
|
|
55.0 | 11 | §4.7.28 |
|
|
55.0 | 11 | §4.7.29 |
|
|
60.0 | 16 | §4.7.30 |
|
|
61.0 | 17 | §4.7.31 |
表4.7-C. 事前定義済のclassファイル属性(場所別)
| 属性 | ロケーション | classファイル
|
|---|---|---|
SourceFile |
ClassFile |
45.3 |
InnerClasses |
ClassFile |
45.3 |
EnclosingMethod |
ClassFile |
49.0 |
SourceDebugExtension |
ClassFile |
49.0 |
BootstrapMethods |
ClassFile |
51.0 |
|
|
ClassFile |
53.0 |
|
|
ClassFile |
55.0 |
|
|
ClassFile |
60.0 |
|
|
ClassFile |
61.0 |
ConstantValue |
field_info |
45.3 |
Code |
method_info |
45.3 |
Exceptions |
method_info |
45.3 |
RuntimeVisibleParameterAnnotations, RuntimeInvisibleParameterAnnotations |
method_info |
49.0 |
AnnotationDefault |
method_info |
49.0 |
MethodParameters |
method_info |
52.0 |
表4.7-C (続き). 事前定義済のclassファイル属性(場所別)
| 属性 | ロケーション | classファイル
|
|---|---|---|
Synthetic |
ClassFile, field_info, method_info |
45.3 |
Deprecated |
ClassFile, field_info, method_info |
45.3 |
Signature |
|
49.0 |
RuntimeVisibleAnnotations, RuntimeInvisibleAnnotations |
|
49.0 |
LineNumberTable |
Code |
45.3 |
LocalVariableTable |
Code |
45.3 |
LocalVariableTypeTable |
Code |
49.0 |
StackMapTable |
Code |
50.0 |
RuntimeVisibleTypeAnnotations, RuntimeInvisibleTypeAnnotations |
|
52.0 |
コンパイラは、classファイル構造、field_info構造、method_info構造およびCode属性(§4.7.3)のattributes表に新しい属性を含むclassファイルを定義および発行できます。 Java Virtual Machineの実装では、これらのattributes表にある新しい属性を認識して使用することが許可されます。 ただし、この仕様の一部として定義されていない属性は、classファイルのセマンティクスに影響を与えません。 Java Virtual Machineの実装は、認識されない属性をサイレントに無視するために必要です。
たとえば、ベンダー固有のデバッグをサポートするための新しい属性の定義が許可されます。 Java Virtual Machineの実装では認識されない属性を無視する必要があるため、その特定のJava Virtual Machine実装を目的としたclassファイルは、classファイルに含まれる追加のデバッグ情報を実装で使用できない場合でも、他の実装で使用できます。
Java Virtual Machineの実装では、単に新しい属性が存在するため、例外をスローしたり、classファイルの使用を拒否したりすることは特に禁止されています。 もちろん、必要なすべての属性が含まれていないclassファイルを指定した場合、classファイルで動作するツールが正しく実行されないことがあります。
同じ属性名を使用し、同じ長さの2つの属性が、どちらかの属性を認識する実装で競合します。 この仕様以外で定義される属性には、The Java Language Specification、 Java SE 26 Edition (JLS§6.1)で説明されているパッケージ命名規則に従って名前を選択する必要があります。
この仕様の将来のバージョンでは、追加属性を定義できます。
ConstantValue属性
ConstantValue属性は、field_info構造(§4.5)のattributes表の固定長属性です。 ConstantValue属性は、定数式(JLS§15.28)の値を表し、次のように使用されます。
field_info構造のattributes表には、最大1つのConstantValue属性を指定できます。
ConstantValue属性の形式は次のとおりです。
ConstantValue_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 constantvalue_index;
}
ConstantValue_attribute構造の項目は次のとおりです。
attribute_name_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、文字列"ConstantValue"を表すCONSTANT_Utf8_info構造体(§4.4.7)である必要があります。
attribute_lengthアイテムの値は2である必要があります。
constantvalue_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、この属性で表される値を示します。 constant_poolエントリは、表4.7.2-Aに示すように、フィールドに適したタイプである必要があります。
表4.7.2-A. 定数値の属性タイプ
| フィールド・タイプ | エントリ・タイプ |
|---|---|
int, short, char, byte, boolean |
CONSTANT_Integer |
float |
CONSTANT_Float |
long |
CONSTANT_Long |
double |
CONSTANT_Double |
String |
CONSTANT_String |
Code属性
Code属性は、method_info構造(§4.6)のattributes表の可変長属性です。 Code属性には、インスタンス初期化メソッド、クラスまたはインタフェース初期化メソッド(§2.9.1、§2.9.2)など、メソッドのJava Virtual Machine命令および補助情報が含まれます。
メソッドがnativeまたはabstractで、クラスまたはインタフェース初期化メソッドではない場合、そのmethod_info構造体のattributes表にCode属性を指定しないでください。 それ以外の場合は、そのmethod_info構造がattributes表に1つのCode属性を持つ必要があります。
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アイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、文字列"Code"を表すCONSTANT_Utf8_info構造体(§4.4.7)である必要があります。
attribute_length項目の値は、最初の6バイトを除く属性の長さを示します。
max_stack項目の値は、メソッドの実行中の任意の時点でのこのメソッドのオペランド・スタックの最大深度(§2.6.2)を示します。
max_locals項目の値は、このメソッド(§2.6.1)の起動時に割り当てられたローカル変数配列内のローカル変数の数を示します。これには、呼出し時にメソッドにパラメータを渡すために使用されるローカル変数も含まれます。
long型またはdouble型の値の最大ローカル変数索引は、max_locals - 2です。 他の型の値の最大ローカル変数索引は、max_locals - 1です。
code_lengthアイテムの値は、このメソッドのcode配列内のバイト数を示します。
code_lengthの値は、ゼロ(code配列は空にできません)より大きく、65536未満である必要があります。
code配列は、メソッドを実装するJava Virtual Machineコードの実際のバイト数を示します。
code配列をバイトアドレス対応マシン上のメモリーに読み込むときに、配列の最初のバイトが4バイト境界に整列されている場合、tableswitchおよびlookupswitch 32ビット・オフセットは4バイト整列されます。 (code配列の整列の結果の詳細は、これらの手順の説明を参照してください。)
code配列の内容に関する詳細な制約は広範囲にわたり、個別のセクション(§4.9)に示されています。
exception_table_lengthアイテムの値は、exception_table配列のエントリ数を示します。
exception_table配列の各エントリは、code配列内の1つの例外ハンドラを記述します。 exception_table配列内のハンドラの順序は重要です(§2.10)。
各exception_tableエントリには、次の4つの項目が含まれます。
2つの項目start_pcおよびend_pcの値は、例外ハンドラがアクティブなcode配列内の範囲を示します。 start_pcの値は、命令のopcodeのcode配列への有効な索引である必要があります。 end_pcの値は、命令のopcodeのcode配列への有効な索引であるか、code配列の長さであるcode_lengthと等しい必要があります。 start_pcの値は、end_pcの値より小さくする必要があります。
start_pcは包含的であり、end_pcは排他的です。つまり、プログラム・カウンタが間隔[start_pc、 end_pc]内にある間は、例外ハンドラがアクティブである必要があります。
end_pcが排他的であるという事実は、Java Virtual Machineの設計における過去の誤りです。メソッドのJava Virtual Machineコードの長さがちょうど65535バイトで、長さが1バイトの命令で終わる場合、その命令は例外ハンドラで保護できません。 コンパイラ・ライターは、メソッド、インスタンス初期化メソッドまたは静的イニシャライザ(任意のコード配列のサイズ)に対して生成されたJava Virtual Machineコードの最大サイズを65534バイトに制限することで、このバグを回避できます。
handler_pcアイテムの値は、例外ハンドラの開始を示します。 アイテムの値は、code配列への有効な索引である必要があり、命令のopcodeのインデックスである必要があります。
catch_typeアイテムの値が0以外の場合は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、この例外ハンドラが捕捉するように指定されている例外のクラスを表すCONSTANT_Class_info構造体(§4.4.1)である必要があります。 例外ハンドラは、スローされた例外が特定のクラスのインスタンスまたはそのサブクラスの1つである場合にのみコールされます。
ベリファイアは、クラスがThrowableまたはThrowableのサブクラス(§4.9.2)であることをチェックします。
catch_typeアイテムの値がゼロの場合、すべての例外に対してこの例外ハンドラがコールされます。
これは、finally (§3.13)の実装に使用されます。
attributes_countアイテムの値は、Code属性の属性の数を示します。
attributes表の各値は、attribute_info構造(§4.7)である必要があります。
Code属性には、任意の数のオプション属性を関連付けることができます。
Code属性のattributes表に表示されるように、この仕様で定義される属性を表4.7-Cにリストします。
Code属性のattributes表に表示されるように定義された属性に関するルールは、§4.7に示されています。
Code属性のattributes表の事前定義されていない属性に関するルールは、§4.7.1に示されています。
StackMapTable属性
StackMapTable属性は、Code属性(§4.7.3)のattributes表の可変長属性です。 StackMapTable属性は、型チェックによる検証プロセス中に使用されます(§4.10.1)。
Code属性のattributes表には、最大1つのStackMapTable属性を指定できます。
バージョン番号が50.0以上のclassファイルでは、メソッドのCode属性にStackMapTable属性がない場合は、暗黙的スタック・マップ属性(§4.10.1)があります。 この暗黙的なスタック・マップ属性は、number_of_entriesがゼロのStackMapTable属性と同等です。
StackMapTable属性の形式は次のとおりです。
StackMapTable_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 number_of_entries;
stack_map_frame entries[number_of_entries];
}
StackMapTable_attribute構造の項目は次のとおりです。
attribute_name_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、文字列"StackMapTable"を表すCONSTANT_Utf8_info構造体(§4.4.7)である必要があります。
attribute_length項目の値は、最初の6バイトを除く属性の長さを示します。
number_of_entriesアイテムの値は、entries表のstack_map_frameエントリの数を示します。
entries表の各エントリは、メソッドの1つのスタック・マップ・フレームを示します。 entries表のスタック・マップ・フレームの順序は重要です。
スタック・マップ・フレームは、適用するバイトコード・オフセット、およびそのオフセットに対するローカル変数とオペランド・スタック・エントリの検証タイプ(明示的または暗黙的)を指定します。
entries表で説明されている各スタック・マップ・フレームは、そのセマンティクスの一部について前のフレームに依存しています。 メソッドの最初のスタック・マップ・フレームは暗黙的であり、型チェッカ(§4.10.1.5)によってメソッド記述子から計算されます。 したがって、entries[0]のstack_map_frame構造体は、メソッドの2番目のスタック・マップ・フレームを記述します。
スタック・マップ・フレームが適用されるバイトコード・オフセットは、フレームで指定された値offset_deltaを(明示的または暗黙的に)取得し、前のフレームがメソッドの初期フレームでないかぎり、前のフレームのバイトコード・オフセットにoffset_delta + 1を追加することによって計算されます。 その場合、スタック・マップ・フレームが適用されるバイトコード・オフセットは、フレームで指定された値offset_deltaです。
実際のバイトコードオフセットを格納するのではなく、オフセットデルタを使用することにより、定義上、スタックマップフレームが正しくソートされた順序になるようにします。 さらに、すべての明示的なフレームに対して式offset_delta + 1を一貫して使用することで(暗黙的な最初のフレームとは対照的に)、重複がないことを保証します。
Code属性のcode配列のオフセットiで命令が開始され、Code属性にStackMapTable属性があり、そのentries配列にバイトコード・オフセットiで適用されるスタック・マップ・フレームが含まれている場合は、バイトコード内の命令に対応するスタック・マップ・フレームがあるとします。
検証タイプは、1つまたは2つの場所のタイプを指定します。ここで、場所は、単一のローカル変数または単一のオペランド・スタック・エントリです。 検証タイプは、1バイトのタグで構成される識別ユニオン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;
}
ローカル変数配列またはオペランド・スタック内の1つの場所を指定する検証タイプは、verification_type_info共用体の次の項目で表されます。
Top_variable_info項目は、ローカル変数の検証タイプがtopであることを示します。
Top_variable_info {
u1 tag = ITEM_Top; /* 0 */
}
Integer_variable_infoアイテムは、ロケーションの検証タイプがintであることを示します。
Integer_variable_info {
u1 tag = ITEM_Integer; /* 1 */
}
Float_variable_infoアイテムは、ロケーションの検証タイプがfloatであることを示します。
Float_variable_info {
u1 tag = ITEM_Float; /* 2 */
}
Null_variable_info型は、ロケーションの検証タイプがnullであることを示します。
Null_variable_info {
u1 tag = ITEM_Null; /* 5 */
}
UninitializedThis_variable_infoアイテムは、ロケーションの検証タイプがuninitializedThisであることを示します。
UninitializedThis_variable_info {
u1 tag = ITEM_UninitializedThis; /* 6 */
}
Object_variable_info項目は、cpool_indexで指定された索引のconstant_pool表にあるCONSTANT_Class_info構造体(§4.4.1)で表されるクラスである検証タイプがあることを示します。
Object_variable_info {
u1 tag = ITEM_Object; /* 7 */
u2 cpool_index;
}
Uninitialized_variable_infoアイテムは、ロケーションの検証タイプがuninitialized(Offset)であることを示します。 Offset項目は、このStackMapTable属性を含むCode属性のcode配列で、その場所に格納されるオブジェクトを作成したnew命令(§new)のオフセットを示します。
Uninitialized_variable_info {
u1 tag = ITEM_Uninitialized; /* 8 */
u2 offset;
}
ローカル変数配列またはオペランド・スタック内の2つの場所を指定する検証タイプは、verification_type_info共用体の次の項目で表されます。
Long_variable_info項目は、2つの場所の最初の場所の検証タイプがlongであることを示します。
Long_variable_info {
u1 tag = ITEM_Long; /* 4 */
}
Double_variable_info項目は、2つの場所の最初の場所の検証タイプがdoubleであることを示します。
Double_variable_info {
u1 tag = ITEM_Double; /* 3 */
}
Long_variable_infoおよびDouble_variable_info項目は、2つの場所の2番目の検証タイプを次のように示します。
2つの場所の最初の場所がローカル変数である場合、次のようになります。
索引が最も高いローカル変数にはできません。
次に大きい番号のローカル変数の検証タイプはtopです。
2つの場所の最初の場所がオペランド・スタック・エントリである場合、次のようになります。
オペランドスタックの最上位の場所であってはいけません。
オペランド・スタックの先頭に近い次の場所は、検証タイプtopです。
スタック・マップ・フレームは、識別された和集合stack_map_frameで表されます。これは、1バイトのタグで構成され、和集合のどのアイテムが使用中かを示し、その後に0バイト以上が続くため、タグに関する詳細情報を提供します。
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]の範囲のタグで表されます。 このフレームタイプは、フレームが前のフレームとまったく同じローカル変数を持ち、オペランドスタックに1つのエントリがあることを示します。 フレームのoffset_delta値は、式frame_type - 64によって指定されます。 フレーム・タイプの後に、1つのスタック・エントリの検証タイプが表示されます。
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で表されます。 このフレームタイプは、フレームが前のフレームとまったく同じローカル変数を持ち、オペランドスタックに1つのエントリがあることを示します。 フレーム・タイプsame_locals_1_stack_item_frameとは異なり、フレームのoffset_delta値は明示的に指定されます。 1つのスタック・エントリの検証タイプは、offset_deltaの後に表示されます。
same_locals_1_stack_item_frame_extended {
u1 frame_type = SAME_LOCALS_1_STACK_ITEM_EXTENDED; /* 247 */
u2 offset_delta;
verification_type_info stack[1];
}
フレーム・タイプchop_frameは、[248-250]の範囲のタグで表されます。 このフレームタイプは、最後の kローカル変数が存在せず、オペランドスタックが空であることを除き、フレームのローカル変数が前のフレームと同じであることを示します。 kの値は、式251 - frame_typeによって指定されます。 フレームのoffset_delta値は明示的に指定されます。
chop_frame {
u1 frame_type = CHOP; /* 248-250 */
u2 offset_delta;
}
前のフレームのローカル変数の検証タイプは、full_frameフレーム型のように構造化された配列であるlocalsによって指定されるとします。 前のフレームのlocals[M-1]がローカル変数Xを表し、locals[M]がローカル変数Yを表す場合、1つのローカル変数を削除すると、新しいフレームのlocals[M-1]はローカル変数Xを表し、locals[M]は未定義になります。
kが前のフレームのlocalsのローカル変数の数より大きい場合、つまり、新しいフレームのローカル変数の数が0より小さい場合に、エラーが発生します。
フレーム・タイプsame_frame_extendedは、タグ251で表されます。 このフレームタイプは、フレームが前のフレームとまったく同じローカル変数を持ち、オペランドスタックが空であることを示します。 フレーム・タイプsame_frameとは異なり、フレームのoffset_delta値は明示的に指定されます。
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の0番目のエントリは、最初の追加ローカル変数の検証タイプを表します。 locals[M]がローカル変数Nを表す場合、次のようになります。
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+1を表します。
locals[M]がLong_variable_infoまたはDouble_variable_infoの場合、locals[M+1]はローカル変数N+2を表します。
索引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番目のエントリは、ローカル変数0の検証タイプを表します。 locals[M]がローカル変数Nを表す場合、次のようになります。
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+1を表します。
locals[M]がLong_variable_infoまたはDouble_variable_infoの場合、locals[M+1]はローカル変数N+2を表します。
索引iのlocals[i]が、メソッドのローカル変数の最大数より大きい索引を持つローカル変数を表す場合は、エラーになります。
stackの0番目のエントリは、オペランド・スタック下部の検証タイプを表し、stackの後続のエントリは、オペランド・スタックの上部に近いスタック・エントリの検証タイプを表します。 オペランドスタックの最下部をスタックエントリ0と呼び、オペランドスタックの後続のエントリをスタックエントリ1、2などと呼びます。stack[M]がスタック・エントリNを表す場合、次のようになります。
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+1を表します。
stack[M]がLong_variable_infoまたはDouble_variable_infoの場合、stack[M+1]はスタック・エントリN+2を表します。
いずれかの索引iについて、stack[i]がメソッドの最大オペランド・スタック・サイズより大きい索引を持つスタック・エントリを表す場合は、エラーになります。
Exceptions属性
Exceptions属性は、method_info構造(§4.6)のattributes表の可変長属性です。 Exceptions属性は、メソッドがスローする可能性のあるチェック例外を示します。
method_info構造のattributes表には、最大1つの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アイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、文字列"Exceptions"を表すCONSTANT_Utf8_info構造体(§4.4.7)である必要があります。
attribute_length項目の値は、最初の6バイトを除く属性の長さを示します。
number_of_exceptionsアイテムの値は、exception_index_table内のエントリの数を示します。
exception_index_table配列の各値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、このメソッドがスローするように宣言されているクラス型を表すCONSTANT_Class_info構造体(§4.4.1)である必要があります。
次の3つの基準のうち少なくとも1つが満たされた場合のみ、メソッドは例外をスローする必要があります。
例外は、RuntimeExceptionのインスタンスまたはそのサブクラスの1つです。
例外は、Errorのインスタンスまたはそのサブクラスの1つです。
例外は、先ほど説明したexception_index_tableで指定されている例外クラスの1つ、またはそれらのサブクラスの1つのインスタンスです。
これらの要件は、Java Virtual Machineでは適用されず、コンパイル時にのみ適用されます。
InnerClasses属性
InnerClasses属性は、ClassFile構造(§4.1)のattributes表の可変長属性です。
クラスまたはインタフェースCの定数プールに、パッケージのメンバーではないクラスまたはインタフェースを表すCONSTANT_Class_infoエントリ(§4.4.1)が少なくとも1つ含まれている場合、CのClassFile構造のattributes表には、InnerClasses属性が1つのみ存在する必要があります。
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アイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、文字列"InnerClasses"を表すCONSTANT_Utf8_info構造体(§4.4.7)である必要があります。
attribute_length項目の値は、最初の6バイトを除く属性の長さを示します。
number_of_classesアイテムの値は、classes配列のエントリ数を示します。
パッケージ・メンバーではないクラスまたはインタフェースCを表すconstant_pool表のすべてのCONSTANT_Class_infoエントリは、classes配列内の対応するエントリを1つのみ持つ必要があります。
クラスまたはインタフェースにクラスまたはインタフェースであるメンバーが含まれている場合、そのconstant_pool表(およびそのInnerClasses属性)は、そのメンバーがクラスによって特に言及されていない場合でも、そのような各メンバー(JLS§13.1)を参照する必要があります。
また、すべてのネストされたクラスおよびネストされたインタフェースのconstant_pool表は、その包含クラスを参照する必要があるため、すべてのネストされたクラスおよびネストされたインタフェースは、包含するクラスごと、およびそれ自身のネストされたクラスおよびインタフェースごとにInnerClasses情報を持ちます。
classes配列の各エントリには、次の4つの項目が含まれます。
inner_class_info_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、Cを表すCONSTANT_Class_info構造体である必要があります。
Cがクラスまたはインタフェースのメンバーでない場合(つまり、Cが最上位クラスまたはインタフェース(JLS§7.6)、ローカル・クラス(JLS§14.3)または匿名クラス(JLS§15.9.5)の場合) - outer_class_info_indexアイテムの値はゼロである必要があります。
それ以外の場合、outer_class_info_indexアイテムの値はconstant_pool表への有効な索引である必要があり、その索引のエントリは、Cがメンバーであるクラスまたはインタフェースを表すCONSTANT_Class_info構造である必要があります。 outer_class_info_index項目の値は、inner_class_info_index項目の値と等しくない必要があります。
Cが匿名(JLS§15.9.5)の場合、inner_name_indexアイテムの値はゼロである必要があります。
それ以外の場合、inner_name_index項目の値はconstant_pool表への有効な索引である必要があり、その索引のエントリは、このclassファイルのコンパイル元のソース・コードで指定されているCの元の単純名を表すCONSTANT_Utf8_info構造である必要があります。
inner_class_access_flagsアイテムの値は、このclassファイルのコンパイル元のソース・コードで宣言されたクラスまたはインタフェースCに対するアクセス権限およびプロパティを示すために使用されるフラグのマスクです。 コンパイラは、ソースコードが使用できない場合、元の情報を回復するために使用されます。 フラグは、表4.7.6-Aで指定します。
表4.7.6-A. ネストされたクラス・アクセスおよびプロパティ・フラグ
| フラグ名 | 値 | 解釈 |
|---|---|---|
ACC_PUBLIC |
0x0001 | ソースでマークまたは暗黙的にpublic。
|
ACC_PRIVATE |
0x0002 | ソースでprivateとマークされています。
|
ACC_PROTECTED |
0x0004 | ソースでprotectedとマークされています。
|
ACC_STATIC |
0x0008 | ソースでマークまたは暗黙的にstatic。
|
ACC_FINAL |
0x0010 | ソースでマークまたは暗黙的にfinal。
|
ACC_INTERFACE |
0x0200 | ソースのinterfaceでした。
|
ACC_ABSTRACT |
0x0400 | ソースでマークまたは暗黙的にabstract。
|
ACC_SYNTHETIC |
0x1000 | 統合が宣言されました。ソース・コードに存在しません。 |
ACC_ANNOTATION |
0x2000 | 注釈インタフェースとして宣言されます。 |
ACC_ENUM |
0x4000 | enumクラスとして宣言されます。
|
表4.7.6-Aに割り当てられていないinner_class_access_flagsアイテムのすべてのビットは、将来使用するために予約されています。 これらは、生成されたclassファイルでゼロに設定する必要があり、Java Virtual Machineの実装では無視する必要があります。
classファイルのバージョン番号が51.0以上で、そのattributes表にInnerClasses属性がある場合、InnerClasses属性のclasses配列内のすべてのエントリについて、inner_name_indexアイテムの値がゼロの場合、outer_class_info_indexアイテムの値はゼロである必要があります。
OracleのJava Virtual Machine実装では、属性によって参照されるクラスまたはインタフェースを表すclassファイルに対して、InnerClasses属性の一貫性をチェックしません。
EnclosingMethod属性
EnclosingMethod属性は、ClassFile構造(§4.1)のattributes表の固定長属性です。 クラスは、ローカル・クラスまたは匿名クラス(JLS§14.3、JLS§15.9.5)を表す場合にのみ、EnclosingMethod属性を持つ必要があります。
ClassFile構造のattributes表には、最大1つのEnclosingMethod属性を指定できます。
EnclosingMethod属性の形式は次のとおりです。
EnclosingMethod_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 class_index;
u2 method_index;
}
EnclosingMethod_attribute構造の項目は次のとおりです。
attribute_name_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、文字列"EnclosingMethod"を表すCONSTANT_Utf8_info構造体(§4.4.7)である必要があります。
attribute_lengthアイテムの値は4である必要があります。
class_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、現在のクラスの宣言を包含する最も内側のクラスを表すCONSTANT_Class_info構造体(§4.4.1)である必要があります。
現在のクラスがメソッドまたはコンストラクタによってすぐに囲まれていない場合、method_indexアイテムの値はゼロである必要があります。
特に、インスタンス・イニシャライザ、静的イニシャライザ、インスタンス変数イニシャライザまたはクラス変数イニシャライザによって現在のクラスがソース・コードにすぐに囲まれた場合は、method_indexをゼロにする必要があります。 (最初の2つはローカル・クラスと匿名クラスの両方に関係しますが、最後の2つはフィールド割当ての右側で宣言された匿名クラスに関係します。)
それ以外の場合、method_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、前述のclass_index属性によって参照されるクラスのメソッドの名前およびタイプを表すCONSTANT_NameAndType_info構造体(§4.4.6)である必要があります。
Javaコンパイラは、method_indexを介して識別されるメソッドが、このEnclosingMethod属性を含むクラスの最も字句的に囲まれたメソッドであることを確認する必要があります。
Synthetic属性
Synthetic属性は、ClassFile、field_infoまたはmethod_info構造(§4.1、§4.5、§4.6)のattributes表の固定長属性です。 ソース・コードに含まれないクラス・メンバーは、Synthetic属性を使用してマークするか、またはACC_SYNTHETICフラグが設定されている必要があります。 この要件に対する唯一の例外は、実装アーティファクトとはみなされないコンパイラ生成メンバーです。次に例を示します。
Synthetic属性は、ネストされたクラスおよびインタフェースをサポートするためにJDK 1.1で導入されました。
これは、仮パラメータおよびモジュールのみにACC_MANDATED (§4.7.24、§4.7.25)のフラグを付けることができるclassファイル形式の制限であり、コンパイラが生成されても実装アーティファクトとはみなされないことを示します。 コンパイラが生成した他の構造体にも、それらが実装アーティファクトとみなされないようにフラグを付ける方法はありません(JLS§13.1)。 この制限は、Java SEプラットフォームのリフレクティブAPIが、このような構成の「必須」ステータスを正確に示していない可能性があることを意味します。
Synthetic属性の形式は次のとおりです。
Synthetic_attribute {
u2 attribute_name_index;
u4 attribute_length;
}
Synthetic_attribute構造の項目は次のとおりです。
attribute_name_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、文字列"Synthetic"を表すCONSTANT_Utf8_info構造体(§4.4.7)である必要があります。
attribute_lengthアイテムの値はゼロである必要があります。
Signature属性
Signature属性は、ClassFile、field_info、method_infoまたはrecord_component_info構造(§4.1、§4.5、§4.6、§4.7.30)のattributes表の固定長属性です。 Signature属性は、Javaプログラミング言語での宣言で型変数またはパラメータ化された型を使用するクラス、インタフェース、コンストラクタ、メソッド、フィールドまたはレコード・コンポーネントのシグネチャ(§4.7.9.1)を格納します。 これらのコンストラクトの詳細は、Java言語仕様、Java SE 26Editionを参照してください。
ClassFile、field_info、method_infoまたはrecord_component_info構造のattributes表には、最大1つのSignature属性を指定できます。
Signature属性の形式は次のとおりです。
Signature_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 signature_index;
}
Signature_attribute構造の項目は次のとおりです。
attribute_name_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、文字列"Signature"を表すCONSTANT_Utf8_info構造体(§4.4.7)である必要があります。
attribute_lengthアイテムの値は2である必要があります。
signature_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 このSignature属性がClassFile構造の属性である場合はクラス・シグネチャを表すCONSTANT_Utf8_info構造体(§4.4.7)、このSignature属性がmethod_info構造の属性である場合はメソッド・シグネチャ、そうでない場合はフィールド・シグネチャである必要があります。
OracleのJava Virtual Machine実装では、クラスのロード中またはリンク中にSignature属性の整形式がチェックされません。 かわりに、Signature属性は、クラス、インタフェース、コンストラクタ、メソッドおよびフィールドの汎用シグネチャを公開するJava SE Platformクラス・ライブラリのメソッドによってチェックされます。 例として、ClassにgetGenericSuperclass、java.lang.reflect.ExecutableにtoGenericStringがあります。
シグネチャは、Java Virtual Machineの型システム外の型を使用するJavaプログラミング言語で記述された宣言をエンコードします。 リフレクションとデバッグ、およびclassファイルのみが使用可能な場合のコンパイルをサポートします。
Javaコンパイラは、宣言で型変数またはパラメータ化された型を使用するクラス、インタフェース、コンストラクタ、メソッド、フィールドまたはレコード・コンポーネントのシグネチャを発行する必要があります。 具体的には、Javaコンパイラは次の内容を取る必要があります:
クラスまたはインタフェースの宣言のためのクラス・シグネチャ。汎用か、またはパラメータ化された型をスーパー・クラスまたはスーパー・インタフェース(あるいはその両方)として持ちます。
汎用型であるか、戻り型または仮パラメータ型として型変数またはパラメータ化された型を持つか、throws句またはその組合せに型変数を持つメソッドまたはコンストラクタ宣言のメソッド・シグネチャ。
メソッドまたはコンストラクタ宣言のthrows句に型変数が含まれていない場合、コンパイラは、メソッド・シグネチャを発行するために、宣言にthrows句がないものとして扱うことができます。
型変数またはパラメータ化された型を使用する任意のフィールド、仮パラメータ、ローカル変数またはレコード・コンポーネント宣言のフィールド署名。
署名は、§4.3.1の表記に従った文法を使用して指定されます。 この表記法に加えて、次のことが必要です:
本番環境の右側の構文 [x]は、ゼロまたは1回の xを表します。 つまり、xはオプションの記号です。 オプションの記号を含む代替方法によって、実際には2つの選択肢が定義されます: オプションの記号とそれを含む記号を省略したもの。
右側が非常に長い場合、2番目の行を明確にインデントすることで、2番目の行で継続できます。
文法には、Javaコンパイラによって生成される型、フィールド、メソッド、仮パラメータ、ローカル変数または型変数の名前を示すターミナル記号識別子が含まれています。 このような名前には、ASCII文字. ; [ / < > : (つまり、メソッド名で禁止されている文字(§4.2.2)およびコロン)を含めることはできませんが、Javaプログラミング言語(JLS§3.8)の識別子では指定できない文字を含めることができます。
署名は、型署名と呼ばれる非端末の階層に依存します。
Java型のシグネチャは、Javaプログラミング言語の参照型またはプリミティブ型のどちらかを表します。
ここでは、便宜上、§4.3.2の次の本番環境が繰り返されています。
B C D F I J S Z
参照型シグネチャは、Javaプログラミング言語の参照型、つまりクラスまたはインタフェース型、型変数または配列型を表します。
クラス・タイプ・シグネチャは、(パラメータ化された)クラスまたはインタフェース・タイプを表します。 クラス・タイプ・シグネチャは、任意の型引数を消去し、各.文字を$文字に変換することで、内部形式(§4.2.1)で示すクラスのバイナリ名に確実にマップできるように構成する必要があります。
型変数シグネチャは、型変数を表します。
配列型のシグネチャは、配列型の1つのディメンションを表します。
/ {PackageSpecifier}
< TypeArgument {TypeArgument} >
+ -T識別子;
クラス・シグネチャは、(汎用的な)クラスまたはインタフェース宣言の型情報をエンコードします。 クラスまたはインタフェースの任意の型パラメータについて説明し、その(パラメータ化された)直接スーパークラスおよび直接スーパーインタフェース(ある場合)をリストします。 型パラメータの名前が、その後ろにクラスのバウンドとインタフェースの境界が続きます。
< TypeParameter {TypeParameter} >
メソッド・シグネチャは、(汎用的な)メソッド宣言の型情報をエンコードします。 メソッドの任意の型パラメータ、仮パラメータの(パラメータ化される可能性がある)型、戻り値の型(ある場合)、およびメソッドのthrows句で宣言された例外の型を記述します。
( {JavaTypeSignature} ) Result {ThrowsSignature}
ここでは、便宜上、§4.3.3の次の本番環境が繰り返されています。
V
Signature属性によってエンコードされたメソッド・シグネチャは、method_info構造体(§4.3.3)のメソッド・ディスクリプタと正確には一致しない場合があります。 特に、メソッド・シグネチャ内の仮パラメータ・タイプの数が、メソッド記述子内のパラメータ記述子の数と同じであるという保証はありません。 数値はほとんどのメソッドで同じですが、Javaプログラミング言語の特定のコンストラクタには、コンパイラがパラメータ記述子を持ち、メソッドのシグネチャからは省略できる、暗黙的に宣言されたパラメータがあります。 パラメータ注釈に関する同様の状況については、§4.7.18のノートを参照してください。
フィールド・シグネチャは、フィールド、仮パラメータ、ローカル変数またはレコード・コンポーネント宣言の(パラメータ化された)型をエンコードします。
SourceFile属性
SourceFile属性は、ClassFile構造(§4.1)のattributes表にあるオプションの固定長属性です。
ClassFile構造のattributes表には、最大1つのSourceFile属性を指定できます。
SourceFile属性の形式は次のとおりです。
SourceFile_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 sourcefile_index;
}
SourceFile_attribute構造の項目は次のとおりです。
attribute_name_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、文字列"SourceFile"を表すCONSTANT_Utf8_info構造体(§4.4.7)である必要があります。
attribute_lengthアイテムの値は2である必要があります。
sourcefile_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、文字列を表すCONSTANT_Utf8_info構造体である必要があります。
sourcefile_index項目によって参照される文字列は、このclassファイルのコンパイル元のソース・ファイルの名前を示すものとして解釈されます。 ファイルを含むディレクトリの名前またはファイルの絶対パス名を示すとは解釈されません。このようなプラットフォーム固有の追加情報は、ファイル名が実際に使用されるときにランタイム・インタプリタまたは開発ツールによって指定される必要があります。
SourceDebugExtension属性
SourceDebugExtension属性は、ClassFile構造(§4.1)のattributes表のオプション属性です。
ClassFile構造のattributes表には、最大1つのSourceDebugExtension属性を指定できます。
SourceDebugExtension属性の形式は次のとおりです。
SourceDebugExtension_attribute {
u2 attribute_name_index;
u4 attribute_length;
u1 debug_extension[attribute_length];
}
SourceDebugExtension_attribute構造の項目は次のとおりです。
attribute_name_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、文字列"SourceDebugExtension"を表すCONSTANT_Utf8_info構造体(§4.4.7)である必要があります。
attribute_length項目の値は、最初の6バイトを除く属性の長さを示します。
debug_extension配列には、Java Virtual Machineに意味的な影響を与えない拡張デバッグ情報が保持されます。 この情報は、ゼロバイトを終了せずに、変更されたUTF-8文字列(§4.4.7)を使用して表されます。
debug_extension配列は、クラスStringのインスタンスで表すことができる文字列よりも長い文字列を示す場合があります。
LineNumberTable属性
LineNumberTable属性は、Code属性(§4.7.3)のattributes表にあるオプションの可変長属性です。 デバッガは、code配列のどの部分が元のソース・ファイル内の特定の行番号に対応しているかを判断するために使用できます。
Code属性のattributes表に複数のLineNumberTable属性が存在する場合は、任意の順序で指定できます。
Code属性のattributes表には、ソース・ファイルの1行当たり複数のLineNumberTable属性が存在する場合があります。 つまり、LineNumberTable属性は、ソース・ファイルの特定の行を表す場合があり、ソース行を含む1対1である必要はありません。
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アイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、文字列"LineNumberTable"を表すCONSTANT_Utf8_info構造体(§4.4.7)である必要があります。
attribute_length項目の値は、最初の6バイトを除く属性の長さを示します。
line_number_table_lengthアイテムの値は、line_number_table配列のエントリ数を示します。
line_number_table配列の各エントリは、元のソース・ファイルの行番号がcode配列の特定のポイントで変化することを示します。 各line_number_tableエントリには、次の2つの項目が含まれている必要があります。
start_pcアイテムの値は、このCode属性のcode配列への有効な索引である必要があります。 この項目は、元のソース・ファイル内の新しい行のコードが開始されるcode配列への索引を示します。
line_numberアイテムの値は、元のソース・ファイル内の対応する行番号を示します。
LocalVariableTable属性
LocalVariableTable属性は、Code属性(§4.7.3)のattributes表にあるオプションの可変長属性です。 メソッドの実行中に、特定のローカル変数の値を決定するためにデバッガが使用できます。
Code属性のattributes表に複数のLocalVariableTable属性が存在する場合は、任意の順序で指定できます。
Code属性のattributes表には、ローカル変数ごとのLocalVariableTable属性が複数存在することはできません。
LocalVariableTable属性の形式は次のとおりです。
LocalVariableTable_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 local_variable_table_length;
{ u2 start_pc;
u2 length;
u2 name_index;
u2 descriptor_index;
u2 index;
} local_variable_table[local_variable_table_length];
}
LocalVariableTable_attribute構造の項目は次のとおりです。
attribute_name_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、文字列"LocalVariableTable"を表すCONSTANT_Utf8_info構造体(§4.4.7)である必要があります。
attribute_length項目の値は、最初の6バイトを除く属性の長さを示します。
local_variable_table_lengthアイテムの値は、local_variable_table配列のエントリ数を示します。
local_variable_table配列の各エントリは、ローカル変数に値を持つcode配列オフセットの範囲を示し、そのローカル変数が見つかる現在のフレームのローカル変数配列への索引を示します。 各エントリには、次の5つの項目が含まれている必要があります。
start_pcアイテムの値は、このCode属性のcode配列への有効な索引である必要があり、命令のopcodeのインデックスである必要があります。
start_pc + lengthの値は、このCode属性のcode配列への有効な索引で、命令のopcodeの索引であるか、またはcode配列の末尾から最初の索引である必要があります。
start_pcおよびlength項目は、指定されたローカル変数が、間隔(start_pc、start_pc + length)のcode配列(つまり、start_pcを含む値とstart_pc + lengthを排他的に含む)内のインデックスにあることを示します。
name_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリには、ローカル変数を示す有効な非修飾名を表すCONSTANT_Utf8_info構造体が含まれている必要があります(§4.2.2)。
descriptor_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリには、ソース・プログラム内のローカル変数の型をエンコードするフィールド記述子を表すCONSTANT_Utf8_info構造体が含まれている必要があります(§4.3.2)。
indexアイテムの値は、現在のフレームのローカル変数配列への有効な索引である必要があります。 指定されたローカル変数は、現在のフレームのローカル変数配列のindexにあります。
指定されたローカル変数がdoubleまたはlong型の場合、indexとindex + 1の両方を占有します。
LocalVariableTypeTable属性
LocalVariableTypeTable属性は、Code属性(§4.7.3)のattributes表にあるオプションの可変長属性です。 メソッドの実行中に、特定のローカル変数の値を決定するためにデバッガが使用できます。
特定のCode属性のattributes表に複数のLocalVariableTypeTable属性が存在する場合、それらは任意の順序で指定できます。
Code属性のattributes表には、ローカル変数ごとのLocalVariableTypeTable属性が複数存在することはできません。
LocalVariableTypeTable属性は、記述子情報ではなく署名情報を提供する点で、LocalVariableTable属性(§4.7.13)とは異なります。 この違いは、型が型変数またはパラメータ化された型を使用する変数にのみ有効です。 このような変数は両方の表に表示されますが、他の型の変数はLocalVariableTableにのみ表示されます。
LocalVariableTypeTable属性の形式は次のとおりです。
LocalVariableTypeTable_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 local_variable_type_table_length;
{ u2 start_pc;
u2 length;
u2 name_index;
u2 signature_index;
u2 index;
} local_variable_type_table[local_variable_type_table_length];
}
LocalVariableTypeTable_attribute構造の項目は次のとおりです。
attribute_name_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、文字列"LocalVariableTypeTable"を表すCONSTANT_Utf8_info構造体(§4.4.7)である必要があります。
attribute_length項目の値は、最初の6バイトを除く属性の長さを示します。
local_variable_type_table_lengthアイテムの値は、local_variable_type_table配列のエントリ数を示します。
local_variable_type_table配列の各エントリは、ローカル変数に値を持つcode配列オフセットの範囲を示し、そのローカル変数が見つかる現在のフレームのローカル変数配列への索引を示します。 各エントリには、次の5つの項目が含まれている必要があります。
start_pcアイテムの値は、このCode属性のcode配列への有効な索引である必要があり、命令のopcodeのインデックスである必要があります。
start_pc + lengthの値は、このCode属性のcode配列への有効な索引で、命令のopcodeの索引であるか、またはcode配列の末尾から最初の索引である必要があります。
start_pcおよびlength項目は、指定されたローカル変数が、間隔(start_pc、start_pc + length)のcode配列(つまり、start_pcを含む値とstart_pc + lengthを排他的に含む)内のインデックスにあることを示します。
name_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリには、ローカル変数を示す有効な非修飾名を表すCONSTANT_Utf8_info構造体が含まれている必要があります(§4.2.2)。
signature_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリには、ソース・プログラム内のローカル変数の型をエンコードするフィールド・シグネチャを表すCONSTANT_Utf8_info構造が含まれている必要があります(§4.7.9.1)。
indexアイテムの値は、現在のフレームのローカル変数配列への有効な索引である必要があります。 指定されたローカル変数は、現在のフレームのローカル変数配列のindexにあります。
指定されたローカル変数がdoubleまたはlong型の場合、indexとindex + 1の両方を占有します。
Deprecated属性
Deprecated属性は、ClassFile、field_infoまたはmethod_info構造(§4.1、§4.5、§4.6)のattributes表のオプションの固定長属性です。 クラス、インタフェース、メソッドまたはフィールドは、クラス、インタフェース、メソッドまたはフィールドが置き換えられたことを示すために、Deprecated属性を使用してマークできます。
コンパイラなどのclassファイル形式を読み取るランタイム・インタプリタまたはツールでは、このマーキングを使用して、置換されたクラス、インタフェース、メソッドまたはフィールドが参照されていることをユーザーに通知できます。 Deprecated属性が存在しても、クラスまたはインタフェースのセマンティクスは変更されません。
Deprecated属性の形式は次のとおりです。
Deprecated_attribute {
u2 attribute_name_index;
u4 attribute_length;
}
Deprecated_attribute構造の項目は次のとおりです。
attribute_name_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、文字列"Deprecated"を表すCONSTANT_Utf8_info構造体(§4.4.7)である必要があります。
attribute_lengthアイテムの値はゼロである必要があります。
RuntimeVisibleAnnotations属性
RuntimeVisibleAnnotations属性は、ClassFile、field_info、method_infoまたはrecord_component_info構造(§4.1、§4.5、§4.6、§4.7.30)のattributes表の可変長属性です。 RuntimeVisibleAnnotations属性は、対応するクラス、フィールド、メソッドまたはレコード・コンポーネントの宣言に、実行時可視注釈を格納します。
ClassFile、field_info、method_infoまたはrecord_component_info構造のattributes表には、最大1つのRuntimeVisibleAnnotations属性を指定できます。
RuntimeVisibleAnnotations属性の形式は次のとおりです。
RuntimeVisibleAnnotations_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 num_annotations;
annotation annotations[num_annotations];
}
RuntimeVisibleAnnotations_attribute構造の項目は次のとおりです。
attribute_name_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、文字列"RuntimeVisibleAnnotations"を表すCONSTANT_Utf8_info構造体(§4.4.7)である必要があります。
attribute_length項目の値は、最初の6バイトを除く属性の長さを示します。
num_annotationsアイテムの値は、構造によって表される実行時可視注釈の数を示します。
annotations表の各エントリは、宣言で1つの実行時可視注釈を表します。 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アイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、フィールド記述子(§4.3.2)を表すCONSTANT_Utf8_info構造体(§4.4.7)である必要があります。 フィールド記述子は、このannotation構造体によって表される注釈のタイプを示します。
num_element_value_pairsアイテムの値は、このannotation構造体によって表される注釈の要素と値のペアの数を示します。
element_value_pairs表の各値は、このannotation構造によって表される注釈内の単一の要素と値のペアを表します。 各element_value_pairsエントリには、次の2つの項目が含まれます。
element_name_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、CONSTANT_Utf8_info構造体(§4.4.7)である必要があります。 constant_poolエントリは、このelement_value_pairsエントリによって表される要素と値のペアの要素の名前を示します。
つまり、このエントリは、type_indexで指定された注釈インタフェースの要素を示します。
valueアイテムの値は、このelement_value_pairsエントリによって表される要素と値のペアの値を表します。
element_value構造
element_value構造は、要素と値のペアの値を表す識別結合です。 このファイルは次のような形式です。
element_value {
u1 tag;
union {
u2 const_value_index;
{ u2 type_name_index;
u2 const_name_index;
} enum_const_value;
u2 class_info_index;
annotation annotation_value;
{ u2 num_values;
element_value values[num_values];
} array_value;
} value;
}
tag項目では、単一のASCII文字を使用して、要素と値のペアの値の型を示します。 これにより、value共用体のどのアイテムが使用されているかが決まります。 表4.7.16.1-Aに、tagアイテムの有効な文字、各文字で示されるタイプ、および各文字のvalue和集合で使用されるアイテムを示します。 表の4番目の列は、value共用体の1つの項目の次の説明で使用されます。
表4.7.16.1-A. tag値の型としての解釈
tagアイテム
|
型 | valueアイテム
|
定数タイプ |
|---|---|---|---|
B |
byte |
const_value_index |
CONSTANT_Integer |
C |
char |
const_value_index |
CONSTANT_Integer |
D |
double |
const_value_index |
CONSTANT_Double |
F |
float |
const_value_index |
CONSTANT_Float |
I |
int |
const_value_index |
CONSTANT_Integer |
J |
long |
const_value_index |
CONSTANT_Long |
S |
short |
const_value_index |
CONSTANT_Integer |
Z |
boolean |
const_value_index |
CONSTANT_Integer |
s |
String |
const_value_index |
CONSTANT_Utf8 |
e |
列挙クラス | enum_const_value |
該当なし |
c |
Class |
class_info_index |
該当なし |
@ |
注釈インタフェース | annotation_value |
該当なし |
[ |
配列型 | array_value |
該当なし |
valueアイテムは、要素と値のペアの値を表します。 項目は共用体であり、その独自の項目は次のとおりです。
const_value_index項目は、この要素と値のペアの値として、プリミティブ型またはString型の定数を示します。
const_value_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、表4.7.16.1-Aの4番目の列で指定されているtag項目に適した型である必要があります。
enum_const_value項目は、この要素と値のペアの値として列挙定数を示します。
enum_const_value項目は、次の2つの項目で構成されます。
type_name_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、フィールド記述子(§4.3.2)を表すCONSTANT_Utf8_info構造体(§4.4.7)である必要があります。 constant_poolエントリは、このelement_value構造体(§4.2.1)によって表されるenum定数の型のバイナリ名の内部形式を示します。
const_name_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、CONSTANT_Utf8_info構造体(§4.4.7)である必要があります。 constant_poolエントリは、このelement_value構造体によって表される列挙定数の単純名を示します。
class_info_index項目は、この要素と値のペアの値としてクラス・リテラルを示します。
class_info_index項目は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、戻り記述子(§4.3.3)を表すCONSTANT_Utf8_info構造体(§4.4.7)である必要があります。 戻り記述子は、このelement_value構造体によって表されるクラス・リテラルに対応する型を指定します。 型は、次のようにクラス・リテラルに対応します。
クラス・リテラルC.classの場合、Cはクラス、インタフェースまたは配列型の名前で、対応する型はCです。 constant_poolの戻り記述子は、ClassTypeまたはArrayTypeになります。
クラス・リテラルp.classの場合、pはプリミティブ型の名前で、対応する型はpです。 constant_poolの戻り記述子は、BaseType文字になります。
クラス・リテラルvoid.classの場合、対応する型はvoidです。 constant_poolの戻り記述子はVになります。
たとえば、クラス・リテラルObject.classはObject型に対応するため、constant_poolエントリはLjava/lang/Object;ですが、クラス・リテラルint.classはint型に対応するため、constant_poolエントリはIです。
クラス・リテラルvoid.classはvoidに対応するため、constant_poolエントリはVですが、クラス・リテラルVoid.classはVoid型に対応するため、constant_poolエントリはLjava/lang/Void;です。
annotation_value項目は、ネストされた注釈をこの要素と値のペアの値として示します。
annotation_valueアイテムの値は、このelement_value構造体で表される注釈を指定するannotation構造体(§4.7.16)です。
array_value項目は、配列をこの要素と値のペアの値として示します。
array_value項目は、次の2つの項目で構成されます。
num_valuesアイテムの値は、このelement_value構造体によって表される配列内の要素の数を示します。
values表の各値は、このelement_value構造で表される配列の対応する要素を示します。
RuntimeInvisibleAnnotations属性
RuntimeInvisibleAnnotations属性は、ClassFile、field_info、method_infoまたはrecord_component_info構造(§4.1、§4.5、§4.6、§4.7.30)のattributes表の可変長属性です。 RuntimeInvisibleAnnotations属性は、対応するクラス、メソッド、フィールドまたはレコード・コンポーネントの宣言に、実行時非表示注釈を格納します。
ClassFile、field_info、method_infoまたはrecord_component_info構造のattributes表には、最大1つのRuntimeInvisibleAnnotations属性を指定できます。
RuntimeInvisibleAnnotations属性は、RuntimeVisibleAnnotations属性(§4.7.16)と似ていますが、RuntimeInvisibleAnnotations属性で表される注釈は、コマンドライン・フラグなどの実装固有のメカニズムを介してこれらの注釈を保持するようにJava Virtual Machineに指示されている場合を除き、リフレクティブAPIによって戻されることができない点が異なります。 このような指示がない場合、Java Virtual Machineはこの属性を無視します。
RuntimeInvisibleAnnotations属性の形式は次のとおりです。
RuntimeInvisibleAnnotations_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 num_annotations;
annotation annotations[num_annotations];
}
RuntimeInvisibleAnnotations_attribute構造の項目は次のとおりです。
attribute_name_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、文字列"RuntimeInvisibleAnnotations"を表すCONSTANT_Utf8_info構造体(§4.4.7)である必要があります。
attribute_length項目の値は、最初の6バイトを除く属性の長さを示します。
num_annotationsアイテムの値は、構造によって表される実行時非表示注釈の数を示します。
annotations表の各エントリは、宣言の1つの実行時非表示注釈を表します。 annotation構造は、§4.7.16で指定されています。
RuntimeVisibleParameterAnnotations属性
RuntimeVisibleParameterAnnotations属性は、method_info構造(§4.6)のattributes表の可変長属性です。 RuntimeVisibleParameterAnnotations属性は、対応するメソッドの仮パラメータの宣言に実行時可視注釈を格納します。
method_info構造のattributes表には、最大1つの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アイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、文字列"RuntimeVisibleParameterAnnotations"を表すCONSTANT_Utf8_info構造体(§4.4.7)である必要があります。
attribute_length項目の値は、最初の6バイトを除く属性の長さを示します。
num_parametersアイテムの値は、この構造で表される実行時可視パラメータ注釈の数を示します。
この数値がメソッド記述子内のパラメータ記述子の数と同じであることは保証されません。
parameter_annotations表の各エントリは、単一の仮パラメータの宣言における実行時可視注釈をすべて表します。 各parameter_annotationsエントリには、次の2つの項目が含まれます。
num_annotationsアイテムの値は、parameter_annotationsエントリに対応する仮パラメータの宣言における実行時可視注釈の数を示します。
annotations表の各エントリは、parameter_annotationsエントリに対応する仮パラメータの宣言に1つの実行時可視注釈を表します。 annotation構造は、§4.7.16で指定されています。
parameter_annotations表のi番目のエントリは、メソッド・ディスクリプタ(§4.3.3)内のi番目のパラメータ・ディスクリプタに対応できますが、必須ではありません。
たとえば、コンパイラは、ソース・コードで明示的に宣言されたパラメータを表すパラメータ記述子のみに対応するエントリを表に作成することを選択できます。 Javaプログラミング言語では、内部クラスのコンストラクタは、明示的に宣言されたパラメータ(JLS§8.8.1)の前に暗黙的に宣言されたパラメータを持つように指定されるため、classファイル内の対応する<init>メソッドには、明示的に宣言されたパラメータを表すパラメータ記述子の前に、暗黙的に宣言されたパラメータを表すパラメータ記述子があります。 最初に明示的に宣言されたパラメータにソース・コードで注釈が付けられている場合、コンパイラはparameter_annotations[0]を作成して、2番目のパラメータ記述子に対応する注釈を格納できます。
RuntimeInvisibleParameterAnnotations属性
RuntimeInvisibleParameterAnnotations属性は、method_info構造(§4.6)のattributes表の可変長属性です。 RuntimeInvisibleParameterAnnotations属性は、対応するメソッドの仮パラメータの宣言に、実行時非表示注釈を格納します。
method_info構造のattributes表には、最大1つのRuntimeInvisibleParameterAnnotations属性を指定できます。
RuntimeInvisibleParameterAnnotations属性は、RuntimeVisibleParameterAnnotations属性(§4.7.18)と似ていますが、RuntimeInvisibleParameterAnnotations属性で表される注釈は、コマンドライン・フラグなどの実装固有のメカニズムによってこれらの注釈を保持するようにJava Virtual Machineが特に指示されている場合を除き、リフレクティブAPIによって戻すことができない点が異なります。 このような指示がない場合、Java Virtual Machineはこの属性を無視します。
RuntimeInvisibleParameterAnnotations属性の形式は次のとおりです。
RuntimeInvisibleParameterAnnotations_attribute {
u2 attribute_name_index;
u4 attribute_length;
u1 num_parameters;
{ u2 num_annotations;
annotation annotations[num_annotations];
} parameter_annotations[num_parameters];
}
RuntimeInvisibleParameterAnnotations_attribute構造の項目は次のとおりです。
attribute_name_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、文字列"RuntimeInvisibleParameterAnnotations"を表すCONSTANT_Utf8_info構造体(§4.4.7)である必要があります。
attribute_length項目の値は、最初の6バイトを除く属性の長さを示します。
num_parametersアイテムの値は、この構造で表される実行時非表示パラメータ注釈の数を示します。
この数値がメソッド記述子内のパラメータ記述子の数と同じであることは保証されません。
parameter_annotations表の各エントリは、単一の仮パラメータの宣言における実行時非可視注釈をすべて表します。 各parameter_annotationsエントリには、次の2つの項目が含まれます。
num_annotationsアイテムの値は、parameter_annotationsエントリに対応する仮パラメータの宣言における実行時非表示注釈の数を示します。
annotations表の各エントリは、parameter_annotationsエントリに対応する仮パラメータの宣言に対する単一の実行時非表示注釈を表します。 annotation構造は、§4.7.16で指定されています。
parameter_annotations表のi番目のエントリは、メソッド・ディスクリプタ(§4.3.3)内のi番目のパラメータ・ディスクリプタに対応できますが、必須ではありません。
parameter_annotations[0]がメソッド記述子の最初のパラメータ記述子に対応していない例については、§4.7.18のノートを参照してください。
RuntimeVisibleTypeAnnotations属性
RuntimeVisibleTypeAnnotations属性は、ClassFile、field_info、method_infoまたはrecord_component_info構造体のattributes表、またはCode属性(§4.1、§4.5、§4.6、§4.7.30、§4.7.3)の可変長属性です。 RuntimeVisibleTypeAnnotations属性は、対応するクラス、フィールド、メソッドまたはレコード・コンポーネントの宣言で使用される型、または対応するメソッド本体の式で使用される型に対して、実行時の表示注釈を格納します。 RuntimeVisibleTypeAnnotations属性には、汎用クラス、インタフェース、メソッドおよびコンストラクタの型パラメータ宣言に対する実行時可視注釈も格納されます。
ClassFile、field_info、method_infoまたはrecord_component_info構造のattributes表、またはCode属性には、最大1つのRuntimeVisibleTypeAnnotations属性を指定できます。
attributes表にRuntimeVisibleTypeAnnotations属性が含まれるのは、attributes表の親構造または属性に対応する種類の宣言または式で型に注釈が付けられている場合のみです。
たとえば、クラス宣言のimplements句の型に対するすべての注釈は、クラスのClassFile構造のRuntimeVisibleTypeAnnotations属性に記録されます。 一方、フィールド宣言の型のすべての注釈は、フィールドのfield_info構造のRuntimeVisibleTypeAnnotations属性に記録されます。
RuntimeVisibleTypeAnnotations属性の形式は次のとおりです。
RuntimeVisibleTypeAnnotations_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 num_annotations;
type_annotation annotations[num_annotations];
}
RuntimeVisibleTypeAnnotations_attribute構造の項目は次のとおりです。
attribute_name_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、文字列"RuntimeVisibleTypeAnnotations"を表すCONSTANT_Utf8_info構造である必要があります。
attribute_length項目の値は、最初の6バイトを除く属性の長さを示します。
num_annotationsアイテムの値は、構造によって表される実行時可視型注釈の数を示します。
annotations表の各エントリは、宣言または式で使用される型に対する1つの実行時可視注釈を表します。 type_annotation構造体の形式は次のとおりです。
type_annotation {
u1 target_type;
union {
type_parameter_target;
supertype_target;
type_parameter_bound_target;
empty_target;
formal_parameter_target;
throws_target;
localvar_target;
catch_target;
offset_target;
type_argument_target;
} target_info;
type_path target_path;
u2 type_index;
u2 num_element_value_pairs;
{ u2 element_name_index;
element_value value;
} element_value_pairs[num_element_value_pairs];
}
最初の3つの項目(target_type、target_infoおよびtarget_path)は、注釈付き型の正確な位置を指定します。 最後の3つの項目(type_index、num_element_value_pairsおよびelement_value_pairs[])は、注釈の独自の型と要素と値のペアを指定します。
type_annotation構造の項目は次のとおりです。
target_typeアイテムの値は、注釈が表示されるターゲットの種類を示します。 様々な種類のターゲットは、宣言および式で型が使用されるJavaプログラミング言語の型コンテキストに対応しています(JLS§4.11)。
target_typeの有効な値は、表4.7.20-Aおよび表4.7.20-Bに示されています。 各値は1バイトのタグで、target_type項目の後に続くtarget_info共用体のどの項目がターゲットの詳細を示すかを示します。
表4.7.20-Aおよび表4.7.20-B内のターゲットの種類は、JLS §4.11内の型コンテキストに対応しています。 つまり、target_type値0x10-0x17および0x40-0x42は型コンテキスト1-11に対応し、target_type値0x43-0x4Bは型コンテキスト12-17に対応しています。
target_typeアイテムの値によって、type_annotation構造がClassFile構造、field_info構造、method_info構造またはCode属性のRuntimeVisibleTypeAnnotations属性に表示されるかどうかが決まります。 表4.7.20-Cに、各有効なtarget_type値を持つtype_annotation構造のRuntimeVisibleTypeAnnotations属性の場所を示します。
target_infoアイテムの値は、宣言または式内のどの型に注釈が付けられているかを正確に表します。
target_info和集合の項目は、§4.7.20.1で指定します。
target_pathアイテムの値は、target_infoで示される型のどの部分に注釈が付けられているかを示します。
type_path構造の形式は、§4.7.20.2で指定します。
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 | ::を使用したメソッド参照式での型識別子 |
offset_target |
| 0x47 | キャスト式の型 | type_argument_target |
| 0x48 | new式または明示的なコンストラクタ呼出し文の汎用コンストラクタの型引数 | type_argument_target |
| 0x49 | メソッド起動式の汎用メソッドの型引数 | type_argument_target |
| 0x4A | ::newを使用したメソッド参照式の汎用コンストラクタの型引数 |
type_argument_target |
| 0x4B | ::Identifierを使用した、メソッド参照式の汎用メソッドの型引数 |
type_argument_target |
表4.7.20-C. target_type値の囲み属性の場所
| 値 | ターゲットの種類 | ロケーション |
|---|---|---|
| 0x00 | 汎用クラスまたはインタフェースの型パラメータの宣言 | ClassFile |
| 0x01 | 汎用メソッドまたはコンストラクタの型パラメータ宣言 | method_info |
| 0x10 | クラスまたはインタフェース宣言のextends句、またはインタフェース宣言のimplements句での型
|
ClassFile |
| 0x11 | 汎用クラスまたはインタフェースの型パラメータ宣言の境界の型 | ClassFile |
| 0x12 | 汎用メソッドまたはコンストラクタの型パラメータ宣言の境界の型 | method_info |
| 0x13 |
フィールドまたはレコード・コンポーネント宣言内の型 |
|
| 0x14 | メソッドまたはコンストラクタの戻り型 | method_info |
| 0x15 | メソッドまたはコンストラクタのレシーバ型 | method_info |
| 0x16 | メソッド、コンストラクタ、またはラムダ式の仮パラメータ宣言の型 | method_info |
| 0x17 | メソッドまたはコンストラクタのthrows句内の型
|
method_info |
| 0x40-0x4B | ローカル変数宣言、リソース変数宣言、例外パラメータ宣言、式での型 | Code |
target_info和集合
target_info和集合の項目(最初の項目を除く)は、宣言または式内のどの型に注釈を付けるかを指定します。 最初の項目は、どの型を指定するのではなく、どの型パラメータの宣言に注釈を付けるのかを指定します。 項目は次のとおりです。
type_parameter_target項目は、汎用クラス、汎用インタフェース、汎用メソッドまたは汎用コンストラクタのi番目の型パラメータの宣言に注釈があることを示します。
type_parameter_target {
u1 type_parameter_index;
}
type_parameter_indexアイテムの値は、注釈を付ける型パラメータ宣言を指定します。 0のtype_parameter_index値は、最初の型パラメータ宣言を指定します。
supertype_target項目は、クラスまたはインタフェース宣言のextends句またはimplements句の型に注釈があることを示します。
supertype_target {
u2 supertype_index;
}
supertype_index値が65535の場合、注釈がクラス宣言のextends句のスーパークラスに表示されるように指定します。
他のsupertype_index値は、包含するClassFile構造のinterfaces配列への索引であり、クラス宣言のimplements句またはインタフェース宣言のextends句のいずれかで、そのスーパーインタフェースに注釈が表示されるように指定します。
type_parameter_bound_target項目は、汎用クラス、インタフェース、メソッドまたはコンストラクタのj番目の型パラメータ宣言のi番目の境界に注釈があることを示します。
type_parameter_bound_target {
u1 type_parameter_index;
u1 bound_index;
}
type_parameter_index項目の値は、注釈付きバインドを持つ型パラメータ宣言を指定します。 0のtype_parameter_index値は、最初の型パラメータ宣言を指定します。
bound_indexアイテムの値は、type_parameter_indexで示される型パラメータ宣言のどの境界に注釈を付けるかを指定します。 0のbound_index値は、型パラメータ宣言の最初のバインドを指定します。
type_parameter_bound_targetアイテムは、バインドに注釈が付けられているが、バインドを構成する型は記録しません。 この型は、適切なSignature属性に格納されているクラス・シグネチャまたはメソッド・シグネチャを検査することによって検出できます。
empty_target項目は、注釈がフィールド宣言の型、レコード・コンポーネント宣言の型、メソッドの戻り型、新しく構築されたオブジェクトの型、またはメソッドまたはコンストラクタの受信側型のいずれかに表示されることを示します。
empty_target {
}
これらの各場所には1つのタイプのみが存在するため、target_info共用体内に表す型ごとの情報はありません。
formal_parameter_target項目は、メソッド、コンストラクタまたはラムダ式の仮パラメータ宣言の型に注釈があることを示します。
formal_parameter_target {
u1 formal_parameter_index;
}
formal_parameter_indexアイテムの値は、注釈付き型を持つ仮パラメータ宣言を指定します。 iのformal_parameter_index値は、メソッド記述子のi番目のパラメータ記述子(§4.3.3)に対応できますが、必須ではありません。
formal_parameter_target項目は、仮パラメータの型に注釈が付けられているが、型自体は記録しないことを記録します。 0のformal_parameter_index値は、常にメソッド記述子内の最初のパラメータ記述子を示すわけではありませんが、この型はメソッド記述子を調べることによって検出できます。parameter_annotations表に関する同様の状況については、§4.7.18のノートを参照してください。
throws_target項目は、メソッドまたはコンストラクタ宣言のthrows句のi番目の型に注釈があることを示します。
throws_target {
u2 throws_type_index;
}
throws_type_indexアイテムの値は、RuntimeVisibleTypeAnnotations属性を囲むmethod_info構造のExceptions属性のexception_index_table配列への索引です。
localvar_target項目は、try-with-resources文でリソースとして宣言された変数を含む、ローカル変数宣言の型に注釈が存在することを示します。
localvar_target {
u2 table_length;
{ u2 start_pc;
u2 length;
u2 index;
} table[table_length];
}
table_lengthアイテムの値は、table配列のエントリ数を示します。 各エントリは、ローカル変数に値があるcode配列オフセットの範囲を示します。 また、ローカル変数が見つかる現在のフレームのローカル変数配列へのインデックスも示します。 各エントリには次の3つの項目が含まれます。
指定されたローカル変数には、間隔[start_pc、 start_pc + length]のcode配列への索引の値があります。つまり、start_pc (包含)とstart_pc + length (排他)の間です。
指定されたローカル変数は、現在のフレームのローカル変数配列の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アイテムの値は、RuntimeVisibleTypeAnnotations属性を囲むCode属性のexception_table配列への索引です。
例外パラメータ宣言に複数の型が存在する可能性は、try文のmulti-catch句から生じます。例外パラメータの型は型の和集合です(JLS§14.20)。 コンパイラは、通常、ユニオン内の型ごとに1つのexception_tableエントリを作成します。これにより、catch_target項目でそれらを区別できます。 これにより、型とその注釈の間の対応が保持されます。
offset_target項目は、注釈がinstanceof式またはnew式の型、またはメソッド参照式の::の前に出現することを示します。
offset_target {
u2 offset;
}
offsetアイテムの値は、instanceof式に対応するバイトコード命令、newバイトコード命令、またはnew式に対応するバイトコード命令、またはメソッド参照式に対応するバイトコード命令のcode配列オフセットを指定します。
type_argument_target項目は、キャスト式のi番目の型、または明示的な型引数リストのi番目の型引数(new式、明示的なコンストラクタ呼出し文、メソッド呼出し式またはメソッド参照式)のいずれかに注釈が出現することを示します。
type_argument_target {
u2 offset;
u1 type_argument_index;
}
offset項目の値は、キャスト式に対応するバイトコード命令、new式に対応するnewバイトコード命令、明示的なコンストラクタ呼出し文に対応するバイトコード命令、メソッド呼出し式に対応するバイトコード命令、またはメソッド参照式に対応するバイトコード命令のいずれかのcode配列オフセットを指定します。
キャスト式の場合、type_argument_indexアイテムの値によって、キャスト演算子のどの型に注釈を付けるかが指定されます。 0のtype_argument_index値は、キャスト演算子の最初の(または唯一の)型を指定します。
キャスト式に複数の型がある可能性は、キャストから交差型に生じます。
明示的な型引数リストの場合、type_argument_indexアイテムの値によって、注釈を付ける型引数が指定されます。 0のtype_argument_index値は、最初の型の引数を指定します。
type_path構造
型が宣言または式で使用される場合、type_path構造体は、型のどの部分に注釈を付けるかを識別します。 型自体に注釈が表示される場合がありますが、型が参照型の場合、注釈が表示される場所が他にもあります。
配列型T[]が宣言または式で使用されている場合、要素型を含む配列型の任意のコンポーネント型に注釈を指定できます。
ネストされた型T1.T2が宣言または式で使用されている場合、注釈は最も内側のメンバー型の名前と、型注釈が許容される包含型(JLS§9.7.4)に指定できます。
パラメータ化された型T<A>またはT<?がA>またはT<? super A>を宣言または式で使用する場合、注釈は任意の型引数または任意のワイルドカード型引数の境界上に存在する可能性があります。
たとえば、次のような注釈が付けられているString[][]の様々な部分について考えてみます。
@Foo String[][] // Annotates the class type String String @Foo [][] // Annotates the array type String[][] String[] @Foo [] // Annotates the array type String[]
または、次の注釈が付けられているネストされた型Outer.Middle.Innerの異なる部分:
@Foo Outer.Middle.Inner Outer.@Foo Middle.Inner Outer.Middle.@Foo Inner
または、次の注釈が付けられているMap<String,Object>およびList<...>のパラメータ化された型の様々な部分:
@Foo Map<String,Object> Map<@Foo String,Object> Map<String,@Foo Object> List<@Foo ? extends String> List<? extends @Foo String>
type_path構造体の形式は次のとおりです。
type_path {
u1 path_length;
{ u1 type_path_kind;
u1 type_argument_index;
} path[path_length];
}
path_lengthアイテムの値は、path配列内のエントリ数を示します。
path_lengthの値が0で、注釈付けされる型がネストされた型である場合、注釈は型注釈が認められる型の最も外側の部分に適用されます。
path_lengthの値が0で、注釈付けされる型がネストされた型でない場合、注釈は型自体に直接表示されます。
path_lengthの値がゼロ以外の場合、path配列の各エントリは、配列型、ネストされた型またはパラメータ化された型の注釈の正確な位置に向けた、反復的な左から右へのステップを表します。 (配列タイプでは、反復処理によって配列タイプ自体、そのコンポーネント・タイプ、そのコンポーネント・タイプのコンポーネント・タイプなどが、要素タイプに到達するまで参照されます。) 各エントリには、次の2つの項目が含まれます。
type_path_kindアイテムの有効な値を、表4.7.20.2-Aにリストします。
表4.7.20.2-A. type_path_kind値の解釈
| 値 | 解釈 |
|---|---|
0 |
配列型での注釈の深さ |
1 |
注釈はネストされた型でより深い |
2 |
注釈は、パラメータ化された型のワイルドカード型引数の境界上にあります。 |
3 |
注釈は、パラメータ化された型の型引数にあります。 |
type_path_kindアイテムの値が0、1または2の場合、type_argument_indexアイテムの値は0です。
type_path_kindアイテムの値が3の場合、type_argument_indexアイテムの値によって、注釈が付けられるパラメータ化された型の型引数が指定されます。ここで、0はパラメータ化された型の最初の型引数を示します。
表4.7.20.2-B. @A Map<@B ? extends @C String, @D List<@E Object>>のtype_path構造
| 注釈 | path_length |
path |
|---|---|---|
@A |
0 |
[] |
@B |
1 |
[{type_path_kind: 3; type_argument_index: 0}] |
@C |
2 |
[{type_path_kind: 3; type_argument_index: 0}, {type_path_kind: 2; type_argument_index: 0}] |
@D |
1 |
[{type_path_kind: 3; type_argument_index: 1}] |
@E |
2 |
[{type_path_kind: 3; type_argument_index: 1}, {type_path_kind: 3; type_argument_index: 0}] |
表4.7.20.2-C. @I String @F [] @G [] @H []のtype_path構造
| 注釈 | path_length |
path |
|---|---|---|
@F |
0 |
[] |
@G |
1 |
[{type_path_kind: 0; type_argument_index: 0}] |
@H |
2 |
[{type_path_kind: 0; type_argument_index: 0}, {type_path_kind: 0; type_argument_index: 0}] |
@I |
3 |
[{type_path_kind: 0; type_argument_index: 0}, {type_path_kind: 0; type_argument_index: 0}, {type_path_kind: 0; type_argument_index: 0}] |
表4.7.20.2-D. @A List<@B Comparable<@F Object @C [] @D [] @E []>>のtype_path構造
| 注釈 | path_length |
path |
|---|---|---|
@A |
0 |
[] |
@B |
1 |
[{type_path_kind: 3; type_argument_index: 0}] |
@C |
2 |
[{type_path_kind: 3; type_argument_index: 0}, {type_path_kind: 3; type_argument_index: 0}] |
@D |
3 |
[{type_path_kind: 3; type_argument_index: 0}, {type_path_kind: 3; type_argument_index: 0}, {type_path_kind: 0; type_argument_index: 0}] |
@E |
4 |
[{type_path_kind: 3; type_argument_index: 0}, {type_path_kind: 3; type_argument_index: 0}, {type_path_kind: 0; type_argument_index: 0}, {type_path_kind: 0; type_argument_index: 0}] |
@F |
5 |
[{type_path_kind: 3; type_argument_index: 0}, {type_path_kind: 3; type_argument_index: 0}, {type_path_kind: 0; type_argument_index: 0}, {type_path_kind: 0; type_argument_index: 0}, {type_path_kind: 0; type_argument_index: 0}] |
表4.7.20.2-E. @A Outer . @B Middle . @C Innerのtype_path構造
| 次のように想定した場合、 |
class Outer {
class Middle {
class Inner {}
}
}
|
|
|---|---|---|
| 注釈 | path_length |
path |
@A |
0 |
[] |
@B |
1 |
[{type_path_kind: 1; type_argument_index: 0}] |
@C |
2 |
[{type_path_kind: 1; type_argument_index: 0}, {type_path_kind: 1; type_argument_index: 0}] |
表4.7.20.2-F. Outer . @A MiddleStatic . @B Innerのtype_path構造
| 次のように想定した場合、 |
class Outer {
static class MiddleStatic {
class Inner {}
}
}
|
|
|---|---|---|
| 注釈 | path_length |
path |
@A |
0 |
[] |
@B |
1 |
[{type_path_kind: 1; type_argument_index: 0}] |
Outer . MiddleStatic . Inner型では、単純名Outerの型注釈は許可されません。これは、その右側の型名MiddleStaticがOuterの内部クラスを参照していないためです。
|
表4.7.20.2-G. Outer . MiddleStatic . @A InnerStaticのtype_path構造
| 次のように想定した場合、 |
class Outer {
static class MiddleStatic {
static class InnerStatic {}
}
}
|
|
|---|---|---|
| 注釈 | path_length |
path |
@A |
0 |
[] |
Outer . MiddleStatic . InnerStatic型では、単純名Outerの型注釈は許可されません。これは、その右側の型名MiddleStaticがOuterの内部クラスを参照していないためです。 同様に、単純名MiddleStaticの型注釈は、その右側の型名InnerStaticがMiddleStaticの内部クラスを参照しないため、許可されません。
|
表4.7.20.2-H. Outer . Middle<@A Foo . @B Bar> . Inner<@D String @C []>のtype_path構造
| 次のように想定した場合、 |
class Outer {
class Middle<T> {
class Inner<U> {}
}
}
|
|
|---|---|---|
| 注釈 | path_length |
path |
@A |
2 |
[{type_path_kind: 1; type_argument_index: 0}, {type_path_kind: 3; type_argument_index: 0}] |
@B |
3 |
[{type_path_kind: 1; type_argument_index: 0}, {type_path_kind: 3; type_argument_index: 0}, {type_path_kind: 1; type_argument_index: 0}] |
@C |
3 |
[{type_path_kind: 1; type_argument_index: 0}, {type_path_kind: 1; type_argument_index: 0}, {type_path_kind: 3; type_argument_index: 0}] |
@D |
4 |
[{type_path_kind: 1; type_argument_index: 0}, {type_path_kind: 1; type_argument_index: 0}, {type_path_kind: 3; type_argument_index: 0}, {type_path_kind: 0; type_argument_index: 0}] |
RuntimeInvisibleTypeAnnotations属性
RuntimeInvisibleTypeAnnotations属性は、ClassFile、field_info、method_infoまたはrecord_component_info構造体のattributes表、またはCode属性(§4.1、§4.5、§4.6、§4.7.30、§4.7.3)の可変長属性です。 RuntimeInvisibleTypeAnnotations属性は、クラス、フィールド、メソッドまたはレコード・コンポーネントの対応する宣言で使用される型、または対応するメソッド本体の式で使用される型に対して、実行時非表示注釈を格納します。 RuntimeInvisibleTypeAnnotations属性には、汎用クラス、インタフェース、メソッドおよびコンストラクタの型パラメータ宣言に対する注釈も格納されます。
ClassFile、field_info、method_infoまたはrecord_component_info構造のattributes表、またはCode属性には、最大1つの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アイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、文字列"RuntimeInvisibleTypeAnnotations"を表すCONSTANT_Utf8_info構造である必要があります。
attribute_length項目の値は、最初の6バイトを除く属性の長さを示します。
num_annotationsアイテムの値は、構造によって表される実行時非可視型注釈の数を示します。
annotations表の各エントリは、宣言または式で使用される型に対する1つの実行時非表示注釈を表します。 type_annotation構造は、§4.7.20で指定されています。
AnnotationDefault属性
AnnotationDefault属性は、特定のmethod_info構造(§4.6)のattributes表の可変長属性、つまり注釈インタフェースの要素を表す属性(JLS§9.6.1)です。 AnnotationDefault属性は、method_info構造体によって表される要素のデフォルト値(JLS§9.6.2)を記録します。
注釈インタフェースの要素を表すmethod_info構造のattributes表には、最大1つのAnnotationDefault属性を指定できます。
AnnotationDefault属性の形式は次のとおりです。
AnnotationDefault_attribute {
u2 attribute_name_index;
u4 attribute_length;
element_value default_value;
}
AnnotationDefault_attribute構造の項目は次のとおりです。
attribute_name_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、文字列"AnnotationDefault"を表すCONSTANT_Utf8_info構造体(§4.4.7)である必要があります。
attribute_length項目の値は、最初の6バイトを除く属性の長さを示します。
default_value項目は、このAnnotationDefault属性を囲むmethod_info構造体によって表される注釈インタフェース要素のデフォルト値を表します。
BootstrapMethods属性
BootstrapMethods属性は、ClassFile構造(§4.1)のattributes表の可変長属性です。 BootstrapMethods属性は、動的に計算された定数および動的に計算されたコール・サイト(§4.4.10)を生成するために使用されるブートストラップ・メソッドを記録します。
ClassFile構造のconstant_pool表に少なくとも1つのCONSTANT_Dynamic_infoまたはCONSTANT_InvokeDynamic_infoエントリがある場合は、ClassFile構造のattributes表に1つのBootstrapMethods属性のみが存在する必要があります。
ClassFile構造のattributes表には、最大1つの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アイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、文字列"BootstrapMethods"を表すCONSTANT_Utf8_info構造体(§4.4.7)である必要があります。
attribute_length項目の値は、最初の6バイトを除く属性の長さを示します。
num_bootstrap_methodsアイテムの値によって、bootstrap_methods配列のブートストラップ・メソッド指定子の数が決まります。
bootstrap_methods表の各エントリには、ブートストラップ・メソッドを指定するCONSTANT_MethodHandle_info構造体への索引と、ブートストラップ・メソッドの静的引数に対する索引のシーケンス(おそらく空)が含まれます。
各bootstrap_methodsエントリには、次の3つの項目が含まれている必要があります。
bootstrap_method_refアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、CONSTANT_MethodHandle_info構造体(§4.4.8)である必要があります。
メソッド・ハンドルは、動的に計算された定数またはコール・サイト(§5.4.3.6)の解決時に解決され、java.lang.invoke.MethodHandleでinvokeWithArgumentsを呼び出すかのように呼び出されます。 メソッド・ハンドルは、§5.4.3.6で説明されている引数の配列を受け入れることができる必要があります。受け入れられない場合、解決は失敗します。
num_bootstrap_argumentsアイテムの値は、bootstrap_arguments配列内のアイテムの数を示します。
bootstrap_arguments配列の各エントリは、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、ロード可能である必要があります(§4.4)。
MethodParameters属性
MethodParameters属性は、method_info構造(§4.6)のattributes表の可変長属性です。 MethodParameters属性は、メソッドの仮パラメータに関する情報(名前など)を記録します。
method_info構造のattributes表には、最大1つの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アイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、文字列"MethodParameters"を表すCONSTANT_Utf8_info構造体(§4.4.7)である必要があります。
attribute_length項目の値は、最初の6バイトを除く属性の長さを示します。
parameters_count項目の値は、属性の包含するmethod_info構造のdescriptor_indexによって参照されるメソッド記述子(§4.3.3)内のパラメータ記述子の数を示します。
これは、Java Virtual Machineの実装がフォーマット・チェック中に強制する必要がある制約ではありません(§4.8)。 メソッド記述子のパラメータ記述子を次のparameters配列の項目と照合するタスクは、Java SEプラットフォームのリフレクション・ライブラリによって行われます。
parameters配列の各エントリには、次の項目のペアが含まれます。
name_indexアイテムの値は、ゼロまたはconstant_pool表への有効な索引である必要があります。
name_indexアイテムの値がゼロの場合、このparameters要素は名前のない仮パラメータを示します。
name_index項目の値が0以外の場合、その索引のconstant_poolエントリは、仮パラメータ(§4.2.2)を示す有効な非修飾名を表すCONSTANT_Utf8_info構造体である必要があります。
access_flagsアイテムの値は次のとおりです。
ACC_FINAL)
仮パラメータがfinalとして宣言されたことを示します。
ACC_SYNTHETIC)
ソース・コードが記述された言語の仕様に従って、仮パラメータがソース・コードで明示的にまたは暗黙的に宣言されなかったことを示します(JLS§13.1)。 (仮パラメータは、このclassファイルを生成したコンパイラの実装アーティファクトです。)
ACC_MANDATED)ソース・コードが記述された言語の仕様に従って、仮パラメータが暗黙的にソース・コードで宣言されたことを示します(JLS§13.1)。 (仮パラメータは言語指定によって義務付けられているため、言語のすべてのコンパイラがそれを出力する必要があります。)
parameters配列のi番目のエントリは、囲んでいるメソッドの記述子のi番目のパラメータ記述子に対応します。 (メソッド記述子は255パラメータに制限されているため、parameters_count項目は1バイトです。) これは事実上、parameters配列がメソッドのすべてのパラメータの情報を格納することを意味します。 parameters配列のエントリが対応するパラメータ記述子を指定する他のスキームを想像することもできますが、MethodParameters属性は不当に複雑になります。
parameters配列のi番目のエントリは、囲んでいるメソッドのSignature属性(存在する場合)のi番目の型、または囲んでいるメソッドのパラメータ注釈のi番目の注釈に対応する場合と対応しない場合があります。
Module属性
Module属性は、ClassFile構造(§4.1)のattributes表の可変長属性です。 Module属性は、モジュールに必要なモジュール、モジュールによってエクスポートおよびオープンされるパッケージ、およびモジュールによって使用および提供されるサービスを示します。
ClassFile構造のattributes表には、最大1つのModule属性を指定できます。
Module属性の形式は次のとおりです。
Module_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 module_name_index;
u2 module_flags;
u2 module_version_index;
u2 requires_count;
{ u2 requires_index;
u2 requires_flags;
u2 requires_version_index;
} requires[requires_count];
u2 exports_count;
{ u2 exports_index;
u2 exports_flags;
u2 exports_to_count;
u2 exports_to_index[exports_to_count];
} exports[exports_count];
u2 opens_count;
{ u2 opens_index;
u2 opens_flags;
u2 opens_to_count;
u2 opens_to_index[opens_to_count];
} opens[opens_count];
u2 uses_count;
u2 uses_index[uses_count];
u2 provides_count;
{ u2 provides_index;
u2 provides_with_count;
u2 provides_with_index[provides_with_count];
} provides[provides_count];
}
Module_attribute構造の項目は次のとおりです。
attribute_name_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、文字列"Module"を表すCONSTANT_Utf8_info構造体(§4.4.7)である必要があります。
attribute_length項目の値は、最初の6バイトを除く属性の長さを示します。
module_name_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、現在のモジュールを示すCONSTANT_Module_info構造体(§4.4.11)である必要があります。
module_flagsアイテムの値は次のとおりです。
ACC_OPEN)このモジュールが開かれていることを示します。
ACC_SYNTHETIC)このモジュールが明示的または暗黙的に宣言されていないことを示します。
ACC_MANDATED)このモジュールが暗黙的に宣言されたことを示します。
module_version_indexアイテムの値は、constant_pool表へのゼロまたは有効な索引である必要があります。 項目の値がゼロの場合、現在のモジュールに関するバージョン情報は存在しません。 アイテムの値が0以外の場合、その索引のconstant_poolエントリは、現在のモジュールのバージョンを表すCONSTANT_Utf8_info構造である必要があります。
requires_count項目の値は、requires表のエントリ数を示します。
現在のモジュールがjava.baseの場合、requires_countはゼロである必要があります。
現在のモジュールがjava.baseでない場合、requires_countは少なくとも1つである必要があります。
requires表の各エントリは、現在のモジュールの依存性を指定します。 各エントリの項目は次のとおりです:
requires_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、現在のモジュールが依存するモジュールを示すCONSTANT_Module_info構造である必要があります。
requires表の1つのエントリは、requires_index項目を使用して特定の名前のモジュールを指定できます。
requires_flagsアイテムの値は次のとおりです。
ACC_TRANSITIVE)現在のモジュールに依存するモジュールが、このエントリによって示されるモジュールへの依存関係を暗黙的に宣言することを示します。
ACC_STATIC_PHASE)この依存関係は、静的フェーズ(コンパイル時)では必須であるが、動的フェーズ(実行時)ではオプションであることを示します。
ACC_SYNTHETIC)この依存関係がモジュール宣言のソースで明示的または暗黙的に宣言されていないことを示します。
ACC_MANDATED)この依存関係がモジュール宣言のソースで暗黙的に宣言されたことを示します。
requires_version_indexアイテムの値は、constant_pool表へのゼロまたは有効な索引である必要があります。 この項目の値がゼロの場合、依存関係に関するバージョン情報は存在しません。 アイテムの値がゼロ以外の場合、その索引のconstant_poolエントリは、requires_indexで指定されたモジュールのバージョンを表すCONSTANT_Utf8_info構造である必要があります。
現在のモジュールがjava.baseでないかぎり、requires表内の1つのエントリに、次のすべてが含まれている必要があります。
java.baseを示すrequires_index項目。
ACC_SYNTHETICフラグが設定されていないrequires_flagsアイテム。 (ACC_MANDATEDおよびACC_TRANSITIVEフラグを設定できます。)
classファイルのバージョン番号が54.0以上である場合、ACC_STATIC_PHASEフラグが設定されているrequires_flagsアイテムは設定されません。
exports_count項目の値は、exports表のエントリ数を示します。
exports表の各エントリは、現在のモジュールによってエクスポートされるパッケージを指定します。これにより、パッケージ内のpublic型とprotected型、およびそれらのpublicメンバーとprotectedメンバーに、現在のモジュールの外側から(場合によっては限定された一連のfriendモジュールから)アクセスできます。
各エントリの項目は次のとおりです:
exports_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、現在のモジュールによってエクスポートされたパッケージを表すCONSTANT_Package_info構造体(§4.4.12)である必要があります。
exports表の1つのエントリは、exports_index項目を使用して特定の名前のパッケージを指定できます。
exports_flagsアイテムの値は次のとおりです。
ACC_SYNTHETIC)このエクスポートがモジュール宣言のソースで明示的または暗黙的に宣言されていないことを示します。
ACC_MANDATED)このエクスポートがモジュール宣言のソースで暗黙的に宣言されたことを示します。
exports_to_countの値は、exports_to_index表のエントリ数を示します。
exports_to_countがゼロの場合、このパッケージは非修飾方式で現在のモジュールによってエクスポートされます。他のモジュールのコードは、パッケージ内のタイプおよびメンバーにアクセスできます。
exports_to_countがゼロ以外の場合、このパッケージは現在のモジュールによって修飾方式でエクスポートされます。exports_to_index表にリストされているモジュール内のコードのみがパッケージ内のタイプおよびメンバーにアクセスできます。
exports_to_index表の各エントリの値は、constant_pool表への有効な索引である必要があります。 この索引のconstant_poolエントリは、このエクスポートされたパッケージの型およびメンバーにコードがアクセスできるモジュールを示すCONSTANT_Module_info構造である必要があります。
exports表の各エントリについて、そのexports_to_index表の1つのエントリは、指定された名前のモジュールを指定することができます。
opens_count項目の値は、opens表のエントリ数を示します。
現在のモジュールが開いている場合は、opens_countをゼロにする必要があります。
opens表の各エントリは、現在のモジュールによってオープンされるパッケージを指定します。これにより、パッケージ内のすべてのタイプとそのすべてのメンバーに、Java SEプラットフォームのリフレクション・ライブラリ(場合によっては限定された一連の"friend"モジュール)を介して、現在のモジュールの外からアクセスできます。
各エントリの項目は次のとおりです:
opens_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、現在のモジュールによってオープンされたパッケージを表すCONSTANT_Package_info構造体である必要があります。
opens表の1つのエントリは、opens_index項目を使用して特定の名前のパッケージを指定できます。
opens_flagsアイテムの値は次のとおりです。
ACC_SYNTHETIC)このオープンがモジュール宣言のソースで明示的または暗黙的に宣言されていないことを示します。
ACC_MANDATED)このオープンがモジュール宣言のソースで暗黙的に宣言されたことを示します。
opens_to_countの値は、opens_to_index表のエントリ数を示します。
opens_to_countがゼロの場合、このパッケージは現在のモジュールによって不適格な方法でオープンされます。他のモジュールのコードは、パッケージ内のタイプおよびメンバーに反映してアクセスできます。
opens_to_countがゼロ以外の場合、このパッケージは現在のモジュールによって修飾方式で開かれます。opens_to_index表にリストされているモジュール内のコードのみが、パッケージ内のタイプおよびメンバーに反映してアクセスできます。
opens_to_index表の各エントリの値は、constant_pool表への有効な索引である必要があります。 この索引のconstant_poolエントリは、このオープン・パッケージの型およびメンバーにコードがアクセスできるモジュールを示すCONSTANT_Module_info構造である必要があります。
opens表の各エントリについて、そのopens_to_index表の1つのエントリは、指定された名前のモジュールを指定することができます。
uses_count項目の値は、uses_index表のエントリ数を示します。
uses_index表の各エントリの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、現在のモジュールがjava.util.ServiceLoaderを介して検出できるサービス・インタフェースを表すCONSTANT_Class_info構造体(§4.4.1)である必要があります。
uses_index表の1つのエントリは、指定された名前のサービス・インタフェースを指定できます。
provides_count項目の値は、provides表のエントリ数を示します。
provides表の各エントリは、特定のサービス・インタフェースのサービス実装を表します。
各エントリの項目は次のとおりです:
provides_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、現在のモジュールがサービス実装を提供するサービス・インタフェースを表すCONSTANT_Class_info構造である必要があります。
provides表の1つのエントリは、provides_index項目を使用して特定の名前のサービス・インタフェースを指定できます。
provides_with_countの値は、provides_with_index表のエントリ数を示します。
provides_with_countはゼロ以外である必要があります。
provides_with_index表の各エントリの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、provides_indexで指定されたサービス・インタフェースのサービス実装を表すCONSTANT_Class_info構造である必要があります。
provides表の各エントリについて、そのprovides_with_index表の1つのエントリは、指定された名前のサービス実装を指定できます。
ModulePackages属性
ModulePackages属性は、ClassFile構造(§4.1)のattributes表の可変長属性です。 ModulePackages属性は、Module属性によってエクスポートまたはオープンされるモジュールのすべてのパッケージと、Module属性に記録されるサービス実装のすべてのパッケージを示します。 ModulePackages属性は、エクスポートもオープンもサービス実装も含まない、モジュール内のパッケージを示すこともできます。
ClassFile構造のattributes表には、最大1つのModulePackages属性を指定できます。
ModulePackages属性の形式は次のとおりです。
ModulePackages_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 package_count;
u2 package_index[package_count];
}
ModulePackages_attribute構造の項目は次のとおりです。
attribute_name_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、文字列"ModulePackages"を表すCONSTANT_Utf8_info構造体(§4.4.7)である必要があります。
attribute_length項目の値は、最初の6バイトを除く属性の長さを示します。
package_count項目の値は、package_index表のエントリ数を示します。
package_index表の各エントリの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、現在のモジュール内のパッケージを表すCONSTANT_Package_info構造体(§4.4.12)である必要があります。
package_index表の1つのエントリは、指定された名前のパッケージを指定することができます。
ModuleMainClass属性
ModuleMainClass属性は、ClassFile構造(§4.1)のattributes表の固定長属性です。 ModuleMainClass属性は、モジュールのメイン・クラスを示します。
ClassFile構造のattributes表には、最大1つのModuleMainClass属性を指定できます。
ModuleMainClass属性の形式は次のとおりです。
ModuleMainClass_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 main_class_index;
}
ModuleMainClass_attribute構造の項目は次のとおりです。
attribute_name_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、文字列"ModuleMainClass"を表すCONSTANT_Utf8_info構造体(§4.4.7)である必要があります。
attribute_lengthアイテムの値は2である必要があります。
main_class_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、現在のモジュールのメイン・クラスを表すCONSTANT_Class_info構造体(§4.4.1)である必要があります。
NestHost属性
NestHost属性は、ClassFile構造のattributes表の固定長属性です。 NestHost属性は、現在のクラスまたはインタフェースが属するネスト(§5.4.4)のネスト・ホストを記録します。
ClassFile構造のattributes表には、最大1つのNestHost属性を指定できます。
NestHost属性の形式は次のとおりです。
NestHost_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 host_class_index;
}
NestHost_attribute構造の項目は次のとおりです。
attribute_name_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、文字列"NestHost"を表すCONSTANT_Utf8_info構造体(§4.4.7)である必要があります。
attribute_lengthアイテムの値は2である必要があります。
host_class_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、現在のクラスまたはインタフェースのネスト・ホストであるクラスまたはインタフェースを表すCONSTANT_Class_info構造体(§4.4.1)である必要があります。
ネスト・ホストをロードできないか、現在のクラスまたはインタフェースと同じランタイム・パッケージに含まれていない場合、または現在のクラスまたはインタフェースに対してネスト・メンバーシップを許可しない場合、アクセス制御中にエラーが発生する可能性があります(§5.4.4)。
NestMembers属性
NestMembers属性は、ClassFile構造(§4.1)のattributes表の可変長属性です。 NestMembers属性は、現在のクラスまたはインタフェース(§5.4.4)によってホストされるネストのメンバーシップを要求する権限があるクラスおよびインタフェースを記録します。
ClassFile構造のattributes表には、最大1つのNestMembers属性を指定できます。
ClassFile構造のattributes表に、NestMembers属性とNestHost属性の両方を含めることはできません。
このルールにより、ネスト・ホストが別のネストのメンバーシップを要求できなくなります。 暗黙的に、ホストする巣のメンバーです。
NestMembers属性の形式は次のとおりです。
NestMembers_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 number_of_classes;
u2 classes[number_of_classes];
}
NestMembers_attribute構造の項目は次のとおりです。
attribute_name_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、文字列"NestMembers"を表すCONSTANT_Utf8_info構造体(§4.4.7)である必要があります。
attribute_length項目の値は、最初の6バイトを除く属性の長さを示します。
number_of_classesアイテムの値は、classes配列のエントリ数を示します。
classes配列の各値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、現在のクラスまたはインタフェースによってホストされるネストのメンバーであるクラスまたはインタフェースを表すCONSTANT_Class_info構造体(§4.4.1)である必要があります。
classes配列はアクセス制御によって参照されます(§5.4.4)。 これは、同じランタイム・パッケージ内にあり、現在のクラスまたはインタフェースを参照するNestHost属性を持つ他のクラスおよびインタフェースへの参照で構成する必要があります。 これらの条件を満たさない配列項目は、アクセス制御によって無視されます。
Record属性
Record属性は、ClassFile構造(§4.1)のattributes表の可変長属性です。 Record属性は、現在のクラスがレコード・クラス(JLS§8.10)であることを示し、レコード・クラスのレコード・コンポーネントに関する情報(JLS§8.10.1)を格納します。
ClassFile構造のattributes表には、最大1つのRecord属性を指定できます。
Record属性の形式は次のとおりです。
Record_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 components_count;
record_component_info components[components_count];
}
Record_attribute構造の項目は次のとおりです。
attribute_name_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、文字列"Record"を表すCONSTANT_Utf8_info構造体(§4.4.7)である必要があります。
attribute_length項目の値は、最初の6バイトを除く属性の長さを示します。
components_count項目の値は、components表のエントリ数を示します。
components表の各エントリは、現在のクラスのレコード・コンポーネントを、レコード・コンポーネントが宣言された順序で指定します。 record_component_info構造体の形式は次のとおりです。
record_component_info {
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
}
record_component_info構造の項目は次のとおりです。
name_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、レコード・コンポーネントを示す有効な非修飾名を表すCONSTANT_Utf8_info構造体(§4.4.7)である必要があります(§4.2.2)。
descriptor_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、レコード・コンポーネントの型をエンコードするフィールド記述子を表すCONSTANT_Utf8_info構造体(§4.4.7)である必要があります(§4.3.2)。
attributes_countアイテムの値は、このレコード・コンポーネントの追加属性の数を示します。
attributes表の各値は、attribute_info構造(§4.7)である必要があります。
レコード・コンポーネントは、任意の数のオプション属性を関連付けることができます。
record_component_info構造のattributes表に表示されるように、この仕様で定義される属性を表4.7-Cにリストします。
record_component_info構造のattributes表に表示されるように定義された属性に関するルールは、§4.7に示されています。
record_component_info構造のattributes表の事前定義されていない属性に関するルールは、§4.7.1に示されています。
PermittedSubclasses属性
PermittedSubclasses属性は、ClassFile構造(§4.1)のattributes表の可変長属性です。 PermittedSubclasses属性は、現在のクラスまたはインタフェースを直接拡張または実装することを認可されているクラスおよびインタフェースを記録します(§5.3.5)。
Javaプログラミング言語では、修飾子sealedを使用して、ダイレクト・サブクラスまたはダイレクト・サブインタフェースを制限するクラスまたはインタフェースを示します。 関連する修飾子finalがACC_FINALフラグに対応するため、この修飾子がclassファイルのACC_SEALEDフラグに対応すると考えられます。 実際、sealedクラスまたはインタフェースは、PermittedSubclasses属性が存在することによって、classファイルに示されます。
access_flagsアイテムにACC_FINALフラグが設定されていないClassFile構造のattributes表には、最大1つのPermittedSubclasses属性を指定できます。
access_flagsアイテムにACC_FINALフラグが設定されているClassFile構造のattributes表には、PermittedSubclasses属性を指定しないでください。
sealedはfinalとは異なります。sealedクラスには、finalクラスにサブクラスがない、認可されたサブクラスのリストがあります。 したがって、ClassFile構造体には、PermittedSubclasses属性があるか、ACC_FINALフラグが設定されている可能性がありますが、両方が設定されているわけではありません。
PermittedSubclasses属性の形式は次のとおりです。
PermittedSubclasses_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 number_of_classes;
u2 classes[number_of_classes];
}
PermittedSubclasses_attribute構造の項目は次のとおりです。
attribute_name_indexアイテムの値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、文字列"PermittedSubclasses"を表すCONSTANT_Utf8_info構造体(§4.4.7)である必要があります。
attribute_length項目の値は、最初の6バイトを除く属性の長さを示します。
number_of_classesアイテムの値は、classes配列のエントリ数を示します。
classes配列の各値は、constant_pool表への有効な索引である必要があります。 その索引のconstant_poolエントリは、現在のクラスまたはインタフェースを直接拡張または実装することを認可されているクラスまたはインタフェースを表すCONSTANT_Class_info構造体(§4.4.1)である必要があります。
classes配列は、現在のクラスまたはインタフェース(§5.3.5)を直接拡張または実装しようとするクラスまたはインタフェースが作成されたときに参照されます。 現在のクラスまたはインタフェースを直接拡張または実装しようとしないクラスまたはインタフェースを表す配列項目は無視されます。
将来のclassファイルがJava Virtual Machine (§5.3)によってロードされると、Java Virtual Machineはまず、ファイルの基本形式がclassファイル(§4.1)であることを保証します。 このプロセスは、フォーマット・チェックと呼ばれます。 チェックは次のとおりです。
最初の4バイトには正しいマジック番号が含まれている必要があります。
すべての事前定義済属性(§4.7)は、StackMapTable、RuntimeVisibleAnnotations、RuntimeInvisibleAnnotations、RuntimeVisibleParameterAnnotations、RuntimeInvisibleParameterAnnotations、RuntimeVisibleTypeAnnotations、RuntimeInvisibleTypeAnnotationsおよびAnnotationDefaultを除き、適切な長さである必要があります。
classファイルは、切り捨てることも、最後に余分なバイトを含めることもできません。
定数プールは、§4.4を通して文書化された制約を満たす必要があります。
たとえば、定数プール内の各CONSTANT_Class_info構造は、そのname_index項目に、CONSTANT_Utf8_info構造の有効な定数プール索引が含まれている必要があります。
定数プール内のすべてのフィールド参照およびメソッド参照には、有効な名前、有効なクラスおよび有効な記述子が必要です(§4.3)。
フォーマット・チェックでは、指定されたフィールドまたはメソッドが実際に指定されたクラスに存在することや、指定された記述子が実際のクラスを参照することを保証しません。 フォーマット・チェックでは、これらのアイテムが適切に形成されていることが保証されます。 より詳細なチェックは、バイトコード自体が検証されたときと、解決時に実行されます。
classファイルの内容の解釈には、基本的なclassファイルの整合性のチェックが必要です。 形式チェックはバイトコード検証とは異なりますが、従来は両方とも整合性チェックの形式であるため、混乱していました。
メソッド、インスタンス初期化メソッド(§2.9.1)、またはクラスまたはインタフェース初期化メソッド(§2.9.2)のコードは、classファイルのmethod_info構造のCode属性のcode配列に格納されます(§4.7.3)。 この項では、Code_attribute構造の内容に関連する制約について説明します。
classファイルの静的制約は、ファイルの整形式を定義する制約です。 これらの制約は、classファイル内のコードに対する静的制約を除き、前の項で説明しています。 classファイル内のコードの静的制約では、Java Virtual Machine命令をcode配列に配置する方法と、個々の命令のオペランドをどのように配置する必要があるかを指定します。
code配列の命令に対する静的制約は次のとおりです。
code配列には、§6.5に記載されている命令のインスタンスのみが表示されます。 予約されたopcodes(§6.2)またはこの仕様に記載されていないopcodesを使用する命令のインスタンスは、code配列に現れてはなりません。
classファイルのバージョン番号が51.0以上である場合、jsr、jsr_wまたはret opcodeを使用する命令のインスタンスは、code配列に含めないでください。
code配列の最初の命令のopcodeは、索引0から始まります。
最後の命令を除くcode配列の各命令について、次の命令のopcodeのインデックスは、現在の命令のopcodeのインデックスと、その命令の長さ(すべてのオペランドを含む)と等しくなります。
wide命令は、これらの目的で他の命令と同様に扱われます。wide命令が変更する操作を指定するopcodeは、そのwide命令のオペランドの1つとして扱われます。 そのオペコードは、計算によって直接到達できないようにする必要があります。
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_icmpge、if_acmpeq、if_acmpeqが必要です。
ジャンプまたはブランチ命令のターゲットは、ワイド命令によって変更される操作を指定するために使用されるopcodeであってはなりません。ジャンプまたはブランチ・ターゲットは、ワイド命令自体である場合があります。
各tableswitch命令の各ターゲット(デフォルトを含む)は、このメソッド内の命令のopcodeである必要があります。
各tableswitch命令は、そのlowおよびhighジャンプ・テーブル・オペランドの値と一致するエントリの数を持ち、そのlow値はそのhigh値以下である必要があります。
tableswitch命令のターゲットは、wide命令で変更する操作を指定するために使用されるopcodeにすることはできません。tableswitchターゲットは、wide命令そのものです。
各lookupswitch命令の各ターゲット(デフォルトを含む)は、このメソッド内の命令のopcodeである必要があります。
各lookupswitch命令には、そのnpairsオペランドの値と一致する多数のmatch-offsetペアが必要です。 match-offsetペアは、符号付き一致値によって数値の順序を大きくしてソートする必要があります。
lookupswitch命令のターゲットは、wide命令で変更する操作を指定するために使用されるopcodeにすることはできません。lookupswitchターゲットは、wide命令そのものです。
各ldc命令と各ldc_w命令のオペランドは、constant_pool表への有効な索引を表す必要があります。 そのインデックスによって参照される定数プールエントリは、以下のいずれかではなく、ロード可能(§4.4)でなければなりません。
CONSTANT_LongまたはCONSTANT_Double型のエントリ。
Jの記述子(longを示す)またはD(doubleを示す)を示すCONSTANT_NameAndType_info構造を参照するCONSTANT_Dynamic型のエントリ。
各ldc2_w命令のオペランドは、constant_pool表への有効な索引を表す必要があります。 その索引によって参照される定数プール・エントリは、ロード可能で、特に次のいずれかである必要があります。
CONSTANT_LongまたはCONSTANT_Double型のエントリ。
Jの記述子(longを示す)またはD(doubleを示す)を示すCONSTANT_NameAndType_info構造を参照するCONSTANT_Dynamic型のエントリ。
後続の定数プール索引も、定数プールへの有効な索引である必要があり、その索引の定数プール・エントリは使用しないでください。
各getfield、putfield、getstaticおよびputstatic命令のオペランドは、constant_pool表への有効な索引を表す必要があります。 その索引によって参照される定数プール・エントリは、CONSTANT_Fieldref型である必要があります。
各invokevirtual命令のindexbyteオペランドは、constant_pool表への有効な索引を表す必要があります。 その索引によって参照される定数プール・エントリは、CONSTANT_Methodref型である必要があります。
各invokespecial命令およびinvokestatic命令のindexbyteオペランドは、constant_pool表への有効な索引を表す必要があります。 classファイルのバージョン番号が52.0より小さい場合、その索引によって参照される定数プール・エントリはCONSTANT_Methodref型である必要があります。classファイルのバージョン番号が52.0以上である場合、その索引によって参照される定数プール・エントリはCONSTANT_MethodrefまたはCONSTANT_InterfaceMethodref型である必要があります。
各invokeinterface命令のindexbyteオペランドは、constant_pool表への有効な索引を表す必要があります。 その索引によって参照される定数プール・エントリは、CONSTANT_InterfaceMethodref型である必要があります。
各invokeinterface命令のcountオペランドの値は、CONSTANT_InterfaceMethodref定数プール・エントリによって参照されるCONSTANT_NameAndType_info構造の記述子によって示される、インタフェース・メソッドに渡される引数を格納するために必要なローカル変数の数を反映する必要があります。
各invokeinterface命令の4番目のオペランド・バイトの値はゼロである必要があります。
各invokedynamic命令のindexbyteオペランドは、constant_pool表への有効な索引を表す必要があります。 その索引によって参照される定数プール・エントリは、CONSTANT_InvokeDynamic型である必要があります。
各invokedynamic命令の3番目と4番目のオペランド・バイトの値はゼロである必要があります。
インスタンス初期化メソッド(§2.9.1)を呼び出すことができるのは、invokespecial命令のみです。
名前が文字'<' ('\\u003c')で始まる他のメソッドは、メソッド呼出し命令によってコールできません。 特に、<clinit>という名前のクラスまたはインタフェース初期化メソッドは、Java Virtual Machine命令から明示的にコールされることはありませんが、Java Virtual Machine自体によってのみ暗黙的にコールされます。
各instanceof、checkcast、newおよびanewarray命令のオペランド、および各multianewarray命令のindexbyteオペランドは、constant_pool表への有効な索引を表す必要があります。 その索引によって参照される定数プール・エントリは、CONSTANT_Class型である必要があります。
new命令は、配列型を表すCONSTANT_Class型の定数プール・エントリを参照できません(§4.3.2)。 new命令を使用して配列を作成することはできません。
anewarray命令は、255を超えるディメンションの配列を作成するために使用できません。
multianewarray命令は、少なくともdimensionsオペランドの値と同じ数のディメンションを持つ型の配列を作成する場合にのみ使用する必要があります。 つまり、multianewarray命令は、そのindexbyteオペランドによって参照される配列型のすべてのディメンションを作成するために必要ではありませんが、配列型よりも多くのディメンションを作成しようとしないでください。
各multianewarray命令のdimensionsオペランドはゼロにできません。
各newarray命令のatypeオペランドには、T_BOOLEAN (4)、T_CHAR (5)、T_FLOAT (6)、T_DOUBLE (7)、T_BYTE (8)、T_SHORT (9)、T_INT (10)またはT_LONG (11)のいずれかの値が必要です。
各iload、fload、aload、istore、fstore、astore、astore、iincおよびret命令のindexオペランドは、max_locals - 1を超える負でない整数である必要があります。
各iload_<n>、fload_<n>、aload_<n>、istore_<n>、fstore_<n>およびastore_<n>命令の暗黙的索引は、max_locals - 1以下である必要があります。
各lload、dload、lstoreおよびdstore命令のindexオペランドは、max_locals - 2以下にする必要があります。
各lload_<n>、dload_<n>、lstore_<n>およびdstore_<n>命令の暗黙的索引は、max_locals - 2以下である必要があります。
iload、fload、aload、istore、aload、istore、fstore、astore、iinc、または ret命令を変更する各 indexbyteオペランドは、max_locals - 1以下の負でない整数を表す必要があります。
lload、dload、lstoreまたはdstore命令を変更する各wide命令のindexbyteオペランドは、max_locals - 2以下の負でない整数を表す必要があります。
code配列の構造的制約は、Java Virtual Machine命令間の関係に関する制約を指定します。 構造上の制約は次のとおりです。
各命令は、呼び出しにつながる実行パスに関係なく、オペランドスタックおよびローカル変数配列内の適切な型と引数の数でのみ実行する必要があります。
§2.3.4および§2.11.1に示されているように、Java Virtual Machineは、boolean、byte、shortおよびchar型の値をint型に暗黙的に変換し、int型の値を想定する命令がそれらに対して動作できるようにします。
複数の異なる実行パスに沿って命令を実行できる場合、その命令の実行前に、取り込まれたパスに関係なく、オペランドスタックの深さ(§2.6.2)が同じである必要があります。
実行中のどの時点でも、オペランド・スタックはmax_stack項目によって暗黙的に示される深さまで拡大できます。
実行中のどの時点でも、オペランドスタックからそれに含まれるより多くの値をポップすることはできません。
実行中は、long型またはdouble型の値を保持するローカル変数ペアの順序を逆にしたり、ペアを分割したりすることはできません。 このようなペアのローカル変数は、いずれも個別に操作できません。
値が割り当てられる前に、ローカル変数(またはlong型またはdouble型の値の場合はローカル変数のペア)にアクセスすることはできません。
各invokespecial命令には、次のいずれかの名前を指定する必要があります。
インスタンスの初期化方法(§2.9.1)
現在のクラスまたはインタフェースのメソッド
現在のクラスのスーパークラス内のメソッド
現在のクラスまたはインタフェースの直接スーパーインタフェース内のメソッド
Object内のメソッド
invokespecial命令がインスタンス初期化メソッドに名前を付ける場合、オペランド・スタックのターゲット参照は初期化されていないクラス・インスタンスである必要があります。 インスタンス初期化メソッドは、初期化済クラス・インスタンスで起動しないでください。 さらに、次もあります。
オペランド・スタックのターゲット参照が現在のクラスの初期化されていないクラス・インスタンスである場合、invokespecialは、現在のクラスまたはその直接スーパークラスのインスタンス初期化メソッドを指定する必要があります。
オペランド・スタックのターゲット参照が以前の新しい命令によって作成されたクラス・インスタンスである場合、invokespecialはそのクラス・インスタンスのインスタンス初期化メソッドに名前を付ける必要があります。
いずれの場合も、命令が例外ハンドラによってカバーされる場合、ローカル変数に格納されているターゲット参照のコピーは、例外ハンドラ・コードによって使用不可として扱われる必要があります。
invokespecial命令がインスタンス初期化メソッドではないメソッドに名前を付けた場合、オペランド・スタックのターゲット参照は、型が現在のクラス(JLS§5.2)と互換性のある代入であるクラス・インスタンスである必要があります。
invokespecialの一般的なルールは、invokespecialによって指定されたクラスまたはインタフェースが、コール元クラスまたはインタフェースの上にある必要があること、およびinvokespecialによってターゲット指定された受信側オブジェクトがコール元クラスまたはインタフェースの下にある必要があることです。 後者の句は特に重要です。クラスまたはインタフェースは、独自のオブジェクトでのみinvokespecialを実行できます。 Prologでの後者の句の実装方法の説明は、§invokespecialを参照してください。
各インスタンス初期化メソッドは、クラスObjectのコンストラクタから導出されたインスタンス初期化メソッドを除き、インスタンス・メンバーがアクセスされる前、およびコール元のインスタンス初期化メソッドが返される前に、thisの別のインスタンス初期化メソッドまたは直接スーパークラスsuperのインスタンス初期化メソッドをコールする必要があります。
ただし、現在のクラスで宣言されているthisのインスタンス・フィールドは、インスタンス初期化メソッドをコールする前に、putfieldによって割当てできます。
インスタンス・メソッドが呼び出された場合、またはインスタンス変数がアクセスされた場合は、インスタンス・メソッドまたはインスタンス変数を含むクラス・インスタンスをすでに初期化する必要があります。
jsrまたはjsr_w命令の実行時に、オペランド・スタックまたはローカル変数に初期化されていないクラス・インスタンスが存在してはいけません。
メソッド呼出し命令のターゲットであるすべてのクラス・インスタンスのタイプ(つまり、オペランド・スタック上のターゲット参照のタイプ)は、命令で指定されたクラスまたはインタフェース・タイプと互換性のある代入である必要があります。
各メソッド呼出しの引数の型は、メソッド記述子と互換性のあるメソッド呼出しである必要があります(JLS§5.3、 §4.3.3)。
各戻り命令は、そのメソッドの戻り型と一致する必要があります。
メソッドがboolean、byte、char、shortまたはintを返す場合、ireturn命令のみを使用できます。
メソッドがfloat、longまたはdoubleを戻す場合は、それぞれfreturn、lreturnまたはdreturn命令のみを使用できます。
メソッドがreference型を戻す場合、areturn命令のみが使用され、戻り値の型はメソッドの戻り記述子(§4.3.3)と互換性のある代入である必要があります。
すべてのインスタンス初期化メソッド、クラスまたはインタフェース初期化メソッド、およびvoidを返すように宣言されたメソッドでは、return命令のみを使用する必要があります。
getfield命令によってアクセスされる、またはputfield命令によって変更されるすべてのクラス・インスタンスのタイプ(つまり、オペランド・スタックのターゲット参照のタイプ)は、命令で指定されたクラス・タイプと互換性がある代入である必要があります。
putfieldまたはputstatic命令によって格納されるすべての値の型は、格納されるクラス・インスタンスまたはクラスのフィールド(§4.3.2)の記述子と互換性がある必要があります。
記述子タイプがboolean、byte、char、shortまたはintの場合、値はintである必要があります。
記述子タイプがfloat、longまたはdoubleの場合、値はそれぞれfloat、longまたはdoubleである必要があります。
ディスクリプタ・タイプがreference型の場合、値はディスクリプタ・タイプと互換性のある代入型である必要があります。
aastore命令によって配列に格納されるすべての値の型は、reference型である必要があります。
aastore命令によって格納される配列のコンポーネント・タイプもreference型である必要があります。
各athrow命令は、クラスThrowableまたはThrowableのサブクラスのインスタンスである値のみをスローする必要があります。
メソッドのCode_attribute構造のexception_table配列のcatch_type項目に記述されている各クラスは、ThrowableまたはThrowableのサブクラスである必要があります。
getfieldまたはputfieldを使用して、現在のクラスとは異なるランタイム・パッケージのメンバーであるスーパークラスで宣言されたprotectedフィールドにアクセスする場合、アクセスされるクラス・インスタンスのタイプ(つまり、オペランド・スタックのターゲット参照のタイプ)は、現在のクラスと互換性のある代入である必要があります。
invokevirtualまたはinvokespecialを使用して、現在のクラスとは異なるランタイム・パッケージのメンバーであるスーパークラスで宣言されたprotectedメソッドにアクセスする場合、アクセスされるクラス・インスタンスのタイプ(つまり、オペランド・スタックのターゲット参照のタイプ)は、現在のクラスと互換性がある割当てである必要があります。
実行がcode配列の一番下から落ちることはありません。
戻りアドレス(returnAddress型の値)はローカル変数からロードできません。
各jsrまたはjsr_w命令に続く命令は、単一のret命令によってのみ返すことができます。
戻される jsrまたは jsr_w命令は、サブルーチンがサブルーチン呼び出しチェーン内にすでに存在する場合、再帰的に呼び出せません。 (finally句内からtry-finally構文を使用すると、サブルーチンはネストできます。)
returnAddressタイプの各インスタンスは、最大で1回に戻すことができます。
ret命令が、returnAddress型の特定のインスタンスに対応するret命令の上のサブルーチン・コール・チェーンのポイントに戻った場合、そのインスタンスは戻りアドレスとして使用できません。
classファイルの検証
Javaプログラミング言語のコンパイラは、前の項のすべての静的制約および構造的制約を満たすclassファイルのみを生成する必要がありますが、Java Virtual Machineでは、ロードを要求されたファイルがそのコンパイラによって生成されたか、適切に形成されているという保証はありません。 Webブラウザなどのアプリケーションは、ソース・コードをダウンロードせず、その後コンパイルします。これらのアプリケーションは、すでにコンパイル済のclassファイルをダウンロードします。 ブラウザは、classファイルが信頼できるコンパイラによって生成されたか、Java Virtual Machineを悪用しようとした敵によって生成されたかを判断する必要があります。
コンパイル時チェックのその他の問題は、バージョン・スキューです。 ユーザーは、PurchaseStockOptionsなどのクラスを正常にコンパイルして、TradingClassのサブクラスにすることができます。 ただし、TradingClassの定義は、既存のバイナリと互換性がない方法でクラスがコンパイルされてから変更されている可能性があります。 メソッドが削除されたか、戻り値の型または修飾子が変更された可能性があります。 フィールドで型が変更されたり、インスタンス変数からクラス変数に変更されたりする場合があります。 メソッドまたは変数のアクセス修飾子がpublicからprivateに変更された可能性があります。 これらの問題については、The Java Language Specification、 Java SE 26 Editionの第13章「バイナリ互換性」を参照してください。
これらの潜在的な問題により、Java Virtual Machineは、組み込むclassファイルによって必要な制約が満たされていることをそれ自体で検証する必要があります。 Java Virtual Machineの実装では、各classファイルがリンク時に必要な制約を満たしていることを検証します(§5.4)。
リンク時検証により、ランタイム・インタプリタのパフォーマンスが向上します。 解釈された各命令の実行時に制約を検証するために実行する必要のある高価なチェックは、排除できます。 Java Virtual Machineでは、これらのチェックがすでに実行されていると想定できます。 たとえば、Java Virtual Machineはすでに次のことを認識しています。
オペランドスタックオーバーフローまたはアンダーフローはありません。
ローカル変数の使用と格納はすべて有効です。
すべてのJava Virtual Machine命令の引数は、有効な型です。
Java Virtual Machineの実装で検証に使用できる戦略は2つあります。
バージョン番号が50.0以上のclassファイルを検証するには、タイプによる検証を使用する必要があります。
バージョン番号が50.0より小さいclassファイルを検証するには、Java ME CLDCおよびJava Cardプロファイルに準拠しているものを除き、すべてのJava Virtual Machine実装でタイプ推論による検証がサポートされている必要があります。
Java ME CLDCおよびJava CardプロファイルをサポートするJava Virtual Machine実装の検証は、それぞれの仕様によって管理されます。
バージョン番号が50.0以上(§4.1)のclassファイルは、本項に記載されている型チェック・ルールを使用して検証する必要があります。
classファイルのバージョン番号が50.0の場合のみ、型チェックが失敗すると、Java Virtual Machineの実装で型推論による検証の実行が試みられる場合があります(§4.10.2)。
これは、新しい検証分野への移行を容易にするために設計された実用的な調整です。 classファイルを操作する多くのツールは、メソッドのスタック・マップ・フレームを調整する必要がある方法で、メソッドのバイトコードを変更することがあります。 ツールがスタックマップフレームに必要な調整を行わない場合、バイトコードが原則的に有効であっても、タイプチェックが失敗する可能性があります(したがって、古いタイプの推論スキームで検証されます)。 実装者がツールを適応させる時間を確保するために、Java Virtual Machineの実装は古い検証規則にまでさかのぼる可能性がありますが、限られた時間しかありません。
型チェックは失敗しても型推論が呼び出されて成功した場合、特定のパフォーマンス・ペナルティが予想されます。 そんな罰は避けられない。 また、ツール・ベンダーの出力を調整する必要があるというシグナルとして機能し、ベンダーにこれらの調整を行うための追加インセンティブを提供します。
要約すると、型推論による検証へのフェイルオーバーでは、Java SEプラットフォームへのスタック・マップ・フレームの段階的な追加(バージョン50.0のclassファイル内に存在しない場合、フェイルオーバーが許可される)と、Java SEプラットフォームからのjsrおよびjsr_w命令の段階的な削除(バージョン50.0のclassファイル内に存在する場合、フェイルオーバーが許可される)の両方がサポートされます。
Java Virtual Machine実装がバージョン50.0のクラス・ファイルで型推論による検証を実行しようとした場合は、型による検証が失敗するすべての場合に検証を実行する必要があります。
つまり、Java Virtual Machine実装は、あるケースで型推論に頼ることはできず、別のケースではそうしないということです。 型チェックで検証されないclassファイルを拒否するか、型チェックが失敗するたびに型推論ベリファイアに一貫してフェイルオーバーする必要があります。
タイプ・チェッカは、Prolog句によって指定されるタイプ・ルールを適用します。 英語のテキストはタイプ・ルールを非公式に説明するために使用され、Prolog句は正式な指定を提供します。
型チェッカには、Code属性(§4.7.3)を持つ各メソッドのスタック・マップ・フレームのリストが必要です。 スタック・マップ・フレームのリストは、Code属性のStackMapTable属性(§4.7.4)によって示されます。 目的は、スタック・マップ・フレームがメソッドの各基本ブロックの先頭に現れるようにすることです。 スタックマップフレームは、各オペランドスタックエントリの検証タイプと、各基本ブロックの先頭にある各ローカル変数の検証タイプを指定します。 タイプ・チェッカは、Code属性を持つ各メソッドのスタック・マップ・フレームを読み取り、これらのマップを使用して、Code属性内の命令の型安全性の証明を生成します。
Prolog述語methodIsTypeSafe (§4.10.1.5)は、Code属性を持つメソッドが型安全かどうかを判断します。 Code属性を持つメソッドが型安全でない場合、検証によってVerifyErrorがスローされ、クラス・ファイルの形式が正しくないことが示されます。
特定のクラスについて、Code属性を持つすべてのメソッドがmethodIsTypeSafeチェックに合格した場合、classファイルの型が正常にチェックされ、バイトコード検証が正常に完了しました。
このセクションでは、タイプ検査のプロセスを詳細に説明します。
17のプロローグ述語(「アクセッサ」)の存在を規定し、特定の期待される動作を有するが、正式な定義はこの仕様では与えられていない。 ここで使用されるPrologの用語Classは、正常にロードされたバイナリ・クラスまたはインタフェース(§5.3)を表し、Prologの用語MethodはClassのメソッドを表し、Prologの用語LoaderはClassのクラス・ローダーを表します。 この仕様では、これらのエンティティに正確な構造を義務付けていません。
クラスClassの名前ClassNameを抽出します。
Trueの場合、クラスClassはインタフェースです。
クラスClassのスーパークラスの名前SuperClassNameを抽出します。
クラスClassの直接スーパーインタフェースの名前のリストInterfaceNamesを抽出します。
クラスClassが、MemberNameおよび記述子MemberDescriptorという名前のフィールドまたはメソッドを宣言することをアサートします。 このアサーションでは、Classのスーパークラスまたはスーパーインタフェースで宣言されたメンバーは考慮されません。
クラスClassの定義クラス・ローダーLoaderを抽出します。
クラス・ローダーLoaderがブートストラップ・クラス・ローダーの場合はtrue。
クラス・ローダーInitiatingLoaderによってロードされたときに(この仕様に従って)表現がClassDefinitionであるNameという名前のクラスが存在する場合はtrueです。
メソッドMethodの名前Nameを抽出します。
メソッドMethodのアクセス・フラグAccessFlagsを抽出します。
メソッドMethodの記述子Descriptorを抽出します。
クラスMemberClassに記述子MemberDescriptorを持つMemberNameという名前のメンバーがあり、protectedである場合はtrueです。
クラスMemberClassに記述子MemberDescriptorを持つMemberNameという名前のメンバーがあり、protectedではない場合はtrueです。
フィールド記述子Descriptorを、対応する検証タイプTypeに変換します(§4.10.1.2)。
記述子タイプbyte、short、booleanおよびcharから導出された検証タイプは、配列コンポーネント・タイプとして使用されない場合、intです。
メソッド記述子Descriptorを、メソッド引数型に対応する検証型のリストArgTypeListおよび戻り型に対応する検証型ReturnTypeに変換します。
記述子タイプbyte、short、booleanおよびcharから導出された検証タイプは、配列コンポーネント・タイプとして使用されない場合、intです。 voidの戻り値は、特殊記号voidで表されます。
ClassのメソッドMethodの命令ストリームParsedCode、および最大オペランド・スタック・サイズMaxStack、ローカル変数の最大数FrameSize、例外ハンドラHandlersおよびスタック・マップStackMapを抽出します。
命令ストリームおよびスタック・マップ属性の表現は、§4.10.1.3および§4.10.1.4で指定されているとおりにする必要があります。
Trueの場合、Class1と Class2のパッケージ名は同じです。
前述のアクセッサは、loadedSuperclassesを定義するために使用されます。このアクセッサは、スーパークラスを持たないObjectに達するまで、各クラスの直接スーパークラスで繰り返してクラスのスーパークラスのリストを生成します。
loadedSuperclasses(Class, [ Superclass | Rest ]) :-
classSuperClassName(Class, SuperclassName),
classDefiningLoader(Class, L),
loadedClass(SuperclassName, L, Superclass),
loadedSuperclasses(Superclass, Rest).
loadedSuperclasses(Class, []) :-
\+ classSuperClassName(Class, _).
メソッドの本体をタイプ・チェックする場合、メソッドに関する情報にアクセスすると便利です。 このために、次のもので構成される6タプルである環境を定義します:
クラス
メソッド
メソッドの宣言された戻り型(またはvoid)
メソッドの指示
オペランドスタックの最大サイズ
a list of exception handlers
環境から情報を抽出するアクセッサを指定します。
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).
currentClassLoader(Environment, Loader) :-
Environment = environment(Class, _Method, _ReturnType,
_Instructions, _, _),
classDefiningLoader(Class, Loader).
thisClass(Environment, Class) :-
Environment = environment(Class, _Method, _ReturnType,
_Instructions, _, _).
thisType(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).
最後に、型ルール全体で使用される一般的な述語を指定します。
notMember(_, []). notMember(X, [A | More]) :- X \= A, notMember(X, More).
どのアクセッサが規定され、どのアクセッサが完全に指定されているかを判断する原則は、classファイルの表現を過剰に指定しないようにすることです。 ClassまたはMethod用語に特定のアクセッサを指定すると、classファイルを表すProlog用語の形式を完全に指定できます。
タイプ・チェッカは、次に示す検証タイプの階層に基づいてタイプ・システムを強制します。
Verification type hierarchy:
top
____________/\____________
/ \
/ \
oneWord twoWord
/ | \ / \
/ | \ / \
int float reference long double
/ \
/ \_____________
/ \
/ \
uninitialized +------------------+
/ \ | reference type |
/ \ | hierarchy |
uninitializedThis uninitialized(Offset) +------------------+
|
|
null
ほとんどの検証型は、§2.3および§2.4で説明されているプリミティブ型および参照型と直接対応しており、表4.3-Aのフィールド記述子で表されます。
プリミティブ型double、float、intおよびlong (フィールド記述子D、F、I、J)は、それぞれ同じ名前の検証タイプに対応しています。
プリミティブ型byte、char、shortおよびboolean (フィールド記述子B、C、S、Z)はすべて、検証型intに対応しています。
クラスおよびインタフェース・タイプ(Lで始まるフィールド記述子)は、ファクターclassを使用する検証タイプに対応します。 検証タイプclass(N, L)は、ローダーLによってロードされたバイナリ名がNのクラスまたはインタフェースのタイプを表します。 Lは、class(N, L)によって表されるクラスの開始ローダー(§5.3)であり、クラスの定義ローダーである場合とそうでない場合があります。
たとえば、クラス・タイプObjectはclass('java/lang/Object', L)として表され、Lによってロードされるクラスjava/lang/Objectの定義ローダーはブートストラップ・ローダーです。
配列型([で始まるフィールド記述子)は、ファクターarrayOfを使用する検証型に対応します。 プリミティブ型byte、char、shortおよびbooleanは検証型に対応していませんが、要素型がbyte、char、shortまたはbooleanの配列型は、検証型に対応しています。doesは、検証型に対応しています。このような検証型は、baload、bastore、caload、castore、saload、sastoreおよびnewarray命令をサポートしています。
検証タイプarrayOf(T)は、コンポーネント・タイプが検証タイプTである配列タイプを表します。
検証タイプarrayOf(byte)は、コンポーネント・タイプがbyteの配列タイプを表します。
検証タイプarrayOf(char)は、コンポーネント・タイプがcharの配列タイプを表します。
検証タイプarrayOf(short)は、コンポーネント・タイプがshortの配列タイプを表します。
検証タイプarrayOf(boolean)は、コンポーネント・タイプがbooleanの配列タイプを表します。
たとえば、配列型int[]およびObject[]は、それぞれ検証型arrayOf(int)およびarrayOf(class('java/lang/Object', L))で表されます。 配列型byte[]およびboolean[][]は、それぞれ検証型arrayOf(byte)およびarrayOf(arrayOf(boolean))で表されます。
残りの検証タイプは次のとおりです。
検証タイプtop、oneWord、twoWordおよびreferenceは、前述のように他の型の抽象的な共用体を示し、Prologでは原子として表されます。
検証タイプuninitialized、uninitializedThisおよびuninitialized(Offset)は、まだ初期化されていないnewで作成されたオブジェクトへの参照を記述します(§2.9.1)。 uninitialized型とuninitializedThis型は原子として表されます。 uninitialized(Offset)型は、Offsetの数値を表す引数にファンクタuninitializedを適用することで表されます。
検証タイプnullは、aconst_null命令の結果を示し、Prologで原子として表されます。
検証タイプのサブタイピング・ルールは次のとおりです。
サブタイピングは再帰的です。
isAssignable(X, X).
型topは、他のすべての型のスーパータイプです。
isAssignable(oneWord, top). isAssignable(twoWord, top).
タイプは、Xのサブタイプであるスーパータイプがある場合、他のタイプのサブタイプXです。
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(null, X) :- isAssignable(reference, X). isAssignable(uninitialized, X) :- isAssignable(reference, X). isAssignable(uninitializedThis, X) :- isAssignable(uninitialized, X). isAssignable(uninitialized(_), X) :- isAssignable(uninitialized, X).
型nullは、すべての参照型のサブタイプです。
isAssignable(null, class(_, _)). isAssignable(null, arrayOf(_)).
これらのサブタイプ・ルールは、必ずしもサブタイピングの最も明白な形式であるとはかぎりません。 参照タイプ間のサブタイピング・ルールと、残りの検証タイプのルール間に明確な分割があります。 分割により、参照型と他の検証型の間で一般的なサブタイピング関係を記述できます。 これらの関係は、型階層内の参照型の位置とは独立して保持され、Java Virtual Machine実装による過剰なクラスのロードを防ぐのに役立ちます。 たとえば、class(foo, L) <: twoWordという形式の問合せに応答して、スーパークラス階層への登山を開始しません。
また、サブタイピングがリフレクシブであることを示すルールもあるため、intからintなどの単純な型の場合と、arrayOf(int)からarrayOf(int)、またはclass('C', Loader)からclass('C', Loader)などの複雑な型の場合の2つの同一の型(両方)間で割り当てることは、常に簡単に行えます。 前述のルールは、2つの異なる参照タイプ間の場合を除き、すべてのサブタイピング関係をカバーします。
isWideningReference述語は、ある参照型から別の参照型への割当てが許可されるかどうかを判断するために使用されます。
isAssignable(From, To) :- isWideningReference(From, To).
isWideningReference述語は再帰的ではありません。拡張は、異なる参照型間の変換時にのみ発生します。
ベリファイアを使用すると、任意のクラスまたはインタフェース・タイプを任意のインタフェース・タイプに拡張できます。
isWideningReference(class(_, _), class(To, L)) :-
loadedClass(To, L, ToClass),
classIsInterface(ToClass).
このアプローチは、Javaプログラミング言語ほど厳密ではありません。参照されるオブジェクトが静的にインタフェースを実装することがわかっていないかぎり、インタフェースへの割当ては許可されません。 Java Virtual Machineでは、かわりにランタイム・チェックを使用して、インタフェース・メソッドの呼出しがインタフェースを実装するオブジェクト(invokeinterface)に対して実際に動作することを確認します。 インタフェース型の変数によって格納される参照が、実際にそのインタフェースを実装するオブジェクトを参照するという要件はありません。
2番目の型が1番目の型のスーパークラスのロードされたクラスを参照する場合、または両方が別の開始ローダーを持つ同じクラスを参照する場合、クラス型を別のクラス型に拡張できます。
isWideningReference(class(From, L1), class(To, L2)) :-
From \= To,
loadedClass(From, L1, FromClass),
loadedClass(To, L2, ToClass),
loadedSuperclasses(FromClass, Supers),
member(ToClass, Supers).
isWideningReference(class(ClassName, L1), class(ClassName, L2)) :-
L1 \= L2,
loadedClass(ClassName, L1, Class),
loadedClass(ClassName, L2, Class).
2つのclass型SとTが同じ名前で同じローダーを持つ場合、前述のルールに従ってisAssignableがすでにtrueであるため、一方から他方に「widen」する必要はありません。
配列型は、Object、Cloneableおよびjava.io.Serializableのサブタイプです。 参照型の配列間のサブタイピングは共変です。
isWideningReference(arrayOf(_), class(ClassName, L)) :-
(ClassName = 'java/lang/Object' ;
ClassName = 'java/lang/Cloneable' ;
ClassName = 'java/io/Serializable')
loadedClass(ClassName, L, LoadedClass),
classDefiningLoader(LoadedClass, BL),
isBootstrapLoader(BL).
isWideningReference(arrayOf(X), arrayOf(Y)) :-
isWideningReference(X, Y).
個々のバイトコード命令は、functorが命令の名前であり、その引数が解析されたオペランドである用語としてPrologに表されます。
たとえば、aload命令は、命令のオペランドである索引Nを含むaload(N)という用語として表されます。
命令全体は、次の形式の用語のリストとして表されます。
instruction(Offset, AnInstruction)
たとえば、instruction(21, aload(1))です。
このリストの指示の順序は、classファイルと同じである必要があります。
一部の命令には、constant_pool表のエントリを参照するオペランドがあります。 このようなエントリは、次の形式のfunctorアプリケーションとして表されます。
CONSTANT_Class_info構造体である定数プール・エントリのclass(Name, Loader)またはarrayOf(ComponentType)(§4.4.1)。
これらは、§4.10.1.2で説明されている検証タイプです。
構造体のname_indexアイテムにクラスまたはインタフェースの名前が指定されている場合、Nameはその名前で、Loaderは定数プールを含むクラスまたはインタフェースの定義ローダーです。
構造体のname_index項目に配列型が指定されている場合、ComponentTypeは配列コンポーネント型です。
CONSTANT_Fieldref_info構造体である定数プール・エントリのfield(FieldClassType, FieldName, FieldDescriptor)(§4.4.2)。
FieldClassTypeは、構造内のclass_index項目によって参照されるクラス、インタフェースまたは配列型の検証タイプです。 FieldNameおよびFieldDescriptorは、構造のname_and_type_index項目によって参照される名前およびフィールド記述子に対応します。
CONSTANT_Methodref_info構造体である定数プール・エントリのmethod(MethodClassType, MethodName, MethodDescriptor)(§4.4.2)。
MethodClassTypeは、構造体のclass_index項目によって参照されるクラス、インタフェースまたは配列型の検証タイプです。 MethodNameおよびMethodDescriptorは、構造のname_and_type_index項目によって参照される名前およびメソッド記述子に対応します。
CONSTANT_InterfaceMethodref_info構造体である定数プール・エントリのimethod(MethodClassType, MethodName, MethodDescriptor)(§4.4.2)。
MethodClassTypeは、構造体のclass_index項目によって参照されるクラス、インタフェースまたは配列型の検証タイプです。 MethodNameおよびMethodDescriptorは、構造のname_and_type_index項目によって参照される名前およびメソッド記述子に対応します。
CONSTANT_String_info構造体である定数プール・エントリのstring(Value)(§4.4.3)。
Valueは、構造のstring_index項目によって参照される文字列です。
CONSTANT_Integer_info構造体である定数プール・エントリのint(Value)(§4.4.4)。
Valueは、構造のbytes項目によって表されるint定数です。
CONSTANT_Float_info構造体である定数プール・エントリのfloat(Value)(§4.4.4)。
Valueは、構造のbytes項目によって表されるfloat定数です。
CONSTANT_Long_info構造体である定数プール・エントリのlong(Value)(§4.4.5)。
Valueは、構造のhigh_bytesおよびlow_bytes項目によって表されるlong定数です。
CONSTANT_Double_info構造体である定数プール・エントリのdouble(Value)(§4.4.5)。
Valueは、構造のhigh_bytesおよびlow_bytes項目によって表されるdouble定数です。
CONSTANT_MethodHandle_info構造体である定数プール・エントリのmethodHandle(Kind, Reference)(§4.4.8)。
Kindは、構造のreference_kind項目の値です。 Referenceは、構造のreference_index項目の値です。
CONSTANT_MethodType_info構造体である定数プール・エントリのmethodType(MethodDescriptor)(§4.4.9)。
MethodDescriptorは、構造のdescriptor_index項目によって参照されるメソッド記述子です。
CONSTANT_Dynamic_info構造体である定数プール・エントリのdconstant(ConstantName, FieldDescriptor)(§4.4.10)。
ConstantNameおよびFieldDescriptorは、構造のname_and_type_index項目によって参照される名前およびフィールド記述子に対応します。 (bootstrap_method_attr_index項目は検証には関係ありません。)
CONSTANT_InvokeDynamic_info構造体である定数プール・エントリのdmethod(CallSiteName, MethodDescriptor)(§4.4.10)。
CallSiteNameおよびMethodDescriptorは、構造のname_and_type_index項目によって参照される名前およびメソッド記述子に対応します。 (bootstrap_method_attr_index項目は検証には関係ありません。)
たとえば、オペランドがクラスBarのF型のフィールドfooを表す定数プール・エントリを参照するgetfield命令は、getfield(field(class('Bar', L), 'foo', 'F'))として表されます。ここで、Lは命令を含むクラスのクラス・ローダーです。 int定数91をロードするためのldc命令は、ldc(int(91))として表されます。
スタックマップフレームは、次の形式の用語のリストとしてPrologで表されます。
stackMap(Offset, TypeState)
説明:
Offsetは、スタック・マップ・フレームが適用されるバイトコード・オフセットを示す整数です(§4.7.4)。
このリスト内のバイトコード・オフセット順序は、classファイル内と同じである必要があります。
TypeStateは、Offsetの命令で予期される受信型の状態です。
型状態は、メソッドのオペランド・スタックおよびローカル変数から検証タイプへのマッピングです。 次の形式を使用します。
frame(Locals, OperandStack, Flags)
説明:
Localsは検証タイプのリストで、リストのi番目の要素(0ベースの索引付けを使用)はローカル変数iのタイプを表します。
サイズ2のタイプ(longおよびdouble)は、2つのローカル変数(§2.6.1)で表され、最初のローカル変数は型自体で、2番目のローカル変数はtop(§4.10.1.7)です。
OperandStackは検証タイプのリストで、リストの最初の要素がオペランド・スタックの先頭のタイプを表し、上位の下にあるスタック・エントリのタイプが適切な順序でリストに表示されます。
サイズ2のタイプ(longおよびdouble)は2つのスタック・エントリで表され、最初のエントリはtopで、2番目のエントリはタイプ自体です。
たとえば、double値、int値およびlong値を持つスタックは、double値のtopおよびdoubleエントリ、int値のintエントリ、およびlong値のtopおよびlongエントリという5つのエントリを持つスタックとして型状態で表されます。 したがって、OperandStackはリスト[top, double, int, top, long]です。
Flagsは、空または単一の要素flagThisUninitを持つリストです。
Localsのいずれかのローカル変数がuninitializedThis型の場合、Flagsは単一の要素flagThisUninitを持ち、それ以外の場合、Flagsは空のリストです。
flagThisUninitは、thisの初期化がまだ完了していない型の状態をマークするためにコンストラクタで使用されます。 このようなタイプの状態では、メソッドから戻すことは不正です。
検証タイプのサブタイピングは、タイプ状態に対してポイント単位で拡張されます。 メソッドのローカル変数配列は、構造によって長さが固定されますが(§4.10.1.5のmethodInitialStackFrameを参照)、オペランド・スタックは大きくなり縮小されるため、サブタイピングに代入性が必要なオペランド・スタックの長さを明示的にチェックする必要があります。
frameIsAssignable(frame(Locals1, StackMap1, Flags1),
frame(Locals2, StackMap2, Flags2)) :-
length(StackMap1, StackMapLength),
length(StackMap2, StackMapLength),
maplist(isAssignable, Locals1, Locals2),
maplist(isAssignable, StackMap1, StackMap2),
subset(Flags1, Flags2).
個々の命令の型規則のほとんど(§4.10.1.9)は、有効な型遷移の概念に依存します。 型遷移が有効になるのは、入力型の状態のオペランド・スタックから予想される型のリストをポップして、期待される結果型に置き換えることができる場合で、オペランド・スタックの長さが宣言された最大サイズを超えない新しい型の状態になるためです。
validTypeTransition(Environment, ExpectedTypesOnStack, ResultType,
frame(Locals, InputOperandStack, Flags),
frame(Locals, NextOperandStack, Flags)) :-
popMatchingList(InputOperandStack, ExpectedTypesOnStack,
InterimOperandStack),
pushOperandStack(InterimOperandStack, ResultType, NextOperandStack),
operandStackHasLegalLength(Environment, NextOperandStack).
スタックからタイプのリストをポップします。
popMatchingList(OperandStack, [], OperandStack).
popMatchingList(OperandStack, [P | Rest], NewOperandStack) :-
popMatchingType(OperandStack, P, TempOperandStack, _ActualType),
popMatchingList(TempOperandStack, Rest, NewOperandStack).
個々のタイプをスタックからポップします。 正確な動作は、スタックの内容によって異なります。 スタックの論理最上位が、指定されたタイプのサブタイプTypeである場合、それをポップします。 タイプが2つのスタック・エントリを占有する場合、スタックの論理最上位は実際には最上位の直下のタイプであり、スタックの最上位は使用できないタイプtopです。
popMatchingType([ActualType | OperandStack],
Type, OperandStack, ActualType) :-
sizeOf(Type, 1),
isAssignable(ActualType, Type).
popMatchingType([top, ActualType | OperandStack],
Type, OperandStack, ActualType) :-
sizeOf(Type, 2),
isAssignable(ActualType, Type).
sizeOf(X, 2) :- isAssignable(X, twoWord).
sizeOf(X, 1) :- isAssignable(X, oneWord).
sizeOf(top, 1).
論理タイプをスタックにプッシュします。 正確な動作は、型のサイズによって異なります。 プッシュされたタイプがサイズ1の場合は、スタックにプッシュするだけです。 プッシュされたタイプがサイズ2の場合は、プッシュしてからtopをプッシュします。
pushOperandStack(OperandStack, 'void', OperandStack).
pushOperandStack(OperandStack, Type, [Type | OperandStack]) :-
sizeOf(Type, 1).
pushOperandStack(OperandStack, Type, [top, Type | OperandStack]) :-
sizeOf(Type, 2).
オペランド・スタックの長さは、宣言された最大サイズを超えないようにしてください。
operandStackHasLegalLength(Environment, OperandStack) :-
length(OperandStack, Length),
maxOperandStackLength(Environment, MaxStack),
Length =< MaxStack.
dup命令は、受信型状態のオペランド・スタックから予期される型をポップして、事前定義された結果型に置き換え、新しい型状態になります。 ただし、これらの命令は、サブタイピング関係によって型を照合する必要がないため、型遷移に関して定義されません。 代わりに、dup命令は、スタック上の型の category (§2.11.1)の観点からオペランドスタックを完全に操作します。
カテゴリ1のタイプは、単一のスタック・エントリを占有します。 スタックの最上部がTypeで、Typeがtopでない場合、カテゴリ1 (Type)の論理型をスタックから切り離すことができます(そうしないと、カテゴリ2型の上半分を示す可能性があります)。 結果は受信スタックで、最上位エントリがポップオフになります。
popCategory1([Type | Rest], Type, Rest) :-
Type \= top,
sizeOf(Type, 1).
カテゴリ2タイプは2つのスタックエントリを占有します。 スタックの最上部がtop型で、その直下にあるエントリがTypeの場合、カテゴリ2の論理型Typeをスタックからポップできます。 結果は受信スタックで、上位2つのエントリがポップオフになります。
popCategory2([top, Type | Rest], Type, Rest) :-
sizeOf(Type, 2).
dup命令は、有効な型遷移のために型がプッシュされるときと基本的に同じ方法で、型のリストをスタックにプッシュします。
canSafelyPush(Environment, InputOperandStack, Type, OutputOperandStack) :-
pushOperandStack(InputOperandStack, Type, OutputOperandStack),
operandStackHasLegalLength(Environment, OutputOperandStack).
canSafelyPushList(Environment, InputOperandStack, Types,
OutputOperandStack) :-
canPushList(InputOperandStack, Types, OutputOperandStack),
operandStackHasLegalLength(Environment, OutputOperandStack).
canPushList(InputOperandStack, [], InputOperandStack).
canPushList(InputOperandStack, [Type | Rest], OutputOperandStack) :-
pushOperandStack(InputOperandStack, Type, InterimOperandStack),
canPushList(InterimOperandStack, Rest, OutputOperandStack).
個々の命令の型規則の多くは、次の句を使用して、型のリストを簡単にスタックからポップします。
canPop(frame(Locals, OperandStack, Flags), Types,
frame(Locals, PoppedOperandStack, Flags)) :-
popMatchingList(OperandStack, Types, PoppedOperandStack).
最後に、特定の配列命令(§aaload、§arraylength、§baload、§bastore)は、配列型であることをチェックするために、オペランド・スタックの型を照合します。 次の句は、型状態からオペランド・スタックのi番目の要素にアクセスします。
nth1OperandStackIs(i, frame(_Locals, OperandStack, _Flags), Element) :- nth1(i, OperandStack, Element).
Code属性を持つメソッドは、コードとスタック・マップ・フレームを1つのストリームにマージでき、各スタック・マップ・フレームが対応する命令の前にあり、マージされたストリームのタイプが正しい場合に、型が安全です。 メソッドの例外ハンドラ(存在する場合)も合法である必要があります。
methodIsTypeSafe(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).
まず例外ハンドラについて考えてみましょう。
例外ハンドラは、次の形式のfunctorアプリケーションによって表されます。
handler(Start, End, Target, ClassName)
その引数は、それぞれ、ハンドラによってカバーされる命令の範囲の開始と終了、ハンドラ・コードの最初の命令、およびこのハンドラが処理するように設計されている例外クラスの名前です。
例外ハンドラは、その開始(Start)がその終了(End)より小さい場合、合法であり、オフセットがStartである命令が存在し、オフセットがEndである命令が存在し、ハンドラの例外クラスがクラスThrowableに割り当て可能である。 ハンドラのクラス・エントリが0の場合、ハンドラの例外クラスはThrowable、それ以外の場合、ハンドラのクラス名です。
handlersAreLegal(Environment) :-
exceptionHandlers(Environment, Handlers),
checklist(handlerIsLegal(Environment), Handlers).
handlerIsLegal(Environment, Handler) :-
Handler = handler(Start, End, Target, _),
Start < End,
allInstructions(Environment, Instructions),
member(instruction(Start, _), Instructions),
offsetStackFrame(Environment, Target, _),
instructionsIncludeEnd(Instructions, End),
currentClassLoader(Environment, CurrentLoader),
handlerExceptionClass(Handler, ExceptionClass, CurrentLoader),
isBootstrapLoader(BL),
isAssignable(ExceptionClass, class('java/lang/Throwable', BL)).
instructionsIncludeEnd(Instructions, End) :-
member(instruction(End, _), Instructions).
instructionsIncludeEnd(Instructions, End) :-
member(endOfCode(End), Instructions).
handlerExceptionClass(handler(_, _, _, 0),
class('java/lang/Throwable', BL), _) :-
isBootstrapLoader(BL).
handlerExceptionClass(handler(_, _, _, Name),
class(Name, L), L) :-
Name \= 0.
命令とスタックマップフレームのストリームに移動します。
命令およびスタック・マップ・フレームを1つのストリームにマージするには、次の4つのケースがあります。
空のStackMapと命令のリストをマージすると、元の命令のリストが生成されます。
mergeStackMapAndCode([], CodeList, CodeList).
Offsetの命令の型状態で始まるスタック・マップ・フレームのリストと、Offsetで始まる命令のリストが指定された場合、マージされたリストはスタック・マップ・フレーム・リストの先頭、命令リストの先頭、2つのリストの末尾のマージです。
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).
それ以外の場合、2つのリストのマージは未定義です。 命令リストには単調に増加するオフセットがあるため、すべてのスタックマップフレームオフセットに対応する命令オフセットがあり、スタックマップフレームが単調に増加する順序でないかぎり、2つのリストのマージは定義されません。
メソッドのマージされたストリームが型が正しいかどうかを判断するには、まずメソッドの初期型状態を推測します。
メソッドの初期型状態は、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のすべての型が2つのエントリ(1つはそれ自体用、1つはtopエントリ)に置き換えられているリストが生成されます。 結果は、Java Virtual Machine内の32ビット・ワードとしてのリストの表現に対応しています。
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の型を計算し、リストに入れます。 スーパークラス(Objectを除く任意のクラスの)を持つクラスの<init>メソッドのthisの型はuninitializedThisで、Objectの<init>メソッドを含む他のインスタンス・メソッドのthisの型はclass(N, L)で、Nはメソッドを含むクラスの名前、Lはその定義クラス・ローダーです。
staticメソッドの初期型状態の場合、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, uninitializedThis) :-
isSubclassInstanceInit(Class, Method).
instanceMethodInitialThisType(Class, Method, class(ClassName, L)) :-
\+ isSubclassInstanceInit(Class, Method),
classClassName(Class, ClassName),
classDefiningLoader(Class, L).
isSubclassInstanceInit(Class, Method) :-
methodName(Method, '<init>'),
classSuperClassName(Class, _).
着信型状態の場合、mergedCodeIsTypeSafe述語は、マージされた命令およびスタック・マップ・フレームのタイプが正しいことをチェックします。
スタックマップフレームと着信タイプの状態がある場合、型状態はスタックマップフレームの型状態に割り当て可能である必要があります。 次に、スタック・マップ・フレームで指定されたタイプ状態を持つストリームのrest型チェックに進むことができます。
mergedCodeIsTypeSafe(Environment, [stackMap(Offset, MapFrame) | MoreCode],
frame(Locals, OperandStack, Flags)) :-
frameIsAssignable(frame(Locals, OperandStack, Flags), MapFrame),
mergedCodeIsTypeSafe(Environment, MoreCode, MapFrame).
マージされたコード・ストリームは、Tに対して安全な型である命令Iで始まり、その例外ハンドラを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を受信オペランド・スタックにプッシュすることは、新しい型状態NextStackFrameを生成する有効な型遷移(§4.10.1.4)です。 ロード命令の実行後、型の状態は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は、命令が格納するように設計されている型です。
より正確には、オペランド・スタック(§4.10.1.4)からTypeと一致する(つまり、Typeのサブタイプである)型ActualTypeをポップし、その型にローカル変数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になります。 一部の値(および対応するタイプ)が2つのローカル変数を占有するため、変更は多少関与します。 したがって、LNの変更には、LN+1の変更(型がNスロットとN+1スロットの両方を占有するため)またはLN-1の変更(ローカルのNは、ローカルのN-1から始まる2つのワード値/タイプの上半分になるため、ローカルの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).
変数が見つかり、1語しか占有されない場合は、これをTypeに変更して終了します。 変数が見つかり、それが2つの単語を占有すると、その型が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に設定して、その2つのワード値の下半分を使用不可としてマークする必要があります。
modifyPreIndexVariable(Type, Type) :- sizeOf(Type, 1). modifyPreIndexVariable(Type, top) :- sizeOf(Type, 2).
protectedメンバーの型チェック
メンバーにアクセスするすべての命令は、protectedメンバーに関するルールと競合する必要があります。 この項では、JLS§6.6.2.1に対応するprotectedチェックについて説明します。
protectedチェックは、現在のクラスのスーパークラスのprotectedメンバーにのみ適用されます。他のクラスのprotectedメンバーは、解決時に行われたアクセス・チェックによって捕捉されます(§5.4.4)。 次の4つのケースがあります。
参照される型がスーパークラスと同じ名前のクラス型でない場合、スーパークラスにすることはできません。したがって、無視しても問題ありません。
passesProtectedCheck(Environment, class(MemberClassName, _), MemberName,
MemberDescriptor, StackFrame) :-
thisClass(Environment, CurrentClass),
\+ hasSuperclassWithName(CurrentClass, MemberClassName).
passesProtectedCheck(Environment, arrayOf(_), MemberName,
MemberDescriptor, StackFrame).
hasSuperclassWithName(Class, SuperclassName) :-
loadedSuperclasses(Class, Supers),
member(Super, Supers),
classClassName(Super, SuperclassName).
MemberClassNameがスーパークラスの名前と同じ場合、解決されるクラスは実際にはスーパークラスである可能性があります。 この場合、異なるランタイム・パッケージ内のMemberClassNameという名前のスーパークラスに、記述子MemberDescriptorを持つMemberNameという名前のprotectedメンバーが存在しない場合、protectedチェックは適用されません。
これは、解決される実際のクラスがこれらのスーパークラスの1つになるためです。この場合、同じランタイム・パッケージ内に存在し、アクセスが合法であることがわかります。または、問題のメンバーがprotectedではなく、チェックが適用されないか、サブクラスになります。この場合、チェックはとにかく成功します。または、同じランタイム・パッケージ内の他のクラスになります。その場合、アクセスは合法で、チェックは実行されません。または、検証者は、解決が強制失敗するため、いずれにしても捕捉されるため、これを問題としてフラグ設定する必要はありません。
passesProtectedCheck(Environment, class(MemberClassName, _), MemberName,
MemberDescriptor, StackFrame) :-
thisClass(Environment, CurrentClass),
hasSuperclassWithName(CurrentClass, MemberClassName),
loadedSuperclasses(CurrentClass, Supers),
classesInOtherPkgWithProtectedMember(
CurrentClass, MemberName, MemberDescriptor, MemberClassName,
Supers, []).
別のランタイム・パッケージにprotectedスーパークラス・メンバーが存在する場合は、MemberClassNameをロードします。問題のメンバーがprotectedでない場合は、チェックは適用されません。 (protected以外のスーパークラス・メンバーの使用は、ほとんど正しくありません。)
passesProtectedCheck(Environment, class(MemberClassName, Loader),
MemberName, MemberDescriptor, StackFrame) :-
thisClass(Environment, CurrentClass),
hasSuperclassWithName(CurrentClass, MemberClassName),
loadedSuperclasses(CurrentClass, Supers),
classesInOtherPkgWithProtectedMember(
CurrentClass, MemberName, MemberDescriptor, MemberClassName,
Supers, List),
List \= [],
loadedClass(MemberClassName, Loader, ReferencedClass),
isNotProtected(ReferencedClass, MemberName, MemberDescriptor).
それ以外の場合、Target型のオブジェクトのメンバーを使用するには、Targetを現在のクラスの型に割り当てる必要があります。
passesProtectedCheck(Environment, class(MemberClassName, Loader),
MemberName, MemberDescriptor,
frame(_Locals, [Target | _Rest], _Flags)) :-
thisClass(Environment, CurrentClass),
hasSuperclassWithName(CurrentClass, MemberClassName),
loadedSuperclasses(CurrentClass, Supers),
classesInOtherPkgWithProtectedMember(
CurrentClass, MemberName, MemberDescriptor, MemberClassName,
Supers, List),
List \= [],
loadedClass(MemberClassName, Loader, ReferencedClass),
isProtected(ReferencedClass, MemberName, MemberDescriptor),
thisType(Environment, ThisType),
isAssignable(Target, ThisType).
Listが、記述子MemberDescriptorを持つMemberNameという名前のprotectedメンバーを宣言するClassとは異なるランタイム・パッケージ内にあるMemberClassNameという名前のSupers内のクラスのセットである場合、述語classesInOtherPkgWithProtectedMember(Class, MemberName, MemberDescriptor, MemberClassName, Supers, List)はtrueです。
classesInOtherPkgWithProtectedMember(_, _, _, _, [], []).
classesInOtherPkgWithProtectedMember(Class, MemberName,
MemberDescriptor, MemberClassName,
[Super | Tail], [Super | T]) :-
classClassName(Super, MemberClassName),
\+ sameRuntimePackage(Class, Super),
isProtected(Super, MemberName, MemberDescriptor),
classesInOtherPkgWithProtectedMember(
Class, MemberName, MemberDescriptor, MemberClassName, Tail, T).
classesInOtherPkgWithProtectedMember(Class, MemberName,
MemberDescriptor, MemberClassName,
[Super | Tail], T) :-
classClassName(Super, MemberClassName),
\+ sameRuntimePackage(Class, Super),
isNotProtected(Super, MemberName, MemberDescriptor),
classesInOtherPkgWithProtectedMember(
Class, MemberName, MemberDescriptor, MemberClassName, Tail, T).
classesInOtherPkgWithProtectedMember(Class, MemberName,
MemberDescriptor, MemberClassName,
[Super | Tail], T) :-
classClassName(Super, MemberClassName),
sameRuntimePackage(Class, Super),
classesInOtherPkgWithProtectedMember(
Class, MemberName, MemberDescriptor, MemberClassName, Tail, T).
classesInOtherPkgWithProtectedMember(Class, MemberName,
MemberDescriptor, MemberClassName,
[Super | Tail], T) :-
classClassName(Super, SuperClassName),
SuperClassName \= MemberClassName,
classesInOtherPkgWithProtectedMember(
Class, MemberName, MemberDescriptor, MemberClassName, Tail, T).
sameRuntimePackage(Class1, Class2) :-
classDefiningLoader(Class1, L),
classDefiningLoader(Class2, L),
samePackageName(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命令は、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命令は、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命令は、発信型の状態を生成する着信オペランド・スタックに型nullを効果的にプッシュできる場合、型安全です。
instructionIsTypeSafe(aconst_null, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [], null, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
オペランドIndexを持つaload命令は型安全であり、オペランドIndexおよび型referenceを持つロード命令が型安全であり、送信型状態NextStackFrameを生成する場合、送信型状態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)).
オペランドCPを使用したanewarray命令は、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命令は、包含メソッドに宣言された戻り型ReturnType (reference型)があり、ReturnTypeに一致する型を受信オペランド・スタックから有効にポップできる場合に、型安全です。
instructionIsTypeSafe(areturn, Environment, _Offset, StackFrame,
afterGoto, ExceptionStackFrame) :-
thisMethodReturnType(Environment, ReturnType),
isAssignable(ReturnType, reference),
canPop(StackFrame, [ReturnType], _PoppedStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
arraylength命令は、着信オペランド・スタックの配列型をint型で有効に置き換えることができ、発信型の状態が得られます。
instructionIsTypeSafe(arraylength, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
nth1OperandStackIs(1, StackFrame, ArrayType),
arrayComponentType(ArrayType, _),
validTypeTransition(Environment, [top], int, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
オペランドIndexを持つastore命令は型安全であり、オペランドIndexおよび型referenceを持つストア命令が型安全であり、送信型状態NextStackFrameが生成される場合は、送信型状態NextStackFrameを生成します。
instructionIsTypeSafe(astore(Index), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
storeIsTypeSafe(Environment, Index, reference, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
0 ≤ n ≤ 3の命令 astoreは、同等の astore命令が型安全であれば型安全です。
instructionHasEquivalentTypeRule(astore_0, astore(0)). instructionHasEquivalentTypeRule(astore_1, astore(1)). instructionHasEquivalentTypeRule(astore_2, astore(2)). instructionHasEquivalentTypeRule(astore_3, astore(3)).
athrow命令は、オペランド・スタックの先頭がThrowableと一致した場合に型安全です。
instructionIsTypeSafe(athrow, _Environment, _Offset, StackFrame,
afterGoto, ExceptionStackFrame) :-
isBootstrapLoader(BL),
canPop(StackFrame, [class('java/lang/Throwable', BL)], _PoppedStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
baload命令は、intに一致する型と、着信オペランド・スタックの小さい配列型をintで有効に置き換えることができる場合に、型安全です。
instructionIsTypeSafe(baload, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :
nth1OperandStackIs(2, StackFrame, ArrayType),
isSmallArray(ArrayType),
validTypeTransition(Environment, [int, top], int,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
配列型は、byteの配列、booleanの配列またはそのサブタイプ(null)の場合、小さい配列型です。
isSmallArray(arrayOf(byte)). isSmallArray(arrayOf(boolean)). isSmallArray(null).
bastore命令は、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命令は、同等のsipush命令が型安全であれば型安全です。
instructionHasEquivalentTypeRule(bipush(Value), sipush(Value)).
caload命令は、入力オペランド・スタックのintおよびcharの配列に一致する型をintで有効に置き換えることができる場合に、型安全です。
instructionIsTypeSafe(caload, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [int, arrayOf(char)], int,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
castore命令は、発信型の状態を生成する着信オペランド・スタックからint、intおよびcharの配列に一致する型を有効なポップできる場合に、型安全です。
instructionIsTypeSafe(castore, _Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
canPop(StackFrame, [int, int, arrayOf(char)], NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
オペランドCPを使用したcheckcast命令は、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命令は、着信オペランド・スタックから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命令は、着信オペランド・スタックのdoubleおよびdoubleに一致する型をdoubleで有効に置換でき、発信タイプの状態が得られます。
instructionIsTypeSafe(dadd, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [double, double], double,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
daload命令は、intに一致する型と、着信オペランド・スタックのdoubleの配列をdoubleで置き換え、発信タイプ状態を生成できる場合に、型安全です。
instructionIsTypeSafe(daload, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [int, arrayOf(double)], double,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
dastore命令は、double、intおよびdoubleの配列に一致する型を、発信型の状態を生成する着信オペランド・スタックから有効にポップできる場合に、型安全です。
instructionIsTypeSafe(dastore, _Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
canPop(StackFrame, [double, int, arrayOf(double)], NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
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_0命令は、発信型の状態を生成する着信オペランド・スタックに型doubleを効果的にプッシュできる場合、型安全です。
instructionIsTypeSafe(dconst_0, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [], double, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
同等の dconst_0命令が型安全であれば、dconst_1命令は型安全です。
instructionHasEquivalentTypeRule(dconst_1, dconst_0).
ddiv命令は、同等のdadd命令が型安全であれば型安全です。
instructionHasEquivalentTypeRule(ddiv, dadd).
オペランドIndexを持つdload命令は型安全であり、オペランドIndexおよび型doubleを持つロード命令が型安全であり、送信型状態NextStackFrameが生成される場合は、送信型状態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命令は、同等のdadd命令が型安全であれば型安全です。
instructionHasEquivalentTypeRule(dmul, dadd).
dneg命令は、着信オペランド・スタックにdoubleに一致する型がある場合、型安全です。 dneg命令は型の状態を変更しません。
instructionIsTypeSafe(dneg, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [double], double,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
drem命令は、同等のdadd命令が型安全である場合、型安全です。
instructionHasEquivalentTypeRule(drem, dadd).
dreturn命令は、包含メソッドの宣言された戻り型がdoubleの場合、型安全です。また、doubleに一致する型を、着信オペランド・スタックから効果的にポップできます。
instructionIsTypeSafe(dreturn, Environment, _Offset, StackFrame,
afterGoto, ExceptionStackFrame) :-
thisMethodReturnType(Environment, double),
canPop(StackFrame, [double], _PoppedStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
オペランドIndexを持つdstore命令は型安全であり、オペランドIndexおよび型doubleを持つストア命令が型安全であり、送信型状態NextStackFrameが生成される場合は、送信型状態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命令は、同等のdadd命令が型安全であれば型安全です。
instructionHasEquivalentTypeRule(dsub, dadd).
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命令は、受信オペランド・スタックの2つのカテゴリ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命令の型安全形式である場合、型安全です。
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命令が type safe form 1 dup_x2 dup_x2命令または type safe form 2 dup_x2命令である場合の type safe formです。
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命令です。受信オペランドスタック上の3つのカテゴリ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命令の型セーフ形式である場合、型セーフです。
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命令がtype safe form 1 dup2 dup2命令またはtype safe form 2 dup2命令である場合に、type safe formです。
dup2FormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
dup2Form1IsTypeSafe(Environment,InputOperandStack, OutputOperandStack).
dup2FormIsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
dup2Form2IsTypeSafe(Environment,InputOperandStack, OutputOperandStack).
dup2命令は、型セーフ形式1のdup2命令です。受信オペランド・スタックの2つのカテゴリ1型(Type1およびType2)を、Type1、Type2、Type1、Type2型で有効に置き換えることができれば、送信型の状態になります。
dup2Form1IsTypeSafe(Environment, InputOperandStack, OutputOperandStack):-
popCategory1(InputOperandStack, Type1, TempStack),
popCategory1(TempStack, Type2, _),
canSafelyPushList(Environment, InputOperandStack, [Type2, Type1],
OutputOperandStack).
dup2命令は、型セーフ形式2のdup2命令です(入力オペランド・スタックのカテゴリ2型Typeを型Type、Typeで有効に置き換えることができれば、送信型の状態になります)。
dup2Form2IsTypeSafe(Environment, InputOperandStack, OutputOperandStack):-
popCategory2(InputOperandStack, Type, _),
canSafelyPush(Environment, InputOperandStack, Type, OutputOperandStack).
dup2_x1命令は、dup2_x1命令の型安全形式である場合、型安全です。
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命令が type safe form 1 dup2_x1 dup2_x1命令または type safe form 2 dup_x2命令である場合の type safe formです。
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命令で、受信オペランド・スタック上の3つのカテゴリ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命令の型安全形式である場合、型安全です。
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命令の型安全形式です。
これは type safe form 1 dup2_x2命令です。
これは type safe form 2 dup2_x2命令です。
これは type safe form 3 dup2_x2命令です。
これは type safe form 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命令で、入力オペランド・スタックでタイプType1、Type2、Type3、Type4、Type1、Type2を持つ4つのカテゴリ1型(Type1、Type2、Type3、Type4)を有効に置き換えることができ、送信型の状態になります。
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命令です。この場合、受信オペランド・スタックでType1型、Type2型、Type3型、Type1型を持つカテゴリ2型、Type1型および2つのカテゴリ1型(Type2型、Type3型)を有効に置き換えることができ、送信型の状態になります。
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命令です。この命令は、Type1、Type2、Type3、Type1、Type2型の着信オペランドスタック上の2つのカテゴリ1の型、Type1、Type2、およびカテゴリ2の型 Type3を有効に置き換えることができ、発信型の状態になります。
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命令です。iffは、Type1、Type2、Type1型の着信オペランドスタック上の2つのカテゴリ2の型(Type1、Type2)を有効に置き換えることができ、発信型の状態になります。
dup2_x2Form4IsTypeSafe(Environment, InputOperandStack, OutputOperandStack) :-
popCategory2(InputOperandStack, Type1, Stack1),
popCategory2(Stack1, Type2, Rest),
canSafelyPushList(Environment, Rest, [Type1, Type2, Type1],
OutputOperandStack).
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命令は、着信オペランド・スタックのfloatおよびfloatに一致する型をfloatで有効に置換でき、発信タイプの状態が得られます。
instructionIsTypeSafe(fadd, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [float, float], float,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
faload命令は、intに一致する型と、着信オペランド・スタックのfloatの配列をfloatで置き換え、発信タイプ状態を生成できる場合に、型安全です。
instructionIsTypeSafe(faload, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [int, arrayOf(float)], float,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
fastore命令は、float、intおよびfloatの配列に一致する型を、発信型の状態を生成する着信オペランド・スタックから有効にポップできる場合に、型安全です。
instructionIsTypeSafe(fastore, _Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
canPop(StackFrame, [float, int, arrayOf(float)], NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
fcmpg命令は、着信オペランド・スタックのfloatおよびfloatに一致する型をintで有効に置換でき、発信タイプ状態が得られます。
instructionIsTypeSafe(fcmpg, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [float, float], int,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
同等の fcmpg命令が型安全であれば、fcmpl命令は型安全です。
instructionHasEquivalentTypeRule(fcmpl, fcmpg).
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命令は、同等の fadd命令が型安全であれば型安全です。
instructionHasEquivalentTypeRule(fdiv, fadd).
オペランドIndexを持つfload命令は型安全であり、オペランドIndexおよび型floatを持つロード命令が型安全であり、送信型状態NextStackFrameを生成する場合、送信型状態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)).
同等の fadd命令が型安全であれば、fmul命令は型安全です。
instructionHasEquivalentTypeRule(fmul, fadd).
fneg命令は、着信オペランド・スタックにfloatに一致する型がある場合、型安全です。 fneg命令は型の状態を変更しません。
instructionIsTypeSafe(fneg, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [float], float,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
frem命令は、同等の fadd命令が型安全であれば型安全です。
instructionHasEquivalentTypeRule(frem, fadd).
freturn命令は、包含メソッドの宣言された戻り型がfloatの場合、型安全です。また、floatに一致する型を、着信オペランド・スタックから効果的にポップできます。
instructionIsTypeSafe(freturn, Environment, _Offset, StackFrame,
afterGoto, ExceptionStackFrame) :-
thisMethodReturnType(Environment, float),
canPop(StackFrame, [float], _PoppedStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
オペランドIndexを持つfstore命令は型安全であり、オペランドIndexおよび型floatを持つストア命令が型安全であり、送信型状態NextStackFrameを生成する場合、送信型状態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命令は、同等の fadd命令が型安全であれば型安全です。
instructionHasEquivalentTypeRule(fsub, fadd).
オペランドCPを使用したgetfield命令は、CPが、FieldClassType型のメンバーである宣言型がFieldTypeであるフィールドを示す定数プール・エントリを参照し、発信型状態を生成する着信オペランド・スタックでFieldClassTypeをFieldTypeに有効に置き換えることができる場合、型安全です。protectedフィールドは、追加のチェックの対象となります(§4.10.1.8)。
instructionIsTypeSafe(getfield(CP), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
CP = field(FieldClassType, FieldName, FieldDescriptor),
parseFieldDescriptor(FieldDescriptor, FieldType),
passesProtectedCheck(Environment, FieldClassType, FieldName,
FieldDescriptor, StackFrame),
validTypeTransition(Environment, [FieldClassType], FieldType,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
オペランドCPを使用したgetstatic命令は、CPが宣言された型がFieldTypeであるフィールドを示す定数プール・エントリを参照し、送信型の状態を生成する受信オペランド・スタックに対してFieldTypeを有効かつプッシュできる場合に、型安全です。
instructionIsTypeSafe(getstatic(CP), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
CP = field(_FieldClassType, _FieldName, FieldDescriptor),
parseFieldDescriptor(FieldDescriptor, FieldType),
validTypeTransition(Environment, [], FieldType,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
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命令は、同等の 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命令は、着信オペランド・スタックのintおよびintに一致する型をintで有効に置換でき、発信型の状態になる場合に、型安全です。
instructionIsTypeSafe(iadd, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [int, int], int,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
iaload命令は、入力オペランド・スタックのintおよびintの配列と一致する型をintで有効に置き換えることができる場合に、型安全です。
instructionIsTypeSafe(iaload, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [int, arrayOf(int)], int,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
iand命令は、同等の iadd命令が型安全であれば型安全です。
instructionHasEquivalentTypeRule(iand, iadd).
iastore命令は、int、intおよびintの配列に一致する型を、発信型の状態を生成する着信オペランド・スタックから有効にポップできる場合に、型安全です。
instructionIsTypeSafe(iastore, _Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
canPop(StackFrame, [int, int, arrayOf(int)], NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
iconst_m1命令は、発信型の状態を生成する着信オペランド・スタックに型intを効果的にプッシュできる場合、型安全です。
instructionIsTypeSafe(iconst_m1, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [], int, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
iconstの他のバリアントに対するルールは同等です。
instructionHasEquivalentTypeRule(iconst_0, iconst_m1). instructionHasEquivalentTypeRule(iconst_1, iconst_m1). instructionHasEquivalentTypeRule(iconst_2, iconst_m1). instructionHasEquivalentTypeRule(iconst_3, iconst_m1). instructionHasEquivalentTypeRule(iconst_4, iconst_m1). instructionHasEquivalentTypeRule(iconst_5, iconst_m1).
idiv命令は、同等の iadd命令が型安全であれば型安全です。
instructionHasEquivalentTypeRule(idiv, iadd).
if_acmpeq命令は、発信型状態NextStackFrameを生成する着信オペランド・スタック上のreferenceおよびreferenceに一致する型を正しくポップでき、命令のオペランド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_icmpeq命令は、発信型状態NextStackFrameを生成する着信オペランド・スタックでintおよびintに一致する型を有効なポップでき、命令のオペランド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)).
ifeq命令は、発信型状態NextStackFrameを生成する着信オペランド・スタックからintに一致する型を有効ポップできる場合、型安全です。また、命令のオペランド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命令は、発信型状態NextStackFrameを生成する着信オペランド・スタックからreferenceに一致する型を有効ポップできる場合に型安全です。命令のオペランドTargetは、着信型状態NextStackFrameを想定した有効なブランチ・ターゲットです。
instructionIsTypeSafe(ifnonnull(Target), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
canPop(StackFrame, [reference], NextStackFrame),
targetIsTypeSafe(Environment, NextStackFrame, Target),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
ifnull命令は、同等のifnonnull命令が型安全であれば型安全です。
instructionHasEquivalentTypeRule(ifnull(Target), ifnonnull(Target)).
最初のオペランドIndexを持つiinc命令は、LIndexがint型の場合、型安全です。 iinc命令は型の状態を変更しません。
instructionIsTypeSafe(iinc(Index, _Value), _Environment, _Offset,
StackFrame, StackFrame, ExceptionStackFrame) :-
StackFrame = frame(Locals, _OperandStack, _Flags),
nth0(Index, Locals, int),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
オペランドIndexを持つiload命令は型安全であり、オペランドIndexおよび型intを持つロード命令が型安全であり、送信型状態NextStackFrameを生成する場合、送信型状態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命令は、同等のiadd命令が型安全であれば型安全です。
instructionHasEquivalentTypeRule(imul, iadd).
ineg命令は、着信オペランド・スタックにintに一致する型がある場合、型安全です。 ineg命令は型の状態を変更しません。
instructionIsTypeSafe(ineg, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [int], int, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
オペランドCPを使用したinstanceof命令は、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命令は型安全です。
最初のオペランドCPは、記述子Descriptorを持つCallSiteNameという名前の動的コール・サイトを示す定数プール・エントリを参照します。
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命令は、次のすべてに該当する場合、型安全です。
最初のオペランドCPは、MethodClassType型のメンバーである記述子Descriptorを持つインタフェース・メソッドを示す定数プール・エントリを参照します。
MethodNameは<init>ではありません。
MethodNameは<clinit>ではありません。
2番目のオペランドCountは、有効なカウント・オペランドです(次を参照)。
MethodClassTypeと一致する型と、着信オペランド・スタックのDescriptorで指定された引数型を、Descriptorで指定された戻り型に置き換えて、発信型の状態を生成できます。
instructionIsTypeSafe(invokeinterface(CP, Count, 0), Environment, _Offset,
StackFrame, NextStackFrame, ExceptionStackFrame) :-
CP = imethod(MethodClassType, MethodName, Descriptor),
MethodName \= '<init>',
MethodName \= '<clinit>',
parseMethodDescriptor(Descriptor, OperandArgList, ReturnType),
reverse([MethodClassType | OperandArgList], StackArgList),
canPop(StackFrame, StackArgList, TempFrame),
validTypeTransition(Environment, [], ReturnType,
TempFrame, NextStackFrame),
countIsValid(Count, StackFrame, TempFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
invokeinterface命令のCountオペランドは、命令の引数のサイズと等しい場合に有効です。 これは、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命令は型安全です。
最初のオペランドCPは、型MethodClassTypeのメンバーである記述子Descriptorを持つMethodNameというメソッドを示す定数プール・エントリを参照します。
次のいずれかです。
MethodNameは<init>ではありません。
MethodNameは<clinit>ではありません。
MethodClassTypeは、現在のクラス、現在のクラスのスーパークラス、または現在のクラスの直接スーパーインタフェースです。
着信オペランド・スタックのDescriptorで指定された現在のクラスおよび引数型と一致する型を、Descriptorで指定された戻り型と置換して、発信型の状態を生成できます。
instructionIsTypeSafe(invokespecial(CP), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
(CP = method(MethodClassType, MethodName, Descriptor) ;
CP = imethod(MethodClassType, MethodName, Descriptor)),
MethodName \= '<init>',
MethodName \= '<clinit>',
validSpecialMethodClassType(Environment, MethodClassType),
parseMethodDescriptor(Descriptor, OperandArgList, ReturnType),
thisType(Environment, ThisType),
reverse([ThisType | OperandArgList], StackArgList),
validTypeTransition(Environment, StackArgList, ReturnType,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
validSpecialMethodClassType(Environment, class(MethodClassName, _)) :-
thisClass(Environment, ThisClass),
classClassName(ThisClass, MethodClassName).
validSpecialMethodClassType(Environment, class(MethodClassName, L)) :-
thisClass(Environment, ThisClass),
loadedSuperclasses(ThisClass, Supers),
member(Super, Supers),
classClassName(Super, MethodClassName),
loadedClass(MethodClassName, L, Super).
validSpecialMethodClassType(Environment, class(MethodClassName, _)) :-
thisClass(Environment, ThisClass),
classInterfaceNames(ThisClass, InterfaceNames),
member(MethodClassName, InterfaceNames).
validSpecialMethodClassType句は、invokespecialがインスタンス初期化メソッド以外の場合、現在のクラス/インタフェースまたはスーパークラス/スーパーインタフェースのメソッドに名前を付ける必要がある構造的制約を適用します。
validTypeTransition句は、インスタンス初期化メソッド以外のinvokespecialが現在のクラスの受信側オブジェクトまたはより深い受信側オブジェクトをターゲットとする構造制約を適用します。 その理由を確認するには、StackArgListが、現在のクラス(invokespecialを実行するクラス)から、メソッドで予期されるオペランド・スタック上の型のリストをシミュレートすることを検討してください。 オペランド・スタックの実際の型は、StackFrameにあります。 validTypeTransitionの効果は、StackFrameのオペランド・スタックから最初の型をポップし、それがStackArgListの最初の項のサブタイプである、つまり現在のクラスであることをチェックすることです。 したがって、実際の受信側タイプは現在のクラスと互換性があります。
鋭い目のリーダーは、この構造的制約を強制すると、protectedメソッドのinvokespecialに関連する構造的制約よりも優先されることに気付く場合があります。 したがって、前述のプロローグ・コードはpassesProtectedCheck (§4.10.1.8)を参照しませんが、インスタンス初期化メソッドのinvokespecialのプロローグ・コードでは、passesProtectedCheckを使用して、特定のprotectedインスタンス初期化メソッドが指定された場合に、実際のレシーバ・タイプが現在のクラスと互換性があることを確認します。
または
MethodNameは<init>です。
Descriptorは、void戻り型を指定します。
DescriptorおよびuninitializedThisで指定された引数型と一致するポップ型を、着信オペランド・スタックから切り離してOperandStackを生成できます。
MethodClassTypeは、現在のクラスまたは現在のクラスの直接スーパークラスです。
送信型の状態は、最初に受信オペランド・スタックをOperandStackに置き換え、次にuninitializedThisのすべてのインスタンスを現在のクラスの型に置き換えることで、受信型の状態から導出されます。
instructionIsTypeSafe(invokespecial(CP), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
(CP = method(MethodClassType, '<init>', Descriptor) ;
CP = imethod(MethodClassType, '<init>', Descriptor)),
parseMethodDescriptor(Descriptor, OperandArgList, void),
reverse([uninitializedThis | OperandArgList], StackArgList),
canPop(StackFrame, StackArgList, frame(Locals, OperandStack, Flags)),
validThisInitClassType(Environment, MethodClassType),
thisType(Environment, This),
substitute(uninitializedThis, This, OperandStack, NextOperandStack),
substitute(uninitializedThis, This, Locals, NextLocals),
substitute(uninitializedThis, top, Locals, ExceptionLocals),
NextStackFrame = frame(NextLocals, NextOperandStack, []),
ExceptionStackFrame = frame(ExceptionLocals, [], Flags).
validThisInitClassType(Environment, class(MethodClassName, _)) :-
thisClass(Environment, ThisClass),
classClassName(ThisClass, MethodClassName).
validThisInitClassType(Environment, class(MethodClassName, _)) :-
thisClass(Environment, ThisClass),
classSuperClassName(ThisClass, MethodClassName).
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).
または
MethodNameは<init>です。
Descriptorは、void戻り型を指定します。
Descriptorで指定された引数型と初期化されていない型uninitialized(Address)と一致するポップ型を、着信オペランド・スタックから切り離してOperandStackを生成できます。
Addressの命令により、新しいMethodClassTypeが作成されました。
送信型の状態は、最初に受信オペランド・スタックをOperandStackに置き換え、次にuninitialized(Address)のすべてのインスタンスをMethodClassTypeに置き換えることで、受信型の状態から導出されます。
メソッドがprotectedの場合、使用方法はprotectedメンバー(§4.10.1.8)へのアクセスを制御する特別なルールに準拠します。
instructionIsTypeSafe(invokespecial(CP), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
(CP = method(MethodClassType, '<init>', Descriptor) ;
CP = imethod(MethodClassType, '<init>', Descriptor)),
parseMethodDescriptor(Descriptor, OperandArgList, void),
reverse([uninitialized(Address) | OperandArgList], StackArgList),
canPop(StackFrame, StackArgList, frame(Locals, OperandStack, Flags)),
allInstructions(Environment, Instructions),
member(instruction(Address, new(MethodClassType)), Instructions),
substitute(uninitialized(Address), MethodClassType,
OperandStack, NextOperandStack),
substitute(uninitialized(Address), MethodClassType, Locals, NextLocals),
substitute(uninitialized(Address), top, Locals, ExceptionLocals),
NextStackFrame = frame(NextLocals, NextOperandStack, Flags),
ExceptionStackFrame = frame(ExceptionLocals, [], Flags),
passesProtectedCheck(Environment, MethodClassType, '<init>',
Descriptor, NextStackFrame).
<init>メソッドのinvokespecialのルールは、個別の例外スタック・フレームを戻す唯一の動機です。 懸念事項は、オブジェクトを初期化するときに、invokespecial呼出しが失敗し、オブジェクトが部分的に初期化されて永続的に使用できない状態になることです。 オブジェクトが初めて初期化に失敗した後、初期化の試行を繰り返さないようにするために、例外ハンドラでは、uninitializedThisまたはuninitialized(Offset)ではなくtop型を持つように、ローカル変数に格納されているオブジェクトへの参照を考慮する必要があります。
現在のオブジェクトを初期化する特殊な場合(つまり、uninitializedThis型の<init>を呼び出す場合)、元のフレームは通常、ローカル変数0の初期化されていないオブジェクトを保持し、フラグflagThisUninitを持ちます。 invokespecialを正常に終了すると、初期化されていないオブジェクトが初期化され、flagThisUninitフラグがオフになります。 ただし、invokespecial呼出しで例外がスローされた場合、例外フレームには、破損したオブジェクト(top型)とflagThisUninitフラグ(古いフラグ)が含まれます。 その型の状態を指定した戻り命令を実行する方法がないため、ハンドラは別の例外(または永久にループ)をスローする必要があります。 実際には、StackMapTable属性(§4.7.4)によって表されるスタック・フレームには、uninitializedThis型を付随して使用せずにflagThisUninitを運ぶ方法がないため、その型の状態のハンドラを表現することもできません。
オブジェクト初期化時にこれらの特別な制約がない場合、例外スタック・フレームのローカル変数の型およびフラグは、入力スタック・フレームのローカル変数の型およびフラグと常に同じになります。
invokestatic命令は、次のすべてに当てはまる場合に型安全です。
最初のオペランドCPは、記述子Descriptorを持つMethodNameというメソッドを示す定数プール・エントリを参照します。
MethodNameは<init>ではありません。
MethodNameは<clinit>ではありません。
着信オペランド・スタックのDescriptorで指定された引数型と一致する型を、Descriptorで指定された戻り型と置換して、発信型の状態を生成できます。
instructionIsTypeSafe(invokestatic(CP), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
(CP = method(_MethodClassType, MethodName, Descriptor) ;
CP = imethod(_MethodClassType, MethodName, Descriptor)),
MethodName \= '<init>',
MethodName \= '<clinit>',
parseMethodDescriptor(Descriptor, OperandArgList, ReturnType),
reverse(OperandArgList, StackArgList),
validTypeTransition(Environment, StackArgList, ReturnType,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
次のすべてに該当する場合、invokevirtual命令は型安全です。
最初のオペランドCPは、型MethodClassTypeのメンバーである記述子Descriptorを持つMethodNameというメソッドを示す定数プール・エントリを参照します。
MethodNameは<init>ではありません。
MethodNameは<clinit>ではありません。
MethodClassTypeと一致する型と、着信オペランド・スタックのDescriptorで指定された引数型を、Descriptorで指定された戻り型に置き換えて、発信型の状態を生成できます。
メソッドがprotectedの場合、使用方法はprotectedメンバー(§4.10.1.8)へのアクセスを制御する特別なルールに準拠します。
instructionIsTypeSafe(invokevirtual(CP), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
CP = method(MethodClassType, MethodName, Descriptor),
MethodName \= '<init>',
MethodName \= '<clinit>',
parseMethodDescriptor(Descriptor, OperandArgList, ReturnType),
reverse(OperandArgList, ArgList),
reverse([MethodClassType | OperandArgList], StackArgList),
validTypeTransition(Environment, StackArgList, ReturnType,
StackFrame, NextStackFrame),
canPop(StackFrame, ArgList, PoppedFrame),
passesProtectedCheck(Environment, MethodClassType, MethodName,
Descriptor, PoppedFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
ior命令は、同等のiadd命令が型安全であれば型安全です。
instructionHasEquivalentTypeRule(ior, iadd).
irem命令は、同等のiadd命令が型安全であれば型安全です。
instructionHasEquivalentTypeRule(irem, iadd).
ireturn命令は、包含メソッドの宣言された戻り型がintの場合、型安全です。また、intに一致する型を、着信オペランド・スタックから効果的にポップできます。
instructionIsTypeSafe(ireturn, Environment, _Offset, StackFrame,
afterGoto, ExceptionStackFrame) :-
thisMethodReturnType(Environment, int),
canPop(StackFrame, [int], _PoppedStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
ishl命令は、同等の iadd命令が型安全であれば型安全です。
instructionHasEquivalentTypeRule(ishl, iadd).
ishr命令は、同等の iadd命令が型安全であれば型安全です。
instructionHasEquivalentTypeRule(ishr, iadd).
iushr命令は、同等の iadd命令が型安全であれば型安全です。
instructionHasEquivalentTypeRule(iushr, iadd).
オペランドIndexを持つistore命令は型安全であり、オペランドIndexおよび型intを持つストア命令が型安全であり、送信型状態NextStackFrameを生成する場合、送信型状態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命令は、同等のiadd命令が型安全であれば型安全です。
instructionHasEquivalentTypeRule(isub, iadd).
ixor命令は、同等の iadd命令が型安全であれば型安全です。
instructionHasEquivalentTypeRule(ixor, iadd).
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命令は、着信オペランド・スタックのlongおよびlongに一致する型をlongで有効に置き換えることができ、発信型の状態になる場合に、型安全です。
instructionIsTypeSafe(ladd, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [long, long], long,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
laload命令は、intに一致する型と、着信オペランド・スタックのlongの配列をlongで置き換え、発信タイプ状態を生成できる場合に、型安全です。
instructionIsTypeSafe(laload, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [int, arrayOf(long)], long,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
land命令は、同等の ladd命令が型安全であれば型安全です。
instructionHasEquivalentTypeRule(land, ladd).
lastore命令は、long、intおよびlongの配列に一致する型を、発信型の状態を生成する着信オペランド・スタックから有効にポップできる場合に、型安全です。
instructionIsTypeSafe(lastore, _Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
canPop(StackFrame, [long, int, arrayOf(long)], NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
lcmp命令は、着信オペランド・スタックのlongおよびlongに一致する型をintで有効に置換でき、発信タイプの状態が得られます。
instructionIsTypeSafe(lcmp, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [long, long], int,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
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).
オペランド CPを持つ ldc命令は、CPが Type型のエンティティーを示す定数プールエントリを指し、Typeはロード可能(§4.4)ですが、longまたは doubleではなく、Typeを着信オペランドスタックに有効にプッシュして発信型の状態を生成できる場合に、型安全です。
instructionIsTypeSafe(ldc(CP), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
loadableConstant(CP, Type),
Type \= long,
Type \= double,
validTypeTransition(Environment, [], Type, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
loadableConstant(CP, Type) :-
member([CP, Type], [
[int(_), int],
[float(_), float],
[long(_), long],
[double(_), double]
]).
loadableConstant(CP, Type) :-
isBootstrapLoader(BL),
member([CP, Type], [
[class(_,_), class('java/lang/Class', BL)],
[arrayOf(_), class('java/lang/Class', BL)],
[string(_), class('java/lang/String', BL)],
[methodHandle(_,_), class('java/lang/invoke/MethodHandle', BL)],
[methodType(_,_), class('java/lang/invoke/MethodType', BL)]
]).
loadableConstant(CP, Type) :-
CP = dconstant(_, FieldDescriptor),
parseFieldDescriptor(FieldDescriptor, Type).
ldc_w命令は、同等の ldc命令が型安全であれば型安全です。
instructionHasEquivalentTypeRule(ldc_w(CP), ldc(CP))
オペランド CPを持つ ldc2_w命令は、CPが Type型のエンティティーを示す定数プールエントリ(Typeは longまたは double)を参照し、Typeを着信オペランドスタックに有効にプッシュして発信型の状態を生成できる場合に、型安全です。
instructionIsTypeSafe(ldc2_w(CP), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
loadableConstant(CP, Type),
(Type = long ; Type = double),
validTypeTransition(Environment, [], Type, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
ldiv命令は、同等の ladd命令が型安全であれば型安全です。
instructionHasEquivalentTypeRule(ldiv, ladd).
オペランドIndexを持つlload命令は型安全であり、オペランドIndexおよび型longを持つロード命令が型安全であり、送信型状態NextStackFrameを生成する場合、送信型状態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命令は、同等の ladd命令が型安全であれば型安全です。
instructionHasEquivalentTypeRule(lmul, ladd).
lneg命令は、着信オペランド・スタックにlongに一致する型がある場合、型安全です。 lneg命令は型の状態を変更しません。
instructionIsTypeSafe(lneg, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [long], long,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
lookupswitch命令は、キーがソートされている場合には型安全であり、新しい型状態BranchStackFrameを生成する着信オペランド・スタックからintを有効ポップでき、命令のターゲットはすべて、着信型状態として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命令は、同等のladd命令が型安全であれば型安全です。
instructionHasEquivalentTypeRule(lor, ladd).
同等の ladd命令が型安全であれば、lrem命令は型安全です。
instructionHasEquivalentTypeRule(lrem, ladd).
lreturn命令は、包含メソッドの宣言された戻り型がlongの場合、型安全です。また、longに一致する型を、着信オペランド・スタックから効果的にポップできます。
instructionIsTypeSafe(lreturn, Environment, _Offset, StackFrame,
afterGoto, ExceptionStackFrame) :-
thisMethodReturnType(Environment, long),
canPop(StackFrame, [long], _PoppedStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
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).
オペランドIndexを持つlstore命令は型安全であり、オペランドIndexおよび型longを持つストア命令が型安全であり、発信型状態NextStackFrameを生成する場合、発信型状態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)).
同等の ladd命令が型安全であれば、lsub命令は型安全です。
instructionHasEquivalentTypeRule(lsub, ladd).
lxor命令は、同等の ladd命令が型安全であれば型安全です。
instructionHasEquivalentTypeRule(lxor, ladd).
monitorenter命令は、発信型の状態を生成する着信オペランド・スタックからreferenceに一致する型を有効ポップできる場合に、型安全です。
instructionIsTypeSafe(monitorenter, _Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
canPop(StackFrame, [reference], NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
monitorexit命令は、同等のmonitorenter命令が型安全であれば型安全です。
instructionHasEquivalentTypeRule(monitorexit, monitorenter).
オペランドCPおよびDimを持つマルチ・アレイ命令は、CPが、ディメンションがDim以上で、Dimが厳密に正である配列型を示す定数プール・エントリを参照し、着信オペランド・スタックのDim int型を、発信タイプ状態を生成するCPで示される型に有効な置換できます。
instructionIsTypeSafe(multianewarray(CP, Dim), Environment, _Offset,
StackFrame, NextStackFrame, ExceptionStackFrame) :-
CP = arrayOf(_),
arrayDimensions(CP, Dimensions),
Dimensions >= Dim,
Dim > 0,
/* Make a list of Dim ints */
findall(int, between(1, Dim, _), IntList),
validTypeTransition(Environment, IntList, CP,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
コンポーネント・タイプが配列タイプでもある配列タイプのディメンションは、そのコンポーネント・タイプのディメンションより1つ以上です。
arrayDimensions(arrayOf(X), XDimensions + 1) :-
arrayDimensions(X, XDimensions).
arrayDimensions(Type, 0) :-
Type \= arrayOf(_).
オフセットOffsetのオペランドCPを持つ新しい命令は、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)のルールで定義されます。
オペランドTypeCodeを持つnewarray命令は、TypeCodeがプリミティブ型ElementTypeに対応している場合、型安全です。また、入力オペランド・スタックの型intを型'array of 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命令は型の状態には影響しません。
instructionIsTypeSafe(nop, _Environment, _Offset, StackFrame,
StackFrame, ExceptionStackFrame) :-
exceptionStackFrame(StackFrame, ExceptionStackFrame).
pop命令は、発信タイプ状態を生成する着信オペランド・スタックからカテゴリ1型を効果的にポップできる場合に、型安全です。
instructionIsTypeSafe(pop, _Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
StackFrame = frame(Locals, [Type | Rest], Flags),
popCategory1([Type | Rest], Type, Rest),
NextStackFrame = frame(Locals, Rest, Flags),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
pop2命令は、pop2命令の型安全形式である場合、型安全です。
instructionIsTypeSafe(pop2, _Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
StackFrame = frame(Locals, InputOperandStack, Flags),
pop2SomeFormIsTypeSafe(InputOperandStack, OutputOperandStack),
NextStackFrame = frame(Locals, OutputOperandStack, Flags),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
pop2命令は、pop2命令がtype safe form 1type safe form 1 pop2命令またはtype safe form 2 pop2命令のtype safe formです。
pop2SomeFormIsTypeSafe(InputOperandStack, OutputOperandStack) :-
pop2Form1IsTypeSafe(InputOperandStack, OutputOperandStack).
pop2SomeFormIsTypeSafe(InputOperandStack, OutputOperandStack) :-
pop2Form2IsTypeSafe(InputOperandStack, OutputOperandStack).
pop2命令は、型セーフ形式1 pop2命令です。ただし、入力オペランドスタックから2種類のサイズ1を有効にポップして、送信型状態を生成できます。
pop2Form1IsTypeSafe([Type1, Type2 | Rest], Rest) :-
popCategory1([Type1 | Rest], Type1, Rest),
popCategory1([Type2 | Rest], Type2, Rest).
pop2命令は、type safe form 2 pop2命令です。ただし、入力オペランドスタックからサイズ2のタイプを有効にポップして、送信タイプの状態が得られます。
pop2Form2IsTypeSafe([top, Type | Rest], Rest) :-
popCategory2([top, Type | Rest], Type, Rest).
オペランドCPを使用したputfield命令は、次のすべてに該当する場合、型安全です。
最初のオペランドCPは、宣言された型がFieldTypeで、型FieldClassTypeのメンバーであるフィールドを示す定数プール・エントリを参照します。
次のいずれかです。
FieldTypeおよびFieldClassTypeに一致するポップ型を、発信型の状態を生成する着信オペランド・スタックから効果的にポップできます。
protectedフィールドは、追加のチェックの対象となります(§4.10.1.8)。
instructionIsTypeSafe(putfield(CP), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
CP = field(FieldClassType, FieldName, FieldDescriptor),
parseFieldDescriptor(FieldDescriptor, FieldType),
canPop(StackFrame, [FieldType], PoppedFrame),
passesProtectedCheck(Environment, FieldClassType, FieldName,
FieldDescriptor, PoppedFrame),
canPop(StackFrame, [FieldType, FieldClassType], NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
または
命令がFieldClassTypeで指定されたクラスのインスタンス初期化メソッドで発生し、クラスによって宣言されたフィールドに代入される場合、FieldTypeおよびuninitializedThisに一致する型を、発信型の状態を生成する着信オペランド・スタックから有効にポップできます。 これにより、thisを完全に初期化する前に、現在のクラスで宣言されているthisのインスタンス・フィールドを割り当てることができます。
instructionIsTypeSafe(putfield(CP), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
CP = field(FieldClassType, FieldName, FieldDescriptor),
thisType(Environment, FieldClassType),
Environment = environment(CurrentClass, CurrentMethod, _, _, _, _),
methodName(CurrentMethod, '<init>'),
classDeclaresMember(CurrentClass, FieldName, FieldDescriptor),
parseFieldDescriptor(FieldDescriptor, FieldType),
canPop(StackFrame, [FieldType, uninitializedThis], NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
オペランドCPを使用したputstatic命令は、CPが宣言された型がFieldTypeであるフィールドを示す定数プール・エントリを参照し、FieldTypeに一致する型を、発信型の状態を生成する着信オペランド・スタックから有効にポップできる場合、型安全です。
instructionIsTypeSafe(putstatic(CP), _Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
CP = field(_FieldClassType, _FieldName, FieldDescriptor),
parseFieldDescriptor(FieldDescriptor, FieldType),
canPop(StackFrame, [FieldType], NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
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命令は、intに一致する型と、着信オペランド・スタック上のshortの配列をintで置換して、発信型の状態を生成できる場合に、型安全です。
instructionIsTypeSafe(saload, Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [int, arrayOf(short)], int,
StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
sastore命令は、発信型の状態を生成する着信オペランド・スタックからint、intおよびshortの配列に一致する型を有効なポップできる場合に、型安全です。
instructionIsTypeSafe(sastore, _Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
canPop(StackFrame, [int, int, arrayOf(short)], NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
sipush命令は、発信型の状態を生成する着信オペランド・スタックに型intを効果的にプッシュできる場合に、型安全です。
instructionIsTypeSafe(sipush(_Value), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
validTypeTransition(Environment, [], int, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
swap命令は、入力オペランド・スタックの2つのカテゴリ1型(Type1およびType2)を、Type2型およびType1型で有効に置換して送信型の状態を生成できる場合に、型安全です。
instructionIsTypeSafe(swap, _Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
StackFrame = frame(_Locals, [Type1, Type2 | Rest], _Flags),
popCategory1([Type1 | Rest], Type1, Rest),
popCategory1([Type2 | Rest], Type2, Rest),
NextStackFrame = frame(_Locals, [Type2, Type1 | Rest], _Flags),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
tableswitch命令は、キーがソートされている場合には型安全であり、新しい型状態BranchStackFrameを生成する着信オペランド・スタックからintを有効ポップでき、命令のターゲットはすべて、着信型状態として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).
ワイド命令は、拡張された命令と同じ規則に従います。
instructionHasEquivalentTypeRule(wide(WidenedInstruction),
WidenedInstruction).
StackMapTable属性を含まないclassファイル(必ずしもバージョン番号が49.0以下)は、型推論を使用して検証する必要があります。
リンク時に、ベリファイアは、各メソッドでデータ・フロー分析を実行して、classファイルの各メソッドのCode属性のcode配列をチェックします。 ベリファイアは、プログラム内の任意の時点で、そのポイントに到達するためにどのコードパスが取られても、次のすべてに当てはまることを確認します。
オペランド・スタックは常に同じサイズで、同じタイプの値が含まれます。
適切な型の値が含まれていることがわからないかぎり、ローカル変数はアクセスされません。
メソッドは、適切な引数を使用して呼び出されます。
フィールドは、適切なタイプの値を使用してのみ割り当てられます。
すべてのopコードには、オペランド・スタックおよびローカル変数配列の引数が適切に入力されています。
効率上の理由から、ベリファイアによって原則的に実行できる特定のテストは、メソッドのコードが実際に最初に呼び出されるまで遅延されます。 そのため、必要でないかぎり、ベリファイアはclassファイルのロードを回避します。
たとえば、メソッドがクラスAのインスタンスを返す別のメソッドを起動し、そのインスタンスが同じタイプのフィールドにのみ割り当てられている場合、ベリファイアはクラスAが実際に存在するかどうかを確認しません。 ただし、B型のフィールドに割り当てられている場合は、Aが Bのサブクラスであることを確認するために、Aと Bの両方の定義をロードする必要があります。
各メソッドのコードは個別に検証されます。 最初に、コードを構成するバイトが一連の命令に分割され、各命令の開始のcode配列への索引が配列に配置されます。 その後、ベリファイアはコードを2回実行し、命令を解析します。 このパスでは、メソッド内の各Java Virtual Machine命令に関する情報を保持するためにデータ構造が構築されます。 各命令のオペランドが存在する場合は、それらが有効であるかどうかがチェックされます。 次に例を示します。
ブランチは、メソッドのcode配列の範囲内である必要があります。
すべての制御フロー命令のターゲットは、それぞれ命令の開始です。 wide命令の場合、wide opcodeは命令の開始とみなされ、そのwide命令によって変更された操作を提供するopcodeは命令の開始とはみなされません。 命令の途中への分岐は許可されません。
どの命令も、そのメソッドが割り当てるローカル変数数以上のインデックスでローカル変数にアクセスまたは変更できません。
定数プールへのすべての参照は、適切な型のエントリである必要があります。 (たとえば、命令getfieldはフィールドを参照する必要があります。)
コードは命令の途中で終わることはありません。
実行はコードの終わりから外すことはできません。
例外ハンドラごとに、ハンドラによって保護されるコードの開始点と終了点は、命令の先頭にあるか、終了点の場合はコードの終わりを直前にする必要があります。 開始点は終了点より前にする必要があります。 例外ハンドラ・コードは、有効な命令から開始する必要があり、wide命令によって変更されるopcodeから開始しないでください。
メソッドの各命令について、ベリファイアは、その命令の実行前に、オペランドスタックの内容とローカル変数配列の内容を記録します。 オペランド・スタックの場合は、スタックの高さとその各値のタイプを知る必要があります。 ローカル変数ごとに、そのローカル変数の内容のタイプ、またはローカル変数に使用不可または不明な値が含まれている(初期化されていない可能性がある)ことを認識する必要があります。 バイトコード・ベリファイアは、オペランド・スタックの値型を決定する際に、整数型(byte、short、charなど)を区別する必要はありません。
次に、データフローアナライザが初期化されます。 メソッドの最初の命令の場合、パラメータを表すローカル変数には、最初にメソッドの型記述子によって示される型の値が含まれます。オペランド・スタックは空です。 他のすべてのローカル変数に不正な値が含まれています。 まだ調査されていないほかの手順については、オペランドスタックまたはローカル変数に関する情報はありません。
最後に、データフローアナライザが実行されます。 各命令について、「changed」ビットは、この命令を調べる必要があるかどうかを示します。 最初は、「changed」ビットは最初の命令に対してのみ設定されます。 データ・フロー・アナライザは、次のループを実行します。
「changed」ビットが設定されているJava Virtual Machine命令を選択します。 「changed」ビットが設定されている命令が残っていない場合、メソッドは正常に検証されました。 それ以外の場合は、選択した命令の「changed」ビットをオフにします。
オペランドスタックおよびローカル変数配列に対する命令の効果を次のようにモデル化します。
命令がオペランドスタックからの値を使用する場合は、スタック上に十分な数の値が存在し、スタック上の最上位の値が適切な型であることを確認してください。 それ以外の場合、検証は失敗します。
命令がローカル変数を使用する場合は、指定されたローカル変数に適切な型の値が含まれていることを確認します。 それ以外の場合、検証は失敗します。
命令がオペランドスタックに値をプッシュする場合は、新しい値のためにオペランドスタックに十分な余地があることを確認してください。 モデル化されたオペランドスタックの先頭に、指定されたタイプを追加します。
命令がローカル変数を変更した場合は、ローカル変数に新しい型が含まれていることを記録します。
現在の命令に従うことができる命令を判別します。 後任者の指示は、次のいずれかになります。
次の命令は、現在の命令が無条件制御転送命令でない場合(goto、return、athrowなど)。 メソッドの最後の命令を「フォールオフ」できる場合、検証は失敗します。
条件付きブランチまたは無条件のブランチまたはスイッチのターゲット。
この説明の例外ハンドラ。
現在の命令の実行終了時のオペランドスタックおよびローカル変数配列の状態を、次のように後続の各命令にマージします。
後続命令が初めてアクセスされた場合は、ステップ2で計算されたオペランドスタックとローカル変数値が、後続命令を実行する前にオペランドスタックとローカル変数配列の状態であることを記録します。 後続命令の「changed」ビットを設定します。
後続命令が以前に表示された場合は、ステップ2で計算されたオペランド・スタックおよびローカル変数値を、すでに存在する値にマージします。 値に変更がある場合は、「changed」ビットを設定します。
例外ハンドラへの制御転送の特別な場合:
例外ハンドラによって示された例外タイプの単一のオブジェクトが、後続命令の実行前のオペランドスタックの状態であることを記録します。 オペランドスタックには、命令が値をプッシュしたかのように、この単一値に十分な余裕がある必要があります。
ステップ2の直前のローカル変数値が、後続の命令を実行する前のローカル変数配列の状態であることを記録します。 ステップ2で計算されたローカル変数値は無関係です。
ステップ1に進みます。
2つのオペランド・スタックをマージするには、各スタックの値の数が同じである必要があります。 スタック上の値の型も同じである必要があります。ただし、異なる型指定のreference値が2つのスタック上の対応する場所に表示される場合がある点が異なります。 この場合、マージされたオペランド・スタックには、2つの型の最初の共通スーパークラス、スーパーインタフェースまたは配列スーパータイプを表す参照型が含まれます。 このようなreference型は、Object型がすべてのクラス、インタフェースおよび配列型のスーパークラスであるため、常に存在します。 オペランドスタックをマージできない場合、メソッドの検証は失敗します。
2つのローカル変数配列状態をマージするために、対応するローカル変数のペアが比較されます。 マージされたローカル変数の値は、前述のルールを使用して計算されます。ただし、対応する値は異なるプリミティブ型であることが許可されます。 その場合、ベリファイアは、マージされたローカル変数に使用不可能な値が含まれていることを記録します。
データ・フロー・アナライザが検証失敗を報告せずにメソッドで実行された場合、そのメソッドはclassファイル・ベリファイアによって正常に検証されました。
特定の命令とデータ型によって、データフローアナライザが複雑になります。 ここでは、それぞれについて詳しく説明します。
longおよびdouble型の値 long型およびdouble型の値は、検証プロセスによって特別に処理されます。
long型またはdouble型の値が索引nのローカル変数に移動されるたびに、索引n+1は、索引nの値によって予約されており、ローカル変数索引として使用できないことを示すように特別にマークされます。 索引n+1に以前存在していた値はすべて使用できなくなります。
値が索引nのローカル変数に移動されるたびに、索引n-1がlong型またはdouble型の値の索引かどうかが調べられます。 その場合、索引n-1のローカル変数が変更され、使用できない値が含まれていることが示されます。 索引nのローカル変数は上書きされているため、索引n-1のローカル変数はlongまたはdouble型の値を表すことができません。
オペランド・スタックでのlong型またはdouble型の値の処理は簡単です。ベリファイアでは、値をスタック上の単一値として扱います。 たとえば、dadd opcodeの検証コード(2つのdouble値を追加)は、スタック上の上位2つの項目が両方ともdouble型であることをチェックします。 オペランド・スタックの長さを計算する場合、long型とdouble型の値の長さは2です。
オペランド・スタックを操作する型指定されていない命令は、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 (Compiling for the Java Virtual Machine)を参照してください。)
クラスmyClassのインスタンス初期化メソッド(§2.9.1)では、新しい未初期化オブジェクトがローカル変数0のthis引数として認識されます。 このメソッドがmyClassの別のインスタンス初期化メソッドまたはthis上のその直接スーパークラスを呼び出す前に、thisに対してメソッドが実行できる唯一の操作は、myClass内で宣言されたフィールドを割り当てることです。
インスタンス・メソッドでデータフロー分析を行う場合、ベリファイアはローカル変数0を初期化して現在のクラスのオブジェクトを含めます。また、インスタンス初期化メソッドの場合、ローカル変数0には、初期化されていないオブジェクトを示す特殊な型が含まれます。 このオブジェクトで適切なインスタンス初期化メソッドが(現在のクラスまたはその直接のスーパークラスから)呼び出されると、オペランド・スタックのベリファイア・モデルおよびローカル変数配列でこの特殊型のすべての出現は、現在のクラス型に置き換えられます。 ベリファイアは、初期化される前、またはオブジェクトを複数回初期化する前に、新しいオブジェクトを使用するコードを拒否します。 また、メソッドの通常のリターンごとに、このメソッドのクラスまたは直接スーパークラスでインスタンス初期化メソッドが呼び出されるようにします。
同様に、Java Virtual Machine命令newの結果として、オペランド・スタックのベリファイア・モデルに特別な型が作成され、プッシュされます。 特殊な型は、クラスインスタンスが作成された命令と、作成された初期化されていないクラスインスタンスの型を示します。 初期化されていないクラス・インスタンスのクラスで宣言されたインスタンス初期化メソッドがそのクラス・インスタンスで呼び出されると、特別な型のすべての出現は、そのクラス・インスタンスの意図した型に置き換えられます。 このタイプの変更は、データフロー分析の進行に伴って後続の指示に伝播される場合があります。
命令番号は、オペランドスタック上に存在しているクラスの未初期化のインスタンスが複数存在する可能性があるため、特殊な型の一部として格納する必要があります。 たとえば、次を実装するJava Virtual Machine命令シーケンスです。
new InputStream(new Foo(), new InputStream("foo"))
一度にオペランド・スタックにInputStreamの初期化されていないインスタンスが2つある可能性があります。 インスタンス初期化メソッドがクラス・インスタンスで呼び出されると、オペランド・スタックまたはクラス・インスタンスと同じオブジェクトであるローカル変数配列で特殊型の出現のみが置換されます。
finally try-finallyコンストラクトを実装するために、バージョン番号50.0以下のclassファイルを生成するJavaプログラミング言語のコンパイラでは、例外処理機能と、jsr (サブルーチンにジャンプ)とret (サブルーチンから戻す)の2つの特別な命令を使用できます。 finally句は、例外ハンドラのコードと同様に、そのメソッドのJava Virtual Machineコード内のサブルーチンとしてコンパイルされます。 サブルーチンを呼び出す jsr命令が実行されると、その戻りアドレス、実行される jsrのあとの命令のアドレスを、returnAddress型の値としてオペランドスタックにプッシュします。 サブルーチンのコードは、戻りアドレスをローカル変数に格納します。 サブルーチンの終了時に、ret命令はローカル変数から戻りアドレスをフェッチし、戻りアドレスで制御を命令に転送します。
制御は、いくつかの異なる方法でfinally句に転送できます(finallyサブルーチンを起動できます)。 try句が正常に完了すると、finallyサブルーチンは、次の式を評価する前にjsr命令を介して呼び出されます。 try句の外部で制御を転送するtry句内のbreakまたはcontinueは、jsrをfinally句のコードに最初に実行します。 try句がreturnを実行する場合、コンパイルされたコードは次のことを行います。
戻り値(ある場合)をローカル変数に保存します。
finally句のコードに対してjsrを実行します。
finally句から戻ると、ローカル変数に保存された値が戻されます。
コンパイラは、try句によってスローされた例外をキャッチする特別な例外ハンドラを設定します。 try句で例外がスローされた場合、この例外ハンドラは次の処理を実行します。
例外をローカル変数に保存します。
finally句に対してjsrを実行します。
finally句から戻ると、例外が再スローされます。
try-finallyコンストラクトの実装の詳細は、§3.13を参照してください。
finally句のコードは、ベリファイアに特別な問題を示します。 通常、特定の命令に複数のパスを介して到達でき、特定のローカル変数にそれらの複数のパスを介して互換性のない値が含まれている場合、そのローカル変数は使用できなくなります。 ただし、finally句は複数の異なる場所からコールされる可能性があり、次のような状況が発生します。
例外ハンドラからの呼出しには、例外を含む特定のローカル変数が含まれる場合があります。
returnを実装するための呼出しには、戻り値を含むローカル変数がある場合があります。
try句の最後からの呼出しは、同じローカル変数内で不確定な値を持つ場合があります。
finally句自体のコードは検証に合格する場合がありますが、ret命令の後続のすべての更新が完了すると、検証者は、例外ハンドラが例外を保持することを期待するローカル変数、またはリターン・コードが戻り値を保持することを期待するローカル変数に、不確定な値が含まれることに注目します。
finally句を含むコードの検証は複雑です。 基本的な考え方は次のとおりです。
各命令は、その命令に到達するために必要な jsrターゲットのリストを追跡します。 ほとんどのコードでは、このリストは空です。 finally句のコード内の命令は、長さ1です。 ネストされたfinallyコードを乗算する場合(非常にまれです)は、1よりも長い場合があります。
その命令に到達するために必要な各命令および各 jsrについて、ビットベクトルは、jsr命令の実行以降にアクセスまたは変更されたすべてのローカル変数から保持されます。
サブルーチンからの戻り値を実装するret命令を実行する場合、その命令を戻すことができるサブルーチンは1つのみである必要があります。 2つの異なるサブルーチンは、その実行を単一のret命令にマージできません。
ret命令に対してデータ・フロー分析を実行するには、特別な手順を使用します。 ベリファイアは、命令が返される必要があるサブルーチンを認識するため、サブルーチンを呼び出すすべての jsr命令を検出し、ret命令の時点でのオペランドスタックおよびローカル変数配列の状態を、jsrのあとの命令のオペランドスタックおよびローカル変数配列にマージできます。 マージでは、ローカル変数に特別な値セットが使用されます。
ビット・ベクトル(前述の構成)がサブルーチンによってアクセスまたは変更されたことを示すローカル変数には、retの時点でローカル変数の型を使用します。
その他のローカル変数の場合は、jsr命令の前にローカル変数の型を使用します。
Java Virtual Machineの次の制限は、classファイル形式で暗黙的です。
クラスごとまたはインタフェースごとの定数プールは、ClassFile構造体の16ビットconstant_pool_countフィールド(§4.1)によって65535エントリに制限されます。 これは、単一のクラスまたはインタフェースの合計複雑度に対する内部制限として機能します。
クラスまたはインタフェースによって宣言できるフィールドの数は、ClassFile構造体のfields_count項目のサイズ(§4.1)によって65535に制限されます。
ClassFile構造のfields_countアイテムの値には、スーパークラスまたはスーパーインタフェースから継承されたフィールドは含まれません。
クラスまたはインタフェースで宣言できるメソッドの数は、ClassFile構造体のmethods_count項目のサイズによって65535に制限されます(§4.1)。
ClassFile構造のmethods_countアイテムの値には、スーパークラスまたはスーパーインタフェースから継承されるメソッドは含まれません。
クラスまたはインタフェースの直接スーパーインタフェースの数は、ClassFile構造体のinterfaces_count項目のサイズによって65535に制限されます(§4.1)。
メソッド(§2.6)の起動時に作成されるフレームのローカル変数配列内のローカル変数の最大数は、メソッドのコードを提供するCode属性(§4.7.3)のmax_locals項目のサイズ、およびJava Virtual Machine命令セットの16ビットのローカル変数索引付けによって、65535に制限されます。
long型とdouble型の値はそれぞれ、2つのローカル変数を予約し、max_locals値に2つの単位を提供するとみなされるため、これらの型のローカル変数を使用すると、この制限がさらに軽減されます。
フレーム内のオペランド・スタックのサイズ(§2.6)は、Code属性のmax_stackフィールド(§4.7.3)によって65535の値に制限されます。
long型とdouble型の値はそれぞれ、max_stack値に2つの単位を寄与するとみなされるため、オペランド・スタックでこれらの型の値を使用すると、この制限がさらに減少します。
メソッド・パラメータの数は、メソッド記述子(§4.3.3)の定義によって255に制限されます。この制限には、インスタンス・メソッドまたはインタフェース・メソッド呼出しの場合、thisの単位が1つ含まれます。
メソッド記述子は、long型またはdouble型のパラメータが長さに2つの単位を寄与するメソッド・パラメータ長の概念によって定義されるため、これらの型のパラメータによって制限がさらに減少します。
フィールド名、メソッド名、フィールド記述子およびメソッド記述子、およびその他の定数文字列値(ConstantValue (§4.7.2)属性で参照されるものを含む)の長さは、CONSTANT_Utf8_info構造の16ビット符号なしlength項目によって65535文字に制限されます(§4.4.7)。
制限は、エンコードされた文字数ではなく、エンコーディングのバイト数にあることに注意してください。 UTF-8は、2バイトまたは3バイトを使用して一部の文字をエンコードします。 したがって、マルチバイト文字を組み込む文字列はさらに制約されます。
配列内のディメンションの数は、multianewarray命令のディメンション・オペコードのサイズと、multianewarray、anewarrayおよびnewarray命令(§4.9.1、§4.9.2)に課される制約によって、255に制限されます。