how to call a C function from JDK 18 using jdk.incubator.foreign

702 views Asked by At

This is similar to How to call a C function from Java 17 using JEP 412: Foreign Function & Memory API

but the API has changed in the move from JDK 17 -> 18.

Asking the question again: Does anybody have a simple example on how to call a C function from Java 18? I'm stuck trying to translate the accepted answer in the Java 17 question.

1

There are 1 answers

1
effbiae On

from this mailing list message: https://mail.openjdk.java.net/pipermail/panama-dev/2022-April/016764.html

i found this link: https://github.com/openjdk/panama-foreign/blob/foreign-jextract/doc/panama_ffi.md

which includes this example:

import jdk.incubator.foreign.Addressable;
import jdk.incubator.foreign.CLinker;
import jdk.incubator.foreign.FunctionDescriptor;
import jdk.incubator.foreign.SymbolLookup;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.NativeSymbol;
import jdk.incubator.foreign.ResourceScope;
import jdk.incubator.foreign.SegmentAllocator;
import jdk.incubator.foreign.VaList;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.Arrays;

import static jdk.incubator.foreign.ValueLayout.*;

public class Examples {

    static CLinker LINKER = CLinker.systemCLinker();

    public static void main(String[] args) throws Throwable {
        strlen();
        strlen_virtual();
        qsort();
        printf();
        vprintf();
    }

    public static void strlen() throws Throwable {
        MethodHandle strlen = LINKER.downcallHandle(
                LINKER.lookup("strlen").get(),
                FunctionDescriptor.of(JAVA_LONG, ADDRESS)
        );

        try (ResourceScope scope = ResourceScope.newConfinedScope()) {
            SegmentAllocator malloc = SegmentAllocator.nativeAllocator(scope);
            MemorySegment hello = malloc.allocateUtf8String("Hello");
            long len = (long) strlen.invoke(hello); // 5
            System.out.println(len);
        }
    }

    public static void strlen_virtual() throws Throwable {
        MethodHandle strlen_virtual = LINKER.downcallHandle(
                FunctionDescriptor.of(JAVA_LONG, ADDRESS)
        );

        try (ResourceScope scope = ResourceScope.newConfinedScope()) {
            SegmentAllocator malloc = SegmentAllocator.nativeAllocator(scope);
            MemorySegment hello = malloc.allocateUtf8String("Hello");
            long len = (long) strlen_virtual.invoke(
                LINKER.lookup("strlen").get(),
                hello); // 5
            System.out.println(len);
        }
    }

    static class Qsort {
        static int qsortCompare(MemoryAddress addr1, MemoryAddress addr2) {
            return addr1.get(JAVA_INT, 0) - addr2.get(JAVA_INT, 0);
        }
    }

    public static void qsort() throws Throwable {
        MethodHandle qsort = LINKER.downcallHandle(
                LINKER.lookup("qsort").get(),
                FunctionDescriptor.ofVoid(ADDRESS, JAVA_LONG, JAVA_LONG, ADDRESS)
        );
        FunctionDescriptor comparDesc = FunctionDescriptor.of(JAVA_INT, ADDRESS, ADDRESS);
        MethodHandle comparHandle = MethodHandles.lookup()
                                         .findStatic(Qsort.class, "qsortCompare",
                                                     CLinker.upcallType(comparDesc));

        try (ResourceScope scope = ResourceScope.newConfinedScope()) {
            NativeSymbol comparFunc = LINKER.upcallStub(
                comparHandle, comparDesc, scope);

            SegmentAllocator malloc = SegmentAllocator.nativeAllocator(scope);
            MemorySegment array = malloc.allocateArray(JAVA_INT, new int[] { 0, 9, 3, 4, 6, 5, 1, 8, 2, 7 });
            qsort.invoke(array, 10L, 4L, comparFunc);
            int[] sorted = array.toArray(JAVA_INT); // [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
            System.out.println(Arrays.toString(sorted));
        }
    }

    public static void printf() throws Throwable {
        MethodHandle printf = LINKER.downcallHandle(
                LINKER.lookup("printf").get(),
                FunctionDescriptor.of(JAVA_INT, ADDRESS).asVariadic(JAVA_INT, JAVA_INT, JAVA_INT)
        );
        try (ResourceScope scope = ResourceScope.newConfinedScope()) {
            SegmentAllocator malloc = SegmentAllocator.nativeAllocator(scope);
            MemorySegment s = malloc.allocateUtf8String("%d plus %d equals %d\n");
            printf.invoke(s, 2, 2, 4);
        }
    }

    public static void vprintf() throws Throwable {

        MethodHandle vprintf = LINKER.downcallHandle(
                LINKER.lookup("vprintf").get(),
                FunctionDescriptor.of(JAVA_INT, ADDRESS, ADDRESS));

        try (ResourceScope scope = ResourceScope.newConfinedScope()) {
            SegmentAllocator malloc = SegmentAllocator.nativeAllocator(scope);
            MemorySegment s = malloc.allocateUtf8String("%d plus %d equals %d\n");
            VaList vlist = VaList.make(builder ->
                     builder.addVarg(JAVA_INT, 2)
                            .addVarg(JAVA_INT, 2)
                            .addVarg(JAVA_INT, 4), scope);
            vprintf.invoke(s, vlist);
        }
    }
}

run as: java --add-modules jdk.incubator.foreign --enable-native-access=ALL-UNNAMED Examples.java