What is wrong with this Java 17 CLinker.getInstance().downcallHandle(CLinker.systemLookup().lookup("radixsort"), ...); with reference to reverse-engineering JEP 412: Foreign Function & Memory API (Incubator) from JEP 419: Foreign Function & Memory API (Second Incubator) as the documentation is elided?
Please refer to the erratum.
JEP 419: Foreign Function & Memory API (Second Incubator) works fine as in the following code snippet and output:
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.util.Arrays;
import jdk.incubator.foreign.CLinker;
import jdk.incubator.foreign.FunctionDescriptor;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemorySegment;
// import jdk.incubator.foreign.ResourceScope;
import jdk.incubator.foreign.SegmentAllocator;
import jdk.incubator.foreign.ValueLayout;
// JEP 419: Foreign Function & Memory API (Second Incubator)
class ForeignFunctionMemoryAPISecondIncubator {
private void radixsort() throws Throwable {
// 1. Find foreign function on the C library path
CLinker linker = CLinker.systemCLinker();
// .get() // Or // .orElseThrow()
MethodHandle radixSort = linker.downcallHandle(
linker.lookup("radixsort").get(),
FunctionDescriptor.ofVoid(ValueLayout.ADDRESS, ValueLayout.JAVA_INT, ValueLayout.ADDRESS, ValueLayout.JAVA_CHAR));
// 2. Allocate on-heap memory to store four strings
String[] javaStrings = { "mouse", "cat", "dog", "car" };
System.out.println("radixsort input: " + Arrays.toString(javaStrings));
// 3. Allocate off-heap memory to store four pointers
// https://openjdk.org/jeps/419 // Erratum // error: cannot find symbol ofSequence
/*
MemorySegment offHeap = MemorySegment.allocateNative(
MemoryLayout.ofSequence(javaStrings.length,
ValueLayout.ADDRESS), ...);
*/
SegmentAllocator allocator = SegmentAllocator.implicitAllocator();
MemorySegment offHeap = allocator.allocateArray(ValueLayout.ADDRESS, javaStrings.length);
// 4. Copy the strings from on-heap to off-heap
for (int i = 0; i < javaStrings.length; i++) {
// Allocate a string off-heap, then store a pointer to it
MemorySegment cString = SegmentAllocator.implicitAllocator().allocateUtf8String(javaStrings[i]);
offHeap.setAtIndex(ValueLayout.ADDRESS, i, cString);
}
// 5. Sort the off-heap data by calling the foreign function
// invoke // unreported exception Throwable; must be caught or declared to be thrown
radixSort.invoke(offHeap, javaStrings.length, MemoryAddress.NULL, '\0');
// 6. Copy the (reordered) strings from off-heap to on-heap
for (int i = 0; i < javaStrings.length; i++) {
MemoryAddress cStringPtr = offHeap.getAtIndex(ValueLayout.ADDRESS, i);
javaStrings[i] = cStringPtr.getUtf8String(0);
}
System.out.println("radixsort output: " + Arrays.toString(javaStrings));
}
public static void main(String[] args) throws Throwable {
System.out.println(String.format("Java Version: %s", System.getProperty("java.version")));
var incubatorForeignFunctionMemoryAPISecond = new ForeignFunctionMemoryAPISecondIncubator();
incubatorForeignFunctionMemoryAPISecond.radixsort();
}
}
// Output
/*
WARNING: Using incubator modules: jdk.incubator.foreign
warning: using incubating module(s): jdk.incubator.foreign
1 warning
Java Version: 18.0.2
radixsort input: [mouse, cat, dog, car]
radixsort output: [car, cat, dog, mouse]
*/
However, JEP 412: Foreign Function & Memory API (Incubator) throws the error as in the following code snippet and exception output:
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.util.Arrays;
import jdk.incubator.foreign.CLinker;
import jdk.incubator.foreign.FunctionDescriptor;
import jdk.incubator.foreign.MemoryAccess;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.ResourceScope;
import jdk.incubator.foreign.SegmentAllocator;
// JEP 412: Foreign Function & Memory API (Incubator)
class ForeignFunctionMemoryAPIIncubator {
private void radixsort() throws Throwable {
// 1. Find foreign function on the C library path
MethodHandle radixSort = CLinker.getInstance().downcallHandle(
CLinker.systemLookup().lookup("radixsort").get(),
MethodType.methodType(MemoryAddress.class, int.class, MemoryAddress.class, char.class),
FunctionDescriptor.of(CLinker.C_POINTER, CLinker.C_INT, CLinker.C_POINTER, CLinker.C_CHAR));
// 2. Allocate on-heap memory to store four strings
String[] javaStrings = { "mouse", "cat", "dog", "car" };
System.out.println("radixsort input: " + Arrays.toString(javaStrings));
// 3. Allocate off-heap memory to store four pointers
// https://openjdk.org/jeps/412 // Erratum // error: cannot find symbol ofSequence
/*
MemorySegment offHeap = MemorySegment.allocateNative(
MemoryLayout.ofSequence(javaStrings.length,
CLinker.C_POINTER), ...);
*/
try (ResourceScope scopeResource = ResourceScope.newConfinedScope()) {
SegmentAllocator allocatorSegment = SegmentAllocator.arenaAllocator(scopeResource);
MemorySegment offHeap = allocatorSegment.allocateArray(CLinker.C_POINTER, javaStrings.length);
// 4. Copy the strings from on-heap to off-heap
for (int i = 0; i < javaStrings.length; i++) {
// Allocate a string off-heap, then store a pointer to it
MemorySegment cString = CLinker.toCString(javaStrings[i], ResourceScope.newImplicitScope());
MemoryAccess.setAddressAtIndex(offHeap, i, cString.address());
}
// 5. Sort the off-heap data by calling the foreign function
// invoke // unreported exception Throwable; must be caught or declared to be thrown
radixSort.invoke(offHeap.address(), javaStrings.length, MemoryAddress.NULL, '\0');
// 6. Copy the (reordered) strings from off-heap to on-heap
for (int i = 0; i < javaStrings.length; i++) {
MemoryAddress cStringPtr = MemoryAccess.getAddressAtIndex(offHeap, i);
// https://openjdk.org/jeps/412 // Erratum // error: cannot find symbol toJavaStringRestricted
// javaStrings[i] = CLinker.toJavaStringRestricted(cStringPtr);
javaStrings[i] = CLinker.toJavaString(cStringPtr);
}
}
System.out.println("radixsort output: " + Arrays.toString(javaStrings));
}
public static void main(String[] args) throws Throwable {
System.out.println(String.format("Java Version: %s", System.getProperty("java.version")));
var functionMemoryAPIIncubatorForeign = new ForeignFunctionMemoryAPIIncubator();
functionMemoryAPIIncubatorForeign.radixsort();
}
}
Output
WARNING: Using incubator modules: jdk.incubator.foreign
warning: using incubating module(s): jdk.incubator.foreign
1 warning
Java Version: 17.0.2
Exception in thread "main" java.lang.IllegalArgumentException: Carrier size mismatch: char != b8[abi/kind=CHAR]
at jdk.incubator.foreign/jdk.internal.foreign.Utils.checkPrimitiveCarrierCompat(Utils.java:111)
at jdk.incubator.foreign/jdk.internal.foreign.abi.SharedUtils.checkCompatibleType(SharedUtils.java:230)
at jdk.incubator.foreign/jdk.internal.foreign.abi.SharedUtils.checkFunctionTypes(SharedUtils.java:251)
at jdk.incubator.foreign/jdk.internal.foreign.abi.aarch64.CallArranger.getBindings(CallArranger.java:99)
at jdk.incubator.foreign/jdk.internal.foreign.abi.aarch64.CallArranger.arrangeDowncall(CallArranger.java:128)
at jdk.incubator.foreign/jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64Linker.downcallHandle(MacOsAArch64Linker.java:84)
at jdk.incubator.foreign/jdk.internal.foreign.AbstractCLinker.downcallHandle(AbstractCLinker.java:44)
at ForeignFunctionMemoryAPIIncubator.radixsort(ForeignFunctionMemoryAPIIncubator.java:19)
at ForeignFunctionMemoryAPIIncubator.main(ForeignFunctionMemoryAPIIncubator.java:68)
Reverse-engineering JEP 412: Foreign Function & Memory API (Incubator) from JEP 419: Foreign Function & Memory API (Second Incubator) not working as expected.
Here's the answer:
MethodHandle radixSort = CLinker.getInstance().downcallHandle( CLinker.systemLookup().lookup("radixsort").get(), MethodType.methodType(void.class, MemoryAddress.class, int.class, MemoryAddress.class, int.class), FunctionDescriptor.ofVoid(CLinker.C_POINTER, CLinker.C_INT, CLinker.C_POINTER, CLinker.C_INT));