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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
38pub enum JniError {
39 Unknown(i32),
42}
43
44#[derive(Debug)]
147pub struct NoException<'env> {
148 _token: (),
149 _env: PhantomData<JniEnv<'env>>,
150}
151
152impl<'env> NoException<'env> {
153 unsafe fn new_env<'a>(_env: &JniEnv<'a>) -> NoException<'a> {
155 Self::new_raw()
157 }
158
159 unsafe fn new_raw<'a>() -> NoException<'a> {
163 NoException {
164 _token: (),
165 _env: PhantomData::<JniEnv>,
166 }
167 }
168
169 unsafe fn clone(&self) -> Self {
172 Self::new_raw()
173 }
174
175 #[cfg(test)]
176 fn test<'a>() -> NoException<'a> {
177 unsafe { Self::new_raw() }
179 }
180}
181
182#[derive(Debug)]
192pub struct Exception<'env> {
193 _token: (),
194 env: &'env JniEnv<'env>,
195}
196
197impl<'env> Exception<'env> {
198 pub fn unwrap(self) -> (Throwable<'env>, NoException<'env>) {
202 let throwable = get_and_clear_exception(self);
203 let token = unsafe { NoException::new_raw() };
205 (throwable, token)
206 }
207
208 unsafe fn new<'a>(env: &'a JniEnv<'a>, _token: NoException) -> Exception<'a> {
213 Self::new_raw(env)
214 }
215
216 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 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
251type JniResult<'env, T> = Result<(T, NoException<'env>), Exception<'env>>;
261
262unsafe 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
279pub 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#[derive(Debug)]
357pub struct JavaVM {
358 java_vm: *mut jni_sys::JavaVM,
359 owned: bool,
360}
361
362impl JavaVM {
363 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 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 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 pub fn list() -> Result<Vec<Self>, JniError> {
416 let mut vms_created: jni_sys::jsize = 0;
417 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 let status = unsafe {
432 JNI_GetCreatedJavaVMs(
433 (java_vms.as_mut_ptr()) as *mut *mut jni_sys::JavaVM,
434 vms_created,
435 (&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 .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 pub unsafe fn raw_jvm(&self) -> *mut jni_sys::JavaVM {
459 self.java_vm
460 }
461
462 pub fn attach(&self, arguments: &AttachArguments) -> Result<JniEnv, JniError> {
467 unsafe { self.attach_generic(arguments, (**self.raw_jvm()).AttachCurrentThread.unwrap()) }
469 }
470
471 pub fn attach_daemon(&self, arguments: &AttachArguments) -> Result<JniEnv, JniError> {
476 unsafe {
478 self.attach_generic(
479 arguments,
480 (**self.raw_jvm()).AttachCurrentThreadAsDaemon.unwrap(),
481 )
482 }
483 }
484
485 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 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 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 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 status => panic!(
553 "GetEnv JNI method returned an unexpected error code {}",
554 status
555 ),
556 }
557 }
558
559 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 if status != jni_sys::JNI_OK {
567 panic!("Could not detach the current thread. Status: {}", status)
568 }
569 }
570
571 unsafe fn from_ptr(java_vm: *mut jni_sys::JavaVM) -> JavaVM {
573 JavaVM {
574 java_vm,
575 owned: false,
576 }
577 }
578}
579
580impl Drop for JavaVM {
584 fn drop(&mut self) {
585 if !self.owned {
586 return;
587 }
588
589 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
601unsafe impl Send for JavaVM {}
603
604unsafe 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 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 mem::forget(env);
1133 }
1134}
1135
1136#[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
1226impl<'vm> JniEnv<'vm> {
1232 pub unsafe fn raw_jvm(&self) -> *mut jni_sys::JavaVM {
1236 self.vm.raw_jvm()
1237 }
1238
1239 pub unsafe fn raw_env(&self) -> *mut jni_sys::JNIEnv {
1243 self.jni_env
1244 }
1245
1246 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 unsafe { NoException::new_env(self) }
1261 }
1262 }
1263
1264 pub fn version(&self) -> JniVersion {
1268 self.version
1269 }
1270
1271 fn has_exception(&self) -> bool {
1272 let value = unsafe { call_jni_method!(self, ExceptionCheck) };
1274 unsafe { bool::__from_jni(self, value) }
1276 }
1277}
1278
1279impl<'vm> Drop for JniEnv<'vm> {
1285 fn drop(&mut self) {
1286 if self.native_method_call {
1291 return;
1292 }
1293
1294 if self.has_exception() {
1295 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 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 }
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
1514fn maybe_get_and_clear_exception<'a>(env: &'a JniEnv<'a>) -> Option<Throwable<'a>> {
1516 let raw_java_throwable = unsafe { call_jni_method!(env, ExceptionOccurred) };
1518 if raw_java_throwable == ptr::null_mut() {
1519 return None;
1520 }
1521 unsafe {
1523 call_jni_method!(env, ExceptionClear);
1524 }
1525 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
1558fn 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
1598fn with_checked_exception<'a, Out, T: FnOnce(NoException<'a>) -> JniResult<'a, Out>>(
1601 token: &NoException<'a>,
1602 function: T,
1603) -> JavaResult<'a, Out> {
1604 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#[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#[doc(hidden)]
1669pub trait JniArgumentType: JniType {}
1670
1671pub trait JavaType {
1679 #[doc(hidden)]
1683 type __JniType: JniType;
1684
1685 #[doc(hidden)]
1691 fn __signature() -> &'static str;
1692}
1693
1694#[doc(hidden)]
1702pub trait ToJni: JavaType {
1703 unsafe fn __to_jni(&self) -> Self::__JniType;
1709}
1710
1711#[doc(hidden)]
1719pub trait FromJni<'env>: JavaType {
1720 unsafe fn __from_jni(env: &'env JniEnv<'env>, value: Self::__JniType) -> Self;
1726}
1727
1728impl<'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
1742impl<'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#[doc(hidden)]
1761pub trait JavaMethodSignature<In: ?Sized, Out: ?Sized> {
1762 fn __signature() -> std::string::String;
1766}
1767
1768pub trait Cast<'env, As: Cast<'env, Object<'env>>>:
1770 JavaType<__JniType = jni_sys::jobject> + ToJni + FromJni<'env>
1771{
1772 fn cast<'a>(&'a self) -> &'a As;
1777}
1778
1779pub struct Object<'env> {
1786 env: &'env JniEnv<'env>,
1787 raw_object: jni_sys::jobject,
1788}
1789
1790impl<'env> Object<'env> {
1796 pub unsafe fn raw_object(&self) -> jni_sys::jobject {
1800 self.raw_object
1801 }
1802
1803 pub fn env(&self) -> &'env JniEnv<'env> {
1805 self.env
1806 }
1807
1808 pub fn class(&self, _token: &NoException) -> Class<'env> {
1812 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 unsafe { Class::__from_jni(self.env, raw_java_class) }
1819 }
1820
1821 pub fn is_same_as(&self, other: &Object, _token: &NoException) -> bool {
1825 let same = unsafe {
1827 call_jni_method!(
1828 self.env(),
1829 IsSameObject,
1830 self.raw_object(),
1831 other.raw_object()
1832 )
1833 };
1834 unsafe { bool::__from_jni(self.env(), same) }
1836 }
1837
1838 pub fn is_instance_of(&self, class: &Class, _token: &NoException) -> bool {
1842 let is_instance = unsafe {
1844 call_jni_method!(
1845 self.env(),
1846 IsInstanceOf,
1847 self.raw_object(),
1848 class.raw_object()
1849 )
1850 };
1851 unsafe { bool::__from_jni(self.env(), is_instance) }
1853 }
1854
1855 pub fn clone(&self, token: &NoException<'env>) -> JavaResult<'env, Object<'env>> {
1866 let raw_object =
1869 unsafe { call_nullable_jni_method!(self.env, NewLocalRef, token, self.raw_object)? };
1870 Ok(unsafe { Self::from_raw(self.env, raw_object) })
1872 }
1873
1874 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
1898impl<'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
1906impl<'env> Drop for Object<'env> {
1910 fn drop(&mut self) {
1911 unsafe {
1913 call_jni_method!(self.env, DeleteLocalRef, self.raw_object);
1914 }
1915 }
1916}
1917
1918impl<'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 let token = unsafe { NoException::new_env(self.env()) };
1937 self.is_same_as(other.cast(), &token)
1938 }
1939 }
1940}
1941
1942impl<'env> fmt::Debug for Object<'env> {
1950 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
1951 if self.env.has_exception() {
1952 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 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
1991impl<'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 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; 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; 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}