@@ -23,14 +23,32 @@ use zenoh_ext::{VarInt, ZDeserializeError, ZDeserializer, ZSerializer};
2323
2424type JResult < T > = core:: result:: Result < T , JniBindingError < ( ) > > ;
2525
26- /// Throw the SDK's `io.zenoh.exceptions.ZError` with the error's message. This
27- /// hand-written JNI surface (ZSerializer/ZDeserializer) throws directly —
28- /// matching the canonical model where every error surfaces as `ZError` (the
29- /// generated wrappers reach it via their `onError` callback; this hand-written
30- /// path throws it straight, so callers no longer need an exception-bridge).
31- /// `ZError(message: String?)` compiles to a `(Ljava/lang/String;)V` ctor.
32- fn throw_jni_error ( env : & mut JNIEnv , err : & impl core:: fmt:: Display ) {
33- let _ = env. throw_new ( "io/zenoh/exceptions/ZError" , err. to_string ( ) ) ;
26+ /// Deliver an error to the foreign `onError` callback — the SAME model the
27+ /// generated wrappers use (no direct `throw` from native). `on_error` is a
28+ /// Kotlin `(je: String?) -> R` function (the binding-error arity); we invoke it
29+ /// with the message via the erased `invoke`. The handler throws `ZError`, so a
30+ /// JVM exception is pending when we return the sentinel. (This hand-written
31+ /// surface holds no native handle locks, so invoking the throwing callback
32+ /// straight from the upcall is safe — no lock is held across the throw.)
33+ fn signal_error ( env : & mut JNIEnv , on_error : & JObject , err : & impl core:: fmt:: Display ) {
34+ // If a JVM exception is already pending (a Java upcall threw), let it
35+ // propagate untouched — do NOT invoke the callback over it (and do not
36+ // clear it): the pending exception surfaces when we return the sentinel.
37+ if env. exception_check ( ) . unwrap_or ( false ) {
38+ return ;
39+ }
40+ let je: JObject = match env. new_string ( err. to_string ( ) ) {
41+ Ok ( s) => s. into ( ) ,
42+ Err ( _) => JObject :: null ( ) ,
43+ } ;
44+ // The callback throws `ZError`, leaving a pending exception; we ignore the
45+ // `Err` and return so it propagates.
46+ let _ = env. call_method (
47+ on_error,
48+ "invoke" ,
49+ "(Ljava/lang/Object;)Ljava/lang/Object;" ,
50+ & [ JValue :: Object ( & je) ] ,
51+ ) ;
3452}
3553
3654/// Helper function to convert a JByteArray into a Vec<u8>.
@@ -190,6 +208,7 @@ pub extern "C" fn Java_io_zenoh_jni_bytes_JNIBytes_serializeViaJNI(
190208 _class : JClass ,
191209 any : JObject ,
192210 token_type : JObject ,
211+ on_error : JObject ,
193212) -> jobject {
194213 || -> JResult < jobject > {
195214 let mut serializer = ZSerializer :: new ( ) ;
@@ -201,7 +220,7 @@ pub extern "C" fn Java_io_zenoh_jni_bytes_JNIBytes_serializeViaJNI(
201220 Ok ( byte_array. as_raw ( ) )
202221 } ( )
203222 . unwrap_or_else ( |err| {
204- throw_jni_error ( & mut env, & err) ;
223+ signal_error ( & mut env, & on_error , & err) ;
205224 JObject :: default ( ) . as_raw ( )
206225 } )
207226}
@@ -332,6 +351,7 @@ pub extern "C" fn Java_io_zenoh_jni_bytes_JNIBytes_deserializeViaJNI(
332351 _class : JClass ,
333352 bytes : JByteArray ,
334353 jtype : JObject ,
354+ on_error : JObject ,
335355) -> jobject {
336356 || -> JResult < jobject > {
337357 let decoded_bytes: Vec < u8 > = decode_byte_array ( & env, bytes) ?;
@@ -345,7 +365,7 @@ pub extern "C" fn Java_io_zenoh_jni_bytes_JNIBytes_deserializeViaJNI(
345365 Ok ( obj)
346366 } ( )
347367 . unwrap_or_else ( |err| {
348- throw_jni_error ( & mut env, & err) ;
368+ signal_error ( & mut env, & on_error , & err) ;
349369 JObject :: default ( ) . as_raw ( )
350370 } )
351371}
0 commit comments