All this is with the new JDK 17.
I'm attempting to turn an on-heap byte array into a MemorySegment and pass that to a native function. I created simple sample code that shows this:
final CLinker cLinker = CLinker.getInstance();
// int strlen(const char *str);
final Optional<MemoryAddress> oSymbolAddress = CLinker.systemLookup().lookup("strlen");
final MethodHandle mh = cLinker.downcallHandle(oSymbolAddress.get(),
MethodType.methodType(int.class, MemoryAddress.class),
FunctionDescriptor.of(C_INT, C_POINTER));
out.println("I found this method handle: " + mh);
final byte[] ba = new byte[100];
ba[0] = 'h';
ba[1] = 'e';
ba[2] = 'l';
ba[3] = 'l';
ba[4] = 'o';
ba[5] = 0;
final MemorySegment stringSegment = MemorySegment.ofArray(ba);
final int result = (Integer) mh.invoke(stringSegment.address());
out.println("The length of the string is: " + result);
It tries to run but it throws:
Exception in thread "main" java.lang.UnsupportedOperationException: Not a native address
at jdk.incubator.foreign/jdk.internal.foreign.MemoryAddressImpl.toRawLongValue(MemoryAddressImpl.java:91)
If instead of using MemorySegment.ofArray(ba)
, I use this:
final MemorySegment stringSegment = MemorySegment.allocateNative(100, newImplicitScope());
stringSegment.asByteBuffer().put(ba);
it works and gives the expected answer (5).
I looked up this function in MemoryAddressImpl.java
and I can see:
@Override
public long toRawLongValue() {
if (segment != null) {
if (segment.base() != null) {
throw new UnsupportedOperationException("Not a native address");
}
segment.checkValidState();
}
return offset();
}
Clearly segment.base()
is returning null.
I don't really understand what's going on. I thought that one of the major advantages of Project Panama would be that native code could access on-heap memory, to avoid having to copy. I certainly can do an allocateNative()
and then copy the byte array into that, but that's a copy that I think should be avoided.
Any ideas on this? Am I doing something wrong or misunderstanding how to use on-heap memory?