rust_jni/jni/
mod.rs

1#[cfg(test)]
2#[macro_use]
3mod testing;
4
5pub mod class;
6pub mod method_calls;
7mod methods;
8pub mod native_method;
9mod primitives;
10pub mod string;
11pub mod throwable;
12
13use attach_arguments::{self, AttachArguments};
14use init_arguments::{self, InitArguments};
15use jni::class::Class;
16use jni::method_calls::call_method;
17use jni::primitives::ToJniTuple;
18use jni::string::String;
19use jni::throwable::Throwable;
20use jni_sys;
21use raw::*;
22use std;
23use std::cell::RefCell;
24use std::fmt;
25use std::marker::PhantomData;
26use std::os::raw::c_void;
27use std::ptr;
28use version::{self, JniVersion};
29
30include!("call_jni_method.rs");
31include!("generate_class.rs");
32
33/// Errors returned by JNI function.
34///
35/// [JNI documentation](https://docs.oracle.com/javase/10/docs/specs/jni/functions.html#return-codes)
36// TODO(#17): add error codes.
37#[derive(Debug, Clone, Copy, PartialEq, Eq)]
38pub enum JniError {
39    /// Unknown error.
40    /// Needed for forward compability.
41    Unknown(i32),
42}
43
44/// A token that represents that there is no pending Java exception in the current thread.
45///
46/// # Pending exceptions
47///
48/// When a JNI function is called, it can throw an exception. Then the current thread is said
49/// to have a pending exception. Most JNI functions must not be called when there is a pending
50/// exception. Read more about exception handling in
51/// [JNI documentation](https://docs.oracle.com/javase/10/docs/specs/jni/design.html#java-exceptions).
52///
53/// # Exception tokens
54///
55/// [`rust-jni`](index.html) tries to push as many programming errors as possible from run-time
56/// to compile-time. To not allow a caller to call JNI methods when there is a pending exception,
57/// these methods will require the caller to provide a [`NoException`](struct.NoException.html)
58/// token. The caller can obtain the token after attaching the thread to the Java VM:
59/// ```
60/// use rust_jni::{AttachArguments, InitArguments, JavaVM, JniVersion};
61///
62/// let init_arguments = InitArguments::get_default(JniVersion::V8).unwrap();
63/// let vm = JavaVM::create(&init_arguments).unwrap();
64/// let env = vm.attach(&AttachArguments::new(&init_arguments)).unwrap();
65/// let token = env.token();
66/// ```
67/// Once obtained, the token can be used to call JNI methods:
68/// ```
69/// # use rust_jni::{AttachArguments, InitArguments, JavaVM, JniVersion, java};
70/// #
71/// # let init_arguments = InitArguments::get_default(JniVersion::V8).unwrap();
72/// # let vm = JavaVM::create(&init_arguments).unwrap();
73/// # let env = vm.attach(&AttachArguments::new(&init_arguments)).unwrap();
74/// let token = env.token();
75/// let string = java::lang::String::empty(&env, &token).unwrap();
76/// ```
77/// [`rust-jni`](index.html) follows Java semantics, where a method either returns a result
78/// or throws an exception. All Java methods return a [`JavaResult`](type.JavaResult.html) value,
79/// which is either an actual result or a [`Throwable`](struct.Throwable.html) value representing
80/// the exception thrown by this method call. Java methods never leave a pending exception,
81/// so they never consume the [`NoException`](struct.NoException.html) token, but they always
82/// require it to be presented:
83/// ```
84/// # use rust_jni::{AttachArguments, InitArguments, JavaVM, JniVersion, java};
85/// #
86/// # let init_arguments = InitArguments::get_default(JniVersion::V8).unwrap();
87/// # let vm = JavaVM::create(&init_arguments).unwrap();
88/// # let env = vm.attach(&AttachArguments::new(&init_arguments)).unwrap();
89/// let token = env.token();
90/// let string = java::lang::Class::find(&env, "java/lang/String", &token).unwrap();
91/// let exception = java::lang::Class::find(&env, "invalid", &token).unwrap_err();
92/// ```
93/// A token can not be obtained twice from a [`JniEnv`](struct.JniEnv.html) value:
94/// ```should_panic
95/// # use rust_jni::{AttachArguments, InitArguments, JavaVM, JniVersion};
96/// #
97/// # let init_arguments = InitArguments::get_default(JniVersion::V8).unwrap();
98/// # let vm = JavaVM::create(&init_arguments).unwrap();
99/// let env = vm.attach(&AttachArguments::new(&init_arguments)).unwrap();
100/// let token = env.token();
101/// let token = env.token(); // panics!
102/// ```
103/// There is no possible way to obtain a token when there is a pending exception.
104/// The token is bound to the [`JniEnv`](struct.JniEnv.html) object, so it can't outlive it:
105/// ```compile_fail
106/// # use rust_jni::{AttachArguments, InitArguments, JavaVM, JniVersion};
107///
108/// # let init_arguments = InitArguments::get_default(JniVersion::V8).unwrap();
109/// # let vm = JavaVM::create(&init_arguments).unwrap();
110/// let token = {
111///     let env = vm.attach(&AttachArguments::new(&init_arguments)).unwrap();
112///     let token = env.token();
113///     token
114/// }; // doesn't compile!
115/// ```
116/// Some JNI methods can throw exceptions themselves. In this case the token will be consumed:
117/// ```compile_fail
118/// # use rust_jni::{AttachArguments, InitArguments, JavaVM, JniVersion, java};
119/// #
120/// # let init_arguments = InitArguments::get_default(JniVersion::V8).unwrap();
121/// # let vm = JavaVM::create(&init_arguments).unwrap();
122/// # let env = vm.attach(&AttachArguments::new(&init_arguments)).unwrap();
123/// let token = env.token();
124/// let exception = java::lang::String::empty(&env, &token).unwrap_err();
125/// exception.throw(token);
126/// java::lang::String::empty(&env, &token); // doesn't compile! Can't use the token any more.
127/// ```
128/// Methods that consume the token will always return an [`Exception`](struct.Exception.html)
129/// token. The [`Exception`](struct.Exception.html) token can be
130/// [`unwrap`](struct.Exception.html#method.unwrap)-ped into a new
131/// [`NoException`](struct.NoException.html) token and a [`Throwable`](struct.Throwable.html)
132/// value with the pending exception. Unwrapping the [`Exception`](struct.Exception.html) token
133///  will clear the pending exception, so it is again safe to call JNI methods:
134/// ```
135/// # use rust_jni::{AttachArguments, InitArguments, JavaVM, JniVersion, java};
136/// #
137/// # let init_arguments = InitArguments::get_default(JniVersion::V8).unwrap();
138/// # let vm = JavaVM::create(&init_arguments).unwrap();
139/// # let env = vm.attach(&AttachArguments::new(&init_arguments)).unwrap();
140/// let token = env.token();
141/// let exception = java::lang::Class::find(&env, "invalid", &token).unwrap_err();
142/// let exception_token = exception.throw(token); // there is a pending exception now.
143/// let (exception, new_token) = exception_token.unwrap();
144/// java::lang::String::empty(&env, &new_token); // can call Java methods again.
145/// ```
146#[derive(Debug)]
147pub struct NoException<'env> {
148    _token: (),
149    _env: PhantomData<JniEnv<'env>>,
150}
151
152impl<'env> NoException<'env> {
153    /// Unsafe because it creates a new no-exception token when there might be a pending exception.
154    unsafe fn new_env<'a>(_env: &JniEnv<'a>) -> NoException<'a> {
155        // Safe because this function ensures correct lifetimes.
156        Self::new_raw()
157    }
158
159    /// Unsafe because:
160    /// 1. It creates a new no-exception token when there might be a pending exception
161    /// 2. Doesn't ensure a correct lifetime
162    unsafe fn new_raw<'a>() -> NoException<'a> {
163        NoException {
164            _token: (),
165            _env: PhantomData::<JniEnv>,
166        }
167    }
168
169    /// Unsafe, because having two tokens will allow calling methods when there is a
170    /// pending exception.
171    unsafe fn clone(&self) -> Self {
172        Self::new_raw()
173    }
174
175    #[cfg(test)]
176    fn test<'a>() -> NoException<'a> {
177        // Safe because only used for unit-testing.
178        unsafe { Self::new_raw() }
179    }
180}
181
182// [`NoException`](struct.NoException.html) can't be passed between threads.
183// TODO(https://github.com/rust-lang/rust/issues/13231): enable when !Send is stable.
184// impl<'env> !Send for NoException<'env> {}
185// impl<'env> !Sync for NoException<'env> {}
186
187/// A dual token to [`NoException`](struct.NoException.html) that represents that there
188/// might be a pending exception in Java.
189///
190/// Read more about exception tokens in [`NoException`](struct.NoException.html) documentation.
191#[derive(Debug)]
192pub struct Exception<'env> {
193    _token: (),
194    env: &'env JniEnv<'env>,
195}
196
197impl<'env> Exception<'env> {
198    /// Get and clear the pending exception and a [`NoException`](struct.NoException.html) token
199    /// to call more JNI methods. The [`Exception`](struct.Exception.html) token is consumed
200    /// by this method and can't be used any more.
201    pub fn unwrap(self) -> (Throwable<'env>, NoException<'env>) {
202        let throwable = get_and_clear_exception(self);
203        // Safe because we just cleared the pending exception.
204        let token = unsafe { NoException::new_raw() };
205        (throwable, token)
206    }
207
208    /// Exchange a [`NoException`](struct.NoException.html) for an
209    /// [`Exception`](struct.Exception.html) token. This means that [`rust-jni`](index.html)
210    /// no onger can prove that there is no pending exception.
211    /// Unsafe because there might not actually be a pending exception when this method is called.
212    unsafe fn new<'a>(env: &'a JniEnv<'a>, _token: NoException) -> Exception<'a> {
213        Self::new_raw(env)
214    }
215
216    /// Unsafe because:
217    /// 1. Unsafe because there might not actually be a pending exception when this method is
218    /// called.
219    /// 2. Doesn't ensure a correct lifetime
220    unsafe fn new_raw<'a>(env: &'a JniEnv<'a>) -> Exception<'a> {
221        Exception { _token: (), env }
222    }
223
224    #[cfg(test)]
225    fn test<'a>(env: &'a JniEnv<'a>) -> Exception<'a> {
226        // Safe because only used for unit-testing.
227        unsafe { Self::new_raw(env) }
228    }
229}
230
231#[cfg(test)]
232mod exception_tests {
233    use super::*;
234    use jni::testing::*;
235
236    #[test]
237    fn unwrap() {
238        const EXCEPTION: jni_sys::jobject = 0x2835 as jni_sys::jobject;
239        let calls = test_raw_jni_env!(vec![
240            JniCall::ExceptionOccurred(ExceptionOccurred { result: EXCEPTION }),
241            JniCall::ExceptionClear(ExceptionClear {}),
242        ]);
243        let vm = test_vm(ptr::null_mut());
244        let env = test_env(&vm, calls.env);
245        let token = Exception::test(&env);
246        let (exception, _) = token.unwrap();
247        calls.assert_eq(&exception, EXCEPTION);
248    }
249}
250
251// [`Exception`](struct.Exception.html) can't be passed between threads.
252// TODO(https://github.com/rust-lang/rust/issues/13231): enable when !Send is stable.
253// impl<'env> !Send for NoException<'env> {}
254// impl<'env> !Sync for NoException<'env> {}
255
256/// A result of a JNI function call. Either a value and a [`NoException`](struct.NoException.html)
257/// token, when the function didn't throw an exception or an [`Exception`](struct.Exception.html)
258/// token when it did or it is unknown if it did.
259/// All JNI methods that are not calls to methods of Java classes use this type as their result.
260type JniResult<'env, T> = Result<(T, NoException<'env>), Exception<'env>>;
261
262/// Create a [`JniResult`](type.JniResult.html) from a nullable pointer.
263///
264/// Will return an [`Exception`](struct.Exception.html) token for the `null` value or the argument
265/// and a [`NoException`](struct.NoException.html) token otherwise.
266/// Unsafe because there might not be a pending exception.
267unsafe fn from_nullable<'a, T>(
268    env: &'a JniEnv<'a>,
269    value: *mut T,
270    token: NoException<'a>,
271) -> JniResult<'a, *mut T> {
272    if value == ptr::null_mut() {
273        Err(Exception::new(env, token))
274    } else {
275        Ok((value, token))
276    }
277}
278
279/// A type that represents a result of a Java method call. A Java method can either return
280/// a result or throw a
281/// [`Throwable`](https://docs.oracle.com/javase/10/docs/api/java/lang/Throwable.html).
282pub type JavaResult<'env, T> = Result<T, Throwable<'env>>;
283
284#[cfg(test)]
285mod jni_result_tests {
286    use super::*;
287
288    #[test]
289    fn from_nullable_null() {
290        let vm = test_vm(ptr::null_mut());
291        let env = test_env(&vm, ptr::null_mut());
292        unsafe {
293            assert!(from_nullable(&env, ptr::null_mut() as *mut i32, NoException::test()).is_err());
294        }
295    }
296
297    #[test]
298    fn from_nullable_non_null() {
299        let vm = test_vm(ptr::null_mut());
300        let env = test_env(&vm, ptr::null_mut());
301        let ptr = 0x1234 as *mut i32;
302        unsafe {
303            let value = from_nullable(&env, ptr, NoException::test());
304            assert!(value.is_ok());
305            assert_eq!(value.unwrap().0, ptr);
306        }
307    }
308}
309
310/// A struct for interacting with the Java VM.
311///
312/// [JNI documentation](https://docs.oracle.com/javase/10/docs/specs/jni/invocation.html#jni_createjavavm)
313///
314/// # Examples
315/// ```
316/// use rust_jni::{InitArguments, JavaVM, JniVersion, JvmOption, JvmVerboseOption};
317/// use std::ptr;
318///
319/// let options = InitArguments::get_default(JniVersion::V8).unwrap()
320///     .with_option(JvmOption::Verbose(JvmVerboseOption::Gc))
321///     .with_option(JvmOption::Verbose(JvmVerboseOption::Jni));
322///
323/// let vm = JavaVM::create(&options).unwrap();
324/// unsafe {
325///     assert_ne!(vm.raw_jvm(), ptr::null_mut());
326/// }
327///
328/// let vms = JavaVM::list().unwrap();
329/// unsafe {
330///     assert_eq!(vms[0].raw_jvm(), vm.raw_jvm());
331/// }
332/// ```
333/// `JavaVM` is `Send + Sync`. It means it can be shared between threads.
334/// ```
335/// use rust_jni::{InitArguments, JavaVM, JniVersion};
336/// use std::ptr;
337/// use std::sync::Arc;
338///
339/// let vm =
340///     Arc::new(JavaVM::create(&InitArguments::get_default(JniVersion::V8).unwrap()).unwrap());
341/// {
342///     let vm = vm.clone();
343///     ::std::thread::spawn(move || {
344///         unsafe {
345///             assert_ne!(vm.raw_jvm(), ptr::null_mut());
346///         }
347///     });
348/// }
349/// unsafe {
350///     assert_ne!(vm.raw_jvm(), ptr::null_mut());
351/// }
352/// ```
353///
354/// The main purpose of [`JavaVM`](struct.JavaVM.html) is to attach threads by provisioning
355/// [`JniEnv`](struct.JniEnv.html)-s.
356#[derive(Debug)]
357pub struct JavaVM {
358    java_vm: *mut jni_sys::JavaVM,
359    owned: bool,
360}
361
362impl JavaVM {
363    /// Create a Java VM with the specified arguments.
364    ///
365    /// [Only one](https://docs.oracle.com/javase/10/docs/specs/jni/invocation.html#jni_createjavavm)
366    /// Java VM per process is supported. When called for the second time will return an error.
367    /// This is the case even if the object is dropped.
368    ///
369    /// [JNI documentation](https://docs.oracle.com/javase/10/docs/specs/jni/invocation.html#jni_createjavavm)
370    pub fn create(arguments: &InitArguments) -> Result<Self, JniError> {
371        let mut java_vm: *mut jni_sys::JavaVM = ptr::null_mut();
372        let mut jni_env: *mut jni_sys::JNIEnv = ptr::null_mut();
373        let mut strings_buffer = vec![];
374        let mut options_buffer = vec![];
375        let mut raw_arguments =
376            init_arguments::to_raw(&arguments, &mut strings_buffer, &mut options_buffer);
377        // Safe because we pass pointers to correct data structures.
378        let status = unsafe {
379            JNI_CreateJavaVM(
380                (&mut java_vm) as *mut *mut jni_sys::JavaVM,
381                (&mut jni_env) as *mut *mut jni_sys::JNIEnv as *mut *mut c_void,
382                &mut raw_arguments.raw_arguments as *mut jni_sys::JavaVMInitArgs as *mut c_void,
383            )
384        };
385        match status {
386            jni_sys::JNI_OK => {
387                // We want to detach the current thread because we want to only allow attaching
388                // a thread once and the `attach` method will panic if the thread is already
389                // attached. Detaching here makes this logic easier to implement.
390                // Safe because `JNI_CreateJavaVM` returned OK and hence `java_vm`
391                // is a valid `jni_sys::JavaVM` pointer and because `JNI_CreateJavaVM` attaches
392                // the current thread.
393                unsafe { Self::detach(java_vm) };
394
395                Ok(Self {
396                    java_vm,
397                    owned: true,
398                })
399            }
400            jni_sys::JNI_EVERSION => panic!(
401                "Got upsupported version error when creating a Java VM. \
402                 Should not happen as `InitArguments` are supposed to check \
403                 for version support."
404            ),
405            jni_sys::JNI_EDETACHED => {
406                panic!("Unexpected `EDETACHED` error when creating a Java VM.")
407            }
408            status => Err(JniError::Unknown(status)),
409        }
410    }
411
412    /// Get a list of created Java VMs.
413    ///
414    /// [JNI documentation](https://docs.oracle.com/javase/10/docs/specs/jni/invocation.html#jni_getcreatedjavavms)
415    pub fn list() -> Result<Vec<Self>, JniError> {
416        let mut vms_created: jni_sys::jsize = 0;
417        // Safe because arguments are correct.
418        let status = unsafe {
419            JNI_GetCreatedJavaVMs(
420                ::std::ptr::null_mut(),
421                0,
422                (&mut vms_created) as *mut jni_sys::jsize,
423            )
424        };
425        match status {
426            jni_sys::JNI_OK => {
427                let mut java_vms: Vec<*mut jni_sys::JavaVM> = vec![];
428                java_vms.resize(vms_created as usize, ::std::ptr::null_mut());
429                let mut tmp: jni_sys::jsize = 0;
430                // Safe because arguments are ensured to be correct.
431                let status = unsafe {
432                    JNI_GetCreatedJavaVMs(
433                        (java_vms.as_mut_ptr()) as *mut *mut jni_sys::JavaVM,
434                        vms_created,
435                        // Technically, a new VM could have been created since the previous call to
436                        // `JNI_GetCreatedJavaVMs`. But then we also technically should not return
437                        // any new ones, because they weren't there wneh this function was called.
438                        (&mut tmp) as *mut jni_sys::jsize,
439                    )
440                };
441                match status {
442                    jni_sys::JNI_OK => Ok(java_vms
443                        .iter()
444                        .cloned()
445                        // Safe because a correct pointer is passed.
446                        .map(|java_vm| unsafe { Self::from_ptr(java_vm) })
447                        .collect()),
448                    status => Err(JniError::Unknown(status)),
449                }
450            }
451            status => Err(JniError::Unknown(status)),
452        }
453    }
454
455    /// Get the raw Java VM pointer.
456    ///
457    /// This function provides low-level access to all of JNI and thus is unsafe.
458    pub unsafe fn raw_jvm(&self) -> *mut jni_sys::JavaVM {
459        self.java_vm
460    }
461
462    /// Attach the current thread to the Java VM with a specific thread name.
463    /// Returns a [`JniEnv`](struct.JniEnv.html) instance for this thread.
464    ///
465    /// [JNI documentation](https://docs.oracle.com/javase/10/docs/specs/jni/invocation.html#attachcurrentthread)
466    pub fn attach(&self, arguments: &AttachArguments) -> Result<JniEnv, JniError> {
467        // Safe because the argument is ensured to be the correct method.
468        unsafe { self.attach_generic(arguments, (**self.raw_jvm()).AttachCurrentThread.unwrap()) }
469    }
470
471    /// Attach the current thread to the Java VM as a daemon with a specific thread name.
472    /// Returns a [`JniEnv`](struct.JniEnv.html) instance for this thread.
473    ///
474    /// [JNI documentation](https://docs.oracle.com/javase/10/docs/specs/jni/invocation.html#attachcurrentthreadasdaemon)
475    pub fn attach_daemon(&self, arguments: &AttachArguments) -> Result<JniEnv, JniError> {
476        // Safe because the argument is ensured to be the correct method.
477        unsafe {
478            self.attach_generic(
479                arguments,
480                (**self.raw_jvm()).AttachCurrentThreadAsDaemon.unwrap(),
481            )
482        }
483    }
484
485    /// Unsafe because:
486    /// 1. One can pass an invalid `attach_fn`.
487    /// 2. The current thread might already be attached.
488    unsafe fn attach_generic(
489        &self,
490        arguments: &AttachArguments,
491        attach_fn: unsafe extern "system" fn(
492            _: *mut jni_sys::JavaVM,
493            _: *mut *mut c_void,
494            _: *mut c_void,
495        ) -> jni_sys::jint,
496    ) -> Result<JniEnv, JniError> {
497        let mut buffer: Vec<u8> = vec![];
498        let mut raw_arguments = attach_arguments::to_raw(arguments, &mut buffer);
499        let mut jni_env: *mut jni_sys::JNIEnv = ::std::ptr::null_mut();
500        let get_env_fn = (**self.raw_jvm()).GetEnv.unwrap();
501        // Safe, because the arguments are correct.
502        let status = get_env_fn(
503            self.raw_jvm(),
504            (&mut jni_env) as *mut *mut jni_sys::JNIEnv as *mut *mut c_void,
505            version::to_raw(arguments.version()),
506        );
507        match status {
508            jni_sys::JNI_EDETACHED => {
509                let status = attach_fn(
510                    self.raw_jvm(),
511                    (&mut jni_env) as *mut *mut jni_sys::JNIEnv as *mut *mut c_void,
512                    (&mut raw_arguments.raw_arguments) as *mut jni_sys::JavaVMAttachArgs
513                        as *mut c_void,
514                );
515                match status {
516                    jni_sys::JNI_OK => {
517                        let mut env = JniEnv {
518                            version: arguments.version(),
519                            vm: self,
520                            jni_env,
521                            has_token: RefCell::new(true),
522                            // We don't want to drop `JniEnv` with a pending exception.
523                            native_method_call: true,
524                        };
525                        if env.has_exception() {
526                            panic!("Newly attached thread has a pending exception.");
527                        }
528                        env.native_method_call = false;
529                        Ok(env)
530                    }
531                    jni_sys::JNI_EVERSION => panic!(
532                        "Got upsupported version error when creating a Java VM. \
533                         Should not happen as `InitArguments` are supposed to check \
534                         for version support."
535                    ),
536                    jni_sys::JNI_EDETACHED => {
537                        panic!("Got `EDETACHED` when trying to attach a thread.")
538                    }
539                    // TODO: panic on more impossible errors.
540                    status => Err(JniError::Unknown(status)),
541                }
542            }
543            jni_sys::JNI_OK => panic!(
544                "This thread is already attached to the JVM. \
545                 Attaching a thread twice is not allowed."
546            ),
547            // According to the
548            // [JNI documentation](https://docs.oracle.com/javase/10/docs/specs/jni/invocation.html#getenv),
549            // can only returd `OK`, `EDETACHED` and `EVERSION`.
550            // Will not return `EVERSION` here, because the version was already checked when
551            // creating the Java VM.
552            status => panic!(
553                "GetEnv JNI method returned an unexpected error code {}",
554                status
555            ),
556        }
557    }
558
559    /// Unsafe because:
560    /// 1. A user might pass an incorrect pointer.
561    /// 2. The current thread might not be attached.
562    unsafe fn detach(java_vm: *mut jni_sys::JavaVM) {
563        let detach_fn = (**java_vm).DetachCurrentThread.unwrap();
564        let status = detach_fn(java_vm);
565        // There is no way to recover from detach failure, except leak or fail.
566        if status != jni_sys::JNI_OK {
567            panic!("Could not detach the current thread. Status: {}", status)
568        }
569    }
570
571    /// Unsafe because one can pass an invalid `java_vm` pointer.
572    unsafe fn from_ptr(java_vm: *mut jni_sys::JavaVM) -> JavaVM {
573        JavaVM {
574            java_vm,
575            owned: false,
576        }
577    }
578}
579
580/// Make [`JavaVM`](struct.JavaVM.html) be destroyed when the value is dropped.
581///
582/// [JNI documentation](https://docs.oracle.com/javase/10/docs/specs/jni/invocation.html#destroyjavavm)
583impl Drop for JavaVM {
584    fn drop(&mut self) {
585        if !self.owned {
586            return;
587        }
588
589        // Safe because the argument is ensured to be the correct by construction.
590        let status = unsafe {
591            let destroy_fn = (**self.java_vm).DestroyJavaVM.unwrap();
592            destroy_fn(self.java_vm)
593        };
594
595        if status != jni_sys::JNI_OK {
596            panic!("Failed destroying the JavaVm. Status: {}", status);
597        }
598    }
599}
600
601/// Make [`JavaVM`](struct.JavaVM.html) sendable between threads. Guaranteed to be safe by JNI.
602unsafe impl Send for JavaVM {}
603
604/// Make [`JavaVM`](struct.JavaVM.html) shareable by multiple threads. Guaranteed to be safe
605/// by JNI.
606unsafe impl Sync for JavaVM {}
607
608#[cfg(test)]
609mod java_vm_tests {
610    use super::*;
611    use init_arguments;
612    use java_string::*;
613    use jni::testing::*;
614    use std::ffi::CStr;
615    use std::mem;
616
617    fn default_args() -> InitArguments {
618        init_arguments::tests::default_args()
619    }
620
621    #[test]
622    fn create() {
623        static mut DETACH_CALLS: i32 = 0;
624        static mut DETACH_ARGUMENT: *mut jni_sys::JavaVM = ptr::null_mut();
625        unsafe extern "system" fn detach(java_vm: *mut jni_sys::JavaVM) -> jni_sys::jint {
626            DETACH_CALLS += 1;
627            DETACH_ARGUMENT = java_vm;
628            jni_sys::JNI_OK
629        }
630
631        let raw_java_vm = jni_sys::JNIInvokeInterface_ {
632            DetachCurrentThread: Some(detach),
633            ..empty_raw_java_vm()
634        };
635        let raw_java_vm_ptr = &mut (&raw_java_vm as jni_sys::JavaVM) as *mut jni_sys::JavaVM;
636        let _locked =
637            setup_create_java_vm_call(CreateJavaVMCall::new(jni_sys::JNI_OK, raw_java_vm_ptr));
638        let arguments = default_args();
639        let vm = JavaVM::create(&arguments).unwrap();
640        assert_eq!(vm.java_vm, raw_java_vm_ptr);
641        assert_eq!(vm.owned, true);
642        assert_eq!(arguments, get_create_java_vm_call_input());
643        unsafe {
644            assert_eq!(DETACH_CALLS, 1);
645            assert_eq!(DETACH_ARGUMENT, raw_java_vm_ptr);
646        };
647        mem::forget(vm);
648    }
649
650    #[test]
651    #[should_panic(expected = "Could not detach the current thread. Status: -1")]
652    fn create_detach_error() {
653        unsafe extern "system" fn detach(_: *mut jni_sys::JavaVM) -> jni_sys::jint {
654            jni_sys::JNI_ERR
655        }
656        let raw_java_vm = jni_sys::JNIInvokeInterface_ {
657            DetachCurrentThread: Some(detach),
658            ..empty_raw_java_vm()
659        };
660        let raw_java_vm_ptr = &mut (&raw_java_vm as jni_sys::JavaVM) as *mut jni_sys::JavaVM;
661        let _locked =
662            setup_create_java_vm_call(CreateJavaVMCall::new(jni_sys::JNI_OK, raw_java_vm_ptr));
663        JavaVM::create(&default_args()).unwrap();
664    }
665
666    #[test]
667    #[should_panic(expected = "upsupported version")]
668    fn create_version_error() {
669        let raw_java_vm = 0x1234 as *mut jni_sys::JavaVM;
670        let _locked =
671            setup_create_java_vm_call(CreateJavaVMCall::new(jni_sys::JNI_EVERSION, raw_java_vm));
672        let arguments = default_args();
673        let _ = JavaVM::create(&arguments);
674    }
675
676    #[test]
677    #[should_panic(expected = "Unexpected `EDETACHED`")]
678    fn create_detached_error() {
679        let raw_java_vm = 0x1234 as *mut jni_sys::JavaVM;
680        let _locked =
681            setup_create_java_vm_call(CreateJavaVMCall::new(jni_sys::JNI_EDETACHED, raw_java_vm));
682        let arguments = default_args();
683        let _ = JavaVM::create(&arguments);
684    }
685
686    #[test]
687    fn create_error() {
688        let raw_java_vm = 0x1234 as *mut jni_sys::JavaVM;
689        let _locked =
690            setup_create_java_vm_call(CreateJavaVMCall::new(jni_sys::JNI_ERR, raw_java_vm));
691        let arguments = default_args();
692        assert_eq!(
693            JavaVM::create(&arguments).unwrap_err(),
694            JniError::Unknown(jni_sys::JNI_ERR as i32),
695        );
696    }
697
698    #[test]
699    fn drop() {
700        static mut DESTROY_CALLS: i32 = 0;
701        static mut DESTROY_ARGUMENT: *mut jni_sys::JavaVM = ptr::null_mut();
702        unsafe extern "system" fn destroy_vm(java_vm: *mut jni_sys::JavaVM) -> jni_sys::jint {
703            DESTROY_CALLS += 1;
704            DESTROY_ARGUMENT = java_vm;
705            jni_sys::JNI_OK
706        }
707
708        let raw_java_vm = jni_sys::JNIInvokeInterface_ {
709            DestroyJavaVM: Some(destroy_vm),
710            ..empty_raw_java_vm()
711        };
712        let raw_java_vm_ptr = &mut (&raw_java_vm as jni_sys::JavaVM) as *mut jni_sys::JavaVM;
713        {
714            let _vm = JavaVM {
715                java_vm: raw_java_vm_ptr,
716                owned: true,
717            };
718            unsafe { assert_eq!(DESTROY_CALLS, 0) };
719        }
720        unsafe {
721            assert_eq!(DESTROY_CALLS, 1);
722            assert_eq!(DESTROY_ARGUMENT, raw_java_vm_ptr);
723        };
724    }
725
726    #[test]
727    fn drop_not_owned() {
728        static mut DESTROY_CALLS: i32 = 0;
729        static mut DESTROY_ARGUMENT: *mut jni_sys::JavaVM = ptr::null_mut();
730        unsafe extern "system" fn destroy_vm(java_vm: *mut jni_sys::JavaVM) -> jni_sys::jint {
731            DESTROY_CALLS += 1;
732            DESTROY_ARGUMENT = java_vm;
733            jni_sys::JNI_OK
734        }
735
736        let raw_java_vm = jni_sys::JNIInvokeInterface_ {
737            DestroyJavaVM: Some(destroy_vm),
738            ..empty_raw_java_vm()
739        };
740        let raw_java_vm_ptr = &mut (&raw_java_vm as jni_sys::JavaVM) as *mut jni_sys::JavaVM;
741        {
742            let _vm = test_vm(raw_java_vm_ptr);
743        }
744        unsafe {
745            assert_eq!(DESTROY_CALLS, 0);
746        };
747    }
748
749    #[test]
750    #[should_panic(expected = "Failed destroying the JavaVm. Status: -1")]
751    fn drop_destroy_error() {
752        unsafe extern "system" fn destroy_vm(_: *mut jni_sys::JavaVM) -> jni_sys::jint {
753            jni_sys::JNI_ERR
754        }
755        let raw_java_vm = jni_sys::JNIInvokeInterface_ {
756            DestroyJavaVM: Some(destroy_vm),
757            ..empty_raw_java_vm()
758        };
759        let raw_java_vm = &mut (&raw_java_vm as jni_sys::JavaVM) as *mut jni_sys::JavaVM;
760        JavaVM {
761            java_vm: raw_java_vm,
762            owned: true,
763        };
764    }
765
766    #[test]
767    fn list() {
768        let raw_java_vm_ptr0 = 0x1234 as *mut jni_sys::JavaVM;
769        let raw_java_vm_ptr1 = 0x5678 as *mut jni_sys::JavaVM;
770        let mut java_vm_ptrs: [*mut jni_sys::JavaVM; 2] = [raw_java_vm_ptr0, raw_java_vm_ptr1];
771        let _locked = setup_get_created_java_vms_call(GetCreatedJavaVMsCall::new(
772            jni_sys::JNI_OK,
773            2,
774            java_vm_ptrs.as_mut_ptr(),
775        ));
776        let vms = JavaVM::list().unwrap();
777        assert_eq!(vms[0].java_vm, raw_java_vm_ptr0);
778        assert_eq!(vms[1].java_vm, raw_java_vm_ptr1);
779    }
780
781    #[test]
782    fn list_error_count() {
783        let _locked = setup_get_created_java_vms_call(GetCreatedJavaVMsCall::new(
784            jni_sys::JNI_ERR,
785            0,
786            ptr::null_mut(),
787        ));
788        assert_eq!(
789            JavaVM::list().unwrap_err(),
790            JniError::Unknown(jni_sys::JNI_ERR as i32)
791        );
792    }
793
794    #[test]
795    fn list_error_list() {
796        let raw_java_vm_ptr0 = 0x1234 as *mut jni_sys::JavaVM;
797        let raw_java_vm_ptr1 = 0x5678 as *mut jni_sys::JavaVM;
798        let mut java_vm_ptrs: [*mut jni_sys::JavaVM; 2] = [raw_java_vm_ptr0, raw_java_vm_ptr1];
799        let _locked = setup_get_created_java_vms_call(GetCreatedJavaVMsCall::new_twice(
800            jni_sys::JNI_OK,
801            jni_sys::JNI_ERR,
802            2,
803            java_vm_ptrs.as_mut_ptr(),
804        ));
805        assert_eq!(
806            JavaVM::list().unwrap_err(),
807            JniError::Unknown(jni_sys::JNI_ERR as i32)
808        );
809    }
810
811    #[test]
812    fn raw_vm() {
813        let raw_java_vm = 0x1234 as *mut jni_sys::JavaVM;
814        let vm = test_vm(raw_java_vm);
815        unsafe {
816            assert_eq!(vm.raw_jvm(), raw_java_vm);
817        }
818        mem::forget(vm);
819    }
820
821    #[test]
822    fn attach() {
823        let calls = test_raw_jni_env!(vec![JniCall::ExceptionCheck(ExceptionCheck {
824            result: jni_sys::JNI_FALSE,
825        })]);
826        static mut GET_ENV_CALLS: i32 = 0;
827        static mut GET_ENV_VM_ARGUMENT: *mut jni_sys::JavaVM = ptr::null_mut();
828        static mut GET_ENV_VERSION_ARGUMENT: jni_sys::jint = 0;
829        unsafe extern "system" fn get_env(
830            java_vm: *mut jni_sys::JavaVM,
831            _: *mut *mut c_void,
832            version: jni_sys::jint,
833        ) -> jni_sys::jint {
834            GET_ENV_CALLS += 1;
835            GET_ENV_VM_ARGUMENT = java_vm;
836            GET_ENV_VERSION_ARGUMENT = version;
837            jni_sys::JNI_EDETACHED
838        }
839        static mut ATTACH_CALLS: i32 = 0;
840        static mut ATTACH_VM_ARGUMENT: *mut jni_sys::JavaVM = ptr::null_mut();
841        static mut ATTACH_ENV_ARGUMENT: *mut c_void = ptr::null_mut();
842        static mut ATTACH_ARGUMENT: *mut c_void = ptr::null_mut();
843        unsafe extern "system" fn attach(
844            java_vm: *mut jni_sys::JavaVM,
845            jni_env: *mut *mut c_void,
846            argument: *mut c_void,
847        ) -> jni_sys::jint {
848            *jni_env = ATTACH_ENV_ARGUMENT;
849            ATTACH_CALLS += 1;
850            ATTACH_VM_ARGUMENT = java_vm;
851            ATTACH_ARGUMENT = argument;
852            jni_sys::JNI_OK
853        }
854        let raw_java_vm = jni_sys::JNIInvokeInterface_ {
855            GetEnv: Some(get_env),
856            AttachCurrentThread: Some(attach),
857            ..empty_raw_java_vm()
858        };
859        let raw_java_vm_ptr = &mut (&raw_java_vm as jni_sys::JavaVM) as *mut jni_sys::JavaVM;
860        let vm = test_vm(raw_java_vm_ptr);
861        let init_arguments = init_arguments::test(JniVersion::V8);
862        unsafe {
863            ATTACH_ENV_ARGUMENT = calls.env as *mut c_void;
864        }
865        let env = vm.attach(&AttachArguments::named(&init_arguments, "test-name"))
866            .unwrap();
867        unsafe {
868            assert_eq!(GET_ENV_CALLS, 1);
869            assert_eq!(GET_ENV_VM_ARGUMENT, raw_java_vm_ptr);
870            assert_eq!(GET_ENV_VERSION_ARGUMENT, version::to_raw(JniVersion::V8));
871            assert_eq!(ATTACH_CALLS, 1);
872            assert_eq!(ATTACH_VM_ARGUMENT, raw_java_vm_ptr);
873            assert_eq!(
874                from_java_string(
875                    CStr::from_ptr((*(ATTACH_ARGUMENT as *mut jni_sys::JavaVMAttachArgs)).name)
876                        .to_bytes_with_nul()
877                ).unwrap(),
878                "test-name"
879            );
880            assert_eq!(env.raw_jvm(), raw_java_vm_ptr);
881            assert_eq!(env.raw_env(), calls.env);
882        }
883        assert_eq!(env.has_token, RefCell::new(true));
884        assert_eq!(env.native_method_call, false);
885        // Don't want to drop a manually created `JniEnv`.
886        mem::forget(env);
887    }
888
889    #[test]
890    #[should_panic(expected = "already attached")]
891    fn attach_already_attached() {
892        unsafe extern "system" fn get_env(
893            _: *mut jni_sys::JavaVM,
894            _: *mut *mut c_void,
895            _: jni_sys::jint,
896        ) -> jni_sys::jint {
897            jni_sys::JNI_OK
898        }
899        unsafe extern "system" fn attach(
900            _: *mut jni_sys::JavaVM,
901            _: *mut *mut c_void,
902            _: *mut c_void,
903        ) -> jni_sys::jint {
904            jni_sys::JNI_OK
905        }
906        let raw_java_vm = jni_sys::JNIInvokeInterface_ {
907            GetEnv: Some(get_env),
908            AttachCurrentThread: Some(attach),
909            ..empty_raw_java_vm()
910        };
911        let raw_java_vm_ptr = &mut (&raw_java_vm as jni_sys::JavaVM) as *mut jni_sys::JavaVM;
912        let vm = test_vm(raw_java_vm_ptr);
913        vm.attach(&AttachArguments::new(&init_arguments::test(JniVersion::V8)))
914            .unwrap();
915    }
916
917    #[test]
918    #[should_panic(expected = "GetEnv JNI method returned an unexpected error code -1")]
919    fn attach_get_env_error() {
920        unsafe extern "system" fn get_env(
921            _: *mut jni_sys::JavaVM,
922            _: *mut *mut c_void,
923            _: jni_sys::jint,
924        ) -> jni_sys::jint {
925            jni_sys::JNI_ERR
926        }
927        unsafe extern "system" fn attach(
928            _: *mut jni_sys::JavaVM,
929            _: *mut *mut c_void,
930            _: *mut c_void,
931        ) -> jni_sys::jint {
932            jni_sys::JNI_OK
933        }
934        let raw_java_vm = jni_sys::JNIInvokeInterface_ {
935            GetEnv: Some(get_env),
936            AttachCurrentThread: Some(attach),
937            ..empty_raw_java_vm()
938        };
939        let raw_java_vm_ptr = &mut (&raw_java_vm as jni_sys::JavaVM) as *mut jni_sys::JavaVM;
940        let vm = test_vm(raw_java_vm_ptr);
941        vm.attach(&AttachArguments::new(&init_arguments::test(JniVersion::V8)))
942            .unwrap();
943    }
944
945    #[test]
946    #[should_panic(expected = "Got `EDETACHED` when trying to attach a thread")]
947    fn attach_cant_attach() {
948        unsafe extern "system" fn get_env(
949            _: *mut jni_sys::JavaVM,
950            _: *mut *mut c_void,
951            _: jni_sys::jint,
952        ) -> jni_sys::jint {
953            jni_sys::JNI_EDETACHED
954        }
955        unsafe extern "system" fn attach(
956            _: *mut jni_sys::JavaVM,
957            _: *mut *mut c_void,
958            _: *mut c_void,
959        ) -> jni_sys::jint {
960            jni_sys::JNI_EDETACHED
961        }
962        let raw_java_vm = jni_sys::JNIInvokeInterface_ {
963            GetEnv: Some(get_env),
964            AttachCurrentThread: Some(attach),
965            ..empty_raw_java_vm()
966        };
967        let raw_java_vm_ptr = &mut (&raw_java_vm as jni_sys::JavaVM) as *mut jni_sys::JavaVM;
968        let vm = test_vm(raw_java_vm_ptr);
969        vm.attach(&AttachArguments::new(&init_arguments::test(JniVersion::V8)))
970            .unwrap();
971    }
972
973    #[test]
974    #[should_panic(expected = "upsupported version")]
975    fn attach_unsupported_version() {
976        unsafe extern "system" fn get_env(
977            _: *mut jni_sys::JavaVM,
978            _: *mut *mut c_void,
979            _: jni_sys::jint,
980        ) -> jni_sys::jint {
981            jni_sys::JNI_EDETACHED
982        }
983        unsafe extern "system" fn attach(
984            _: *mut jni_sys::JavaVM,
985            _: *mut *mut c_void,
986            _: *mut c_void,
987        ) -> jni_sys::jint {
988            jni_sys::JNI_EVERSION
989        }
990        let raw_java_vm = jni_sys::JNIInvokeInterface_ {
991            GetEnv: Some(get_env),
992            AttachCurrentThread: Some(attach),
993            ..empty_raw_java_vm()
994        };
995        let raw_java_vm_ptr = &mut (&raw_java_vm as jni_sys::JavaVM) as *mut jni_sys::JavaVM;
996        let vm = test_vm(raw_java_vm_ptr);
997        vm.attach(&AttachArguments::new(&init_arguments::test(JniVersion::V8)))
998            .unwrap();
999    }
1000
1001    #[test]
1002    fn attach_attach_error() {
1003        unsafe extern "system" fn get_env(
1004            _: *mut jni_sys::JavaVM,
1005            _: *mut *mut c_void,
1006            _: jni_sys::jint,
1007        ) -> jni_sys::jint {
1008            jni_sys::JNI_EDETACHED
1009        }
1010        unsafe extern "system" fn attach(
1011            _: *mut jni_sys::JavaVM,
1012            _: *mut *mut c_void,
1013            _: *mut c_void,
1014        ) -> jni_sys::jint {
1015            jni_sys::JNI_ERR
1016        }
1017        let raw_java_vm = jni_sys::JNIInvokeInterface_ {
1018            GetEnv: Some(get_env),
1019            AttachCurrentThread: Some(attach),
1020            ..empty_raw_java_vm()
1021        };
1022        let raw_java_vm_ptr = &mut (&raw_java_vm as jni_sys::JavaVM) as *mut jni_sys::JavaVM;
1023        let vm = test_vm(raw_java_vm_ptr);
1024        assert_eq!(
1025            vm.attach(&AttachArguments::new(&init_arguments::test(JniVersion::V8)))
1026                .unwrap_err(),
1027            JniError::Unknown(jni_sys::JNI_ERR as i32)
1028        );
1029    }
1030
1031    #[test]
1032    #[should_panic(expected = "Newly attached thread has a pending exception")]
1033    fn attach_pending_exception() {
1034        let calls = test_raw_jni_env!(vec![JniCall::ExceptionCheck(ExceptionCheck {
1035            result: jni_sys::JNI_TRUE,
1036        })]);
1037        unsafe extern "system" fn get_env(
1038            _: *mut jni_sys::JavaVM,
1039            _: *mut *mut c_void,
1040            _: jni_sys::jint,
1041        ) -> jni_sys::jint {
1042            jni_sys::JNI_EDETACHED
1043        }
1044        static mut ATTACH_ENV_ARGUMENT: *mut c_void = ptr::null_mut();
1045        unsafe extern "system" fn attach(
1046            _: *mut jni_sys::JavaVM,
1047            jni_env: *mut *mut c_void,
1048            _: *mut c_void,
1049        ) -> jni_sys::jint {
1050            *jni_env = ATTACH_ENV_ARGUMENT;
1051            jni_sys::JNI_OK
1052        }
1053        let raw_java_vm = jni_sys::JNIInvokeInterface_ {
1054            GetEnv: Some(get_env),
1055            AttachCurrentThread: Some(attach),
1056            ..empty_raw_java_vm()
1057        };
1058        let raw_java_vm_ptr = &mut (&raw_java_vm as jni_sys::JavaVM) as *mut jni_sys::JavaVM;
1059        let vm = test_vm(raw_java_vm_ptr);
1060        unsafe {
1061            ATTACH_ENV_ARGUMENT = calls.env as *mut c_void;
1062        }
1063        vm.attach(&AttachArguments::new(&init_arguments::test(JniVersion::V8)))
1064            .unwrap();
1065    }
1066
1067    #[test]
1068    fn attach_daemon() {
1069        let calls = test_raw_jni_env!(vec![JniCall::ExceptionCheck(ExceptionCheck {
1070            result: jni_sys::JNI_FALSE,
1071        })]);
1072        static mut GET_ENV_CALLS: i32 = 0;
1073        static mut GET_ENV_VM_ARGUMENT: *mut jni_sys::JavaVM = ptr::null_mut();
1074        static mut GET_ENV_VERSION_ARGUMENT: jni_sys::jint = 0;
1075        unsafe extern "system" fn get_env(
1076            java_vm: *mut jni_sys::JavaVM,
1077            _: *mut *mut c_void,
1078            version: jni_sys::jint,
1079        ) -> jni_sys::jint {
1080            GET_ENV_CALLS += 1;
1081            GET_ENV_VM_ARGUMENT = java_vm;
1082            GET_ENV_VERSION_ARGUMENT = version;
1083            jni_sys::JNI_EDETACHED
1084        }
1085        static mut ATTACH_CALLS: i32 = 0;
1086        static mut ATTACH_VM_ARGUMENT: *mut jni_sys::JavaVM = ptr::null_mut();
1087        static mut ATTACH_ENV_ARGUMENT: *mut c_void = ptr::null_mut();
1088        static mut ATTACH_ARGUMENT: *mut c_void = ptr::null_mut();
1089        unsafe extern "system" fn attach(
1090            java_vm: *mut jni_sys::JavaVM,
1091            jni_env: *mut *mut c_void,
1092            argument: *mut c_void,
1093        ) -> jni_sys::jint {
1094            *jni_env = ATTACH_ENV_ARGUMENT;
1095            ATTACH_CALLS += 1;
1096            ATTACH_VM_ARGUMENT = java_vm;
1097            ATTACH_ARGUMENT = argument;
1098            jni_sys::JNI_OK
1099        }
1100        let raw_java_vm = jni_sys::JNIInvokeInterface_ {
1101            GetEnv: Some(get_env),
1102            AttachCurrentThreadAsDaemon: Some(attach),
1103            ..empty_raw_java_vm()
1104        };
1105        let raw_java_vm_ptr = &mut (&raw_java_vm as jni_sys::JavaVM) as *mut jni_sys::JavaVM;
1106        let vm = test_vm(raw_java_vm_ptr);
1107        let init_arguments = init_arguments::test(JniVersion::V8);
1108        unsafe {
1109            ATTACH_ENV_ARGUMENT = calls.env as *mut c_void;
1110        }
1111        let env = vm.attach_daemon(&AttachArguments::named(&init_arguments, "test-name"))
1112            .unwrap();
1113        unsafe {
1114            assert_eq!(GET_ENV_CALLS, 1);
1115            assert_eq!(GET_ENV_VM_ARGUMENT, raw_java_vm_ptr);
1116            assert_eq!(GET_ENV_VERSION_ARGUMENT, version::to_raw(JniVersion::V8));
1117            assert_eq!(ATTACH_CALLS, 1);
1118            assert_eq!(ATTACH_VM_ARGUMENT, raw_java_vm_ptr);
1119            assert_eq!(
1120                from_java_string(
1121                    CStr::from_ptr((*(ATTACH_ARGUMENT as *mut jni_sys::JavaVMAttachArgs)).name)
1122                        .to_bytes_with_nul()
1123                ).unwrap(),
1124                "test-name"
1125            );
1126            assert_eq!(env.raw_jvm(), raw_java_vm_ptr);
1127            assert_eq!(env.raw_env(), calls.env);
1128        }
1129        assert_eq!(env.has_token, RefCell::new(true));
1130        assert_eq!(env.native_method_call, false);
1131        // Don't want to drop a manually created `JniEnv`.
1132        mem::forget(env);
1133    }
1134}
1135
1136/// The interface for interacting with Java.
1137/// All calls to Java are performed through this interface.
1138/// JNI methods can only be called from threads, explicitly attached to the Java VM.
1139/// [`JniEnv`](struct.JniEnv.html) represents such a thread.
1140///
1141/// [JNI documentation](https://docs.oracle.com/javase/10/docs/specs/jni/functions.html#interface-function-table)
1142///
1143/// # Examples
1144/// ```
1145/// use rust_jni::{AttachArguments, InitArguments, JavaVM, JniEnv, JniVersion};
1146/// use std::ptr;
1147///
1148/// let init_arguments = InitArguments::get_default(JniVersion::V8).unwrap();
1149/// let vm = JavaVM::create(&init_arguments).unwrap();
1150/// let env = vm.attach(&AttachArguments::new(&init_arguments)).unwrap();
1151/// unsafe {
1152///     assert_ne!(env.raw_env(), ptr::null_mut());
1153/// }
1154/// ```
1155/// [`JniEnv`](struct.JniEnv.html) is
1156/// [`!Send`](https://doc.rust-lang.org/std/marker/trait.Send.html). It means it can't be passed
1157/// between threads:
1158/// ```compile_fail
1159/// # use rust_jni::{AttachArguments, InitArguments, JavaVM, JniEnv, JniVersion};
1160/// #
1161/// # let init_arguments = InitArguments::get_default(JniVersion::V8).unwrap();
1162/// # let vm = JavaVM::create(&init_arguments).unwrap();
1163/// let env = vm.attach(&AttachArguments::new(&init_arguments)).unwrap();
1164/// {
1165///     ::std::thread::spawn(move || {
1166///         unsafe { env.raw_env() }; // doesn't compile!
1167///     });
1168/// }
1169/// ```
1170/// Instead, you need to attach each new thread to the VM:
1171/// ```
1172/// # use rust_jni::{AttachArguments, InitArguments, JavaVM, JniEnv, JniVersion};
1173/// # use std::ptr;
1174/// use std::sync::Arc;
1175///
1176/// let init_arguments = InitArguments::get_default(JniVersion::V8).unwrap();
1177/// let vm = Arc::new(JavaVM::create(&init_arguments).unwrap());
1178/// let env = vm.attach(&AttachArguments::new(&init_arguments)).unwrap();
1179/// {
1180///     let vm = vm.clone();
1181///     ::std::thread::spawn(move || {
1182///         let env = vm.attach(&AttachArguments::new(&init_arguments)).unwrap();
1183///         unsafe {
1184///             assert_ne!(env.raw_env(), ptr::null_mut());
1185///         }
1186///     });
1187/// }
1188/// unsafe {
1189///     assert_ne!(env.raw_env(), ptr::null_mut());
1190/// }
1191/// ```
1192/// The thread is automatically detached once the [`JniEnv`](struct.JniEnv.html) is dropped.
1193///
1194/// [`JniEnv`](struct.JniEnv.html) can't outlive the parent [`JavaVM`](struct.JavaVM.html).
1195/// This code is not allowed:
1196/// ```compile_fail
1197/// # use rust_jni::{AttachArguments, InitArguments, JavaVM, JniEnv, JniVersion};
1198/// #
1199/// let env = {
1200///     let init_arguments = InitArguments::get_default(JniVersion::V8).unwrap();
1201///     let vm = JavaVM::create(&init_arguments).unwrap();
1202///     vm.attach(&AttachArguments::new(&init_arguments)).unwrap() // doesn't compile!
1203/// };
1204/// ```
1205/// [`JniEnv`](struct.JniEnv.html) represents a thread, attached to the Java VM. Thus there
1206/// can't be two [`JniEnv`](struct.JniEnv.html)-s per thread.
1207/// [`JavaVM::attach`](struct.JavaVM.html#methods.attach) will panic if you attempt to do so:
1208/// ```should_panic
1209/// # use rust_jni::{AttachArguments, InitArguments, JavaVM, JniEnv, JniVersion};
1210/// #
1211/// # let init_arguments = InitArguments::get_default(JniVersion::V8).unwrap();
1212/// # let vm = JavaVM::create(&init_arguments).unwrap();
1213/// let env = vm.attach(&AttachArguments::new(&init_arguments)).unwrap();
1214/// let env = vm.attach(&AttachArguments::new(&init_arguments)).unwrap(); // panics!
1215/// ```
1216// TODO: docs about panicing on detach when there's a pending exception.
1217#[derive(Debug)]
1218pub struct JniEnv<'vm> {
1219    version: JniVersion,
1220    vm: &'vm JavaVM,
1221    jni_env: *mut jni_sys::JNIEnv,
1222    has_token: RefCell<bool>,
1223    native_method_call: bool,
1224}
1225
1226// [`JniEnv`](struct.JniEnv.html) can't be passed between threads.
1227// TODO(https://github.com/rust-lang/rust/issues/13231): enable when !Send is stable.
1228// impl<'vm> !Send for JniEnv<'vm> {}
1229// impl<'vm> !Sync for JniEnv<'vm> {}
1230
1231impl<'vm> JniEnv<'vm> {
1232    /// Get the raw Java VM pointer.
1233    ///
1234    /// This function provides low-level access to all of JNI and thus is unsafe.
1235    pub unsafe fn raw_jvm(&self) -> *mut jni_sys::JavaVM {
1236        self.vm.raw_jvm()
1237    }
1238
1239    /// Get the raw JNI environment pointer.
1240    ///
1241    /// This function provides low-level access to all of JNI and thus is unsafe.
1242    pub unsafe fn raw_env(&self) -> *mut jni_sys::JNIEnv {
1243        self.jni_env
1244    }
1245
1246    /// Get a [`NoException`](struct.NoException.html) token indicating that there is no pending
1247    /// exception in this thread.
1248    ///
1249    /// Read more about tokens in [`NoException`](struct.NoException.html) documentation.
1250    // TODO(#22): Return a token with the env if possible:
1251    // https://stackoverflow.com/questions/50891977/can-i-return-a-value-and-a-reference-to-it-from-a-function.
1252    pub fn token(&self) -> NoException {
1253        if !*self.has_token.borrow() {
1254            panic!("Trying to obtain a second `NoException` token from the `JniEnv` value.");
1255        } else if self.has_exception() {
1256            panic!("Trying to obtain a `NoException` token when there is a pending exception.");
1257        } else {
1258            *self.has_token.borrow_mut() = false;
1259            // Safe because there's no exception.
1260            unsafe { NoException::new_env(self) }
1261        }
1262    }
1263
1264    /// Get JNI version.
1265    ///
1266    /// [JNI documentation](https://docs.oracle.com/javase/10/docs/specs/jni/functions.html#getversion)
1267    pub fn version(&self) -> JniVersion {
1268        self.version
1269    }
1270
1271    fn has_exception(&self) -> bool {
1272        // Safe because the argument is ensured to be the correct by construction.
1273        let value = unsafe { call_jni_method!(self, ExceptionCheck) };
1274        // Safe because `bool` conversion is safe internally.
1275        unsafe { bool::__from_jni(self, value) }
1276    }
1277}
1278
1279/// `Drop` detaches the current thread from the Java VM.
1280/// [It's not safe](https://developer.android.com/training/articles/perf-jni#exceptions)
1281/// to do so with an exception pending, so it panics if this happens.
1282///
1283/// [JNI documentation](https://docs.oracle.com/javase/10/docs/specs/jni/invocation.html#detachcurrentthread)
1284impl<'vm> Drop for JniEnv<'vm> {
1285    fn drop(&mut self) {
1286        // In native calls `JniEnv` is build from a raw pointer, without attaching the current
1287        // thread, and thus the thread doesn't need to be detached. A native method can return
1288        // with a pending exception to propagate it to Java code, so no need to panic on pending
1289        // exceptions either.
1290        if self.native_method_call {
1291            return;
1292        }
1293
1294        if self.has_exception() {
1295            // Safe because the argument is ensured to be the correct by construction.
1296            unsafe { call_jni_method!(self, ExceptionDescribe) };
1297            panic!(
1298                "Dropping `JniEnv` with a pending exception is not allowed. Please clear the \
1299                 exception by unwrapping the exception token before dropping it."
1300            );
1301        }
1302        // Safe because the current thread is guaranteed to be attached and the argument is correct.
1303        unsafe { JavaVM::detach(self.raw_jvm()) };
1304    }
1305}
1306
1307#[cfg(test)]
1308fn test_vm(ptr: *mut jni_sys::JavaVM) -> JavaVM {
1309    JavaVM {
1310        java_vm: ptr,
1311        owned: false,
1312    }
1313}
1314
1315#[cfg(test)]
1316fn test_env<'vm>(vm: &'vm JavaVM, ptr: *mut jni_sys::JNIEnv) -> JniEnv<'vm> {
1317    JniEnv {
1318        version: JniVersion::V8,
1319        vm: &vm,
1320        jni_env: ptr,
1321        has_token: RefCell::new(true),
1322        native_method_call: true,
1323    }
1324}
1325
1326#[cfg(test)]
1327mod jni_env_tests {
1328    use super::*;
1329    use jni::testing::*;
1330
1331    #[test]
1332    fn raw_jvm() {
1333        let vm = test_vm(0x1234 as *mut jni_sys::JavaVM);
1334        let env = test_env(&vm, ptr::null_mut());
1335        unsafe {
1336            assert_eq!(env.raw_jvm(), vm.raw_jvm());
1337        }
1338    }
1339
1340    #[test]
1341    fn raw_env() {
1342        let vm = test_vm(ptr::null_mut());
1343        let jni_env = 0x5678 as *mut jni_sys::JNIEnv;
1344        let env = test_env(&vm, jni_env);
1345        unsafe {
1346            assert_eq!(env.raw_env(), jni_env);
1347        }
1348    }
1349
1350    #[test]
1351    fn version() {
1352        let vm = test_vm(ptr::null_mut());
1353        let env = test_env(&vm, ptr::null_mut());
1354        assert_eq!(env.version(), JniVersion::V8);
1355    }
1356
1357    #[test]
1358    fn drop() {
1359        let calls = test_raw_jni_env!(vec![JniCall::ExceptionCheck(ExceptionCheck {
1360            result: jni_sys::JNI_FALSE,
1361        })]);
1362        static mut DETACH_CALLS: i32 = 0;
1363        static mut DETACH_ARGUMENT: *mut jni_sys::JavaVM = ptr::null_mut();
1364        unsafe extern "system" fn detach(java_vm: *mut jni_sys::JavaVM) -> jni_sys::jint {
1365            DETACH_CALLS += 1;
1366            DETACH_ARGUMENT = java_vm;
1367            jni_sys::JNI_OK
1368        }
1369        let raw_java_vm = jni_sys::JNIInvokeInterface_ {
1370            DetachCurrentThread: Some(detach),
1371            ..empty_raw_java_vm()
1372        };
1373        let vm = test_vm(&mut (&raw_java_vm as jni_sys::JavaVM) as *mut jni_sys::JavaVM);
1374        {
1375            let _env = JniEnv {
1376                version: JniVersion::V8,
1377                vm: &vm,
1378                jni_env: calls.env,
1379                has_token: RefCell::new(true),
1380                native_method_call: false,
1381            };
1382            unsafe {
1383                assert_eq!(DETACH_CALLS, 0);
1384            }
1385        }
1386        unsafe {
1387            assert_eq!(DETACH_CALLS, 1);
1388            assert_eq!(DETACH_ARGUMENT, vm.java_vm);
1389        }
1390    }
1391
1392    #[test]
1393    fn drop_native_method() {
1394        let vm = test_vm(ptr::null_mut());
1395        test_env(&vm, ptr::null_mut());
1396        // This test would fail if any JNI methods were called by the `JniEnv::drop` method.
1397    }
1398
1399    #[test]
1400    #[should_panic(expected = "Dropping `JniEnv` with a pending exception is not allowed")]
1401    fn drop_exception_pending() {
1402        let calls = test_raw_jni_env!(vec![
1403            JniCall::ExceptionCheck(ExceptionCheck {
1404                result: jni_sys::JNI_TRUE,
1405            }),
1406            JniCall::ExceptionDescribe(ExceptionDescribe {}),
1407        ]);
1408        unsafe extern "system" fn destroy_vm(_: *mut jni_sys::JavaVM) -> jni_sys::jint {
1409            jni_sys::JNI_OK
1410        }
1411        unsafe extern "system" fn detach(_: *mut jni_sys::JavaVM) -> jni_sys::jint {
1412            jni_sys::JNI_OK
1413        }
1414        let raw_java_vm = jni_sys::JNIInvokeInterface_ {
1415            DestroyJavaVM: Some(destroy_vm),
1416            DetachCurrentThread: Some(detach),
1417            ..empty_raw_java_vm()
1418        };
1419        let vm = test_vm(&mut (&raw_java_vm as jni_sys::JavaVM) as *mut jni_sys::JavaVM);
1420        JniEnv {
1421            version: JniVersion::V8,
1422            vm: &vm,
1423            jni_env: calls.env,
1424            has_token: RefCell::new(true),
1425            native_method_call: false,
1426        };
1427    }
1428
1429    #[test]
1430    #[should_panic(expected = "Could not detach the current thread. Status: -1")]
1431    fn drop_detach_error() {
1432        let calls = test_raw_jni_env!(vec![JniCall::ExceptionCheck(ExceptionCheck {
1433            result: jni_sys::JNI_FALSE,
1434        })]);
1435        unsafe extern "system" fn detach(_: *mut jni_sys::JavaVM) -> jni_sys::jint {
1436            jni_sys::JNI_ERR
1437        }
1438        let raw_java_vm = jni_sys::JNIInvokeInterface_ {
1439            DetachCurrentThread: Some(detach),
1440            ..empty_raw_java_vm()
1441        };
1442        let vm = test_vm(&mut (&raw_java_vm as jni_sys::JavaVM) as *mut jni_sys::JavaVM);
1443        JniEnv {
1444            version: JniVersion::V8,
1445            vm: &vm,
1446            jni_env: calls.env,
1447            has_token: RefCell::new(true),
1448            native_method_call: false,
1449        };
1450    }
1451
1452    #[test]
1453    fn token() {
1454        let calls = test_raw_jni_env!(vec![JniCall::ExceptionCheck(ExceptionCheck {
1455            result: jni_sys::JNI_FALSE,
1456        })]);
1457        let raw_java_vm_ptr = 0x1234 as *mut jni_sys::JavaVM;
1458        let vm = test_vm(raw_java_vm_ptr);
1459        let env = test_env(&vm, calls.env);
1460        env.token();
1461        assert_eq!(env.has_token, RefCell::new(false));
1462    }
1463
1464    #[test]
1465    #[should_panic(expected = "Trying to obtain a second `NoException` token from the `JniEnv`")]
1466    fn token_twice() {
1467        let calls = test_raw_jni_env!(vec![JniCall::ExceptionCheck(ExceptionCheck {
1468            result: jni_sys::JNI_FALSE,
1469        })]);
1470        unsafe extern "system" fn detach(_: *mut jni_sys::JavaVM) -> jni_sys::jint {
1471            jni_sys::JNI_OK
1472        }
1473        let raw_java_vm = jni_sys::JNIInvokeInterface_ {
1474            DetachCurrentThread: Some(detach),
1475            ..empty_raw_java_vm()
1476        };
1477        let vm = test_vm(&mut (&raw_java_vm as jni_sys::JavaVM) as *mut jni_sys::JavaVM);
1478        let env = JniEnv {
1479            version: JniVersion::V8,
1480            vm: &vm,
1481            jni_env: calls.env,
1482            has_token: RefCell::new(false),
1483            native_method_call: true,
1484        };
1485        env.token();
1486    }
1487
1488    #[test]
1489    #[should_panic(
1490        expected = "Trying to obtain a `NoException` token when there is a pending exception"
1491    )]
1492    fn token_pending_exception() {
1493        let calls = test_raw_jni_env!(vec![
1494            JniCall::ExceptionCheck(ExceptionCheck {
1495                result: jni_sys::JNI_TRUE,
1496            }),
1497            JniCall::ExceptionCheck(ExceptionCheck {
1498                result: jni_sys::JNI_FALSE,
1499            }),
1500        ]);
1501        unsafe extern "system" fn detach(_: *mut jni_sys::JavaVM) -> jni_sys::jint {
1502            jni_sys::JNI_OK
1503        }
1504        let raw_java_vm = jni_sys::JNIInvokeInterface_ {
1505            DetachCurrentThread: Some(detach),
1506            ..empty_raw_java_vm()
1507        };
1508        let vm = test_vm(&mut (&raw_java_vm as jni_sys::JavaVM) as *mut jni_sys::JavaVM);
1509        let env = test_env(&vm, calls.env);
1510        env.token();
1511    }
1512}
1513
1514/// Get and clear the pending exception.
1515fn maybe_get_and_clear_exception<'a>(env: &'a JniEnv<'a>) -> Option<Throwable<'a>> {
1516    // Safe because the argument is ensured to be correct references by construction.
1517    let raw_java_throwable = unsafe { call_jni_method!(env, ExceptionOccurred) };
1518    if raw_java_throwable == ptr::null_mut() {
1519        return None;
1520    }
1521    // Safe because the argument is ensured to be correct references by construction.
1522    unsafe {
1523        call_jni_method!(env, ExceptionClear);
1524    }
1525    // Safe because the arguments are correct.
1526    Some(unsafe { Throwable::__from_jni(env, raw_java_throwable) })
1527}
1528
1529#[cfg(test)]
1530mod maybe_get_and_clear_exception_tests {
1531    use super::*;
1532    use jni::testing::*;
1533
1534    #[test]
1535    fn exception() {
1536        const EXCEPTION: jni_sys::jobject = 0x2835 as jni_sys::jobject;
1537        let calls = test_raw_jni_env!(vec![
1538            JniCall::ExceptionOccurred(ExceptionOccurred { result: EXCEPTION }),
1539            JniCall::ExceptionClear(ExceptionClear {}),
1540        ]);
1541        let vm = test_vm(ptr::null_mut());
1542        let env = test_env(&vm, calls.env);
1543        let exception = maybe_get_and_clear_exception(&env).unwrap();
1544        calls.assert_eq(&exception, EXCEPTION);
1545    }
1546
1547    #[test]
1548    fn exception_not_found() {
1549        let calls = test_raw_jni_env!(vec![JniCall::ExceptionOccurred(ExceptionOccurred {
1550            result: ptr::null_mut(),
1551        })]);
1552        let vm = test_vm(ptr::null_mut());
1553        let env = test_env(&vm, calls.env);
1554        assert_eq!(maybe_get_and_clear_exception(&env), None);
1555    }
1556}
1557
1558/// Get and clear the pending exception.
1559fn get_and_clear_exception<'a>(token: Exception<'a>) -> Throwable<'a> {
1560    match maybe_get_and_clear_exception(token.env) {
1561        None => panic!(
1562            "No pending exception in presence of an Exception token. Should not ever happen."
1563        ),
1564        Some(exception) => exception,
1565    }
1566}
1567
1568#[cfg(test)]
1569mod get_and_clear_exception_tests {
1570    use super::*;
1571    use jni::testing::*;
1572
1573    #[test]
1574    fn exception() {
1575        const EXCEPTION: jni_sys::jobject = 0x2835 as jni_sys::jobject;
1576        let calls = test_raw_jni_env!(vec![
1577            JniCall::ExceptionOccurred(ExceptionOccurred { result: EXCEPTION }),
1578            JniCall::ExceptionClear(ExceptionClear {}),
1579        ]);
1580        let vm = test_vm(ptr::null_mut());
1581        let env = test_env(&vm, calls.env);
1582        let exception = get_and_clear_exception(Exception::test(&env));
1583        calls.assert_eq(&exception, EXCEPTION);
1584    }
1585
1586    #[test]
1587    #[should_panic(expected = "No pending exception in presence of an Exception token")]
1588    fn exception_not_found() {
1589        let calls = test_raw_jni_env!(vec![JniCall::ExceptionOccurred(ExceptionOccurred {
1590            result: ptr::null_mut(),
1591        })]);
1592        let vm = test_vm(ptr::null_mut());
1593        let env = test_env(&vm, calls.env);
1594        get_and_clear_exception(Exception::test(&env));
1595    }
1596}
1597
1598/// Take a function that produces a [`JniResult`](type.JniResult.html), call it and produce
1599/// a [`JavaResult`](type.JavaResult.html) from it.
1600fn with_checked_exception<'a, Out, T: FnOnce(NoException<'a>) -> JniResult<'a, Out>>(
1601    token: &NoException<'a>,
1602    function: T,
1603) -> JavaResult<'a, Out> {
1604    // Safe, because we check for a pending exception after the call.
1605    let token = unsafe { token.clone() };
1606    match function(token) {
1607        Ok((value, _)) => Ok(value),
1608        Err(token) => Err(get_and_clear_exception(token)),
1609    }
1610}
1611
1612#[cfg(test)]
1613mod with_checked_exception_tests {
1614    use super::*;
1615    use jni::testing::*;
1616
1617    #[test]
1618    fn no_exception() {
1619        let result = with_checked_exception(&NoException::test(), |_| {
1620            Ok((17, NoException::test()))
1621        }).unwrap();
1622        assert_eq!(result, 17);
1623    }
1624
1625    #[test]
1626    fn exception() {
1627        const EXCEPTION: jni_sys::jobject = 0x2835 as jni_sys::jobject;
1628        let calls = test_raw_jni_env!(vec![
1629            JniCall::ExceptionOccurred(ExceptionOccurred { result: EXCEPTION }),
1630            JniCall::ExceptionClear(ExceptionClear {}),
1631        ]);
1632        let vm = test_vm(ptr::null_mut());
1633        let env = test_env(&vm, calls.env);
1634        let exception = with_checked_exception::<i32, _>(&NoException::test(), |_| {
1635            Err(Exception::test(&env))
1636        }).unwrap_err();
1637        calls.assert_eq(&exception, EXCEPTION);
1638    }
1639}
1640
1641/// A trait that represents a JNI type. It's implemented for all JNI primitive types
1642/// and [`jobject`](https://docs.rs/jni-sys/0.3.0/jni_sys/type.jobject.html).
1643/// Implements Java method calls and provides the default value for this JNI type.
1644///
1645/// THIS TRAIT SHOULD NOT BE USED MANUALLY.
1646///
1647/// This trait should only be implemented for classes by generated code.
1648#[doc(hidden)]
1649pub trait JniType {
1650    fn default() -> Self;
1651
1652    unsafe fn call_method<In: ToJniTuple>(
1653        object: &Object,
1654        method_id: jni_sys::jmethodID,
1655        arguments: In,
1656    ) -> Self;
1657
1658    unsafe fn call_static_method<In: ToJniTuple>(
1659        class: &Class,
1660        method_id: jni_sys::jmethodID,
1661        arguments: In,
1662    ) -> Self;
1663}
1664
1665/// A trait that represents JNI types that can be passed as arguments to JNI functions.
1666///
1667/// THIS TRAIT SHOULD NOT BE USED MANUALLY.
1668#[doc(hidden)]
1669pub trait JniArgumentType: JniType {}
1670
1671/// A trait that represents Rust types that are mappable to JNI types.
1672/// This trait has to be implemented for all types that need to be passed as arguments
1673/// to or returned from Java functions.
1674///
1675/// THIS TRAIT SHOULD NOT BE USED MANUALLY.
1676///
1677/// This trait should only be implemented and used by generated code.
1678pub trait JavaType {
1679    /// The corresponding JNI type.
1680    ///
1681    /// Should only be implemented and used by generated code.
1682    #[doc(hidden)]
1683    type __JniType: JniType;
1684
1685    /// Compute the signature for this Java type.
1686    ///
1687    /// THIS METHOD SHOULD NOT BE CALLED MANUALLY.
1688    ///
1689    /// Should only be implemented and used by generated code.
1690    #[doc(hidden)]
1691    fn __signature() -> &'static str;
1692}
1693
1694/// A trait for mapping types to their JNI types.
1695/// This trait has to be implemented for all types that need to be passed as arguments
1696/// to Java functions.
1697///
1698/// THIS TRAIT SHOULD NOT BE USED MANUALLY.
1699///
1700/// This trait should only be implemented and used by generated code.
1701#[doc(hidden)]
1702pub trait ToJni: JavaType {
1703    /// Map the value to a JNI type value.
1704    ///
1705    /// THIS METHOD SHOULD NOT BE CALLED MANUALLY.
1706    ///
1707    /// Should only be implemented and used by generated code.
1708    unsafe fn __to_jni(&self) -> Self::__JniType;
1709}
1710
1711/// A trait for constructing types from their JNI types and [`JniEnv`](struct.JniEnv.html)
1712/// references. This trait has to be implemented for all types that the user wants to pass
1713/// return from Java functions.
1714///
1715/// THIS TRAIT SHOULD NOT BE USED MANUALLY.
1716///
1717/// This trait should only be implemented and used by generated code.
1718#[doc(hidden)]
1719pub trait FromJni<'env>: JavaType {
1720    /// Construct a value from a JNI type value.
1721    ///
1722    /// THIS METHOD SHOULD NOT BE CALLED MANUALLY.
1723    ///
1724    /// Should only be implemented and used by generated code.
1725    unsafe fn __from_jni(env: &'env JniEnv<'env>, value: Self::__JniType) -> Self;
1726}
1727
1728/// Make references mappable to JNI types of their referenced types.
1729impl<'a, T> JavaType for &'a T
1730where
1731    T: JavaType + ?Sized,
1732{
1733    #[doc(hidden)]
1734    type __JniType = T::__JniType;
1735
1736    #[doc(hidden)]
1737    fn __signature() -> &'static str {
1738        T::__signature()
1739    }
1740}
1741
1742/// Make references mappable from JNI types of their referenced types.
1743impl<'a, T> ToJni for &'a T
1744where
1745    T: ToJni,
1746{
1747    unsafe fn __to_jni(&self) -> Self::__JniType {
1748        T::__to_jni(self)
1749    }
1750}
1751
1752/// A trait that represents Rust function types that are mappable to Java function types.
1753/// This trait is separate from `JavaType` because this one doesn't need to be exposed
1754/// in the public crate API.
1755///
1756/// THIS TRAIT SHOULD NOT BE USED MANUALLY.
1757// TODO: reimplement it in a way that it returns `&'static str`.
1758// `concat!` doesn't acceps arbitrary expressions of type `&'static str`, so it can't be
1759// implemented that way today.
1760#[doc(hidden)]
1761pub trait JavaMethodSignature<In: ?Sized, Out: ?Sized> {
1762    /// Get the method's JNI signature.
1763    ///
1764    /// THIS METHOD SHOULD NOT BE CALLED MANUALLY.
1765    fn __signature() -> std::string::String;
1766}
1767
1768/// A trait for casting Java object types to their superclasses.
1769pub trait Cast<'env, As: Cast<'env, Object<'env>>>:
1770    JavaType<__JniType = jni_sys::jobject> + ToJni + FromJni<'env>
1771{
1772    /// Cast the object to itself or one of it's superclasses.
1773    ///
1774    /// Doesn't actually convert anything, the result is just the same object
1775    /// interpreted as one of it's superclasses.
1776    fn cast<'a>(&'a self) -> &'a As;
1777}
1778
1779/// A type representing the
1780/// [`java.lang.Object`](https://docs.oracle.com/javase/10/docs/api/java/lang/Object.html) class
1781/// -- the root class of Java's class hierarchy.
1782///
1783/// [`Object` javadoc](https://docs.oracle.com/javase/10/docs/api/java/lang/Object.html)
1784// TODO: examples.
1785pub struct Object<'env> {
1786    env: &'env JniEnv<'env>,
1787    raw_object: jni_sys::jobject,
1788}
1789
1790// [`Object`](struct.Object.html) can't be passed between threads.
1791// TODO(https://github.com/rust-lang/rust/issues/13231): enable when !Send is stable.
1792// impl<'env> !Send for Object<'env> {}
1793// impl<'env> !Sync for Object<'env> {}
1794
1795impl<'env> Object<'env> {
1796    /// Get the raw object pointer.
1797    ///
1798    /// This function provides low-level access to the Java object and thus is unsafe.
1799    pub unsafe fn raw_object(&self) -> jni_sys::jobject {
1800        self.raw_object
1801    }
1802
1803    /// Get the [`JniEnv`](../../struct.JniEnv.html) this object is bound to.
1804    pub fn env(&self) -> &'env JniEnv<'env> {
1805        self.env
1806    }
1807
1808    /// Get the object's class.
1809    ///
1810    /// [JNI documentation](https://docs.oracle.com/javase/10/docs/specs/jni/functions.html#getobjectclass)
1811    pub fn class(&self, _token: &NoException) -> Class<'env> {
1812        // Safe because arguments are ensured to be correct references by construction.
1813        let raw_java_class = unsafe { call_jni_method!(self.env, GetObjectClass, self.raw_object) };
1814        if raw_java_class == ptr::null_mut() {
1815            panic!("Object {:?} doesn't have a class.", self.raw_object);
1816        }
1817        // Safe because the argument is ensured to be correct references by construction.
1818        unsafe { Class::__from_jni(self.env, raw_java_class) }
1819    }
1820
1821    /// Compare with another Java object by reference.
1822    ///
1823    /// [JNI documentation](https://docs.oracle.com/javase/10/docs/specs/jni/functions.html#issameobject)
1824    pub fn is_same_as(&self, other: &Object, _token: &NoException) -> bool {
1825        // Safe because arguments are ensured to be correct references by construction.
1826        let same = unsafe {
1827            call_jni_method!(
1828                self.env(),
1829                IsSameObject,
1830                self.raw_object(),
1831                other.raw_object()
1832            )
1833        };
1834        // Safe because `bool` conversion is safe internally.
1835        unsafe { bool::__from_jni(self.env(), same) }
1836    }
1837
1838    /// Check if the object is an instance of the class.
1839    ///
1840    /// [JNI documentation](https://docs.oracle.com/javase/10/docs/specs/jni/functions.html#isinstanceof)
1841    pub fn is_instance_of(&self, class: &Class, _token: &NoException) -> bool {
1842        // Safe because arguments are ensured to be correct references by construction.
1843        let is_instance = unsafe {
1844            call_jni_method!(
1845                self.env(),
1846                IsInstanceOf,
1847                self.raw_object(),
1848                class.raw_object()
1849            )
1850        };
1851        // Safe because `bool` conversion is safe internally.
1852        unsafe { bool::__from_jni(self.env(), is_instance) }
1853    }
1854
1855    /// Clone the [`Object`](struct.Object.html). This is not a deep clone of the Java object,
1856    /// but a Rust-like clone of the value. Since Java objects are reference counted, this will
1857    /// increment the reference count.
1858    ///
1859    /// This method has a different signature from the one in the
1860    /// [`Clone`](https://doc.rust-lang.org/nightly/core/clone/trait.Clone.html) trait because
1861    /// cloning a Java object is only safe when there is no pending exception and because
1862    /// cloning a java object cat throw an exception.
1863    ///
1864    /// [JNI documentation](https://docs.oracle.com/javase/10/docs/specs/jni/functions.html#newlocalref)
1865    pub fn clone(&self, token: &NoException<'env>) -> JavaResult<'env, Object<'env>> {
1866        // Safe because arguments are ensured to be the correct by construction and because
1867        // `NewLocalRef` throws an exception before returning `null`.
1868        let raw_object =
1869            unsafe { call_nullable_jni_method!(self.env, NewLocalRef, token, self.raw_object)? };
1870        // Safe because the argument is a valid class reference.
1871        Ok(unsafe { Self::from_raw(self.env, raw_object) })
1872    }
1873
1874    /// Construct from a raw pointer. Unsafe because an invalid pointer may be passed
1875    /// as the argument.
1876    /// Unsafe because an incorrect object reference can be passed.
1877    unsafe fn from_raw(env: &'env JniEnv<'env>, raw_object: jni_sys::jobject) -> Self {
1878        Self { env, raw_object }
1879    }
1880}
1881
1882object_java_class!(
1883    Object,
1884    "[`Object`](struct.Object.html)",
1885    constructors = (),
1886    methods = (
1887        doc = "Convert the object to a string.",
1888        link = "[`Object::toString` javadoc](https://docs.oracle.com/javase/10/docs/api/java/lang/Object.html#toString())",
1889        java_name = "toString",
1890        to_string() -> String<'env>,
1891        doc = "Compare to another Java object.",
1892        link = "[`Object::equals`](https://docs.oracle.com/javase/10/docs/api/java/lang/Object.html#equals(java.lang.Object))",
1893        java_name = "equals",
1894        equals(other: &Object) -> bool,
1895    ),
1896);
1897
1898/// Make [`Object`](struct.Object.html) convertible from
1899/// [`jobject`](https://docs.rs/jni-sys/0.3.0/jni_sys/type.jobject.html).
1900impl<'env> FromJni<'env> for Object<'env> {
1901    unsafe fn __from_jni(env: &'env JniEnv<'env>, value: Self::__JniType) -> Self {
1902        Self::from_raw(env, value)
1903    }
1904}
1905
1906/// Make [`Object`](struct.Object.html)-s reference be deleted when the value is dropped.
1907///
1908/// [JNI documentation](https://docs.oracle.com/javase/10/docs/specs/jni/functions.html#deletelocalref)
1909impl<'env> Drop for Object<'env> {
1910    fn drop(&mut self) {
1911        // Safe because the argument is ensured to be correct references by construction.
1912        unsafe {
1913            call_jni_method!(self.env, DeleteLocalRef, self.raw_object);
1914        }
1915    }
1916}
1917
1918/// Allow comparing [`Object`](struct.Object.html) to Java objects. Java objects are compared
1919/// by-reference to preserve original Java semantics. To compare objects by value, call the
1920/// [`equals`](struct.Object.html#method.equals) method.
1921///
1922/// Will panic if there is a pending exception in the current thread.
1923///
1924/// This is mostly a convenience for using `assert_eq!()` in tests. Always prefer using
1925/// [`is_same_as`](struct.Object.html#methods.is_same_as) to comparing with `==`, because
1926/// the former checks for a pending exception in compile-time rather than the run-time.
1927impl<'env, T> PartialEq<T> for Object<'env>
1928where
1929    T: Cast<'env, Object<'env>>,
1930{
1931    fn eq(&self, other: &T) -> bool {
1932        if self.env().has_exception() {
1933            panic!("Comparing Java objects with a pending exception in the current thread")
1934        } else {
1935            // Safe because we checked that there is no pending exception.
1936            let token = unsafe { NoException::new_env(self.env()) };
1937            self.is_same_as(other.cast(), &token)
1938        }
1939    }
1940}
1941
1942/// Allow displaying Java objects for debug purposes.
1943///
1944/// [`Object::toString`](https://docs.oracle.com/javase/10/docs/api/java/lang/Object.html#toString())
1945///
1946/// This is mostly a convenience for debugging. Always prefer using
1947/// [`to_string`](struct.Object.html#methods.to_string) to printing the object as is, because
1948/// the former checks for a pending exception in compile-time rather than the run-time.
1949impl<'env> fmt::Debug for Object<'env> {
1950    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
1951        if self.env.has_exception() {
1952            // Can't call `to_string` with a pending exception.
1953            write!(
1954                formatter,
1955                "Object {{ env: {:?}, object: {:?}, string: \
1956                 <can't call Object::toString string because of a pending exception in the current thread> }}",
1957                self.env, self.raw_object
1958            )
1959        } else {
1960            // Safe because we checked that there is no pending exception.
1961            let token = unsafe { NoException::new_env(self.env) };
1962            match self.to_string(&token) {
1963                Ok(string) => write!(
1964                    formatter,
1965                    "Object {{ env: {:?}, object: {:?} string: {} }}",
1966                    self.env,
1967                    self.raw_object,
1968                    string.as_string(&token),
1969                ),
1970                Err(exception) => match exception.to_string(&token) {
1971                    Ok(message) => write!(
1972                        formatter,
1973                        "Object {{ env: {:?}, object: {:?}, string: \
1974                         <Object::toString threw an exception: {:?}> }}",
1975                        self.env,
1976                        self.raw_object,
1977                        message.as_string(&token)
1978                    ),
1979                    Err(_) => write!(
1980                        formatter,
1981                        "Object {{ env: {:?}, object: {:?}, string: \
1982                         <Object::toString threw an exception> }}",
1983                        self.env, self.raw_object
1984                    ),
1985                },
1986            }
1987        }
1988    }
1989}
1990
1991/// Allow displaying Java objects.
1992///
1993/// [`Object::toString` javadoc](https://docs.oracle.com/javase/10/docs/api/java/lang/Object.html#toString())
1994///
1995/// This is mostly a convenience for debugging. Always prefer using
1996/// [`to_string`](struct.Object.html#methods.to_string) to printing the object as is, because
1997/// the former checks for a pending exception in compile-time rather than the run-time.
1998impl<'env> fmt::Display for Object<'env> {
1999    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
2000        if self.env.has_exception() {
2001            panic!("Displaying a Java object with a pending exception in the current thread.");
2002        } else {
2003            // Safe because we checked that there is no pending exception.
2004            let token = unsafe { NoException::new_env(self.env) };
2005            match self.to_string(&token) {
2006                Ok(string) => write!(formatter, "{}", string.as_string(&token)),
2007                Err(exception) => match exception.to_string(&token) {
2008                    Ok(message) => write!(
2009                        formatter,
2010                        "Object::toString threw an exception: {}",
2011                        message.as_string(&token)
2012                    ),
2013                    Err(_) => write!(
2014                        formatter,
2015                        "<Object::toString threw an exception which could not be formatted>"
2016                    ),
2017                },
2018            }
2019        }
2020    }
2021}
2022
2023#[cfg(test)]
2024pub fn test_object<'env>(env: &'env JniEnv<'env>, raw_object: jni_sys::jobject) -> Object<'env> {
2025    Object { env, raw_object }
2026}
2027
2028#[cfg(test)]
2029mod object_tests {
2030    use super::*;
2031    use jni::class::test_class;
2032    use jni::testing::*;
2033    use std::mem;
2034
2035    #[cfg(test)]
2036    fn test_value<'env>(env: &'env JniEnv<'env>, raw_object: jni_sys::jobject) -> Object<'env> {
2037        test_object(env, raw_object)
2038    }
2039
2040    generate_object_tests!(Object, "Ljava/lang/Object;");
2041
2042    #[test]
2043    fn raw_object() {
2044        let vm = test_vm(ptr::null_mut());
2045        let env = test_env(&vm, ptr::null_mut());
2046        let raw_object = 0x91011 as jni_sys::jobject;
2047        let object = test_object(&env, raw_object);
2048        unsafe {
2049            assert_eq!(object.raw_object(), raw_object);
2050        }
2051        mem::forget(object);
2052    }
2053
2054    #[test]
2055    fn env() {
2056        let vm = test_vm(ptr::null_mut());
2057        let jni_env = 0x5678 as *mut jni_sys::JNIEnv;
2058        let env = test_env(&vm, jni_env);
2059        let raw_object = 0x91011 as jni_sys::jobject;
2060        let object = test_object(&env, raw_object);
2061        unsafe {
2062            assert_eq!(object.env().raw_env(), jni_env);
2063        }
2064        mem::forget(object);
2065    }
2066
2067    #[test]
2068    fn cast() {
2069        let vm = test_vm(ptr::null_mut());
2070        let env = test_env(&vm, ptr::null_mut());
2071        let object = test_value(&env, ptr::null_mut());
2072        assert_eq!(&object as *const _, object.cast() as *const _);
2073        mem::forget(object);
2074    }
2075
2076    #[test]
2077    fn class() {
2078        const RAW_OBJECT: jni_sys::jobject = 0x093599 as jni_sys::jobject;
2079        const RAW_CLASS: jni_sys::jobject = 0x347658 as jni_sys::jobject;
2080        let calls = test_raw_jni_env!(vec![JniCall::GetObjectClass(GetObjectClass {
2081            object: RAW_OBJECT,
2082            result: RAW_CLASS,
2083        })]);
2084        let vm = test_vm(ptr::null_mut());
2085        let env = test_env(&vm, calls.env);
2086        let object = test_value(&env, RAW_OBJECT);
2087        let class = object.class(&NoException::test());
2088        calls.assert_eq(&class, RAW_CLASS);
2089    }
2090
2091    #[test]
2092    #[should_panic(expected = "doesn't have a class")]
2093    fn class_not_found() {
2094        const RAW_OBJECT: jni_sys::jobject = 0x093599 as jni_sys::jobject;
2095        let calls = test_raw_jni_env!(vec![JniCall::GetObjectClass(GetObjectClass {
2096            object: RAW_OBJECT,
2097            result: ptr::null_mut(),
2098        })]);
2099        let vm = test_vm(ptr::null_mut());
2100        let env = test_env(&vm, calls.env);
2101        let object = test_value(&env, RAW_OBJECT);
2102        object.class(&NoException::test());
2103    }
2104
2105    #[test]
2106    fn is_same_as_same() {
2107        const RAW_OBJECT1: jni_sys::jobject = 0x91011 as jni_sys::jobject;
2108        const RAW_OBJECT2: jni_sys::jobject = 0x93486 as jni_sys::jobject;
2109        let calls = test_raw_jni_env!(vec![JniCall::IsSameObject(IsSameObject {
2110            object1: RAW_OBJECT1,
2111            object2: RAW_OBJECT2,
2112            result: jni_sys::JNI_TRUE,
2113        })]);
2114        let vm = test_vm(ptr::null_mut());
2115        let env = test_env(&vm, calls.env);
2116        let object1 = test_value(&env, RAW_OBJECT1);
2117        let object2 = test_value(&env, RAW_OBJECT2);
2118        assert!(object1.is_same_as(&object2, &NoException::test()));
2119    }
2120
2121    #[test]
2122    fn is_same_as_not_same() {
2123        const RAW_OBJECT1: jni_sys::jobject = 0x91011 as jni_sys::jobject;
2124        const RAW_OBJECT2: jni_sys::jobject = 0x93486 as jni_sys::jobject;
2125        let calls = test_raw_jni_env!(vec![JniCall::IsSameObject(IsSameObject {
2126            object1: RAW_OBJECT1,
2127            object2: RAW_OBJECT2,
2128            result: jni_sys::JNI_FALSE,
2129        })]);
2130        let vm = test_vm(ptr::null_mut());
2131        let env = test_env(&vm, calls.env);
2132        let object1 = test_value(&env, RAW_OBJECT1);
2133        let object2 = test_value(&env, RAW_OBJECT2);
2134        assert!(!object1.is_same_as(&object2, &NoException::test()));
2135    }
2136
2137    #[test]
2138    fn is_instance_of() {
2139        const RAW_OBJECT: jni_sys::jobject = 0x91011 as jni_sys::jobject;
2140        const RAW_CLASS: jni_sys::jobject = 0x93486 as jni_sys::jobject;
2141        let calls = test_raw_jni_env!(vec![JniCall::IsInstanceOf(IsInstanceOf {
2142            object: RAW_OBJECT,
2143            class: RAW_CLASS,
2144            result: jni_sys::JNI_TRUE,
2145        })]);
2146        let vm = test_vm(ptr::null_mut());
2147        let env = test_env(&vm, calls.env);
2148        let object = test_object(&env, RAW_OBJECT);
2149        let class = test_class(&env, RAW_CLASS);
2150        assert!(object.is_instance_of(&class, &NoException::test()));
2151    }
2152
2153    #[test]
2154    fn is_not_instance_of() {
2155        const RAW_OBJECT: jni_sys::jobject = 0x91011 as jni_sys::jobject;
2156        const RAW_CLASS: jni_sys::jobject = 0x93486 as jni_sys::jobject;
2157        let calls = test_raw_jni_env!(vec![JniCall::IsInstanceOf(IsInstanceOf {
2158            object: RAW_OBJECT,
2159            class: RAW_CLASS,
2160            result: jni_sys::JNI_FALSE,
2161        })]);
2162        let vm = test_vm(ptr::null_mut());
2163        let env = test_env(&vm, calls.env);
2164        let object = test_object(&env, RAW_OBJECT);
2165        let class = test_class(&env, RAW_CLASS);
2166        assert!(!object.is_instance_of(&class, &NoException::test()));
2167    }
2168
2169    #[test]
2170    fn debug() {
2171        const RAW_OBJECT: jni_sys::jobject = 0x924858 as jni_sys::jobject;
2172        const RAW_CLASS: jni_sys::jobject = 0x239875 as jni_sys::jobject;
2173        const METHOD_ID: jni_sys::jmethodID = 0x2835 as jni_sys::jmethodID;
2174        const RAW_STRING: jni_sys::jstring = 0x92385 as jni_sys::jstring;
2175        const LENGTH: usize = 5;
2176        const SIZE: usize = 11; // `"test-string".len()`.
2177        static mut METHOD_CALLS: i32 = 0;
2178        static mut METHOD_ENV_ARGUMENT: *mut jni_sys::JNIEnv = ptr::null_mut();
2179        type VariadicFn = unsafe extern "C" fn(
2180            env: *mut jni_sys::JNIEnv,
2181            object: jni_sys::jobject,
2182            method_id: jni_sys::jmethodID,
2183            ...
2184        ) -> jni_sys::jstring;
2185        type TestFn = unsafe extern "C" fn(
2186            env: *mut jni_sys::JNIEnv,
2187            object: jni_sys::jobject,
2188            method_id: jni_sys::jmethodID,
2189        ) -> jni_sys::jstring;
2190        unsafe extern "C" fn method(
2191            env: *mut jni_sys::JNIEnv,
2192            object: jni_sys::jobject,
2193            method_id: jni_sys::jmethodID,
2194        ) -> jni_sys::jstring {
2195            assert_eq!(object, RAW_OBJECT);
2196            assert_eq!(method_id, METHOD_ID);
2197            METHOD_CALLS += 1;
2198            METHOD_ENV_ARGUMENT = env;
2199            RAW_STRING
2200        }
2201        let raw_jni_env = jni_sys::JNINativeInterface_ {
2202            CallObjectMethod: Some(unsafe { mem::transmute::<TestFn, VariadicFn>(method) }),
2203            ..empty_raw_jni_env()
2204        };
2205        let calls = test_raw_jni_env!(
2206            vec![
2207                JniCall::ExceptionCheck(ExceptionCheck {
2208                    result: jni_sys::JNI_FALSE,
2209                }),
2210                JniCall::GetObjectClass(GetObjectClass {
2211                    object: RAW_OBJECT,
2212                    result: RAW_CLASS,
2213                }),
2214                JniCall::GetMethodID(GetMethodID {
2215                    class: RAW_CLASS,
2216                    name: "toString".to_owned(),
2217                    signature: "()Ljava/lang/String;".to_owned(),
2218                    result: METHOD_ID,
2219                }),
2220                JniCall::ExceptionOccurred(ExceptionOccurred {
2221                    result: ptr::null_mut(),
2222                }),
2223                JniCall::DeleteLocalRef(DeleteLocalRef { object: RAW_CLASS }),
2224                JniCall::GetStringLength(GetStringLength {
2225                    string: RAW_STRING,
2226                    result: LENGTH as jni_sys::jsize,
2227                }),
2228                JniCall::GetStringUTFLength(GetStringUTFLength {
2229                    string: RAW_STRING,
2230                    result: SIZE as jni_sys::jsize,
2231                }),
2232                JniCall::GetStringUTFRegion(GetStringUTFRegion {
2233                    string: RAW_STRING,
2234                    start: 0,
2235                    len: LENGTH as jni_sys::jsize,
2236                    buffer: "test-string".to_owned(),
2237                }),
2238                JniCall::DeleteLocalRef(DeleteLocalRef { object: RAW_STRING }),
2239            ],
2240            raw_jni_env
2241        );
2242        let vm = test_vm(ptr::null_mut());
2243        let env = test_env(&vm, calls.env);
2244        let object = test_value(&env, RAW_OBJECT);
2245        assert!(format!("{:?}", object).contains("string: test-string"));
2246    }
2247
2248    #[test]
2249    fn debug_exception_pending() {
2250        let calls = test_raw_jni_env!(vec![JniCall::ExceptionCheck(ExceptionCheck {
2251            result: jni_sys::JNI_TRUE,
2252        })]);
2253        let vm = test_vm(ptr::null_mut());
2254        let env = test_env(&vm, calls.env);
2255        let object = test_value(&env, ptr::null_mut());
2256        assert!(format!("{:?}", object).contains(
2257            "string: <can't call Object::toString string \
2258             because of a pending exception in the current thread>",
2259        ));
2260    }
2261
2262    #[test]
2263    fn debug_exception_thrown() {
2264        const RAW_OBJECT: jni_sys::jobject = 0x924858 as jni_sys::jobject;
2265        const RAW_CLASS: jni_sys::jobject = 0x239875 as jni_sys::jobject;
2266        const RAW_EXCEPTION_CLASS: jni_sys::jobject = 0x912376 as jni_sys::jobject;
2267        const METHOD_ID: jni_sys::jmethodID = 0x923476 as jni_sys::jmethodID;
2268        const EXCEPTION_METHOD_ID: jni_sys::jmethodID = 0x8293659 as jni_sys::jmethodID;
2269        const RAW_STRING: jni_sys::jstring = 0x92385 as jni_sys::jstring;
2270        const EXCEPTION: jni_sys::jobject = 0x2835 as jni_sys::jobject;
2271        const LENGTH: usize = 5;
2272        const SIZE: usize = 11; // `"test-string".len()`.
2273        static mut METHOD_CALLS: i32 = 0;
2274        static mut METHOD_ENV_ARGUMENT: *mut jni_sys::JNIEnv = ptr::null_mut();
2275        type VariadicFn = unsafe extern "C" fn(
2276            env: *mut jni_sys::JNIEnv,
2277            object: jni_sys::jobject,
2278            method_id: jni_sys::jmethodID,
2279            ...
2280        ) -> jni_sys::jstring;
2281        type TestFn = unsafe extern "C" fn(
2282            env: *mut jni_sys::JNIEnv,
2283            object: jni_sys::jobject,
2284            method_id: jni_sys::jmethodID,
2285        ) -> jni_sys::jstring;
2286        unsafe extern "C" fn method(
2287            env: *mut jni_sys::JNIEnv,
2288            object: jni_sys::jobject,
2289            method_id: jni_sys::jmethodID,
2290        ) -> jni_sys::jstring {
2291            METHOD_CALLS += 1;
2292            if METHOD_CALLS == 1 {
2293                assert_eq!(object, RAW_OBJECT);
2294                assert_eq!(method_id, METHOD_ID);
2295                METHOD_ENV_ARGUMENT = env;
2296            } else {
2297                assert_eq!(object, EXCEPTION);
2298                assert_eq!(method_id, EXCEPTION_METHOD_ID);
2299                assert_eq!(env, METHOD_ENV_ARGUMENT);
2300            }
2301            RAW_STRING
2302        }
2303        let raw_jni_env = jni_sys::JNINativeInterface_ {
2304            CallObjectMethod: Some(unsafe { mem::transmute::<TestFn, VariadicFn>(method) }),
2305            ..empty_raw_jni_env()
2306        };
2307        let calls = test_raw_jni_env!(
2308            vec![
2309                JniCall::ExceptionCheck(ExceptionCheck {
2310                    result: jni_sys::JNI_FALSE,
2311                }),
2312                JniCall::GetObjectClass(GetObjectClass {
2313                    object: RAW_OBJECT,
2314                    result: RAW_CLASS,
2315                }),
2316                JniCall::GetMethodID(GetMethodID {
2317                    class: RAW_CLASS,
2318                    name: "toString".to_owned(),
2319                    signature: "()Ljava/lang/String;".to_owned(),
2320                    result: METHOD_ID,
2321                }),
2322                JniCall::ExceptionOccurred(ExceptionOccurred { result: EXCEPTION }),
2323                JniCall::ExceptionClear(ExceptionClear {}),
2324                JniCall::DeleteLocalRef(DeleteLocalRef { object: RAW_CLASS }),
2325                JniCall::GetObjectClass(GetObjectClass {
2326                    object: EXCEPTION,
2327                    result: RAW_EXCEPTION_CLASS,
2328                }),
2329                JniCall::GetMethodID(GetMethodID {
2330                    class: RAW_EXCEPTION_CLASS,
2331                    name: "toString".to_owned(),
2332                    signature: "()Ljava/lang/String;".to_owned(),
2333                    result: EXCEPTION_METHOD_ID,
2334                }),
2335                JniCall::ExceptionOccurred(ExceptionOccurred {
2336                    result: ptr::null_mut(),
2337                }),
2338                JniCall::DeleteLocalRef(DeleteLocalRef {
2339                    object: RAW_EXCEPTION_CLASS,
2340                }),
2341                JniCall::GetStringLength(GetStringLength {
2342                    string: RAW_STRING,
2343                    result: LENGTH as jni_sys::jsize,
2344                }),
2345                JniCall::GetStringUTFLength(GetStringUTFLength {
2346                    string: RAW_STRING,
2347                    result: SIZE as jni_sys::jsize,
2348                }),
2349                JniCall::GetStringUTFRegion(GetStringUTFRegion {
2350                    string: RAW_STRING,
2351                    start: 0,
2352                    len: LENGTH as jni_sys::jsize,
2353                    buffer: "test-string".to_owned(),
2354                }),
2355                JniCall::DeleteLocalRef(DeleteLocalRef { object: RAW_STRING }),
2356                JniCall::DeleteLocalRef(DeleteLocalRef { object: EXCEPTION }),
2357            ],
2358            raw_jni_env
2359        );
2360        let vm = test_vm(ptr::null_mut());
2361        let env = test_env(&vm, calls.env);
2362        let object = test_value(&env, RAW_OBJECT);
2363        assert!(
2364            format!("{:?}", object)
2365                .contains("string: <Object::toString threw an exception: \"test-string\">")
2366        );
2367    }
2368
2369    #[test]
2370    fn debug_exception_thrown_twice() {
2371        const RAW_OBJECT: jni_sys::jobject = 0x924858 as jni_sys::jobject;
2372        const RAW_CLASS: jni_sys::jobject = 0x239875 as jni_sys::jobject;
2373        const RAW_EXCEPTION_CLASS: jni_sys::jobject = 0x912376 as jni_sys::jobject;
2374        const METHOD_ID: jni_sys::jmethodID = 0x923476 as jni_sys::jmethodID;
2375        const EXCEPTION_METHOD_ID: jni_sys::jmethodID = 0x8293659 as jni_sys::jmethodID;
2376        const EXCEPTION: jni_sys::jobject = 0x2835 as jni_sys::jobject;
2377        const EXCEPTION2: jni_sys::jobject = 0x2836 as jni_sys::jobject;
2378        static mut METHOD_CALLS: i32 = 0;
2379        static mut METHOD_ENV_ARGUMENT: *mut jni_sys::JNIEnv = ptr::null_mut();
2380        type VariadicFn = unsafe extern "C" fn(
2381            env: *mut jni_sys::JNIEnv,
2382            object: jni_sys::jobject,
2383            method_id: jni_sys::jmethodID,
2384            ...
2385        ) -> jni_sys::jstring;
2386        type TestFn = unsafe extern "C" fn(
2387            env: *mut jni_sys::JNIEnv,
2388            object: jni_sys::jobject,
2389            method_id: jni_sys::jmethodID,
2390        ) -> jni_sys::jstring;
2391        unsafe extern "C" fn method(
2392            env: *mut jni_sys::JNIEnv,
2393            object: jni_sys::jobject,
2394            method_id: jni_sys::jmethodID,
2395        ) -> jni_sys::jstring {
2396            METHOD_CALLS += 1;
2397            if METHOD_CALLS == 1 {
2398                assert_eq!(object, RAW_OBJECT);
2399                assert_eq!(method_id, METHOD_ID);
2400                METHOD_ENV_ARGUMENT = env;
2401            } else {
2402                assert_eq!(object, EXCEPTION);
2403                assert_eq!(method_id, EXCEPTION_METHOD_ID);
2404                assert_eq!(env, METHOD_ENV_ARGUMENT);
2405            }
2406            ptr::null_mut()
2407        }
2408        let raw_jni_env = jni_sys::JNINativeInterface_ {
2409            CallObjectMethod: Some(unsafe { mem::transmute::<TestFn, VariadicFn>(method) }),
2410            ..empty_raw_jni_env()
2411        };
2412        let calls = test_raw_jni_env!(
2413            vec![
2414                JniCall::ExceptionCheck(ExceptionCheck {
2415                    result: jni_sys::JNI_FALSE,
2416                }),
2417                JniCall::GetObjectClass(GetObjectClass {
2418                    object: RAW_OBJECT,
2419                    result: RAW_CLASS,
2420                }),
2421                JniCall::GetMethodID(GetMethodID {
2422                    class: RAW_CLASS,
2423                    name: "toString".to_owned(),
2424                    signature: "()Ljava/lang/String;".to_owned(),
2425                    result: METHOD_ID,
2426                }),
2427                JniCall::ExceptionOccurred(ExceptionOccurred { result: EXCEPTION }),
2428                JniCall::ExceptionClear(ExceptionClear {}),
2429                JniCall::DeleteLocalRef(DeleteLocalRef { object: RAW_CLASS }),
2430                JniCall::GetObjectClass(GetObjectClass {
2431                    object: EXCEPTION,
2432                    result: RAW_EXCEPTION_CLASS,
2433                }),
2434                JniCall::GetMethodID(GetMethodID {
2435                    class: RAW_EXCEPTION_CLASS,
2436                    name: "toString".to_owned(),
2437                    signature: "()Ljava/lang/String;".to_owned(),
2438                    result: EXCEPTION_METHOD_ID,
2439                }),
2440                JniCall::ExceptionOccurred(ExceptionOccurred { result: EXCEPTION2 }),
2441                JniCall::ExceptionClear(ExceptionClear {}),
2442                JniCall::DeleteLocalRef(DeleteLocalRef {
2443                    object: RAW_EXCEPTION_CLASS,
2444                }),
2445                JniCall::DeleteLocalRef(DeleteLocalRef { object: EXCEPTION2 }),
2446                JniCall::DeleteLocalRef(DeleteLocalRef { object: EXCEPTION }),
2447            ],
2448            raw_jni_env
2449        );
2450        let vm = test_vm(ptr::null_mut());
2451        let env = test_env(&vm, calls.env);
2452        let object = test_value(&env, RAW_OBJECT);
2453        assert!(format!("{:?}", object).contains("string: <Object::toString threw an exception>"));
2454    }
2455}