@@ -3856,6 +3856,95 @@ impl<'local> Env<'local> {
38563856 }
38573857}
38583858
3859+ /// An opaque wrapper around a `Result<T, Error>` that supports mapping errors
3860+ /// with access to an [`Env`] reference.
3861+ ///
3862+ /// This returned by [`EnvUnowned::with_env`] and is designed for use within
3863+ /// native method implementations where unwinding can't be caught by the JVM and
3864+ /// will abort the process.
3865+ ///
3866+ /// There is no `.unwrap()` that would panic on error but, if necessary, you can
3867+ /// use [Self::unwrap_result] to get the inner `Result` and handle the error as
3868+ /// you see fit.
3869+ ///
3870+ /// In most cases it is recommended to either:
3871+ /// 1. Use [Self::map_err] to convert errors to an Ok value that can be returned
3872+ /// from your native method or to throw an exception using the provided `Env`
3873+ /// reference.
3874+ /// 2. Use [Self::unwrap_or_default] to return a default value (often `null` for
3875+ /// reference types) in case of an error, considering that `null` is often a
3876+ /// valid return value for Java methods.
3877+ #[ must_use = "the inner `Result` may be an `Err` variant, which should be handled. `map_err` has access to JNI" ]
3878+ #[ derive( Debug ) ]
3879+ pub struct EnvResult < ' unowned_frame , T , E >
3880+ where
3881+ E : From < Error > ,
3882+ {
3883+ env_ptr : * mut jni_sys:: JNIEnv ,
3884+ result : std:: result:: Result < T , E > ,
3885+ _lifetime : std:: marker:: PhantomData < & ' unowned_frame ( ) > ,
3886+ }
3887+
3888+ impl < ' unowned_frame , T , E > EnvResult < ' unowned_frame , T , E >
3889+ where
3890+ E : From < Error > ,
3891+ {
3892+ /// Creates a new [`EnvResult`] from a raw JNI environment pointer and a
3893+ /// `Result`.
3894+ ///
3895+ /// # Safety
3896+ ///
3897+ /// The `ptr` must be a valid, non-null pointer to a `JNIEnv`.
3898+ pub ( crate ) unsafe fn new (
3899+ env_ptr : * mut jni_sys:: JNIEnv ,
3900+ result : std:: result:: Result < T , E > ,
3901+ ) -> Self {
3902+ Self {
3903+ env_ptr,
3904+ result,
3905+ _lifetime : std:: marker:: PhantomData ,
3906+ }
3907+ }
3908+
3909+ /// Maps the error value of this [`EnvResult`] to another type using the
3910+ /// provided closure.
3911+ ///
3912+ /// The closure is provided with a mutable reference to an `Env` that can
3913+ /// access JNI and be used to throw exceptions if needed.
3914+ ///
3915+ /// The closure effectively runs within a call to [`EnvUnowned::with_env`]
3916+ /// that will catch any panics and return an [`Error::PanicCaught`] if a
3917+ /// panic occurs.
3918+ pub fn map_err < F , O > ( self , f : F ) -> EnvResult < ' unowned_frame , T , O >
3919+ where
3920+ F : FnOnce ( & mut Env < ' unowned_frame > , E ) -> O ,
3921+ O : From < Error > ,
3922+ {
3923+ let mut unowned_env: EnvUnowned < ' unowned_frame > =
3924+ unsafe { EnvUnowned :: from_raw ( self . env_ptr ) } ;
3925+ unowned_env. with_env ( |env| self . result . map_err ( |e| f ( env, e) ) )
3926+ }
3927+
3928+ /// Consumes the `SafeResult`, returning the wrapped `Result`
3929+ ///
3930+ /// Be careful within the implementation of native methods to not `.unwrap()`
3931+ /// or `.expect()` the `Result` and potentially unwind across the FFI boundary.
3932+ pub fn unwrap_result ( self ) -> std:: result:: Result < T , E > {
3933+ self . result
3934+ }
3935+
3936+ /// Consumes the [`EnvResult`], returning the wrapped `Ok` value or `T::default()`.
3937+ ///
3938+ /// Note that all [`Reference`] types, like [`JObject`] or [`JString`] etc implement
3939+ /// `Default`, which will return a `null` which is often valid for returning to Java.
3940+ pub fn unwrap_or_default ( self ) -> T
3941+ where
3942+ T : Default ,
3943+ {
3944+ self . result . unwrap_or_default ( )
3945+ }
3946+ }
3947+
38593948/// Represents an external (unowned) JNI stack frame and thread attachment that
38603949/// was passed to a native method call.
38613950///
@@ -3896,22 +3985,28 @@ impl<'unowned_frame> EnvUnowned<'unowned_frame> {
38963985 /// method implementations in cases where you have named the lifetime for
38973986 /// the caller's JNI stack frame.
38983987 ///
3899- /// Since it would lead to undefined behaviour to allow Rust code to unwind
3900- /// across a native method call boundary, this API wraps the closure
3901- /// in a [`catch_unwind`] to catch any panics and return an [`Error::PanicCaught`]
3902- /// if a panic occurs.
3988+ /// It returns an [`EnvResult`] that is an opaque wrapper around your
3989+ /// `Result` that continues to borrow your `EnvUnowned` reference to support
3990+ /// mapping errors with access to an [`Env`], so you may choose to throw
3991+ /// errors as exceptions.
3992+ ///
3993+ /// To avoid the risk of unwinding into the JVM (which will abort the
3994+ /// process) this API wraps the closure in a [`catch_unwind`] to catch any
3995+ /// panics and return an [`Error::PanicCaught`] if a panic occurs.
39033996 ///
39043997 /// Note: This API does not create a new JNI stack frame, which is normally
39053998 /// what you want when implementing a native method, since the JVM will
39063999 /// clean up the JNI stack frame when the native method returns.
3907- pub fn with_env < F , T , E > ( & mut self , f : F ) -> std:: result:: Result < T , E >
4000+ ///
4001+ /// Note: This API returns an [`EnvResult`]
4002+ pub fn with_env < F , T , E > ( & mut self , f : F ) -> EnvResult < ' unowned_frame , T , E >
39084003 where
39094004 F : FnOnce ( & mut Env < ' unowned_frame > ) -> std:: result:: Result < T , E > ,
39104005 E : From < Error > ,
39114006 {
39124007 // Safety: we trust that self.ptr a valid, non-null pointer
39134008 let mut guard = unsafe { AttachGuard :: from_unowned ( self . ptr ) } ;
3914- guard. with_env_current_frame ( |env| {
4009+ let result = guard. with_env_current_frame ( |env| {
39154010 // ☠☠☠☠☠☠☠☠☠☠☠☠☠☠ !WARNING! ☠☠☠☠☠☠☠☠☠☠☠☠☠☠
39164011 //
39174012 // We are casting the Env<'lifetime> here so the closure will
@@ -3955,7 +4050,8 @@ impl<'unowned_frame> EnvUnowned<'unowned_frame> {
39554050 Ok ( ret) => ret,
39564051 Err ( payload) => Err ( Error :: PanicCaught ( panic_payload_to_string ( payload) ) . into ( ) ) ,
39574052 }
3958- } )
4053+ } ) ;
4054+ unsafe { EnvResult :: new ( self . ptr , result) }
39594055 }
39604056
39614057 /// Runs a closure with a [`Env`] based on an unowned JNI thread attachment
@@ -4006,7 +4102,7 @@ impl<'unowned_frame> EnvUnowned<'unowned_frame> {
40064102 /// know represents a valid JNI attachment for the current thread.
40074103 ///
40084104 /// If you are implementing a native method in Rust though, you should
4009- /// prefer to use the `EnvUnowned` type as the first argument to your
4105+ /// prefer to use the [ `EnvUnowned`] type as the first argument to your
40104106 /// native method and avoid the need to use a raw pointer.
40114107 ///
40124108 /// If you have a raw [`crate::sys::JNIEnv`] pointer, this API should be
@@ -4035,6 +4131,16 @@ impl<'unowned_frame> EnvUnowned<'unowned_frame> {
40354131 _lifetime : std:: marker:: PhantomData ,
40364132 }
40374133 }
4134+
4135+ /// Returns the raw pointer to the underlying [`crate::sys::JNIEnv`].
4136+ pub fn as_raw ( & self ) -> * mut jni_sys:: JNIEnv {
4137+ self . ptr
4138+ }
4139+
4140+ /// Consumes the [`EnvUnowned`] and returns the raw pointer to the underlying [`crate::sys::JNIEnv`].
4141+ pub fn into_raw ( self ) -> * mut jni_sys:: JNIEnv {
4142+ self . ptr
4143+ }
40384144}
40394145
40404146#[ derive( Debug ) ]
0 commit comments