外部関数は、通常、オンデマンドでロードできるライブラリに存在します。 各ライブラリは、特定のABI (アプリケーション・バイナリ・インタフェース)に準拠しています。 ABIは、ライブラリが構築されたコンパイラ、OS、およびプロセッサに関連付けられた呼び出し規則とデータ型のセットです。 たとえば、Linux/x64上のCコンパイラは通常、SystemV ABIに準拠するライブラリを構築します。
リンカーは、特定のABIで使用される呼び出し規則とデータ型について詳細に把握しています。 リンカーは、そのABIに準拠するすべてのライブラリについて、JVM内で実行されているJavaコードとライブラリ内の外部関数を仲介できます。 特に次の点が重要です。
- リンカーを使用すると、downcall method handlesRESTRICTEDを介して、Javaコードを外部関数とリンクできます
- リンカーを使用すると、外部関数はupcall stubsRESTRICTEDの生成を介してJavaメソッド・ハンドルをコールできます。
size_t
タイプの正規のレイアウトを提供することを選択できます。 64ビット・プラットフォームでは、この正規レイアウトはValueLayout.JAVA_LONG
と等しい場合があります。 リンカーでサポートされる正規レイアウトは、canonicalLayouts()
メソッドを介して公開されます。このメソッドは、タイプ名から正規レイアウトへのマップを返します。
さらに、リンカーはABIに準拠するライブラリ内の外部関数を検索する方法も提供します。 各リンカーは、ABIに関連付けられたOSとプロセッサの組み合わせで一般的に使用されるライブラリ・セットを選択します。 たとえば、Linux/x64のリンカーは2つのライブラリを選択できます: 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
ネイティブ関数のアドレスを検索するために使用されます。 そのアドレスは、FunctionDescriptor
(その下の詳細)として表される関数のシグネチャの「プラットフォーム依存の説明」とともに、ネイティブ・リンカーのdowncallHandle(MemorySegment, FunctionDescriptor, Option...)
RESTRICTEDメソッドに渡されます。 取得されたdowncallメソッド・ハンドルは、次のように起動されます:
try (Arena arena = Arena.ofConfined()) {
MemorySegment str = arena.allocateFrom("Hello");
long len = (long) strlen.invokeExact(str); // 5
}
シグネチャの説明
ネイティブ・リンカーと対話する場合、クライアントは、リンク先のC関数のシグネチャについてプラットフォームに依存する説明を提供する必要があります。 この説明(function descriptor
)は、C関数のパラメータ・タイプおよび戻り型(もしあれば)に関連付けられたレイアウトを定義します。
bool
、int
などのスカラーC型は、適切なキャリアの「値レイアウト」としてモデル化されます。 スカラー型とそれに対応する正規レイアウトとの間の「マッピング」は、ネイティブ・リンカー(下記参照)によって実装されるABIに依存します。
コンポジット型は、「グループ・レイアウト」としてモデル化されます。 具体的には、C struct
型は「構造体レイアウト」にマップされ、C union
型はunion layout
にマップされます。 構造体または共用体のレイアウトを定義する場合、クライアントはC内の対応する複合型定義のサイズおよび整列制約に注意する必要があります。 たとえば、2つの構造体フィールド間のパディングは、適切なサイズの「パディング・レイアウト」メンバーを結果の構造体レイアウトに追加することで、明示的にモデル化する必要があります。
最後に、int**
やint(*)(size_t*, size_t*)
などのポインタ型は、アドレス・レイアウトとしてモデル化されます。 ポインタ型の空間境界が静的にわかっている場合は、アドレス・レイアウトを「ターゲット・レイアウト」に関連付けることができます。 たとえば、C int[2]
配列を指すことが知られているポインタは、ターゲット・レイアウトが要素数が2で要素タイプがValueLayout.JAVA_INT
のシーケンス・レイアウトであるアドレス・レイアウトとしてモデル化できます。
すべてのネイティブ・リンカー実装は、次のタイプのセットに対して正規のレイアウトを提供することが保証されています。
bool
char
short
int
long
long long
float
double
size_t
wchar_t
void*
long
は、Linux/x64のレイアウト定数ValueLayout.JAVA_LONG
にマップされますが、Windows/x64のレイアウト定数ValueLayout.JAVA_INT
にマップされます。 同様に、C型size_t
は、64ビット・プラットフォームではレイアウト定数ValueLayout.JAVA_LONG
にマップされますが、32ビット・プラットフォームではレイアウト定数ValueLayout.JAVA_INT
にマップされます。
ネイティブ・リンカーは通常、Cの符号なし整数型に正規のレイアウトを提供しません。 かわりに、対応する符号付き整数型に関連付けられた正規のレイアウトを使用してモデル化されます。 たとえば、C型unsigned long
は、Linux/x64のレイアウト定数ValueLayout.JAVA_LONG
にマップされますが、Windows/x64のレイアウト定数ValueLayout.JAVA_INT
にマップされます。
次の表に、"System Vアプリケーション・バイナリ・インタフェース" (ここに示すすべての例では、これらのプラットフォーム依存マッピングを想定しています。)に従ってC型をLinux/x64でモデル化する方法の例を示します。
Cタイプ レイアウト Javaタイプ bool
ValueLayout.JAVA_BOOLEAN
boolean
char
unsigned char
ValueLayout.JAVA_BYTE
byte
short
unsigned short
ValueLayout.JAVA_SHORT
short
int
unsigned int
ValueLayout.JAVA_INT
int
long
unsigned long
ValueLayout.JAVA_LONG
long
long long
unsigned long long
ValueLayout.JAVA_LONG
long
float
ValueLayout.JAVA_FLOAT
float
double
ValueLayout.JAVA_DOUBLE
double
size_t
ValueLayout.JAVA_LONG
long
char*
,int**
,struct Point*
ValueLayout.ADDRESS
MemorySegment
int (*ptr)[10]
ValueLayout.ADDRESS.withTargetLayout( MemoryLayout.sequenceLayout(10, ValueLayout.JAVA_INT) );MemorySegment
struct Point { int x; long y; };
MemoryLayout.structLayout( ValueLayout.JAVA_INT.withName("x"), MemoryLayout.paddingLayout(32), ValueLayout.JAVA_LONG.withName("y") );MemorySegment
union Choice { float a; int b; }
MemoryLayout.unionLayout( ValueLayout.JAVA_FLOAT.withName("a"), ValueLayout.JAVA_INT.withName("b") );MemorySegment
すべてのネイティブ・リンカー実装は、明確に定義されたレイアウトのサブセットをサポートします。 より正式には、ネイティブ・リンカーNL
で次の場合、レイアウトL
がサポートされます:
L
は値レイアウトV
で、V.withoutName()
は標準レイアウトです。L
は順序レイアウトS
で、次のすべての条件が保持されます:S
の境界整列制約は、その「自然整列」に設定されますS.elementLayout()
は、NL
でサポートされているレイアウトです。
L
はグループ・レイアウトG
で、次のすべての条件が保持されます:G
の整列制約は、その「自然整列」に設定されますG
のサイズは、その整列制約の倍数ですG.memberLayouts()
の各メンバー・レイアウトは、パディング・レイアウトまたはNL
でサポートされているレイアウトのいずれかですG
には、非パディング・レイアウト要素の整列、または(2)を満たすために厳密に必要なパディング以外は含まれません。
L
が少なくとも1つ存在する構造体です。 これにより、メンバー・レイアウト間のパディングを回避し、構造体レイアウトの最後にパディングを回避できます。 たとえば、
// No padding between the 2 element layouts:
MemoryLayout noFieldPadding = MemoryLayout.structLayout(
ValueLayout.JAVA_INT,
ValueLayout.JAVA_DOUBLE.withByteAlignment(4));
// No padding at the end of the struct:
MemoryLayout noTrailingPadding = MemoryLayout.structLayout(
ValueLayout.JAVA_DOUBLE.withByteAlignment(4),
ValueLayout.JAVA_INT);
ネイティブ・リンカーは、引数/戻りレイアウトがそのリンカーでサポートされるレイアウトであり、シーケンス・レイアウトではない関数記述子のみをサポートします。
関数ポインタ
場合によっては、Javaコードを一部のネイティブ関数への関数ポインタとして渡すと便利です。これは、upcall stubRESTRICTEDを使用して実現します。 これを示すために、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
を使用してC型のsize_t
型をマップし、ValueLayout.ADDRESS
を使用して最初のポインタ・パラメータ(配列ポインタ)と最後のパラメータ(関数ポインタ)の両方をマップします。
上で取得したqsort
ダウンコール・ハンドルを呼び出すには、関数ポインタを最後のパラメータとして渡す必要があります。 つまり、既存のメソッド・ハンドルから関数ポインタを作成する必要があります。 まず、ポインタ(例:「メモリー・セグメント」)として渡された2つのint要素を比較できるJavaメソッドを記述します。
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
を指定できます。 これによって、比較メソッドが比較する配列要素のコンテンツにアクセスできるようになります。 次に、その関数記述子を適切な「メソッド・タイプ」に「なる」し、コンパレータ・メソッド・ハンドルの検索に使用します。 次のように、そのメソッドを指すアップ・コール・スタブを作成し、ファンクション・ポインタとしてqsort
ダウンコール・ハンドルに渡すことができます。
try (Arena arena = Arena.ofConfined()) {
MemorySegment comparFunc = linker.upcallStub(comparHandle, comparDesc, arena);
MemorySegment array = arena.allocateFrom(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、この場合は)にサイズ変更する必要があります。 また、Javaコードから直接作成された他のネイティブ・セグメントと同様に、セグメントをバッキングするメモリー・リージョンの存続期間を自動的に管理できるように、既存の「アリーナ」にセグメントをアタッチすることが望ましい場合があります。 これらの操作はどちらも、次のように制限付きメソッドMemorySegment.reinterpret(long, Arena, Consumer)
RESTRICTEDを使用して実行されます:
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, ...);
省略記号のかわりに渡される引数は、可変個引数と呼ばれます。 可変個引数関数は、基本的に、...
を固定の数と型の「可変個引数パラメータ」のリストに置き換えることで、複数の非変数関数に「特殊化」できるテンプレートです。
可変個引数として渡される値は、Cでデフォルトの引数昇格を受けることに注意してください。 たとえば、次の引数プロモーションが適用されます:
_Bool
->unsigned int
[signed] char
->[signed] int
[signed] short
->[signed] int
float
->double
...
の置換に使用できる型に制限が課されます。これは、可変関数の特殊な形式の可変個パラメータには常に昇格された型があるためです。
ネイティブ・リンカーは、特殊な形式の可変個引数関数のリンクのみをサポートします。 特殊な形式の可変個引数関数を、特殊な形式を記述する関数記述子を使用してリンクできます。 さらに、Linker.Option.firstVariadicArg(int)
リンカー・オプションを指定して、パラメータ・リスト内の最初の可変個パラメータを示す必要があります。 対応する引数レイアウト(もしあれば)、および特殊な関数記述子の次のすべての引数レイアウトは、「可変個引数レイアウト」と呼ばれます。
ネイティブ・リンカーは、デフォルトの引数昇格を自動的に実行しません。 ただし、非プロモート型の引数を可変個引数として渡すことはCではサポートされないため、ネイティブ・リンカーは、非プロモートC型に対応する任意の可変個引数値レイアウトと特殊関数記述子をリンクする試みを拒否します。 C int
タイプのサイズはプラットフォーム固有であるため、どのレイアウトが拒否されるかもプラットフォーム固有です。 例として: Linux/x64では、C型_Bool
, (unsigned) char
, (unsigned) short
およびfloat
(とりわけ)に対応するレイアウトはリンカーによって拒否されます。 canonicalLayouts()
メソッドを使用すると、特定のC型に対応するレイアウトを検索できます。
既知の可変個引数関数とは、C標準ライブラリで定義されるprintf
関数です:
int printf(const char *format, ...);
printf("%d plus %d equals %d", 2, 2, 4);
(char*, int, int, int)
です。 次に、「リンカー・オプション」を使用して、提供された関数記述子(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()) {
//prints "2 plus 2 equals 4"
int res = (int)printf.invokeExact(arena.allocateFrom("%d plus %d equals %d"), 2, 2, 4);
}
安全に関する考慮事項
ダウンコール・メソッド・ハンドルの作成は本質的に安全ではありません。 一般的に、外部ライブラリ内のシンボルには十分なシグネチャ情報 (例、外部ファンクション・パラメータのアリティおよびタイプ)が含まれていません。 その結果、リンカーのランタイムはリンケージ・リクエストを検証できません。 クライアントが無効なリンケージ・リクエスト(例:引数のレイアウトが多すぎる関数記述子を指定)を介して取得されたダウン・コール・メソッド・ハンドルと対話する場合、このような対話の結果は不特定であり、JVMがクラッシュする可能性があります。アップコール・スタブが外部関数に渡されると、アップコール・スタブに関連付けられた関数ポインタをアップコール・スタブの型と互換性のない型にキャストし、結果として得られる関数ポインタを介して関数を呼び出そうとすると、JVMクラッシュが発生する可能性があります。 さらに、アップ・コール・スタブに関連付けられたメソッド・ハンドルが「メモリー・セグメント」を返す場合、クライアントは、アップ・コールの完了後にこのアドレスが無効になることがないようにする必要があります。 これは、通常、アップ・コールはダウン・コール・メソッド・ハンドルの起動のコンテキストで実行されるため、不特定の動作を引き起こし、JVMがクラッシュする可能性があります。
- 実装要件:
- このインタフェースの実装は不変、スレッド・セーフ、およびvalue-basedです。
- 導入されたバージョン:
- 22
-
ネストされたクラスのサマリー
-
メソッドのサマリー
修飾子と型メソッド説明このリンカーによって実装されたABIで使用されるデータ型の名前と、その「標準レイアウト」との間の、変更不可能なマッピングを返します。一般的に使用される一連のライブラリ内のシンボル・ルックアップを返します。downcallHandle
(FunctionDescriptor function, Linker.Option... options) Restricted.指定されたシグネチャで外部ファンクションをコールするために使用されるメソッド・ハンドルを作成します。downcallHandle
(MemorySegment address, FunctionDescriptor function, Linker.Option... options) Restricted.指定されたシグネチャおよびアドレスを持つ外部ファンクションをコールするために使用されるメソッド・ハンドルを作成します。static Linker
基礎となるネイティブ・プラットフォームに関連付けられたABIのリンカーを返します。upcallStub
(MethodHandle target, FunctionDescriptor function, Arena arena, Linker.Option... options) Restricted.指定されたアリーナに関連付けられた関数ポインタとして、ほかの外部関数に渡すことができるアップコール・スタブを作成します。
-
メソッドの詳細
-
nativeLinker
static Linker nativeLinker()基礎となるネイティブ・プラットフォームに関連付けられたABIのリンカーを返します。基礎となるネイティブ・プラットフォームは、Javaランタイムが現在実行されているOSとプロセッサの組合せです。
- APIのノート:
- 現在、OSとプロセッサの異なる組み合わせに対してリンカーを取得することはできません。
- 実装要件:
- ネイティブ・リンカーの実装では、「基本C型」の正規のレイアウトが提供されることが保証されています。
- 実装上のノート:
- 返されるリンカーに関連付けられた「デフォルト・ルックアップ」によって公開されるライブラリは、Javaランタイムが現在実行されているプロセスにロードされるネイティブ・ライブラリです。 たとえば、Linuxの場合、これらのライブラリには通常、
libc
、libm
およびlibdl
が含まれます。 - 戻り値:
- ベースとなるネイティブ・プラットフォームに関連付けられたABIのリンカー
-
downcallHandle
sealed MethodHandle downcallHandle(MemorySegment address, FunctionDescriptor function, Linker.Option... options) downcallHandle
は、Javaプラットフォームの制限付きメソッドです。制限されたメソッドは安全ではありません。不適切に使用した場合、JVMがクラッシュまたはメモリーが破損する場合があります。指定されたシグネチャおよびアドレスを持つ外部ファンクションをコールするために使用されるメソッド・ハンドルを作成します。このメソッドの呼出しは、次のコードと同じです:
linker.downcallHandle(function).bindTo(symbol);
- パラメータ:
address
- 「ベース・アドレス」がターゲット外部ファンクションのアドレスであるネイティブ・メモリー・セグメントfunction
- ターゲット外部関数の関数記述子options
- このリンケージ・リクエストに関連付けられたリンカー・オプション- 戻り値:
- ダウンコール・メソッド・ハンドル
- 例外:
IllegalArgumentException
- 指定された関数記述子がこのリンカーでサポートされていない場合IllegalArgumentException
-!address.isNative()
の場合、またはaddress.equals(MemorySegment.NULL)
の場合IllegalArgumentException
- リンカー・オプションの無効な組み合わせが指定されている場合IllegalCallerException
- 呼び出し元が、ネイティブ・アクセスが有効になっていないモジュール内にある場合- 関連項目:
-
downcallHandle
sealed MethodHandle downcallHandle(FunctionDescriptor function, Linker.Option... options) downcallHandle
は、Javaプラットフォームの制限付きメソッドです。プログラムは、制限付きメソッドへのアクセスが有効になっている場合にのみdowncallHandle
を使用できます。制限されたメソッドは安全ではありません。不適切に使用した場合、JVMがクラッシュまたはメモリーが破損する場合があります。指定されたシグネチャで外部ファンクションをコールするために使用されるメソッド・ハンドルを作成します。戻されるメソッド・ハンドルに関連付けられたJava 「メソッド・タイプ」は、引数からのderivedであり、関数記述子内の戻りレイアウトですが、ターゲット外部関数のアドレスが導出される、
MemorySegment
型の先行パラメータが追加されています。 さらに、関数記述子の戻りレイアウトがグループ・レイアウトである場合、結果のdowncallメソッド・ハンドルは、SegmentAllocator
型の追加の先行パラメータを受け入れます。このパラメータは、ダウンコール・メソッド・ハンドルによって返された構造体に関連付けられたメモリー・リージョンを割り当てるためにリンカー・ランタイムによって使用されます。ダウンコール・メソッド・ハンドルを呼び出すと、リンカーは、対応するレイアウトがアドレス・レイアウトである
MemorySegment
型の引数A
に対して次の保証を提供します。A.scope().isAlive() == true
。 それ以外の場合、呼出しはIllegalStateException
をスローA.isAccessibleBy(T) == true
などのスレッドT
で起動が発生します。 それ以外の場合、呼出しはWrongThreadException
をスローA
は、起動中も存続します。 たとえば、「共有アリーナ」を使用してA
を取得した場合、ダウンコール・メソッド・ハンドルがまだ実行されている間にアリーナを「閉じる」しようとすると、IllegalStateException
になります。
また、指定されたファンクション記述子の戻りレイアウトがアドレス・レイアウトの場合、戻されたメソッド・ハンドルを呼び出すと、グローバル・スコープに関連付けられたネイティブ・セグメントが返されます。 通常の状況では、返されるセグメントのサイズは
0
です。 ただし、ファンクション記述子の戻りレイアウトに「ターゲット・レイアウト」T
がある場合、返されるセグメントのサイズはT.byteSize()
に設定されます。外部ファンクションのターゲット・アドレスを表す
MemorySegment
がMemorySegment.NULL
アドレスである場合、戻されたメソッド・ハンドルはIllegalArgumentException
をスローします。 引数がMemorySegment
で、対応するレイアウトが「グループ・レイアウト」の場合、リンカーはセグメントのコンテンツへのアクセスを試みる可能性があります。 そのため、MemorySegment.get(ValueLayout.OfByte, long)
メソッドまたはMemorySegment.copy(MemorySegment, long, MemorySegment, long, long)
メソッドで指定された例外のいずれかがスローされる場合があります。 引数がMemorySegment
で、対応するレイアウトがアドレス・レイアウトの場合、ヒープ・メモリー・セグメントがLinker.Option.critical(boolean)
リンカー・オプションによって明示的に許可されていないかぎり、そのセグメントがヒープ・メモリー・セグメントである場合、リンカーはIllegalArgumentException
をスローします。 戻されたメソッド・ハンドルは、渡された引数がnull
であれば、さらにNullPointerException
をスローします。- パラメータ:
function
- ターゲット外部関数の関数記述子options
- このリンケージ・リクエストに関連付けられたリンカー・オプション- 戻り値:
- ダウンコール・メソッド・ハンドル
- 例外:
IllegalArgumentException
- 指定された関数記述子がこのリンカーでサポートされていない場合IllegalArgumentException
- リンカー・オプションの無効な組み合わせが指定されている場合IllegalCallerException
- 呼び出し元が、ネイティブ・アクセスが有効になっていないモジュール内にある場合
-
upcallStub
sealed MemorySegment upcallStub(MethodHandle target, FunctionDescriptor function, Arena arena, Linker.Option... options) upcallStub
は、Javaプラットフォームの制限付きメソッドです。プログラムは、制限付きメソッドへのアクセスが有効になっている場合にのみupcallStub
を使用できます。制限されたメソッドは安全ではありません。不適切に使用した場合、JVMがクラッシュまたはメモリーが破損する場合があります。指定されたアリーナに関連付けられた関数ポインタとして、ほかの外部関数に渡すことができるアップコール・スタブを作成します。 このような関数ポインタを外部コードから呼び出すと、指定されたメソッド・ハンドルが実行されます。返されたメモリー・セグメントのアドレスは、新しく割り当てられたアップコール・スタブを指し、指定されたアリーナに関連付けられます。 したがって、返されたアップコール・スタブ・セグメントの寿命は、指定されたアリーナによって制御されます。 たとえば、指定されたアリーナが限定されたアリーナである場合、返されたアップ・コール・スタブ・セグメントは、指定された限定されたアリーナがclosedの場合に割当て解除されます。
対応するレイアウトがアドレス・レイアウトであるアップ・コール・スタブ引数は、グローバル・スコープに関連付けられたネイティブ・セグメントです。 通常の場合、このセグメント引数のサイズは
0
です。 ただし、アドレス・レイアウトに「ターゲット・レイアウト」T
がある場合、セグメント引数のサイズはT.byteSize()
に設定されます。ターゲット・メソッド・ハンドルは例外をスローしません。 ターゲット・メソッド・ハンドルが例外をスローすると、JVMは突然終了します。 これを回避するには、クライアントがtry/catchブロック内のターゲット・メソッド・ハンドルのコードをラップして、予期しない例外を捕捉する必要があります。 これは、
MethodHandles.catchException(MethodHandle, Class, MethodHandle)
メソッド・ハンドル・コンビネータを使用して実行でき、対応するcatchブロックで必要に応じて例外を処理できます。- パラメータ:
target
- ターゲット・メソッド・ハンドルfunction
- upcallスタブ関数記述子arena
- 戻されたアップ・コール・スタブ・セグメントに関連付けられたアリーナoptions
- このリンケージ・リクエストに関連付けられたリンカー・オプション- 戻り値:
- アドレスがアップ・コール・スタブのアドレスである長さ0のセグメント
- 例外:
IllegalArgumentException
- 指定された関数記述子がこのリンカーでサポートされていない場合IllegalArgumentException
-target
の型がfunction
のderivedの型と互換性がない場合IllegalArgumentException
- ターゲット・メソッド・ハンドルが例外をスローできると判断された場合IllegalStateException
-arena.scope().isAlive() == false
の場合WrongThreadException
-arena
が限定アリーナで、このメソッドがアリーナの所有者スレッド以外のスレッドT
からコールされる場合IllegalCallerException
- 呼び出し元が、ネイティブ・アクセスが有効になっていないモジュール内にある場合
-
defaultLookup
SymbolLookup defaultLookup()一般的に使用される一連のライブラリ内のシンボル・ルックアップを返します。各
Linker
は、Linker
でサポートされているOSとプロセッサの組合せで役立つと広く認識されているライブラリの選択を担当します。 したがって、シンボル・ルックアップによって公開されるシンボルの正確なセットは指定されず、Linker
によって異なります。- 実装上のノート:
defaultLookup()
の結果は、時間の経過とともに安定している一連の記号を公開することを強くお薦めします。 以前にシンボル・ルックアップによって公開されたシンボルが公開されなくなった場合、defaultLookup()
のクライアントは失敗する可能性があります。実装者が複数のOSとプロセッサの組合せに対して
Linker
実装を提供する場合、defaultLookup()
の結果は、すべてのOSとプロセッサの組合せに一貫性のある一連の記号を可能なかぎり公開することを強くお薦めします。- 戻り値:
- 一般的に使用される一連のライブラリ内のシンボルのシンボル・ルックアップ検
-
canonicalLayouts
Map<String,MemoryLayout> canonicalLayouts()このリンカーによって実装されたABIで使用されるデータ型の名前と、その「標準レイアウト」との間の、変更不可能なマッピングを返します。各
Linker
は、Linker
でサポートされているOSとプロセッサの組合せで役立つと広く認識されているデータ型を選択します。 したがって、リンカーによって公開されるデータ型名と正規のレイアウトの正確なセットは指定されず、Linker
によって異なります。- 実装上のノート:
canonicalLayouts()
の結果は、時間の経過とともに安定している一連の記号を公開することを強くお薦めします。canonicalLayouts()
のクライアントは、以前にリンカーによって公開されていたデータ型が公開されなくなった場合、または正規のレイアウトが更新された場合に失敗する可能性があります。実装者が複数のOSとプロセッサの組合せに対して
Linker
実装を提供する場合、canonicalLayouts()
の結果は、すべてのOSとプロセッサの組合せに一貫性のある一連の記号を可能なかぎり公開することを強くお薦めします。- 戻り値:
- このリンカーによって実装されたABIで使用されるデータ型の名前と、その「標準レイアウト」との間の変更不可能なマッピング
-
downcallHandle
を使用できます。