Linker
は、JavaプラットフォームのプレビューAPIです。
外部関数は通常、オンデマンドでロードできるライブラリに存在します。 各ライブラリは、特定のABI (アプリケーション・バイナリ・インタフェース)に準拠しています。 ABIは、ライブラリが構築されたコンパイラ、OS、およびプロセッサに関連付けられた呼び出し規則とデータ型のセットです。 たとえば、Linux/x64上のCコンパイラは通常、SystemV ABIに準拠するライブラリを構築します。
リンカーは、特定のABIで使用される呼び出し規則とデータ型について詳細に把握しています。 そのABIに準拠するライブラリの場合、リンカーはJVMで実行されているJavaコードとライブラリ内の外部関数を仲介できます。 特に次の点が重要です。
- リンカーを使用すると、Javaコードは「ダウンコール・メソッド・ハンドル」を介して外部関数とリンクできます
- リンカーを使用すると、外部関数が「アップコール・スタブ」の生成を介してJavaメソッド・ハンドルをコールできます。
libc
とlibm
。 これらのライブラリの関数は、「シンボル・ルックアップ」を介して公開されます。
ネイティブ関数の呼び出し
「ネイティブ・リンカー」を使用すると、Cライブラリ (ネイティブ関数)に定義されている関数に対してリンクできます。 Javaから、標準Cライブラリで定義されているstrlen
関数に停止するとします:
size_t strlen(const char *s);
strlen
を公開するダウンコール・メソッド・ハンドルを取得します:
Linker linker = Linker.nativeLinker();
MethodHandle strlen = linker.downcallHandle(
linker.defaultLookup().find("strlen").orElseThrow(),
FunctionDescriptor.of(JAVA_LONG, ADDRESS)
);
strlen
ネイティブ関数のアドレスを検索するために使用されます。 そのアドレスは、ネイティブ・リンカーのdowncallHandle(MemorySegment, FunctionDescriptor, Option...)
メソッドにFunctionDescriptor
PREVIEW (その下の詳細)として表される関数のシグネチャの「プラットフォーム依存の説明」とともに渡されます。 取得されたdowncallメソッド・ハンドルは、次のように起動されます:
try (Arena arena = Arena.ofConfined()) {
MemorySegment str = arena.allocateUtf8String("Hello");
long len = (long) strlen.invokeExact(str); // 5
}
シグネチャの説明
ネイティブ・リンカーと対話する場合、クライアントは、リンク先のC関数のシグネチャについてプラットフォームに依存する説明を提供する必要があります。 この説明(function descriptor
PREVIEW)は、C関数のパラメータ・タイプおよび戻り型(もしあれば)に関連付けられたレイアウトを定義します。
bool
、int
などのスカラーCタイプは、適切なキャリアの「値レイアウト」PREVIEWとしてモデル化されます。 スカラー・タイプとそれに対応するレイアウトとの間のマッピングは、ネイティブ・リンカーによって実装されるABIに依存します。 たとえば、C型のlong
は、Linux/x64のレイアウト定数ValueLayout.JAVA_LONG
PREVIEWにマップされますが、Windows/x64のレイアウト定数ValueLayout.JAVA_INT
PREVIEWにマップされます。 同様に、C型のsize_t
は64ビット・プラットフォームではレイアウト定数ValueLayout.JAVA_LONG
PREVIEWにマップされますが、32ビット・プラットフォームではレイアウト定数ValueLayout.JAVA_INT
PREVIEWにマップされます。
コンポジット・タイプは、「グループ・レイアウト」PREVIEWとしてモデル化されます。 具体的には、C struct
型は「構造体レイアウト」PREVIEWにマップされ、C union
型はunion layout
PREVIEWにマップされます。 構造体または共用体のレイアウトを定義する場合、クライアントはC内の対応する複合型定義のサイズおよび整列制約に注意する必要があります。 たとえば、2つの構造体フィールド間のパディングは、適切なサイズの「パディング・レイアウト」PREVIEWメンバーを結果の構造体レイアウトに追加することで、明示的にモデル化する必要があります。
最後に、int**
やint(*)(size_t*, size_t*)
などのポインタ型は、「アドレス・レイアウト」PREVIEWとしてモデル化されます。 ポインタ型の空間境界が静的にわかっている場合は、アドレス・レイアウトを「ターゲット・レイアウト」PREVIEWに関連付けることができます。 たとえば、C int[2]
配列を指すことがわかっているポインタは、ターゲット・レイアウトが要素数が2で要素タイプがValueLayout.JAVA_INT
PREVIEWのシーケンス・レイアウトであるアドレス・レイアウトとしてモデル化できます。
次の表に、C型がLinux/x64でモデル化される方法の例を示します:
Cタイプ レイアウト Javaタイプ bool
ValueLayout.JAVA_BOOLEAN
PREVIEWboolean
char
ValueLayout.JAVA_BYTE
PREVIEWbyte
short
ValueLayout.JAVA_SHORT
PREVIEWshort
int
ValueLayout.JAVA_INT
PREVIEWint
long
ValueLayout.JAVA_LONG
PREVIEWlong
long long
ValueLayout.JAVA_LONG
PREVIEWlong
float
ValueLayout.JAVA_FLOAT
PREVIEWfloat
double
ValueLayout.JAVA_DOUBLE
PREVIEWdouble
size_t
ValueLayout.JAVA_LONG
PREVIEWlong
char*
,int**
,struct Point*
ValueLayout.ADDRESS
PREVIEWMemorySegment
PREVIEWint (*ptr)[10]
ValueLayout.ADDRESS.withTargetLayout( MemoryLayout.sequenceLayout(10, ValueLayout.JAVA_INT) );MemorySegment
PREVIEWstruct Point { int x; long y; };
MemoryLayout.structLayout( ValueLayout.JAVA_INT.withName("x"), MemoryLayout.paddingLayout(32), ValueLayout.JAVA_LONG.withName("y") );MemorySegment
PREVIEWunion Choice { float a; int b; }
MemoryLayout.unionLayout( ValueLayout.JAVA_FLOAT.withName("a"), ValueLayout.JAVA_INT.withName("b") );MemorySegment
PREVIEW
ネイティブ・リンカーのすべての実装は、メモリー・レイアウトのサブセットで動作します。 より正式には、ネイティブ・リンカーNL
で次の場合、レイアウトL
がサポートされます:
L
は値レイアウトV
で、V.withoutName()
はequalPREVIEWで、次のいずれかのレイアウト定数のいずれかです:L
はアドレス・レイアウトA
で、A.withoutTargetLayout().withoutName()
はequalPREVIEWからValueLayout.ADDRESS
PREVIEWですL
は順序レイアウトS
で、次のすべての条件が保持されます:S
の境界整列制約は、その「自然整列」に設定されますS.elementLayout()
は、NL
でサポートされているレイアウトです。
L
はグループ・レイアウトG
で、次のすべての条件が保持されます:G
の整列制約は、その「自然整列」に設定されますG
のサイズは、その整列制約の倍数ですG.memberLayouts()
の各メンバー・レイアウトは、パディング・レイアウトまたはNL
でサポートされているレイアウトのいずれかですG
には、非パディング・レイアウト要素の整列、または(2)を満たすために厳密に必要なパディング以外は含まれません。
関数ポインタ
場合によっては、一部のネイティブ関数への関数ポインタとしてJavaコードを渡すのに役立つことがあります。これは「アップコール・スタブ」を使用することで実現されます。 これを示すために、C標準ライブラリの次の関数について考えてみます:void qsort(void *base, size_t nmemb, size_t size,
int (*compar)(const void *, const void *));
qsort
関数は、関数ポインタ(compar
パラメータ)として渡されるカスタム・コンパレータ関数を使用して、配列の内容をソートするために使用できます。 Javaからqsort
関数をコールできるようにするには、まず、次のように、停止コール・メソッド・ハンドルを作成する必要があります:
Linker linker = Linker.nativeLinker();
MethodHandle qsort = linker.downcallHandle(
linker.defaultLookup().find("qsort").orElseThrow(),
FunctionDescriptor.ofVoid(ADDRESS, JAVA_LONG, JAVA_LONG, ADDRESS)
);
ValueLayout.JAVA_LONG
PREVIEWを使用してC型のsize_t
型をマップし、最初のポインタ・パラメータ(配列ポインタ)と最後のパラメータ(関数ポインタ)の両方にValueLayout.ADDRESS
PREVIEWを使用します。
上で取得したqsort
ダウンコール・ハンドルを呼び出すには、関数ポインタを最後のパラメータとして渡す必要があります。 つまり、既存のメソッド・ハンドルから関数ポインタを作成する必要があります。 まず、ポインタとして渡された2つのint要素を比較できるJavaメソッドを記述します。(例:「メモリー・セグメント」PREVIEW):
class Qsort {
static int qsortCompare(MemorySegment elem1, MemorySegment elem2) {
return Integer.compare(elem1.get(JAVA_INT, 0), elem2.get(JAVA_INT, 0));
}
}
FunctionDescriptor comparDesc = FunctionDescriptor.of(JAVA_INT,
ADDRESS.withTargetLayout(JAVA_INT),
ADDRESS.withTargetLayout(JAVA_INT));
MethodHandle comparHandle = MethodHandles.lookup()
.findStatic(Qsort.class, "qsortCompare",
comparDesc.toMethodType());
int[]
配列の要素へのポインタであることがわかっているため、両方のパラメータのアドレス・レイアウトのターゲット・レイアウトとしてValueLayout.JAVA_INT
PREVIEWを指定できます。 これによって、比較メソッドが比較する配列要素のコンテンツにアクセスできるようになります。 次に、その関数記述子を適切な「メソッド・タイプ」に「ターン」PREVIEWし、これを使用してコンパレータ・メソッド・ハンドルの検索を行います。 次に、そのメソッドを指すアップコール・スタブを作成し、これを関数ポインタとして、次のようにqsort
ダウンコール・ハンドルに渡すことができます:
try (Arena arena = Arena.ofConfined()) {
MemorySegment comparFunc = linker.upcallStub(comparHandle, comparDesc, arena);
MemorySegment array = arena.allocateArray(JAVA_INT, 0, 9, 3, 4, 6, 5, 1, 8, 2, 7);
qsort.invokeExact(array, 10L, 4L, comparFunc);
int[] sorted = array.toArray(JAVA_INT); // [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
}
qsort
メソッド・ハンドルに渡します。 呼出し後、オフ・ヒープ配列の内容は、Javaで記述されたコンパレータ関数に従ってソートされます。 次に、ソートされた要素を含む新しいJava配列をセグメントから抽出します。
ポインタを返す関数
ネイティブ関数と対話する場合は、これらの関数がメモリーのリージョンを割り当て、その領域へのポインタを返すことが一般的です。 C標準ライブラリの次の関数について考えてみます:void *malloc(size_t size);
malloc
関数は、指定されたサイズのメモリーのリージョンを割り当て、そのメモリーのリージョンへのポインタを返します。このポインタは、あとでC標準ライブラリの別の関数を使用して割り当て解除されます:
void free(void *ptr);
free
関数は、メモリーのリージョンへのポインタを取得し、その領域の割当てを解除します。 この項では、「安全」割当てAPI (次に示すアプローチは、malloc
およびfree
以外の割当て関数に一般化できます)の提供を目的として、これらのネイティブ関数との対話方法を示します。
まず、次のように、malloc
およびfree
のダウンコール・メソッド・ハンドルを作成する必要があります:
Linker linker = Linker.nativeLinker();
MethodHandle malloc = linker.downcallHandle(
linker.defaultLookup().find("malloc").orElseThrow(),
FunctionDescriptor.of(ADDRESS, JAVA_LONG)
);
MethodHandle free = linker.downcallHandle(
linker.defaultLookup().find("free").orElseThrow(),
FunctionDescriptor.ofVoid(ADDRESS)
);
malloc
)を返すネイティブ・ファンクションが呼び出されると、Javaランタイムは、返されるポインタのサイズまたは存続期間を把握できません。 次のコードについて検討します。
MemorySegment segment = (MemorySegment)malloc.invokeExact(100);
malloc
ダウンコール・メソッド・ハンドルによって返されるセグメントのサイズは、zeroです。 さらに、返されるセグメントのスコープは、常に有効である新しいスコープです。 セグメントに安全にアクセスできるようにするには、セグメントを適切なサイズ(100、この場合は)にサイズ変更する必要があります。 また、セグメントを既存の「アリーナ」PREVIEWに接続して、Javaコードから直接作成された他のネイティブ・セグメントと同様に、セグメントをバッキングするメモリーのリージョンの存続期間を自動的に管理できるようにすることも望ましい場合があります。 これらの操作はいずれも、次のように制限付きメソッドMemorySegment.reinterpret(long, Arena, Consumer)
PREVIEWを使用して実行されます:
MemorySegment allocateMemory(long byteSize, Arena arena) throws Throwable {
MemorySegment segment = (MemorySegment) malloc.invokeExact(byteSize); // size = 0, scope = always alive
return segment.reinterpret(byteSize, arena, s -> {
try {
free.invokeExact(s);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}); // size = byteSize, scope = arena.scope()
}
allocateMemory
メソッドは、2つのパラメータを受け入れます: サイズとアリーナ。 このメソッドは、malloc
ダウンコール・メソッド・ハンドルをコールし、新しいサイズ(allocateMemory
メソッドに渡されるサイズ)および新しいスコープ(指定されたアリーナのスコープ)を指定することで、返されたセグメントを安全に再解釈します。 このメソッドは、指定されたアリーナが閉じられるときに実行される「クリーン・アップ処理」も指定します。 驚くべきことに、クリーンアップ・アクションは、セグメントをfree
ダウンコール・メソッド・ハンドルに渡して、メモリーの基礎となるリージョンの割当てを解除します。 allocateMemory
メソッドは次のように使用できます:
try (Arena arena = Arena.ofConfined()) {
MemorySegment segment = allocateMemory(100, arena);
} // 'free' called here
allocateMemory
から取得したセグメントが、限定されたアリーナによって管理される他のセグメントとして機能することに注意してください。 具体的には、取得したセグメントは必要なサイズを持ち、単一のスレッド(狭いアリーナを作成したスレッド)によってのみアクセスでき、その存続期間は周囲のtry-with-resourcesブロックに関連付けられます。
可変個引数関数
可変個引数関数とは、可変数と引数の型を受け入れることができるC関数です。 次のように宣言されます:- 次のように、仮パラメータ・リストの最後に末尾の省略記号(
...
)が付きます:void foo(int x, ...);
- 次のようなプロトタイプ・レス関数と呼ばれる、空の仮パラメータ・リストを使用:
void foo();
...
または空の仮パラメータ・リストを固定数値およびタイプの「可変個引数パラメータ」のリストで置き換えることで、「特殊化」を複数の可変個引数関数にできるテンプレートです。
可変個引数として渡される値は、Cでデフォルトの引数昇格を受けることに注意してください。 たとえば、次の引数プロモーションが適用されます:
_Bool
->unsigned int
[signed] char
->[signed] int
[signed] short
->[signed] int
float
->double
ネイティブ・リンカーは、特殊な形式の可変個引数関数のリンクのみをサポートします。 特殊な形式の可変個引数関数を、特殊な形式を記述する関数記述子を使用してリンクできます。 さらに、パラメータ・リストの最初の可変個引数パラメータを示すには、Linker.Option.firstVariadicArg(int)
PREVIEWリンカー・オプションを指定する必要があります。 対応する引数レイアウト(もしあれば)、および特殊な関数記述子の次のすべての引数レイアウトは、「可変個引数レイアウト」と呼ばれます。 プロトタイプ・レス関数の場合、Linker.Option.firstVariadicArg(int)
PREVIEWに渡される索引は常に0
である必要があります。
ネイティブ・リンカーは、特殊な関数記述子を、デフォルトの引数プロモーション (前述のとおり)の対象となるC型に対応する任意の可変個引数レイアウトにリンクする試みを拒否します。 却下されるレイアウトはプラットフォーム固有ですが、例として: Linux/x64では、ValueLayout.JAVA_BOOLEAN
PREVIEW、ValueLayout.JAVA_BYTE
PREVIEW、ValueLayout.JAVA_CHAR
PREVIEW、ValueLayout.JAVA_SHORT
PREVIEWおよびValueLayout.JAVA_FLOAT
PREVIEWのレイアウトは拒否されます。
既知の可変個引数関数とは、C標準ライブラリで定義されるprintf
関数です:
int printf(const char *format, ...);
printf("%d plus %d equals %d", 2, 2, 4);
(char*, int, int, int)
です。 次に、「リンカー・オプション」PREVIEWを使用して、指定された関数記述子(0から開始)の最初の可変個引数レイアウトの位置を指定する必要があります。 この場合、最初のパラメータが書式文字列(非可変個引数)であるため、最初の可変個引数索引を次のように1に設定する必要があります:
Linker linker = Linker.nativeLinker();
MethodHandle printf = linker.downcallHandle(
linker.defaultLookup().find("printf").orElseThrow(),
FunctionDescriptor.of(JAVA_INT, ADDRESS, JAVA_INT, JAVA_INT, JAVA_INT),
Linker.Option.firstVariadicArg(1) // first int is variadic
);
try (Arena arena = Arena.ofConfined()) {
int res = (int)printf.invokeExact(arena.allocateUtf8String("%d plus %d equals %d"), 2, 2, 4); //prints "2 plus 2 equals 4"
}
安全に関する考慮事項
ダウンコール・メソッド・ハンドルの作成は本質的に安全ではありません。 一般的に、外部ライブラリ内のシンボルには十分なシグネチャ情報 (例、外部ファンクション・パラメータのアリティおよびタイプ)が含まれていません。 その結果、リンカーのランタイムはリンケージ・リクエストを検証できません。 クライアントが無効なリンケージ・リクエスト(例:引数のレイアウトが多すぎる関数記述子を指定)を介して取得されたダウン・コール・メソッド・ハンドルと対話する場合、このような対話の結果は不特定であり、JVMがクラッシュする可能性があります。アップコール・スタブが外部関数に渡されると、アップコール・スタブに関連付けられた関数ポインタをアップコール・スタブの型と互換性のない型にキャストし、結果として得られる関数ポインタを介して関数を呼び出そうとすると、JVMクラッシュが発生する可能性があります。 さらに、アップコール・スタブに関連付けられたメソッド・ハンドルが「メモリー・セグメント」PREVIEWを返す場合、クライアントは、アップコールの完了後にこのアドレスが無効になることができないことを確認する必要があります。 これは、通常、アップ・コールはダウン・コール・メソッド・ハンドルの起動のコンテキストで実行されるため、不特定の動作を引き起こし、JVMがクラッシュする可能性があります。
- 実装要件:
- このインタフェースの実装は不変、スレッド・セーフ、およびvalue-basedです。
- 導入されたバージョン:
- 19
-
ネストされたクラスのサマリー
ネストされたクラス修飾子と型インタフェース説明static interface
Preview.リンカー・オプションは、リンケージ・リクエストに追加のパラメータを指定するために使用されます。 -
メソッドのサマリー
修飾子と型メソッド説明一般的に使用される一連のライブラリ内のシンボル・ルックアップを返します。downcallHandle
(FunctionDescriptorPREVIEW function, Linker.OptionPREVIEW... options) 指定されたシグネチャで外部関数を呼び出すために使用されるメソッド・ハンドルを作成します。downcallHandle
(MemorySegmentPREVIEW address, FunctionDescriptorPREVIEW function, Linker.OptionPREVIEW... options) 指定されたシグネチャとアドレスで外部関数を呼び出すために使用されるメソッド・ハンドルを作成します。基礎となるネイティブ・プラットフォームに関連付けられたABIのリンカーを返します。upcallStub
(MethodHandle target, FunctionDescriptorPREVIEW function, ArenaPREVIEW arena, Linker.OptionPREVIEW... options) 指定されたアリーナに関連付けられた関数ポインタとして、ほかの外部関数に渡すことができるアップコール・スタブを作成します。
-
メソッドの詳細
-
nativeLinker
基礎となるネイティブ・プラットフォームに関連付けられたABIのリンカーを返します。 基礎となるネイティブ・プラットフォームは、Javaランタイムが現在実行されているOSとプロセッサの組合せです。- APIのノート:
- 現在、OSとプロセッサの異なる組み合わせに対してリンカーを取得することはできません。
- 実装上のノート:
- 返されるリンカーに関連付けられた「デフォルト・ルックアップ」によって公開されるライブラリは、Javaランタイムが現在実行されているプロセスにロードされるネイティブ・ライブラリです。 たとえば、Linuxの場合、これらのライブラリには通常、
libc
、libm
およびlibdl
が含まれます。 - 戻り値:
- ベースとなるネイティブ・プラットフォームに関連付けられたABIのリンカー
- 例外:
UnsupportedOperationException
- 基盤となるネイティブ・プラットフォームがサポートされていない場合。
-
downcallHandle
MethodHandle downcallHandle(MemorySegmentPREVIEW address, FunctionDescriptorPREVIEW function, Linker.OptionPREVIEW... options) 指定されたシグネチャとアドレスで外部関数を呼び出すために使用されるメソッド・ハンドルを作成します。このメソッドの呼出しは、次のコードと同じです:
linker.downcallHandle(function).bindTo(symbol);
このメソッドは「制限付き」です。 制限されたメソッドは安全ではなく、誤って使用するとJVMがクラッシュしたり、悪化したりするとメモリーが破損する可能性があります。 したがって、クライアントは制限付きメソッドに応じて屈折し、可能な場合は安全でサポートされている機能を使用する必要があります。
- パラメータ:
address
- 「ベース・アドレス」PREVIEWがターゲット外部関数のアドレスであるネイティブ・メモリー・セグメント。function
- ターゲット外部関数の関数記述子。options
- このリンケージ・リクエストに関連付けられたリンカー・オプション。- 戻り値:
- ダウンコール・メソッド・ハンドル。
- 例外:
IllegalArgumentException
- 指定された関数記述子がこのリンカーでサポートされていない場合。IllegalArgumentException
-!address.isNative()
の場合、またはaddress.equals(MemorySegment.NULL)
の場合。IllegalArgumentException
- リンカー・オプションの無効な組み合わせが指定されている場合。IllegalCallerException
- 呼び出し元が、ネイティブ・アクセスが有効になっていないモジュール内にある場合。- 関連項目:
-
downcallHandle
MethodHandle downcallHandle(FunctionDescriptorPREVIEW function, Linker.OptionPREVIEW... options) 指定されたシグネチャで外部関数を呼び出すために使用されるメソッド・ハンドルを作成します。返されたメソッド・ハンドルに関連付けられたJava 「メソッド・タイプ」は、引数からのderivedPREVIEWで、関数記述子内の戻りレイアウトですが、ターゲット外部関数のアドレスが導出される
MemorySegment
PREVIEW型の追加の先頭パラメータを備えています。 さらに、関数記述子の戻りレイアウトがグループ・レイアウトの場合、結果として作成されるダウンコール・メソッド・ハンドルは、ダウンコール・メソッド・ハンドルによって返される構造体に関連付けられたメモリー・リージョンを割り当てるためにリンカー・ランタイムによって使用される、SegmentAllocator
PREVIEW型の追加の先頭パラメータを受け入れます。ダウンコール・メソッド・ハンドルを呼び出すと、リンカーは、対応するレイアウトがアドレス・レイアウトPREVIEWである
MemorySegment
PREVIEW型の引数A
に対して、次の保証を提供します:A.scope().isAlive() == true
。 それ以外の場合、呼出しはIllegalStateException
をスローA.isAccessibleBy(T) == true
などのスレッドT
で起動が発生します。 それ以外の場合、呼出しはWrongThreadException
をスローA
は、起動中も存続します。 たとえば、A
が「共有アリーナ」PREVIEWを使用して取得された場合、ダウンコール・メソッド・ハンドルがまだ実行されている間にアリーナを「閉じ」PREVIEWようとすると、IllegalStateException
になります。
さらに、指定された関数記述子の戻りレイアウトが「アドレス・レイアウト」PREVIEWの場合、戻されたメソッド・ハンドルを呼び出すと、常に存続している新しいスコープに関連付けられたネイティブ・セグメントが返されます。 通常の状況では、返されるセグメントのサイズは
0
です。 ただし、関数記述子の戻りレイアウトに「ターゲット・レイアウト」PREVIEWT
がある場合、返されるセグメントのサイズはT.byteSize()
に設定されます。外部関数のターゲット・アドレスを表す
MemorySegment
PREVIEWがMemorySegment.NULL
PREVIEWアドレスである場合、戻されたメソッド・ハンドルはIllegalArgumentException
をスローします。 返されたメソッド・ハンドルは、渡された引数がnull
である場合、さらにNullPointerException
をスローします。このメソッドは「制限付き」です。 制限されたメソッドは安全ではなく、誤って使用するとJVMがクラッシュしたり、悪化したりするとメモリーが破損する可能性があります。 したがって、クライアントは制限付きメソッドに応じて屈折し、可能な場合は安全でサポートされている機能を使用する必要があります。
- パラメータ:
function
- ターゲット外部関数の関数記述子。options
- このリンケージ・リクエストに関連付けられたリンカー・オプション。- 戻り値:
- ダウンコール・メソッド・ハンドル。
- 例外:
IllegalArgumentException
- 指定された関数記述子がこのリンカーでサポートされていない場合。IllegalArgumentException
- リンカー・オプションの無効な組み合わせが指定されている場合。IllegalCallerException
- 呼び出し元が、ネイティブ・アクセスが有効になっていないモジュール内にある場合。
-
upcallStub
MemorySegmentPREVIEW upcallStub(MethodHandle target, FunctionDescriptorPREVIEW function, ArenaPREVIEW arena, Linker.OptionPREVIEW... options) 指定されたアリーナに関連付けられた関数ポインタとして、ほかの外部関数に渡すことができるアップコール・スタブを作成します。 このような関数ポインタを外部コードから呼び出すと、指定されたメソッド・ハンドルが実行されます。返されたメモリー・セグメントのアドレスは、新しく割り当てられたアップコール・スタブを指し、指定されたアリーナに関連付けられます。 したがって、返されたアップコール・スタブ・セグメントの寿命は、指定されたアリーナによって制御されます。 たとえば、指定されたアリーナが限定されたアリーナの場合、指定された限定されたアリーナがclosedPREVIEWのとき、返されるアップコール・スタブ・セグメントは割り当て解除されます。
対応するレイアウトが「アドレス・レイアウト」PREVIEWであるアップコール・スタブ引数は、常に存在する新しいスコープに関連付けられたネイティブ・セグメントです。 通常の場合、このセグメント引数のサイズは
0
です。 ただし、アドレス・レイアウトに「ターゲット・レイアウト」PREVIEWT
がある場合、セグメント引数のサイズはT.byteSize()
に設定されます。ターゲット・メソッド・ハンドルは例外をスローしません。 ターゲット・メソッド・ハンドルが例外をスローすると、JVMは突然終了します。 これを回避するには、クライアントがtry/catchブロック内のターゲット・メソッド・ハンドルのコードをラップして、予期しない例外を捕捉する必要があります。 これは、
MethodHandles.catchException(MethodHandle, Class, MethodHandle)
メソッド・ハンドル・コンビネータを使用して実行でき、対応するcatchブロックで必要に応じて例外を処理できます。このメソッドは「制限付き」です。 制限されたメソッドは安全ではなく、誤って使用するとJVMがクラッシュしたり、悪化したりするとメモリーが破損する可能性があります。 したがって、クライアントは制限付きメソッドに応じて屈折し、可能な場合は安全でサポートされている機能を使用する必要があります。
- パラメータ:
target
- ターゲット・メソッド・ハンドル。function
- upcallスタブ関数記述子。arena
- 返されたアップコール・スタブ・セグメントに関連付けられたアリーナ。options
- このリンケージ・リクエストに関連付けられたリンカー・オプション。- 戻り値:
- アドレスがアップ・コール・スタブのアドレスである長さゼロのセグメント。
- 例外:
IllegalArgumentException
- 指定された関数記述子がこのリンカーでサポートされていない場合。IllegalArgumentException
-target
のタイプがfunction
のderivedPREVIEW型と互換性がない場合。IllegalArgumentException
- ターゲット・メソッド・ハンドルが例外をスローできると判断された場合。IllegalStateException
-arena.scope().isAlive() == false
の場合WrongThreadException
-arena
が限定されたアリーナで、このメソッドは、アリーナの所有者スレッド以外のスレッドT
からコールされます。IllegalCallerException
- 呼び出し元が、ネイティブ・アクセスが有効になっていないモジュール内にある場合。
-
defaultLookup
SymbolLookupPREVIEW defaultLookup()一般的に使用される一連のライブラリ内のシンボル・ルックアップを返します。各
Linker
PREVIEWは、Linker
PREVIEWでサポートされているOSとプロセッサの組合せで広く認識されているライブラリを選択します。 したがって、シンボル・ルックアップによって公開されるシンボルの正確なセットは指定されず、Linker
PREVIEWによって異なります。- 実装上のノート:
defaultLookup()
の結果は、時間の経過とともに安定している一連の記号を公開することを強くお薦めします。 以前にシンボル・ルックアップによって公開されたシンボルが公開されなくなった場合、defaultLookup()
のクライアントは失敗する可能性があります。実装者が複数のOSとプロセッサの組合せに対して
Linker
PREVIEW実装を提供する場合、defaultLookup()
の結果が、すべてのOSとプロセッサの組合せにわたって一貫した一連の記号を可能なかぎり公開することを強くお薦めします。- 戻り値:
- 一般的に使用される一連のライブラリ内のシンボルのシンボル・ルックアップ。
-
Linker
を使用できます。