Terminology:

  • downcall method handle: call native functions from Java
  • upcall stub: call Java functions from native code

Ingedients:

  • Linker.nativeLinker() returns a platform-specific Linker
    • produces method handles given the adress of functions and their descriptors
  • SymbolLookup retrieves the adress of a symbol in a library
    • symbols can be functions or global variables
    • the adress of a symbol is modelled as a zero-length MemorySegment
    • Linker#defaultLookup returns the standard C library symbols loaded with the Java runtime
    • SymbolLookup#libraryLookup can load other libraries

Example:

public static void main(String[] args) throws Throwable {  
    // default lookup contains standard C library loaded with the Java runtime
    Linker linker = Linker.nativeLinker();  
    SymbolLookup stdlib = linker.defaultLookup();  
  
    // search for `strlen` and describe its input and output types  
    MemorySegment strlenAddress = stdlib.find("strlen").orElseThrow();  
    FunctionDescriptor descriptor = FunctionDescriptor.of(  
        ValueLayout.JAVA_LONG,    // result
        ValueLayout.ADDRESS       // input
    );  
  
    // method handle represents native function in Java  
    MethodHandle strlen = linker.downcallHandle(strlenAddress, descriptor);  
  
    // allocate off-heap memory and call function  
    try (Arena arena = Arena.ofConfined()) {  
        MemorySegment str = arena.allocateFrom("Hello");  
        long len = (long) strlen.invoke(str);  
        System.out.println(STR."Length = \{len}");  // 5
    }  
}

References: