How to return a optional string from native methods?
tison
wander4096 at gmail.com
Sat Sep 30 03:18:19 UTC 2023
Thank you! Finally I work out a string conversion utility:
private static String readUtf8String(MemorySegment segment) {
try (Arena arena = Arena.ofConfined()) {
final MemorySegment string =
segment.reinterpret(Long.MAX_VALUE, arena, NativeMethods::disposeString);
return string.getUtf8String(0);
}
}
#[no_mangle]
pub unsafe extern "C" fn dispose_string(s: *mut c_char) {
drop(CString::from_raw(s))
}
Hopefully, this doesn't create too much overhead (I'm not sure the overhead
of creating Arena), and no other leaks. It likes JNIEnv#NewString and IIUC
JNIEnv#NewString also copy bytes.
> Native functions can just return NULL/0, and the linker will
automatically translate that into MemorySegment.NULL.
Good to know!
> So, there is no FFM-specific API for the native code to interact with the
Java side
OK. Perhaps I should return an error union in this case and decode & throw
exceptions on the Java side.
Best,
tison.
Jorn Vernee <jorn.vernee at oracle.com> 于2023年9月29日周五 23:42写道:
> > And, if we think of error handling, how do we properly return a NULL
> from native methods, and how do we throw exceptions from native methods?
> > MemorySegment.NULL seems an Object without overriding equals, and
> JNIEnv's throw counterpart is missing or I don't find it (perhaps upcall
> helps but it's still a bit away from real-world usage).
>
> Native functions can just return NULL/0, and the linker will automatically
> translate that into MemorySegment.NULL.
>
> As for throwing a Java exception from native code, similar to what is
> possible with JNI Throw(New) [1], this is not supported. The Linker
> operates on the general assumption that the native code it is interacting
> with has no knowledge about Java at all. So, there is no FFM-specific API
> for the native code to interact with the Java side.
>
> Jorn
>
> [1]:
> https://docs.oracle.com/en/java/javase/21/docs/specs/jni/functions.html#throw
>
> On 29/09/2023 16:08, tison wrote:
>
> Here is my native method written in Rust:
>
> #[no_mangle]
> pub unsafe extern "C" fn datafusion_version() -> *const c_char {
> CString::new(datafusion::DATAFUSION_VERSION).unwrap().into_raw()
> }
>
> And below is my attempt to bind it with FFM APIs:
>
> static MethodHandle createMethodHandle(String name, FunctionDescriptor
> descriptor) {
> final MemorySegment fp = LOADER.lookup.find(name).orElseThrow();
> return LOADER.linker.downcallHandle(fp, descriptor);
> }
>
> private static final MethodHandle datafusionVersionMethodHandle =
> NativeLoader.createMethodHandle(datafusionVersionMethodName,
> datafusionVersionMethodDesc);
>
> @SneakyThrows
> public static String datafusionVersion() {
> final MemorySegment version = (MemorySegment)
> datafusionVersionMethodHandle.invokeExact();
> return version.getUtf8String(0);
> }
>
> I noticed that the returned MemorySegment is always with length 0 and thus
> any access with return OutOfBoundException.
>
> In the demos from JEP, all memory segments are allocated from the Java
> side and the native code only move or modify those allocated memory
> segments instead of allocate/shrink memory.
>
> I wonder what is the formal method to allocate a string from native
> methods and pass back to the Java world.
>
> And, if we think of error handling, how do we properly return a NULL from
> native methods, and how do we throw exceptions from native methods?
>
> MemorySegment.NULL seems an Object without overriding equals, and JNIEnv's
> throw counterpart is missing or I don't find it (perhaps upcall helps but
> it's still a bit away from real-world usage).
>
> Best,
> tison.
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/panama-dev/attachments/20230930/5690e3a8/attachment.htm>
More information about the panama-dev
mailing list