Интерфейс Linker
public sealed interface Linker
Внешние функции обычно находятся в библиотеках, которые можно загружать по запросу. Каждая библиотека соответствует определённому ABI (двоичному интерфейсу приложения). ABI представляет собой набор соглашений о вызовах и типов данных, связанных с компилятором, ОС и процессором, для которых была собрана библиотека. Например, компилятор C в Linux/x64 обычно собирает библиотеки, соответствующие ABI SystemV.
Компоновщик подробно знает соглашения о вызовах и типы данных, используемые конкретным ABI. Для любой библиотеки, соответствующей этому ABI, компоновщик может выступать посредником между кодом Java, выполняющимся в JVM, и внешними функциями в библиотеке. В частности:
- Компоновщик позволяет коду Java связываться с внешними функциями с помощью дескрипторов методов для вызовов внизОГРАНИЧЕННЫЙ; и
- Компоновщик позволяет внешним функциям вызывать дескрипторы методов Java с помощью создания заглушек для вызовов вверхОГРАНИЧЕННЫЙ.
size_t. На 64-разрядных платформах эта каноническая компоновка может совпадать с ValueLayout.JAVA_LONG. Канонические компоновки, поддерживаемые компоновщиком, доступны через метод canonicalLayouts(), который возвращает карту, сопоставляющую имена типов с каноническими компоновками. Кроме того, компоновщик предоставляет способ поиска внешних функций в библиотеках, соответствующих ABI. Каждый компоновщик выбирает набор библиотек, широко используемых в сочетании ОС и процессора, связанных с ABI. Например, компоновщик для Linux/x64 может выбрать две библиотеки: libc и libm. Функции из этих библиотек доступны через поиск символов.
Вызов нативных функций
Нативный компоновщик можно использовать для связывания с функциями, определёнными в библиотеках C (нативными функциями). Предположим, что мы хотим выполнить вызов из Java функцииstrlen, определённой в стандартной библиотеке C: size_t strlen(const char *s);
strlen, получается с помощью нативного компоновщика следующим образом: Linker linker = Linker.nativeLinker();
MethodHandle strlen = linker.downcallHandle(
linker.defaultLookup().findOrThrow("strlen"),
FunctionDescriptor.of(JAVA_LONG, ADDRESS)
);
strlen. Затем этот адрес передаётся вместе с зависящим от платформы описанием сигнатуры функции, представленным в виде FunctionDescriptor (подробнее об этом ниже), методу downcallHandle(MemorySegment, FunctionDescriptor, Option...)ОГРАНИЧЕННЫЙ нативного компоновщика. Полученный дескриптор метода для вызова вниз вызывается следующим образом: try (Arena arena = Arena.ofConfined()) {
MemorySegment str = arena.allocateFrom("Hello");
long len = (long) strlen.invokeExact(str); // 5
}
Описание сигнатур C
При взаимодействии с нативным компоновщиком клиенты должны предоставлять зависящее от платформы описание сигнатуры функции C, с которой они хотят связаться. Это описание,function descriptor, задаёт компоновки, соответствующие типам параметров и типу возвращаемого значения (если он есть) функции C. Скалярные типы C, такие как bool, int, моделируются как компоновки значений с подходящим типом-носителем. Соответствие скалярного типа его канонической компоновке зависит от ABI, реализуемого нативным компоновщиком (см. ниже).
Составные типы моделируются как групповые компоновки. В частности, тип C struct соответствует компоновке структуры, тогда как тип C union соответствует union layout. При определении компоновки структуры или объединения клиенты должны учитывать ограничения на размер и выравнивание соответствующего определения составного типа в C. Например, заполнение между двумя полями структуры должно моделироваться явно путём добавления в полученную компоновку структуры члена-компоновки заполнения подходящего размера.
Наконец, типы указателей, такие как int** и int(*)(size_t*, size_t*), моделируются как компоновки адресов. Если пространственные границы типа указателя известны статически, с компоновкой адреса можно связать целевую компоновку. Например, указатель, заведомо указывающий на массив C int[2], можно смоделировать как компоновку адреса, целевая компоновка которой является компоновкой последовательности с количеством элементов, равным 2, и типом элемента ValueLayout.JAVA_INT.
Для всех реализаций нативного компоновщика гарантируется предоставление канонических компоновок для следующих типов:
boolcharshortintlonglong longfloatdoublesize_twchar_tvoid*
long соответствует константе компоновки ValueLayout.JAVA_LONG в Linux/x64, но соответствует константе компоновки ValueLayout.JAVA_INT в Windows/x64. Аналогично, тип C size_t соответствует константе компоновки ValueLayout.JAVA_LONG на 64-разрядных платформах, но соответствует константе компоновки ValueLayout.JAVA_INT на 32-разрядных платформах. Нативный компоновщик обычно не предоставляет канонические компоновки для беззнаковых целочисленных типов C. Вместо этого для них используются канонические компоновки соответствующих знаковых целочисленных типов. Например, тип C unsigned long соответствует константе компоновки ValueLayout.JAVA_LONG в Linux/x64, но соответствует константе компоновки ValueLayout.JAVA_INT в Windows/x64.
В следующей таблице приведены примеры моделирования типов C в Linux/x64 согласно «System V Application Binary Interface» (во всех приведённых здесь примерах предполагается использование этих зависящих от платформы соответствий):
Тип C Компоновка Тип Java boolValueLayout.JAVA_BOOLEANbooleanchar
unsigned charValueLayout.JAVA_BYTEbyteshort
unsigned shortValueLayout.JAVA_SHORTshortint
unsigned intValueLayout.JAVA_INTintlong
unsigned longValueLayout.JAVA_LONGlonglong long
unsigned long longValueLayout.JAVA_LONGlongfloatValueLayout.JAVA_FLOATfloatdoubleValueLayout.JAVA_DOUBLEdoublesize_tValueLayout.JAVA_LONGlongchar*,int**,struct Point*ValueLayout.ADDRESSMemorySegmentint (*ptr)[10]ValueLayout.ADDRESS.withTargetLayout( MemoryLayout.sequenceLayout(10, ValueLayout.JAVA_INT) );MemorySegmentstruct Point { int x; long y; };MemoryLayout.structLayout( ValueLayout.JAVA_INT.withName("x"), MemoryLayout.paddingLayout(4), ValueLayout.JAVA_LONG.withName("y") );MemorySegmentunion Choice { float a; int b; }MemoryLayout.unionLayout( ValueLayout.JAVA_FLOAT.withName("a"), ValueLayout.JAVA_INT.withName("b") );MemorySegment
Нативный компоновщик поддерживает только дескрипторы функций, аргументами и возвращаемыми значениями которых являются корректно сформированные компоновки. Более формально, компоновка `L` корректно сформирована, если:
-
Lявляется компоновкой значения, аLполучена из канонической компоновкиCтак, чтоL.byteAlignment() <= C.byteAlignment() -
Lявляется компоновкой последовательностиSи выполняются все следующие условия:-
L.byteAlignment()равна естественному выравниванию компоновки последовательности, и -
S.elementLayout()является корректно сформированной компоновкой.
-
-
Lявляется групповой компоновкойGи выполняются все следующие условия:-
G.byteAlignment()равно естественному выравниванию групповой компоновки -
G.byteSize()кратноG.byteAlignment() - Каждая компоновка-член в
G.memberLayouts()является либо компоновкой заполнения, либо корректно сформированной компоновкой - Перед каждой компоновкой-членом, не являющейся компоновкой заполнения,
EвG.memberLayouts()может располагаться необязательная компоновка-член заполнения, размер которой является минимальным размером, необходимым для выравниванияE -
Gсодержит необязательную завершающую компоновку-член заполнения, размер которой является минимальным размером, удовлетворяющим условию (2)
-
Дескриптор функции корректно сформирован, если его компоновки аргументов и возвращаемого значения корректно сформированы и не являются компоновками последовательностей. Гарантируется, что нативный компоновщик отклонит некорректно сформированные дескрипторы функций. Однако нативный компоновщик может отклонить и корректно сформированные дескрипторы функций согласно правилам, зависящим от платформы. Например, некоторые нативные компоновщики могут отклонять упакованные компоновки структур — компоновки структур, компоновки-члены которых имеют ослабленные ограничения на выравнивание, чтобы избежать вставки дополнительного заполнения.
Указатели на функции
Иногда бывает полезно передать код Java в качестве указателя на функцию некоторой нативной функции; это достигается с помощью заглушки для вызова вверхОГРАНИЧЕННЫЙ. Рассмотрим для примера следующую функцию из стандартной библиотеки C:void qsort(void *base, size_t nmemb, size_t size,
int (*compar)(const void *, const void *));
qsort можно использовать для сортировки содержимого массива с помощью пользовательской функции сравнения, передаваемой в качестве указателя на функцию (параметр compar). Чтобы иметь возможность вызвать функцию qsort из Java, сначала нужно создать для неё дескриптор метода для вызова вниз следующим образом: Linker linker = Linker.nativeLinker();
MethodHandle qsort = linker.downcallHandle(
linker.defaultLookup().findOrThrow("qsort"),
FunctionDescriptor.ofVoid(ADDRESS, JAVA_LONG, JAVA_LONG, ADDRESS)
);
ValueLayout.JAVA_LONG для сопоставления типа C size_t, а ValueLayout.ADDRESS — для первого параметра-указателя (указателя на массив) и последнего параметра (указателя на функцию). Чтобы вызвать полученный выше дескриптор для вызова вниз qsort, нам нужно передать в качестве последнего параметра указатель на функцию. То есть нужно создать указатель на функцию на основе существующего дескриптора метода. Сначала напишем метод Java, который может сравнивать два элемента типа int, переданных как указатели (то есть как сегменты памяти):
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().findOrThrow("malloc"),
FunctionDescriptor.of(ADDRESS, JAVA_LONG)
);
MethodHandle free = linker.downcallHandle(
linker.defaultLookup().findOrThrow("free"),
FunctionDescriptor.ofVoid(ADDRESS)
);
malloc), с помощью дескриптора метода для вызова вниз среде выполнения Java неизвестны размер и время жизни возвращённого указателя. Рассмотрим следующий код: MemorySegment segment = (MemorySegment)malloc.invokeExact(100);
malloc, равен нулю. Кроме того, область действия возвращённого сегмента является глобальной. Чтобы обеспечить безопасный доступ к сегменту, необходимо небезопасным образом изменить его размер на требуемый (в данном случае 100). Также может быть желательно связать сегмент с некоторой существующей ареной, чтобы временем жизни области памяти, на которую опирается сегмент, можно было управлять автоматически, как и для любого другого нативного сегмента, созданного непосредственно из кода Java. Обе эти операции выполняются с помощью ограниченного метода MemorySegment.reinterpret(long, Arena, Consumer)ОГРАНИЧЕННЫЙ следующим образом: 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 принимает два параметра: размер и арену. Метод вызывает дескриптор метода для вызова вниз 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.
Хорошо известный пример функции с переменным числом аргументов — функция printf, определённая в стандартной библиотеке C:
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().findOrThrow("printf"),
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, поскольку вызов вверх обычно выполняется в контексте вызова дескриптора метода для вызова вниз.
- Требования к реализации:
- Реализации этого интерфейса неизменяемы, потокобезопасны и основаны на значениях.
- С версии:
- 22
Краткое описание вложенных классов
| Модификатор и тип | Интерфейс | Описание |
|---|---|---|
static interface |
Linker.Option |
Параметр компоновщика используется для передачи дополнительных параметров в запрос на связывание. |
Краткое описание методов
| Модификатор и тип | Метод | Описание |
|---|---|---|
Map |
canonicalLayouts() |
Возвращает неизменяемое сопоставление между именами типов данных, используемых ABI, реализованным этим компоновщиком, и их каноническими компоновками. |
SymbolLookup |
defaultLookup() |
Возвращает поиск символов в наборе часто используемых библиотек. |
MethodHandle |
downcallHandle |
Ограниченный. Создаёт дескриптор метода, используемый для вызова внешней функции с заданной сигнатурой. |
MethodHandle |
downcallHandle |
Ограниченный. Создаёт дескриптор метода, используемый для вызова внешней функции с заданными сигнатурой и адресом. |
static Linker |
nativeLinker() |
Возвращает компоновщик для ABI, связанного с базовой нативной платформой. |
MemorySegment |
upcallStub |
Ограниченный. Создаёт заглушку для вызова вверх, которую можно передать другим внешним функциям как указатель на функцию, связанную с заданной ареной. |
Подробное описание методов
nativeLinker
static Linker nativeLinker()
Базовая нативная платформа — это сочетание операционной системы и процессора, на котором в данный момент выполняется среда выполнения Java.
- Примечание к API:
- В настоящее время невозможно получить компоновщик для другого сочетания операционной системы и процессора.
- Требования к реализации:
- Гарантируется, что реализация нативного компоновщика предоставляет канонические структуры данных для базовых типов C.
- Примечание по реализации:
- Библиотеки, доступные через поиск по умолчанию, связанный с возвращенным компоновщиком, — это нативные библиотеки, загруженные в процесс, в котором в данный момент выполняется среда выполнения Java. Например, в Linux эти библиотеки обычно включают
libc,libmиlibdl. - Возвращает:
- компоновщик для ABI, связанного с базовой нативной платформой
downcallHandle
MethodHandle downcallHandle(MemorySegment address, FunctionDescriptor function, Linker.Option... options)
downcallHandle является ограниченным методом платформы Java. Вызов этого метода эквивалентен следующему коду:
linker.downcallHandle(function, options).bindTo(address);
- Параметры:
-
address— сегмент нативной памяти, чей базовый адрес является адресом целевой внешней функции -
function— дескриптор функции целевой внешней функции -
options— параметры компоновщика, связанные с этим запросом на компоновку - Возвращает:
- дескриптор метода нисходящего вызова
- Выбрасывает:
-
IllegalArgumentException— если предоставленный дескриптор функции не поддерживается этим компоновщиком -
IllegalArgumentException— если!address.isNative()или еслиaddress.equals(MemorySegment.NULL) -
IllegalArgumentException— если указано недопустимое сочетание параметров компоновщика -
IllegalCallerException— если вызывающий код находится в модуле, для которого не включен доступ к нативному коду - См. также:
downcallHandle
MethodHandle downcallHandle(FunctionDescriptor function, Linker.Option... options)
downcallHandle является ограниченным методом платформы Java. downcallHandle только при включенном доступе к ограниченным методам. Тип метода Java, связанный с возвращенным дескриптором метода, выводится из аргумента и возвращаемого значения в дескрипторе функции, но имеет дополнительный начальный параметр типа MemorySegment, из которого извлекается адрес целевой внешней функции. Кроме того, если возвращаемое значение дескриптора функции представляет собой групповую структуру данных, результирующий дескриптор метода нисходящего вызова принимает дополнительный начальный параметр типа SegmentAllocator, который среда выполнения компоновщика использует для выделения области памяти, связанной со структурой, возвращаемой дескриптором метода нисходящего вызова.
При вызове дескриптора метода нисходящего вызова компоновщик обеспечивает следующие гарантии для любого аргумента A типа MemorySegment, соответствующая структура данных которого является структурой данных адреса:
-
A.scope().isAlive() == true. В противном случае при вызове выбрасываетсяIllegalStateException; - Вызов выполняется в потоке
Tтаким образом, чтоA.isAccessibleBy(T) == true. В противном случае при вызове выбрасываетсяWrongThreadException; и -
Aостается активным во время вызова. Например, еслиAполучен с помощью общей арены, любая попытка закрыть арену, пока выполняется дескриптор метода нисходящего вызова, приведет к выбрасываниюIllegalStateException.
Кроме того, если возвращаемое значение предоставленного дескриптора функции является структурой данных адреса, при вызове возвращенного дескриптора метода будет возвращен нативный сегмент, связанный с глобальной областью видимости. В обычных условиях размер возвращенного сегмента равен 0. Однако если возвращаемое значение дескриптора функции имеет целевую структуру данных T, размер возвращенного сегмента будет установлен в T.byteSize().
Возвращенный дескриптор метода выбрасывает IllegalArgumentException, если MemorySegment, представляющий целевой адрес внешней функции, является MemorySegment.NULL адресом. Если аргумент является MemorySegment, соответствующая структура данных которого является групповой структурой данных, компоновщик может попытаться получить доступ к содержимому сегмента. Поэтому могут быть выброшены исключения, указанные в методах MemorySegment.get(ValueLayout.OfByte, long) или MemorySegment.copy(MemorySegment, long, MemorySegment, long, long). Если аргумент является MemorySegment, соответствующая структура данных которого является структурой данных адреса, компоновщик выбрасывает IllegalArgumentException, если сегмент относится к кучевой памяти, за исключением случая, когда кучевые сегменты памяти явно разрешены параметром компоновщика Linker.Option.critical(boolean). Кроме того, возвращенный дескриптор метода выбрасывает NullPointerException, если любой переданный ему аргумент равен null.
- Параметры:
-
function— дескриптор функции целевой внешней функции -
options— параметры компоновщика, связанные с этим запросом на компоновку - Возвращает:
- дескриптор метода нисходящего вызова
- Выбрасывает:
-
IllegalArgumentException— если предоставленный дескриптор функции не поддерживается этим компоновщиком -
IllegalArgumentException— если указано недопустимое сочетание параметров компоновщика -
IllegalCallerException— если вызывающий код находится в модуле, для которого не включен доступ к нативному коду
upcallStub
MemorySegment upcallStub(MethodHandle target, FunctionDescriptor function, Arena arena, Linker.Option... options)
upcallStub является ограниченным методом платформы Java. upcallStub только при включенном доступе к ограниченным методам.Адрес возвращенного сегмента памяти указывает на вновь выделенную заглушку восходящего вызова, а сам сегмент связан с предоставленной ареной. Поэтому время жизни возвращенного сегмента заглушки восходящего вызова определяется предоставленной ареной. Например, если предоставлена ограниченная арена, возвращенный сегмент заглушки восходящего вызова будет освобожден при закрытии предоставленной ограниченной арены.
Аргумент заглушки восходящего вызова, соответствующая структура данных которого является структурой данных адреса, представляет собой нативный сегмент, связанный с глобальной областью видимости. В обычных условиях размер этого аргумента-сегмента равен 0. Однако если структура данных адреса имеет целевую структуру данных T, размер аргумента-сегмента будет установлен в T.byteSize().
Целевой дескриптор метода не должен выбрасывать исключения. Если целевой дескриптор метода выбрасывает исключение, JVM немедленно завершит работу. Чтобы избежать этого, клиентам следует обернуть код целевого дескриптора метода в блок try/catch, перехватывающий любые непредвиденные исключения. Это можно сделать с помощью комбинатора дескрипторов метода MethodHandles.catchException(MethodHandle, Class, MethodHandle) и обработать исключения нужным образом в соответствующем блоке catch.
- Параметры:
-
target— целевой дескриптор метода -
function— дескриптор функции заглушки восходящего вызова -
arena— арена, связанная с возвращенным сегментом заглушки восходящего вызова -
options— параметры компоновщика, связанные с этим запросом на компоновку - Возвращает:
- сегмент нулевой длины, адрес которого является адресом заглушки восходящего вызова
- Выбрасывает:
-
IllegalArgumentException— если предоставленный дескриптор функции не поддерживается этим компоновщиком -
IllegalArgumentException— если типtargetнесовместим с типом, выведенным изfunction -
IllegalArgumentException— если установлено, что целевой дескриптор метода может выбросить исключение -
IllegalStateException— еслиarena.scope().isAlive() == false -
WrongThreadException— еслиarenaявляется ограниченной ареной, а этот метод вызван из потокаT, отличного от потока-владельца арены -
IllegalCallerException— если вызывающий код находится в модуле, для которого не включен доступ к нативному коду
defaultLookup
SymbolLookup defaultLookup()
Каждый Linker отвечает за выбор библиотек, широко признанных полезными для сочетания операционной системы и процессора, поддерживаемого Linker. Поэтому точный набор символов, доступных через поиск символов, не определен и различается у разных Linker.
- Примечание по реализации:
- Настоятельно рекомендуется, чтобы результат
defaultLookup()предоставлял набор символов, который остается стабильным с течением времени. КлиентыdefaultLookup(), вероятно, перестанут работать, если символ, ранее доступный через поиск символов, больше не будет доступен.Если разработчик предоставляет реализации
Linkerдля нескольких сочетаний операционной системы и процессора, настоятельно рекомендуется, чтобы результатdefaultLookup()предоставлял, насколько это возможно, согласованный набор символов для всех сочетаний операционной системы и процессора. - Возвращает:
- поиск символов в наборе часто используемых библиотек
canonicalLayouts
Map<String, MemoryLayout> canonicalLayouts()
Каждый Linker отвечает за выбор типов данных, широко признанных полезными для сочетания операционной системы и процессора, поддерживаемого Linker. Поэтому точный набор имен типов данных и канонических структур данных, доступных через компоновщик, не определен и различается у разных Linker.
- Примечание по реализации:
- Настоятельно рекомендуется, чтобы результат
canonicalLayouts()предоставлял набор символов, который остается стабильным с течением времени. КлиентыcanonicalLayouts(), вероятно, перестанут работать, если тип данных, ранее доступный через компоновщик, больше не будет доступен или если его каноническая структура данных будет изменена.Если разработчик предоставляет реализации
Linkerдля нескольких сочетаний операционной системы и процессора, настоятельно рекомендуется, чтобы результатcanonicalLayouts()предоставлял, насколько это возможно, согласованный набор символов для всех сочетаний операционной системы и процессора. - Возвращает:
- неизменяемое сопоставление имен типов данных, используемых ABI, реализованным этим компоновщиком, с их каноническими структурами данных
© 1993, 2025, Oracle and/or its affiliates. All rights reserved.
Documentation extracted from Debian's OpenJDK Development Kit package.
Licensed under the GNU General Public License, version 2, with the Classpath Exception.
Various third party code in OpenJDK is licensed under different licenses (see Debian package).
Java and OpenJDK are trademarks or registered trademarks of Oracle and/or its affiliates.
https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/foreign/Linker.html
downcallHandleтолько при включенном доступе к ограниченным методам.