Here is a minimal working example (requires Java 22 or later):
(just using libc free
for elaborating the behaviour)
import java.lang.foreign.*;
import java.lang.invoke.MethodHandle;
public class Main {
public static final Linker nativeLinker = Linker.nativeLinker();
public static final SymbolLookup stdlibLookup = nativeLinker.defaultLookup();
public static final SymbolLookup loaderLookup = SymbolLookup.loaderLookup();
private static final FunctionDescriptor DESCRIPTOR$free = FunctionDescriptor.ofVoid(ValueLayout.ADDRESS);
private static final MethodHandle HANDLE$free =
loaderLookup.find("free")
.or(() -> stdlibLookup.find("free"))
.map(symbolSegment -> nativeLinker.downcallHandle(symbolSegment, DESCRIPTOR$free))
.orElseThrow(() -> new RuntimeException("libc function free not found but y?"));
public static void free(MemorySegment address) {
try {
// I want to allow user code to use Java null and MemorySegment.NULL (C NULL) interchangeably
HANDLE$free.invokeExact(address != null ? address : MemorySegment.NULL);
} catch (Throwable throwable) {
throwable.printStackTrace(System.err);
}
}
public static void main(String[] args) {
free(null); // free(MemorySegment.NULL) will produce same result
}
}
Run program and there will be an exception:
java.lang.invoke.WrongMethodTypeException: handle's method type (MemorySegment)void but found (Object)void
at java.base/java.lang.invoke.Invokers.newWrongMethodTypeException(Invokers.java:521)
at java.base/java.lang.invoke.Invokers.checkExactType(Invokers.java:530)
at Main.free(Main.java:19)
at Main.main(Main.java:26)
Java complains that the argument used to call invokeExact
is an Object
, not MemorySegment
. However, the expression
address != null ? address : MemorySegment.NULL
should have type MemorySegment
of course. And if we make a small modification to the code:
public static void free(MemorySegment address) {
try {
MemorySegment temp = address != null ? address : MemorySegment.NULL;
HANDLE$free.invokeExact(temp);
} catch (Throwable throwable) {
throwable.printStackTrace(System.err);
}
}
it works correctly.
Also
HANDLE$free.invoke(address != null ? address : MemorySegment.NULL)
worksHANDLE$free.invokeExact((MemorySegment)(address != null ? address : MemorySegment.NULL))
works, butHANDLE$free.invokeExact((address != null ? address : MemorySegment.NULL))
does not work
Is this some kind of deliberate design, implementation limitation or JVM bugs?
Object
, when both the true and the false expressions are both of typeMemorySegment
. I can perfectly understand the confusion. – Isocracy