0Pricing
Java Academy · Урок

Дескрипторы вызовов 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_INT
  • long/size_t — JAVA_LONG
  • double — 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 — локальная установка не требуется.

Все уроки этого курса

  1. Почему FFM лучше JNI
  2. MemorySegment и Arena
  3. Дескрипторы вызовов downcall
  4. Макеты и структуры
← Назад к Java Academy