0Pricing
Java Academy · Lekcja

Uchwyty downcall

Wywoływanie funkcji natywnych

Uchwyty downcall to bezpłatna lekcja Java Academy na CoddyKit. To lekcja 3 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Java Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Java Academy zawiera 4 lekcji w sumie.

Wywoływanie funkcji natywnych

Downcall to wywołanie z Javy do kodu natywnego. FFM reprezentuje każdą funkcję natywną jako obiekt MethodHandle utworzony na podstawie obiektu Linker.

W tej lekcji omówiono wyszukiwanie, opisywanie i wywoływanie funkcji C.

Linker natywny

Linker.nativeLinker() zwraca linker dla uruchomionej platformy. Zna on konwencję wywoływania C i potrafi tworzyć uchwyty do funkcji natywnych.

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);
    }
}

Wyszukiwanie symbolu

Potrzebny jest adres funkcji. linker.defaultLookup() przeszukuje standardową bibliotekę C, a SymbolLookup.libraryLookup(name, arena) ładuje konkretną bibliotekę współdzieloną.

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);
    }
}

Opisywanie sygnatury

Obiekt FunctionDescriptor opisuje sygnaturę C za pomocą layoutów. Dla size_t strlen(const char*) wartością zwracaną jest long, a argumentem wskaźnik (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);
    }
}

Budowanie uchwytu

Połącz symbol i deskryptor za pomocą downcallHandle, aby otrzymać obiekt MethodHandle, który można wywoływać tak jak każdą metodę Javy.

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());
    }
}

Wywoływanie funkcji

Aby wywołać strlen, należy przekazać wskaźnik do napisu natywnego. Napis należy przydzielić w obiekcie arena, a następnie wywołać uchwyt za pomocą invoke. Wynik zostanie zwrócony jako wartość typu long w Javie.

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);
        }
    }
}

Mapowanie typów

Każdy typ C jest odwzorowywany na layout:

  • int na ValueLayout.JAVA_INT
  • long/size_t na JAVA_LONG
  • double na JAVA_DOUBLE
  • dowolny wskaźnik na ADDRESS

Poprawne mapowanie ma kluczowe znaczenie, ponieważ błąd powoduje uszkodzenie danych przez wywołanie.

Funkcje zwracające wskaźniki

Gdy funkcja C zwraca wskaźnik, uchwyt zwraca obiekt MemorySegment o zerowej długości. Aby odczytać dane poza jego początkiem, trzeba wykonać operację reinterpretacji do właściwego rozmiaru za pomocą reinterpret(byteSize).

Przekazywanie wielu argumentów

FunctionDescriptor.of(returnLayout, arg1, arg2, ...) wymienia argumenty w kolejności ich przekazywania. W przypadku funkcji void należy użyć FunctionDescriptor.ofVoid(args...), bez layoutu wartości zwracanej.

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);
    }
}

Obsługa błędów za pomocą throws

Ponieważ MethodHandle.invoke deklaruje throws Throwable, metoda wywołująca musi obsłużyć ten wyjątek lub zadeklarować jego propagowanie. Dlatego przykłady deklarują throws Throwable.

Buforowanie uchwytu

Budowanie uchwytu downcall jest stosunkowo kosztowne, dlatego należy wykonać je raz i ponownie używać uchwytu. Typowym wzorcem jest przypisanie uchwytu do pola static final podczas inicjalizacji klasy, a następnie wielokrotne jego wywoływanie.

Sam uchwyt jest bezpieczny w przypadku równoczesnych wywołań z wielu wątków.

Szybkie sprawdzenie

Proszę przypomnieć sobie, jak opisuje się argument będący wskaźnikiem C.

Podsumowanie

Dowiedzieli się Państwo, jak wywoływać funkcje natywne:

  • Linker.nativeLinker() wraz z obiektem SymbolLookup służy do wyszukiwania symboli
  • FunctionDescriptor opisuje sygnaturę za pomocą layoutów
  • downcallHandle zwraca obiekt MethodHandle, który można wywołać za pomocą invoke
  • Wskaźniki są mapowane na ADDRESS, a invoke zgłasza Throwable

Następnie: opisywanie złożonych danych natywnych za pomocą layoutów i struktur.

Często zadawane pytania

Czy lekcja „Uchwyty downcall” jest bezpłatna?

Tak — pełny tekst „Uchwyty downcall” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Java Academy, przejdź na CoddyKit PRO. Kurs Java Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „Uchwyty downcall”?

Wywoływanie funkcji natywnych Ćwiczysz Java Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć Java Academy?

Nie wymagamy żadnego doświadczenia. Java Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 3 z 4.

Ile czasu zajmuje lekcja „Uchwyty downcall”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji Java Academy?

Tak. Każda lekcja Java Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Dlaczego FFM zamiast JNI
  2. MemorySegment i Arena
  3. Uchwyty downcall
  4. Układy pamięci i struktury
← Powrót do Java Academy