モジュール java.base

パッケージ java.lang.invoke

java.lang.invokeパッケージは、Java Virtual Machineとやりとりするための低レベルのプリミティブを提供します。

Java Virtual Machine仕様で説明されているように、このパッケージの特定の型には、仮想マシンによる特別な処理が与えられます。

  • クラスMethodHandle VarHandleには、型記述子に関係なくリンクできる「シグネチャ・ポリモーフィック・メソッド」が含まれています。 通常、メソッドのリンク時には型記述子が厳密に一致する必要があります。
  • JVMのバイト・コード形式では、クラスMethodHandleMethodTypeの即値定数がサポートされます。
  • invokedynamic命令では、カスタム・メソッド呼出し動作のためにMethodHandle定数を使用してCallSiteオブジェクトを動的に解決します。
  • ldc命令では、カスタム定数値を動的に解決するためにbootstrap MethodHandle定数を使用します。

コール・サイトおよび定数の動的解決方法

次の低レベル情報は、Java仮想マシン仕様の関連部分のサマリーです。 完全な詳細については、この仕様の現在のバージョンを参照してください。

動的に計算されたコール・サイト

invokedynamic命令は、最初はリンク解除された状態です。 この状態では、起動する命令のターゲット・メソッドはありません。

JVMがinvokedynamic命令を実行するには、最初にlinkedの手順が必要です。 リンクは、呼出しの静的情報コンテンツが与えられた「ブートストラップ法」を呼び出して行い、呼出しの動作を提供するCallSiteを生成する必要があります。

invokedynamic命令は、自身のブートストラップ・メソッドを定数プール参照として静的に指定します。 定数プール参照では、invokestaticのような起動名およびメソッド・タイプの記述子と、その他の呼出し指示も指定します。

動的に計算された定数

定数プールには、CONSTANT_Dynamicとタグ付けされた定数が含まれ、その解決を実行するブートストラップ・メソッドが備えられています。 このような「動的定数」は当初未解決の状態です。 JVMで動的に計算された定数を使用するには、まず「解決済」にする必要があります。 動的に計算された定数の解決は、定数の静的情報コンテンツを指定し、静的に宣言された定数の値を生成する必要がある「ブートストラップ法」をコールすることで実現されます。

動的に計算される各定数は、その独自のブートストラップ・メソッドを定数プール参照として静的に指定します。 定数プール参照では、getstaticのような定数名およびフィールド・タイプ記述子も指定し、他のフィールド参照指示も指定します。 (つまり、動的に計算される定数は、CONSTANT_Fieldrefとして動的に計算されるコール・サイトになるのは、CONSTANT_Methodrefに対するものです。)

ブートストラップ・メソッドの実行

動的に計算されたコール・サイトまたは定数を解決するには、次のアイテムの定数プールから定数を解決します。
  • ブートストラップ・メソッド、CONSTANT_MethodHandle
  • CONSTANT_NameAndType記述子のタイプ・コンポーネントから導出されたClassまたはMethodType
  • 静的引数((静的引数自体が動的に計算される定数である可能性があることに注意してください。)がある場合)

その後、MethodHandle.invokeの場合と同様に、次の引数を指定してブートストラップ・メソッドが呼び出されます。

  • MethodHandles.Lookup (動的に計算された定数または呼出しサイトが発生する「呼び出し元クラス」のルックアップ・オブジェクト)
  • String (CONSTANT_NameAndTypeに示された名前)
  • MethodTypeまたはClass (CONSTANT_NameAndTypeの解決された型記述子)
  • 定数の解決された型記述子であるClass (動的定数の場合)
  • その他の解決済静的引数(ある場合)

動的に計算されたコール・サイトの場合、戻される結果はCallSiteへのnull以外の参照である必要があります。 コール・サイト・ターゲットの型は、起動タイプ記述子から導出された型と完全に等しく、ブートストラップ・メソッドに渡される必要があります。 これらの条件が満たされない場合は、BootstrapMethodErrorがスローされます。 正常に終了すると、呼出しサイトはinvokedynamicの手順に永続的にリンクされます。

動的に計算される定数の場合、ブートストラップ・メソッドの最初のパラメータをMethodHandles.Lookupに代入できる必要があります。 この条件が満たされない場合は、BootstrapMethodErrorがスローされます。 成功すると、ブートストラップ・メソッドの結果は解決済の定数値としてキャッシュされます。

例外がEという場合は、ブートストラップ・メソッドの実行中に例外が発生すると、解決は失敗して異常終了します。 E の型がErrorまたはサブクラスである場合、Eは再スローされます。そうでない場合は、EをラップするBootstrapMethodErrorがスローされます。 このような場合、後でinvokedynamic命令を実行したり、動的に計算された定数をロードしようとするたびに、同じエラーがスローされます。

解決のタイミング

invokedynamic命令は、最初の実行前にリンクされます。 動的に計算される定数は、最初に(スタック上でプッシュするか、ブートストラップ・メソッド・パラメータとしてリンクします)が使用される直前に解決されます。 リンケージを実装するブートストラップ・メソッド・コールは、最初の実行または最初の使用を試行しているスレッド内で発生します。

そのようなスレッドがいくつか存在する場合、ブートストラップ・メソッドがいくつかのスレッド内で並行して呼び出される可能性があります。 したがって、グローバル・アプリケーション・データにアクセスするブートストラップ・メソッドでは、競合状態に対する通常の予防策を講じる必要があります。 いずれにせよ、すべてのinvokedynamic命令は、リンクされていない状態、一意のCallSiteオブジェクトにリンクされた状態、のいずれかになります。

