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?