Дескрипторы вызовов downcall
Вызывайте нативные функции
«Дескрипторы вызовов downcall» — бесплатный урок Java Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Java Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Java Academy содержит 4 уроков всего.
Вызов нативных функций
Вызов вниз — это вызов из Java в нативный код. FFM представляет каждую нативную функцию как MethodHandle, созданный с помощью Linker.
В этом уроке показано, как найти, описать и вызвать функцию C.
Нативный компоновщик
Linker.nativeLinker() возвращает компоновщик для текущей платформы. Он знает соглашение о вызовах C и может создавать дескрипторы для нативных функций.
import java.lang.foreign.Linker;
public class Main {
public static void main(String[] args) {
Linker linker = Linker.nativeLinker();
System.out.println("Got native linker: " + linker);
}
}Поиск символа
Вам нужен адрес функции. linker.defaultLookup() ищет символ в стандартной библиотеке C, а SymbolLookup.libraryLookup(name, arena) загружает конкретную разделяемую библиотеку.
import java.lang.foreign.*;
public class Main {
public static void main(String[] args) {
Linker linker = Linker.nativeLinker();
SymbolLookup stdlib = linker.defaultLookup();
MemorySegment addr = stdlib.find("strlen").orElseThrow();
System.out.println("strlen address found: " + addr);
}
}Описание сигнатуры
FunctionDescriptor описывает сигнатуру C в терминах разметок. Для size_t strlen(const char*) возвращаемое значение имеет тип long, а аргументом является указатель (ADDRESS).
import java.lang.foreign.*;
public class Main {
public static void main(String[] args) {
FunctionDescriptor desc = FunctionDescriptor.of(
ValueLayout.JAVA_LONG, // return: size_t
ValueLayout.ADDRESS); // arg: const char*
System.out.println("Descriptor: " + desc);
}
}Создание дескриптора
Объедините символ и дескриптор с помощью downcallHandle, чтобы получить MethodHandle, который можно вызывать подобно любому методу Java.
import java.lang.foreign.*;
import java.lang.invoke.MethodHandle;
public class Main {
public static void main(String[] args) {
Linker linker = Linker.nativeLinker();
MethodHandle strlen = linker.downcallHandle(
linker.defaultLookup().find("strlen").orElseThrow(),
FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.ADDRESS));
System.out.println("Handle ready: " + strlen.type());
}
}Вызов функции
Чтобы вызвать strlen, передайте указатель на нативную строку. Выделите строку в Arena, а затем вызовите дескриптор с помощью invoke. Результат возвращается как значение типа long в Java.
import java.lang.foreign.*;
import java.lang.invoke.MethodHandle;
public class Main {
public static void main(String[] args) throws Throwable {
Linker linker = Linker.nativeLinker();
MethodHandle strlen = linker.downcallHandle(
linker.defaultLookup().find("strlen").orElseThrow(),
FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.ADDRESS));
try (Arena arena = Arena.ofConfined()) {
MemorySegment str = arena.allocateUtf8String("Hello, FFM");
long len = (long) strlen.invoke(str);
System.out.println("Length = " + len);
}
}
}Сопоставление типов
Каждому типу C соответствует определённая разметка:
int—ValueLayout.JAVA_INTlong/size_t—JAVA_LONGdouble—JAVA_DOUBLE- любой указатель —
ADDRESS
Крайне важно указать правильные соответствия, иначе вызов повредит данные.
Функции, возвращающие указатели
Если функция C возвращает указатель, дескриптор возвращает MemorySegment нулевого размера. Чтобы читать данные за его началом, необходимо переинтерпретировать его до нужного размера с помощью reinterpret(byteSize).
Передача множества аргументов
FunctionDescriptor.of(returnLayout, arg1, arg2, ...) перечисляет аргументы по порядку. Для функции void используйте FunctionDescriptor.ofVoid(args...) без разметки возвращаемого значения.
import java.lang.foreign.*;
public class Main {
public static void main(String[] args) {
// int (*)(const char*, int)
FunctionDescriptor two = FunctionDescriptor.of(
ValueLayout.JAVA_INT, ValueLayout.ADDRESS, ValueLayout.JAVA_INT);
// void (*)(int)
FunctionDescriptor noReturn = FunctionDescriptor.ofVoid(ValueLayout.JAVA_INT);
System.out.println(two + " / " + noReturn);
}
}Обработка ошибок с помощью throws
Поскольку MethodHandle.invoke объявляет throws Throwable, вызывающий метод должен обработать это исключение или объявить его. Поэтому в примерах объявлено throws Throwable.
Кэширование дескриптора
Создание дескриптора вызова вниз относительно затратно, поэтому выполните его один раз и используйте повторно. Распространённый шаблон — сохранить дескриптор в поле static final при инициализации класса и вызывать его много раз.
Сам дескриптор безопасен для одновременных вызовов из нескольких потоков.
Быстрая проверка
Вспомните, как описывается аргумент-указатель C.
Итоги
Вы научились вызывать нативные функции:
Linker.nativeLinker()вместе сSymbolLookupиспользуются для поиска символовFunctionDescriptorописывает сигнатуру с помощью разметокdowncallHandleвозвращаетMethodHandle, который можно вызвать с помощьюinvoke- Указатели соответствуют
ADDRESS, аinvokeвыбрасываетThrowable
Далее: описание сложных нативных данных с помощью разметок и структур.
Часто задаваемые вопросы
Урок «Дескрипторы вызовов downcall» бесплатный?
Да — полный текст урока «Дескрипторы вызовов downcall» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Java Academy, подпишись на CoddyKit PRO. Курс Java Academy содержит 4 уроков всего.
Чему я научусь в уроке «Дескрипторы вызовов downcall»?
Вызывайте нативные функции Ты практикуешь Java Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Java Academy?
Предыдущий опыт не требуется. Java Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.
Сколько времени занимает урок «Дескрипторы вызовов downcall»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Java Academy?
Да. Каждый урок Java Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Почему FFM лучше JNI
- MemorySegment и Arena
- Дескрипторы вызовов downcall
- Макеты и структуры