個別に変更可能な動作を備えたinvokedynamic命令が必要なアプリケーションでは、ブートストラップ・メソッドはそれぞれ異なるCallSiteオブジェクトを生成すべきです(リンク・リクエストごとに1つずつ)。 また、アプリケーションでは単一のCallSiteオブジェクトをいくつかのinvokedynamic命令にリンクすることもできますが、その場合、ターゲット・メソッドへの変更がすべての命令で可視になります。

単一の動的計算コール・サイトまたは定数のブートストラップ・メソッドがいくつかのスレッドによって同時に実行された場合、JVMは1つのブートストラップ・メソッドの結果を選択し、それをすべてのスレッドにインストールして可視状態にする必要があります。 他のブートストラップ・メソッド呼出しは完了できますが、その結果は無視されます。

討論:これらのルールでは、JVMがコール・サイトを共有したり、"注意"ブートストラップ・メソッド・コールを発行することはできません。 invokedynamic命令は、最初の呼出しの直前にリンク解除からリンクされるまで最大1回遷移します。 完了したブートストラップ・メソッド呼出しの効果を取り消す方法はありません。

ブートストラップ・メソッドのタイプ

動的に計算されたコール・サイトの場合、ブートストラップ・メソッドは、パラメータ・タイプがMethodHandles.LookupStringMethodTypeおよび任意の静的引数の型で起動されます。戻り型はCallSiteです。

動的に計算された定数の場合、ブートストラップ・メソッドは、パラメータ・タイプがMethodHandles.LookupStringClassおよび任意の静的引数の型で呼び出されます。戻り型はClassによって表される型です。

MethodHandle.invokeは起動されたメソッド型とブートストラップ・メソッド・ハンドルのメソッド型との間の対応を可能にするため、ブートストラップ・メソッドの宣言には柔軟性があります。 動的に計算される定数の場合、ブートストラップ・メソッド・ハンドルの最初のパラメータ型をMethodHandles.Lookupに代入できる必要があります。それ以外の制約では、動的に計算されるコール・サイトおよび動的に計算される定数のブートストラップ・メソッドに同じ柔軟性が適用されます。 ノート: この制約により、ブートストラップ・メソッドが静的引数のパラメータ・タイプのみを使用して起動される可能性があるため、静的引数(参照、名前および型のメタデータ・パラメータを宣言しない、または必要としないメソッドなど)と互換性のあるメソッドの範囲がより多くサポートされます。

たとえば、動的に計算される呼出しサイトでは、最初の引数はMethodHandles.LookupではなくObjectであり、戻り型もCallSiteではなくObjectである可能性があります。 (積上げ引数の型と数によって、適切に型指定された静的メソッドおよびコンストラクタにブートストラップ・メソッドの法的タイプが制限されることに注意してください。)

プッシュ値がプリミティブ型の場合は、ボックス変換によって参照に変換できます。 ブートストラップ・メソッドが可変引数メソッドである(修飾子ビット0x0080が設定されている)場合、ここに指定された引数の一部または全部が末尾の配列パラメータ内に集められる可能性があります。 (これは特殊なルールでなく、CONSTANT_MethodHandle定数、可変引数メソッド用の修飾子ビット、およびasVarargsCollector変換の間の相互作用の有用な結果です。)

これらの規則に基づき、動的に計算されるコール・サイトの正しいブートストラップ・メソッド宣言の例で、様々な数のNに特別な引数が指定されています。 最初の行(マークされた*)は任意の数の余分な引数に対して機能します。

静的引数型
Nブートストラップ・メソッドの例
*
  • CallSite bootstrap(Lookup caller, String name, MethodType type, Object... args)
  • CallSite bootstrap(Object... args)
  • CallSite bootstrap(Object caller, Object... nameAndTypeWithArgs)
0
  • CallSite bootstrap(Lookup caller, String name, MethodType type)
  • CallSite bootstrap(Lookup caller, Object... nameAndType)
1 CallSite bootstrap(Lookup caller, String name, MethodType type, Object arg)
2
  • CallSite bootstrap(Lookup caller, String name, MethodType type, Object... args)
  • CallSite bootstrap(Lookup caller, String name, MethodType type, String... args)
  • CallSite bootstrap(Lookup caller, String name, MethodType type, String x, int y)
最後の例では、余分な引数がそれぞれStringInteger (またはint)型であると仮定しています。 2番目から最後の例では、余分な引数はすべてString型であると仮定しています。 その他の例は、あらゆる型の追加引数で動作します。 2番目および3番目の例を除くすべての例では、動的に計算される定数定数を使用して、戻り型が、定数が宣言された型(Objectなど、常に互換性がある)であると互換性があるように変更されている場合にも、動的に計算される定数を使用します。

動的に計算される定数は、ブートストラップ・メソッドへの静的引数として提供できるため、ブートストラップ引数の型に制限はありません。 ただし、boolean型、byte型、shortまたはshort型の引数は、CONSTANT_Integerから提供される「直接」の定数プール・エントリでは、asTypeの変換は必要ないため、可変サイズ変換は実行できません。

前述の例では、戻り型は常にCallSiteですが、ブートストラップ・メソッドに必要な機能ではありません。 動的に計算される呼出しサイトの場合、ブートストラップ・メソッドの戻り型が(asType変換の使用)からCallSiteに変換可能であることのみが必要です。つまり、ブートストラップ・メソッドの戻り型がObjectまたはConstantCallSiteになる可能性があります。 動的に解決される定数の場合、ブートストラップ・メソッドの戻り型は、フィールド型記述子で表される定数の型に変換可能である必要があります。 たとえば、動的定数に"C" (char)のフィールド・タイプ記述子がある場合、ブートストラップ・メソッドの戻りタイプはObjectCharacter、または、charであり、intまたはIntegerではない可能性があります。

導入されたバージョン:
1.7