1 #![cfg(feature = "invocation")]
2 use std::{convert::TryFrom, str::FromStr};
3 
4 use jni::{
5     descriptors::Desc,
6     errors::Error,
7     objects::{
8         AutoElements, AutoLocal, JByteBuffer, JList, JObject, JString, JThrowable, JValue,
9         ReleaseMode,
10     },
11     signature::{JavaType, Primitive, ReturnType},
12     strings::JNIString,
13     sys::{jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jobject, jshort, jsize},
14     JNIEnv,
15 };
16 
17 mod util;
18 use util::{attach_current_thread, unwrap};
19 
20 static ARRAYLIST_CLASS: &str = "java/util/ArrayList";
21 static EXCEPTION_CLASS: &str = "java/lang/Exception";
22 static ARITHMETIC_EXCEPTION_CLASS: &str = "java/lang/ArithmeticException";
23 static RUNTIME_EXCEPTION_CLASS: &str = "java/lang/RuntimeException";
24 static INTEGER_CLASS: &str = "java/lang/Integer";
25 static MATH_CLASS: &str = "java/lang/Math";
26 static STRING_CLASS: &str = "java/lang/String";
27 static MATH_ABS_METHOD_NAME: &str = "abs";
28 static MATH_TO_INT_METHOD_NAME: &str = "toIntExact";
29 static MATH_ABS_SIGNATURE: &str = "(I)I";
30 static MATH_TO_INT_SIGNATURE: &str = "(J)I";
31 static TEST_EXCEPTION_MESSAGE: &str = "Default exception thrown";
32 static TESTING_OBJECT_STR: &str = "TESTING OBJECT";
33 
34 #[test]
call_method_returning_null()35 pub fn call_method_returning_null() {
36     let mut env = attach_current_thread();
37     // Create an Exception with no message
38     let obj = AutoLocal::new(
39         unwrap(env.new_object(EXCEPTION_CLASS, "()V", &[]), &env),
40         &env,
41     );
42     // Call Throwable#getMessage must return null
43     let message = unwrap(
44         env.call_method(&obj, "getMessage", "()Ljava/lang/String;", &[]),
45         &env,
46     );
47     let message_ref = env.auto_local(unwrap(message.l(), &env));
48 
49     assert!(message_ref.is_null());
50 }
51 
52 #[test]
is_instance_of_same_class()53 pub fn is_instance_of_same_class() {
54     let mut env = attach_current_thread();
55     let obj = AutoLocal::new(
56         unwrap(env.new_object(EXCEPTION_CLASS, "()V", &[]), &env),
57         &env,
58     );
59     assert!(unwrap(env.is_instance_of(&obj, EXCEPTION_CLASS), &env));
60 }
61 
62 #[test]
is_instance_of_superclass()63 pub fn is_instance_of_superclass() {
64     let mut env = attach_current_thread();
65     let obj = AutoLocal::new(
66         unwrap(env.new_object(ARITHMETIC_EXCEPTION_CLASS, "()V", &[]), &env),
67         &env,
68     );
69     assert!(unwrap(env.is_instance_of(&obj, EXCEPTION_CLASS), &env));
70 }
71 
72 #[test]
is_instance_of_subclass()73 pub fn is_instance_of_subclass() {
74     let mut env = attach_current_thread();
75     let obj = AutoLocal::new(
76         unwrap(env.new_object(EXCEPTION_CLASS, "()V", &[]), &env),
77         &env,
78     );
79     assert!(!unwrap(
80         env.is_instance_of(&obj, ARITHMETIC_EXCEPTION_CLASS),
81         &env,
82     ));
83 }
84 
85 #[test]
is_instance_of_not_superclass()86 pub fn is_instance_of_not_superclass() {
87     let mut env = attach_current_thread();
88     let obj = AutoLocal::new(
89         unwrap(env.new_object(ARITHMETIC_EXCEPTION_CLASS, "()V", &[]), &env),
90         &env,
91     );
92     assert!(!unwrap(env.is_instance_of(&obj, ARRAYLIST_CLASS), &env));
93 }
94 
95 #[test]
is_instance_of_null()96 pub fn is_instance_of_null() {
97     let mut env = attach_current_thread();
98     let obj = JObject::null();
99     assert!(unwrap(env.is_instance_of(&obj, ARRAYLIST_CLASS), &env));
100     assert!(unwrap(env.is_instance_of(&obj, EXCEPTION_CLASS), &env));
101     assert!(unwrap(
102         env.is_instance_of(&obj, ARITHMETIC_EXCEPTION_CLASS),
103         &env,
104     ));
105 }
106 
107 #[test]
is_same_object_diff_references()108 pub fn is_same_object_diff_references() {
109     let env = attach_current_thread();
110     let string = env.new_string(TESTING_OBJECT_STR).unwrap();
111     let ref_from_string = unwrap(env.new_local_ref(&string), &env);
112     assert!(unwrap(env.is_same_object(&string, &ref_from_string), &env));
113     unwrap(env.delete_local_ref(ref_from_string), &env);
114 }
115 
116 #[test]
is_same_object_same_reference()117 pub fn is_same_object_same_reference() {
118     let env = attach_current_thread();
119     let string = env.new_string(TESTING_OBJECT_STR).unwrap();
120     assert!(unwrap(env.is_same_object(&string, &string), &env));
121 }
122 
123 #[test]
is_not_same_object()124 pub fn is_not_same_object() {
125     let env = attach_current_thread();
126     let string = env.new_string(TESTING_OBJECT_STR).unwrap();
127     let same_src_str = env.new_string(TESTING_OBJECT_STR).unwrap();
128     assert!(!unwrap(env.is_same_object(string, same_src_str), &env));
129 }
130 
131 #[test]
is_not_same_object_null()132 pub fn is_not_same_object_null() {
133     let env = attach_current_thread();
134     assert!(unwrap(
135         env.is_same_object(JObject::null(), JObject::null()),
136         &env,
137     ));
138 }
139 
140 #[test]
get_static_public_field()141 pub fn get_static_public_field() {
142     let mut env = attach_current_thread();
143 
144     let min_int_value = env
145         .get_static_field(INTEGER_CLASS, "MIN_VALUE", "I")
146         .unwrap()
147         .i()
148         .unwrap();
149 
150     assert_eq!(min_int_value, i32::min_value());
151 }
152 
153 #[test]
get_static_public_field_by_id()154 pub fn get_static_public_field_by_id() {
155     let mut env = attach_current_thread();
156 
157     // One can't pass a JavaType::Primitive(Primitive::Int) to
158     //   `get_static_field_id` unfortunately: #137
159     let field_type = "I";
160     let field_id = env
161         .get_static_field_id(INTEGER_CLASS, "MIN_VALUE", field_type)
162         .unwrap();
163 
164     let field_type = JavaType::from_str(field_type).unwrap();
165     let min_int_value = env
166         .get_static_field_unchecked(INTEGER_CLASS, field_id, field_type)
167         .unwrap()
168         .i()
169         .unwrap();
170 
171     assert_eq!(min_int_value, i32::min_value());
172 }
173 
174 #[test]
pop_local_frame_pending_exception()175 pub fn pop_local_frame_pending_exception() {
176     let mut env = attach_current_thread();
177 
178     env.push_local_frame(16).unwrap();
179 
180     env.throw_new(RUNTIME_EXCEPTION_CLASS, "Test Exception")
181         .unwrap();
182 
183     // Pop the local frame with a pending exception
184     unsafe { env.pop_local_frame(&JObject::null()) }
185         .expect("JNIEnv#pop_local_frame must work in case of pending exception");
186 
187     env.exception_clear().unwrap();
188 }
189 
190 #[test]
push_local_frame_pending_exception()191 pub fn push_local_frame_pending_exception() {
192     let mut env = attach_current_thread();
193 
194     env.throw_new(RUNTIME_EXCEPTION_CLASS, "Test Exception")
195         .unwrap();
196 
197     // Push a new local frame with a pending exception
198     env.push_local_frame(16)
199         .expect("JNIEnv#push_local_frame must work in case of pending exception");
200 
201     env.exception_clear().unwrap();
202 
203     unsafe { env.pop_local_frame(&JObject::null()) }.unwrap();
204 }
205 
206 #[test]
push_local_frame_too_many_refs()207 pub fn push_local_frame_too_many_refs() {
208     let env = attach_current_thread();
209 
210     // Try to push a new local frame with a ridiculous size
211     let frame_size = i32::max_value();
212     env.push_local_frame(frame_size)
213         .expect_err("push_local_frame(2B) must Err");
214 
215     unsafe { env.pop_local_frame(&JObject::null()) }.unwrap();
216 }
217 
218 #[test]
with_local_frame()219 pub fn with_local_frame() {
220     let mut env = attach_current_thread();
221 
222     let s = env
223         .with_local_frame_returning_local::<_, jni::errors::Error>(16, |env| {
224             let res = env.new_string("Test")?;
225             Ok(res.into())
226         })
227         .unwrap()
228         .into();
229 
230     let s = env
231         .get_string(&s)
232         .expect("The object returned from the local frame must remain valid");
233     assert_eq!(s.to_str().unwrap(), "Test");
234 }
235 
236 #[test]
with_local_frame_pending_exception()237 pub fn with_local_frame_pending_exception() {
238     let mut env = attach_current_thread();
239 
240     env.throw_new(RUNTIME_EXCEPTION_CLASS, "Test Exception")
241         .unwrap();
242 
243     // Try to allocate a frame of locals
244     env.with_local_frame(16, |_| -> Result<_, Error> { Ok(()) })
245         .expect("JNIEnv#with_local_frame must work in case of pending exception");
246 
247     env.exception_clear().unwrap();
248 }
249 
250 #[test]
call_method_ok()251 pub fn call_method_ok() {
252     let mut env = attach_current_thread();
253 
254     let s = env.new_string(TESTING_OBJECT_STR).unwrap();
255 
256     let v: jint = env
257         .call_method(s, "indexOf", "(I)I", &[JValue::Int('S' as i32)])
258         .expect("JNIEnv#call_method should return JValue")
259         .i()
260         .unwrap();
261 
262     assert_eq!(v, 2);
263 }
264 
265 #[test]
call_method_with_bad_args_errs()266 pub fn call_method_with_bad_args_errs() {
267     let mut env = attach_current_thread();
268 
269     let s = env.new_string(TESTING_OBJECT_STR).unwrap();
270 
271     let is_bad_typ = env
272         .call_method(
273             &s,
274             "indexOf",
275             "(I)I",
276             &[JValue::Float(std::f32::consts::PI)],
277         )
278         .map_err(|error| matches!(error, Error::InvalidArgList(_)))
279         .expect_err("JNIEnv#callmethod with bad arg type should err");
280 
281     assert!(
282         is_bad_typ,
283         "ErrorKind::InvalidArgList expected when passing bad value type"
284     );
285 
286     let is_bad_len = env
287         .call_method(
288             &s,
289             "indexOf",
290             "(I)I",
291             &[JValue::Int('S' as i32), JValue::Long(3)],
292         )
293         .map_err(|error| matches!(error, Error::InvalidArgList(_)))
294         .expect_err("JNIEnv#call_method with bad arg lengths should err");
295 
296     assert!(
297         is_bad_len,
298         "ErrorKind::InvalidArgList expected when passing bad argument lengths"
299     );
300 }
301 
302 #[test]
call_static_method_ok()303 pub fn call_static_method_ok() {
304     let mut env = attach_current_thread();
305 
306     let x = JValue::from(-10);
307     let val: jint = env
308         .call_static_method(MATH_CLASS, MATH_ABS_METHOD_NAME, MATH_ABS_SIGNATURE, &[x])
309         .expect("JNIEnv#call_static_method should return JValue")
310         .i()
311         .unwrap();
312 
313     assert_eq!(val, 10);
314 }
315 
316 #[test]
call_static_method_unchecked_ok()317 pub fn call_static_method_unchecked_ok() {
318     let mut env = attach_current_thread();
319 
320     let x = JValue::from(-10);
321     let math_class = env.find_class(MATH_CLASS).unwrap();
322     let abs_method_id = env
323         .get_static_method_id(&math_class, MATH_ABS_METHOD_NAME, MATH_ABS_SIGNATURE)
324         .unwrap();
325     let val: jint = unsafe {
326         env.call_static_method_unchecked(
327             &math_class,
328             abs_method_id,
329             ReturnType::Primitive(Primitive::Int),
330             &[x.as_jni()],
331         )
332     }
333     .expect("JNIEnv#call_static_method_unchecked should return JValue")
334     .i()
335     .unwrap();
336 
337     assert_eq!(val, 10);
338 }
339 
340 #[test]
call_new_object_unchecked_ok()341 pub fn call_new_object_unchecked_ok() {
342     let mut env = attach_current_thread();
343 
344     let test_str = env.new_string(TESTING_OBJECT_STR).unwrap();
345     let string_class = env.find_class(STRING_CLASS).unwrap();
346 
347     let ctor_method_id = env
348         .get_method_id(&string_class, "<init>", "(Ljava/lang/String;)V")
349         .unwrap();
350     let val: JObject = unsafe {
351         env.new_object_unchecked(
352             &string_class,
353             ctor_method_id,
354             &[JValue::from(&test_str).as_jni()],
355         )
356     }
357     .expect("JNIEnv#new_object_unchecked should return JValue");
358 
359     let jstr = JString::try_from(val).expect("asd");
360     let javastr = env.get_string(&jstr).unwrap();
361     let rstr = javastr.to_str().unwrap();
362     assert_eq!(rstr, TESTING_OBJECT_STR);
363 }
364 
365 #[test]
call_new_object_with_bad_args_errs()366 pub fn call_new_object_with_bad_args_errs() {
367     let mut env = attach_current_thread();
368 
369     let string_class = env.find_class(STRING_CLASS).unwrap();
370 
371     let is_bad_typ = env
372         .new_object(&string_class, "(Ljava/lang/String;)V", &[JValue::Int(2)])
373         .map_err(|error| matches!(error, Error::InvalidArgList(_)))
374         .expect_err("JNIEnv#new_object with bad arg type should err");
375 
376     assert!(
377         is_bad_typ,
378         "ErrorKind::InvalidArgList expected when passing bad value type"
379     );
380 
381     let s = env.new_string(TESTING_OBJECT_STR).unwrap();
382 
383     let is_bad_len = env
384         .new_object(
385             &string_class,
386             "(Ljava/lang/String;)V",
387             &[JValue::from(&s), JValue::Int(2)],
388         )
389         .map_err(|error| matches!(error, Error::InvalidArgList(_)))
390         .expect_err("JNIEnv#new_object with bad arg type should err");
391 
392     assert!(
393         is_bad_len,
394         "ErrorKind::InvalidArgList expected when passing bad argument lengths"
395     );
396 }
397 
398 /// Check that we get a runtime error if trying to instantiate with an array class.
399 ///
400 /// Although the JNI spec for `NewObjectA` states that the class "must not refer to an array class"
401 /// (and could therefor potentially trigger undefined behaviour if that rule is violated) we
402 /// expect that `JNIEnv::new_object()` shouldn't ever get as far as calling `NewObjectA` since
403 /// it will first fail (with a safe, runtime error) to lookup a method ID for any constructor.
404 /// (consistent with how [getConstructors()](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Class.html#getConstructors())
405 /// doesn't expose constructors for array classes)
406 #[test]
call_new_object_with_array_class()407 pub fn call_new_object_with_array_class() {
408     let mut env = attach_current_thread();
409 
410     let byte_array = env.new_byte_array(16).unwrap();
411     let array_class = env.get_object_class(byte_array).unwrap();
412     // We just make up a plausible constructor signature
413     let result = env.new_object(&array_class, "(I)[B", &[JValue::Int(16)]);
414 
415     assert!(result.is_err())
416 }
417 
418 #[test]
call_static_method_throws()419 pub fn call_static_method_throws() {
420     let mut env = attach_current_thread();
421 
422     let x = JValue::Long(4_000_000_000);
423     let is_java_exception = env
424         .call_static_method(
425             MATH_CLASS,
426             MATH_TO_INT_METHOD_NAME,
427             MATH_TO_INT_SIGNATURE,
428             &[x],
429         )
430         .map_err(|error| matches!(error, Error::JavaException))
431         .expect_err("JNIEnv#call_static_method_unsafe should return error");
432 
433     // Throws a java.lang.ArithmeticException: integer overflow
434     assert!(
435         is_java_exception,
436         "ErrorKind::JavaException expected as error"
437     );
438     assert_pending_java_exception(&mut env);
439 }
440 
441 #[test]
call_static_method_with_bad_args_errs()442 pub fn call_static_method_with_bad_args_errs() {
443     let mut env = attach_current_thread();
444 
445     let x = JValue::Double(4.567_891_23);
446     let is_bad_typ = env
447         .call_static_method(
448             MATH_CLASS,
449             MATH_TO_INT_METHOD_NAME,
450             MATH_TO_INT_SIGNATURE,
451             &[x],
452         )
453         .map_err(|error| matches!(error, Error::InvalidArgList(_)))
454         .expect_err("JNIEnv#call_static_method with bad arg type should err");
455 
456     assert!(
457         is_bad_typ,
458         "ErrorKind::InvalidArgList expected when passing bad value type"
459     );
460 
461     let is_bad_len = env
462         .call_static_method(
463             MATH_CLASS,
464             MATH_TO_INT_METHOD_NAME,
465             MATH_TO_INT_SIGNATURE,
466             &[JValue::Int(2), JValue::Int(3)],
467         )
468         .map_err(|error| matches!(error, Error::InvalidArgList(_)))
469         .expect_err("JNIEnv#call_static_method with bad arg lengths should err");
470 
471     assert!(
472         is_bad_len,
473         "ErrorKind::InvalidArgList expected when passing bad argument lengths"
474     );
475 }
476 
477 #[test]
java_byte_array_from_slice()478 pub fn java_byte_array_from_slice() {
479     let env = attach_current_thread();
480     let buf: &[u8] = &[1, 2, 3];
481     let java_array = AutoLocal::new(
482         env.byte_array_from_slice(buf)
483             .expect("JNIEnv#byte_array_from_slice must create a java array from slice"),
484         &env,
485     );
486 
487     assert!(!java_array.is_null());
488     let mut res: [i8; 3] = [0; 3];
489     env.get_byte_array_region(&java_array, 0, &mut res).unwrap();
490     assert_eq!(res[0], 1);
491     assert_eq!(res[1], 2);
492     assert_eq!(res[2], 3);
493 }
494 
495 macro_rules! test_auto_array_read_write {
496     ( $test_name:tt, $jni_type:ty, $new_array:tt, $get_array:tt, $set_array:tt ) => {
497         #[test]
498         pub fn $test_name() {
499             let env = attach_current_thread();
500 
501             // Create original Java array
502             let buf: &[$jni_type] = &[0 as $jni_type, 1 as $jni_type];
503             let java_array = env
504                 .$new_array(2)
505                 .expect(stringify!(JNIEnv#$new_array must create a Java $jni_type array with given size));
506 
507             // Insert array elements
508             let _ = env.$set_array(&java_array, 0, buf);
509 
510             // Use a scope to test Drop
511             {
512                 // Get byte array elements auto wrapper
513                 let mut auto_ptr: AutoElements<$jni_type> = unsafe {
514                     // Make sure the lifetime is tied to the environment,
515                     // not the particular JNIEnv reference
516                     let mut temporary_env: JNIEnv = env.unsafe_clone();
517                     temporary_env.get_array_elements(&java_array, ReleaseMode::CopyBack).unwrap()
518                 };
519 
520                 // Check array size
521                 assert_eq!(auto_ptr.len(), 2);
522 
523                 // Check pointer access
524                 let ptr = auto_ptr.as_ptr();
525                 assert_eq!(unsafe { *ptr.offset(0) } as i32, 0);
526                 assert_eq!(unsafe { *ptr.offset(1) } as i32, 1);
527 
528                 // Check pointer From access
529                 let ptr: *mut $jni_type = std::convert::From::from(&auto_ptr);
530                 assert_eq!(unsafe { *ptr.offset(0) } as i32, 0);
531                 assert_eq!(unsafe { *ptr.offset(1) } as i32, 1);
532 
533                 // Check pointer into() access
534                 let ptr: *mut $jni_type = (&auto_ptr).into();
535                 assert_eq!(unsafe { *ptr.offset(0) } as i32, 0);
536                 assert_eq!(unsafe { *ptr.offset(1) } as i32, 1);
537 
538                 // Check slice access
539                 //
540                 // # Safety
541                 //
542                 // We make sure that the slice is dropped before also testing access via `Deref`
543                 // (to ensure we don't have aliased references)
544                 unsafe {
545                     let slice = std::slice::from_raw_parts(auto_ptr.as_ptr(), auto_ptr.len());
546                     assert_eq!(slice[0] as i32, 0);
547                     assert_eq!(slice[1] as i32, 1);
548                 }
549 
550                 // Check access via Deref
551                 assert_eq!(auto_ptr[0] as i32, 0);
552                 assert_eq!(auto_ptr[1] as i32, 1);
553 
554                 // Modify via DerefMut
555                 let tmp = auto_ptr[1];
556                 auto_ptr[1] = auto_ptr[0];
557                 auto_ptr[0] = tmp;
558 
559                 // Commit would be necessary here, if there were no closure
560                 //auto_ptr.commit().unwrap();
561             }
562 
563             // Confirm modification of original Java array
564             let mut res: [$jni_type; 2] = [0 as $jni_type; 2];
565             env.$get_array(&java_array, 0, &mut res).unwrap();
566             assert_eq!(res[0] as i32, 1);
567             assert_eq!(res[1] as i32, 0);
568         }
569     };
570 }
571 
572 // Test generic get_array_elements
573 test_auto_array_read_write!(
574     get_array_elements,
575     jint,
576     new_int_array,
577     get_int_array_region,
578     set_int_array_region
579 );
580 
581 // Test type-specific array accessors
582 test_auto_array_read_write!(
583     get_int_array_elements,
584     jint,
585     new_int_array,
586     get_int_array_region,
587     set_int_array_region
588 );
589 
590 test_auto_array_read_write!(
591     get_long_array_elements,
592     jlong,
593     new_long_array,
594     get_long_array_region,
595     set_long_array_region
596 );
597 
598 test_auto_array_read_write!(
599     get_byte_array_elements,
600     jbyte,
601     new_byte_array,
602     get_byte_array_region,
603     set_byte_array_region
604 );
605 
606 test_auto_array_read_write!(
607     get_boolean_array_elements,
608     jboolean,
609     new_boolean_array,
610     get_boolean_array_region,
611     set_boolean_array_region
612 );
613 
614 test_auto_array_read_write!(
615     get_char_array_elements,
616     jchar,
617     new_char_array,
618     get_char_array_region,
619     set_char_array_region
620 );
621 
622 test_auto_array_read_write!(
623     get_short_array_elements,
624     jshort,
625     new_short_array,
626     get_short_array_region,
627     set_short_array_region
628 );
629 
630 test_auto_array_read_write!(
631     get_float_array_elements,
632     jfloat,
633     new_float_array,
634     get_float_array_region,
635     set_float_array_region
636 );
637 
638 test_auto_array_read_write!(
639     get_double_array_elements,
640     jdouble,
641     new_double_array,
642     get_double_array_region,
643     set_double_array_region
644 );
645 
646 #[test]
647 #[ignore] // Disabled until issue #283 is resolved
get_long_array_elements_commit()648 pub fn get_long_array_elements_commit() {
649     let mut env = attach_current_thread();
650 
651     // Create original Java array
652     let buf: &[i64] = &[1, 2, 3];
653     let java_array = env
654         .new_long_array(3)
655         .expect("JNIEnv#new_long_array must create a java array with given size");
656 
657     // Insert array elements
658     let _ = env.set_long_array_region(&java_array, 0, buf);
659 
660     // Get long array elements auto wrapper
661     let mut auto_ptr = unsafe {
662         env.get_array_elements(&java_array, ReleaseMode::CopyBack)
663             .unwrap()
664     };
665 
666     // Copying the array depends on the VM vendor/version/GC combinations.
667     // If the wrapped array is not being copied, we can skip the test.
668     if !auto_ptr.is_copy() {
669         return;
670     }
671 
672     // Check pointer access
673     let ptr = auto_ptr.as_ptr();
674 
675     // Modify
676     unsafe {
677         *ptr.offset(0) += 1;
678         *ptr.offset(1) += 1;
679         *ptr.offset(2) += 1;
680     }
681 
682     // Check that original Java array is unmodified
683     let mut res: [i64; 3] = [0; 3];
684     env.get_long_array_region(&java_array, 0, &mut res).unwrap();
685     assert_eq!(res[0], 1);
686     assert_eq!(res[1], 2);
687     assert_eq!(res[2], 3);
688 
689     auto_ptr.commit().unwrap();
690 
691     // Confirm modification of original Java array
692     env.get_long_array_region(&java_array, 0, &mut res).unwrap();
693     assert_eq!(res[0], 2);
694     assert_eq!(res[1], 3);
695     assert_eq!(res[2], 4);
696 }
697 
698 #[test]
get_array_elements_critical()699 pub fn get_array_elements_critical() {
700     let mut env = attach_current_thread();
701 
702     // Create original Java array
703     let buf: &[u8] = &[1, 2, 3];
704     let java_array = env
705         .byte_array_from_slice(buf)
706         .expect("JNIEnv#byte_array_from_slice must create a java array from slice");
707 
708     // Use a scope to test Drop
709     {
710         // Get primitive array elements auto wrapper
711         let mut auto_ptr = unsafe {
712             env.get_array_elements_critical(&java_array, ReleaseMode::CopyBack)
713                 .unwrap()
714         };
715 
716         // Check array size
717         assert_eq!(auto_ptr.len(), 3);
718 
719         // Convert void pointer to a &[i8] slice, without copy
720         //
721         // # Safety
722         //
723         // We make sure that the slice is dropped before also testing access via `Deref`
724         // (to ensure we don't have aliased references)
725         unsafe {
726             let slice = std::slice::from_raw_parts(auto_ptr.as_ptr(), auto_ptr.len());
727             assert_eq!(slice[0], 1);
728             assert_eq!(slice[1], 2);
729             assert_eq!(slice[2], 3);
730         }
731 
732         // Also check access via `Deref`
733         assert_eq!(auto_ptr[0], 1);
734         assert_eq!(auto_ptr[1], 2);
735         assert_eq!(auto_ptr[2], 3);
736 
737         // Modify via `DerefMut`
738         auto_ptr[0] += 1;
739         auto_ptr[1] += 1;
740         auto_ptr[2] += 1;
741     }
742 
743     // Confirm modification of original Java array
744     let mut res: [i8; 3] = [0; 3];
745     env.get_byte_array_region(&java_array, 0, &mut res).unwrap();
746     assert_eq!(res[0], 2);
747     assert_eq!(res[1], 3);
748     assert_eq!(res[2], 4);
749 }
750 
751 #[test]
get_object_class()752 pub fn get_object_class() {
753     let env = attach_current_thread();
754     let string = env.new_string("test").unwrap();
755     let result = env.get_object_class(string);
756     assert!(result.is_ok());
757     assert!(!result.unwrap().is_null());
758 }
759 
760 #[test]
get_object_class_null_arg()761 pub fn get_object_class_null_arg() {
762     let env = attach_current_thread();
763     let null_obj = JObject::null();
764     let result = env
765         .get_object_class(null_obj)
766         .map_err(|error| matches!(error, Error::NullPtr(_)))
767         .expect_err("JNIEnv#get_object_class should return error for null argument");
768     assert!(result, "ErrorKind::NullPtr expected as error");
769 }
770 
771 #[test]
new_direct_byte_buffer()772 pub fn new_direct_byte_buffer() {
773     let mut env = attach_current_thread();
774     let vec: Vec<u8> = vec![0, 1, 2, 3];
775     let (addr, len) = {
776         // (would use buf.into_raw_parts() on nightly)
777         let buf = vec.leak();
778         (buf.as_mut_ptr(), buf.len())
779     };
780     let result = unsafe { env.new_direct_byte_buffer(addr, len) };
781     assert!(result.is_ok());
782     assert!(!result.unwrap().is_null());
783 }
784 
785 #[test]
new_direct_byte_buffer_invalid_addr()786 pub fn new_direct_byte_buffer_invalid_addr() {
787     let mut env = attach_current_thread();
788     let result = unsafe { env.new_direct_byte_buffer(std::ptr::null_mut(), 5) };
789     assert!(result.is_err());
790 }
791 
792 #[test]
get_direct_buffer_capacity_ok()793 pub fn get_direct_buffer_capacity_ok() {
794     let mut env = attach_current_thread();
795     let vec: Vec<u8> = vec![0, 1, 2, 3];
796     let (addr, len) = {
797         // (would use buf.into_raw_parts() on nightly)
798         let buf = vec.leak();
799         (buf.as_mut_ptr(), buf.len())
800     };
801     let result = unsafe { env.new_direct_byte_buffer(addr, len) }.unwrap();
802     assert!(!result.is_null());
803 
804     let capacity = env.get_direct_buffer_capacity(&result).unwrap();
805     assert_eq!(capacity, 4);
806 }
807 
808 #[test]
get_direct_buffer_capacity_wrong_arg()809 pub fn get_direct_buffer_capacity_wrong_arg() {
810     let env = attach_current_thread();
811     let wrong_obj = unsafe { JByteBuffer::from_raw(env.new_string("wrong").unwrap().into_raw()) };
812     let capacity = env.get_direct_buffer_capacity(&wrong_obj);
813     assert!(capacity.is_err());
814 }
815 
816 #[test]
get_direct_buffer_capacity_null_arg()817 pub fn get_direct_buffer_capacity_null_arg() {
818     let env = attach_current_thread();
819     let result = env.get_direct_buffer_capacity(&JObject::null().into());
820     assert!(result.is_err());
821 }
822 
823 #[test]
get_direct_buffer_address_ok()824 pub fn get_direct_buffer_address_ok() {
825     let mut env = attach_current_thread();
826     let vec: Vec<u8> = vec![0, 1, 2, 3];
827     let (addr, len) = {
828         // (would use buf.into_raw_parts() on nightly)
829         let buf = vec.leak();
830         (buf.as_mut_ptr(), buf.len())
831     };
832     let result = unsafe { env.new_direct_byte_buffer(addr, len) }.unwrap();
833     assert!(!result.is_null());
834 
835     let dest_buffer = env.get_direct_buffer_address(&result).unwrap();
836     assert_eq!(addr, dest_buffer);
837 }
838 
839 #[test]
get_direct_buffer_address_wrong_arg()840 pub fn get_direct_buffer_address_wrong_arg() {
841     let env = attach_current_thread();
842     let wrong_obj: JObject = env.new_string("wrong").unwrap().into();
843     let result = env.get_direct_buffer_address(&wrong_obj.into());
844     assert!(result.is_err());
845 }
846 
847 #[test]
get_direct_buffer_address_null_arg()848 pub fn get_direct_buffer_address_null_arg() {
849     let env = attach_current_thread();
850     let result = env.get_direct_buffer_address(&JObject::null().into());
851     assert!(result.is_err());
852 }
853 
854 // Group test for testing the family of new_PRIMITIVE_array functions with correct arguments
855 #[test]
new_primitive_array_ok()856 pub fn new_primitive_array_ok() {
857     let env = attach_current_thread();
858     const SIZE: jsize = 16;
859 
860     let result = env.new_boolean_array(SIZE);
861     assert!(result.is_ok());
862     assert!(!result.unwrap().is_null());
863 
864     let result = env.new_byte_array(SIZE);
865     assert!(result.is_ok());
866     assert!(!result.unwrap().is_null());
867 
868     let result = env.new_char_array(SIZE);
869     assert!(result.is_ok());
870     assert!(!result.unwrap().is_null());
871 
872     let result = env.new_short_array(SIZE);
873     assert!(result.is_ok());
874     assert!(!result.unwrap().is_null());
875 
876     let result = env.new_int_array(SIZE);
877     assert!(result.is_ok());
878     assert!(!result.unwrap().is_null());
879 
880     let result = env.new_long_array(SIZE);
881     assert!(result.is_ok());
882     assert!(!result.unwrap().is_null());
883 
884     let result = env.new_float_array(SIZE);
885     assert!(result.is_ok());
886     assert!(!result.unwrap().is_null());
887 
888     let result = env.new_double_array(SIZE);
889     assert!(result.is_ok());
890     assert!(!result.unwrap().is_null());
891 }
892 
893 // Group test for testing the family of new_PRIMITIVE_array functions with wrong arguments
894 #[test]
new_primitive_array_wrong()895 pub fn new_primitive_array_wrong() {
896     let mut env = attach_current_thread();
897     const WRONG_SIZE: jsize = -1;
898 
899     let result = env.new_boolean_array(WRONG_SIZE).map(|arr| arr.as_raw());
900     assert_exception(&result, "JNIEnv#new_boolean_array should throw exception");
901     assert_pending_java_exception(&mut env);
902 
903     let result = env.new_byte_array(WRONG_SIZE).map(|arr| arr.as_raw());
904     assert_exception(&result, "JNIEnv#new_byte_array should throw exception");
905     assert_pending_java_exception(&mut env);
906 
907     let result = env.new_char_array(WRONG_SIZE).map(|arr| arr.as_raw());
908     assert_exception(&result, "JNIEnv#new_char_array should throw exception");
909     assert_pending_java_exception(&mut env);
910 
911     let result = env.new_short_array(WRONG_SIZE).map(|arr| arr.as_raw());
912     assert_exception(&result, "JNIEnv#new_short_array should throw exception");
913     assert_pending_java_exception(&mut env);
914 
915     let result = env.new_int_array(WRONG_SIZE).map(|arr| arr.as_raw());
916     assert_exception(&result, "JNIEnv#new_int_array should throw exception");
917     assert_pending_java_exception(&mut env);
918 
919     let result = env.new_long_array(WRONG_SIZE).map(|arr| arr.as_raw());
920     assert_exception(&result, "JNIEnv#new_long_array should throw exception");
921     assert_pending_java_exception(&mut env);
922 
923     let result = env.new_float_array(WRONG_SIZE).map(|arr| arr.as_raw());
924     assert_exception(&result, "JNIEnv#new_float_array should throw exception");
925     assert_pending_java_exception(&mut env);
926 
927     let result = env.new_double_array(WRONG_SIZE).map(|arr| arr.as_raw());
928     assert_exception(&result, "JNIEnv#new_double_array should throw exception");
929     assert_pending_java_exception(&mut env);
930 }
931 
932 #[test]
get_super_class_ok()933 fn get_super_class_ok() {
934     let mut env = attach_current_thread();
935     let result = env.get_superclass(ARRAYLIST_CLASS);
936     assert!(result.is_ok());
937     assert!(result.unwrap().is_some());
938 }
939 
940 #[test]
get_super_class_null()941 fn get_super_class_null() {
942     let mut env = attach_current_thread();
943     let result = env.get_superclass("java/lang/Object");
944     assert!(result.is_ok());
945     assert!(result.unwrap().is_none());
946 }
947 
948 #[test]
convert_byte_array()949 fn convert_byte_array() {
950     let env = attach_current_thread();
951     let src: Vec<u8> = vec![1, 2, 3, 4];
952     let java_byte_array = env.byte_array_from_slice(&src).unwrap();
953 
954     let dest = env.convert_byte_array(java_byte_array);
955     assert!(dest.is_ok());
956     assert_eq!(dest.unwrap(), src);
957 }
958 
959 #[test]
local_ref_null()960 fn local_ref_null() {
961     let env = attach_current_thread();
962     let null_obj = JObject::null();
963 
964     let result = env.new_local_ref::<&JObject>(&null_obj);
965     assert!(result.is_ok());
966     assert!(result.unwrap().is_null());
967 
968     // try to delete null reference
969     let result = env.delete_local_ref(null_obj);
970     assert!(result.is_ok());
971 }
972 
973 #[test]
new_global_ref_null()974 fn new_global_ref_null() {
975     let env = attach_current_thread();
976     let null_obj = JObject::null();
977     let result = env.new_global_ref(null_obj);
978     assert!(result.is_ok());
979     assert!(result.unwrap().is_null());
980 }
981 
982 #[test]
new_weak_ref_null()983 fn new_weak_ref_null() {
984     let env = attach_current_thread();
985     let null_obj = JObject::null();
986     let result = unwrap(env.new_weak_ref(null_obj), &env);
987     assert!(result.is_none());
988 }
989 
990 #[test]
auto_local_null()991 fn auto_local_null() {
992     let env = attach_current_thread();
993     let null_obj = JObject::null();
994     {
995         let auto_ref = AutoLocal::new(null_obj, &env);
996         assert!(auto_ref.is_null());
997     }
998 }
999 
1000 #[test]
short_lifetime_with_local_frame()1001 fn short_lifetime_with_local_frame() {
1002     let mut env = attach_current_thread();
1003     let object = short_lifetime_with_local_frame_sub_fn(&mut env);
1004     assert!(object.is_ok());
1005 }
1006 
short_lifetime_with_local_frame_sub_fn<'local>( env: &'_ mut JNIEnv<'local>, ) -> Result<JObject<'local>, Error>1007 fn short_lifetime_with_local_frame_sub_fn<'local>(
1008     env: &'_ mut JNIEnv<'local>,
1009 ) -> Result<JObject<'local>, Error> {
1010     env.with_local_frame_returning_local(16, |env| {
1011         env.new_object(INTEGER_CLASS, "(I)V", &[JValue::from(5)])
1012     })
1013 }
1014 
1015 #[test]
short_lifetime_list()1016 fn short_lifetime_list() {
1017     let mut env = attach_current_thread();
1018     let first_list_object = short_lifetime_list_sub_fn(&mut env).unwrap();
1019     let value = env.call_method(first_list_object, "intValue", "()I", &[]);
1020     assert_eq!(value.unwrap().i().unwrap(), 1);
1021 }
1022 
short_lifetime_list_sub_fn<'local>( env: &'_ mut JNIEnv<'local>, ) -> Result<JObject<'local>, Error>1023 fn short_lifetime_list_sub_fn<'local>(
1024     env: &'_ mut JNIEnv<'local>,
1025 ) -> Result<JObject<'local>, Error> {
1026     let list_object = env.new_object(ARRAYLIST_CLASS, "()V", &[])?;
1027     let list = JList::from_env(env, &list_object)?;
1028     let element = env.new_object(INTEGER_CLASS, "(I)V", &[JValue::from(1)])?;
1029     list.add(env, &element)?;
1030     short_lifetime_list_sub_fn_get_first_element(env, &list)
1031 }
1032 
short_lifetime_list_sub_fn_get_first_element<'local>( env: &'_ mut JNIEnv<'local>, list: &'_ JList<'local, '_, '_>, ) -> Result<JObject<'local>, Error>1033 fn short_lifetime_list_sub_fn_get_first_element<'local>(
1034     env: &'_ mut JNIEnv<'local>,
1035     list: &'_ JList<'local, '_, '_>,
1036 ) -> Result<JObject<'local>, Error> {
1037     let mut iterator = list.iter(env)?;
1038     Ok(iterator.next(env)?.unwrap())
1039 }
1040 
1041 #[test]
get_object_array_element()1042 fn get_object_array_element() {
1043     let mut env = attach_current_thread();
1044     let array = env
1045         .new_object_array(1, STRING_CLASS, JObject::null())
1046         .unwrap();
1047     assert!(!array.is_null());
1048     assert!(env.get_object_array_element(&array, 0).unwrap().is_null());
1049     let test_str = env.new_string("test").unwrap();
1050     env.set_object_array_element(&array, 0, test_str).unwrap();
1051     assert!(!env.get_object_array_element(&array, 0).unwrap().is_null());
1052 }
1053 
1054 #[test]
throw_new()1055 pub fn throw_new() {
1056     let mut env = attach_current_thread();
1057 
1058     let result = env.throw_new(RUNTIME_EXCEPTION_CLASS, "Test Exception");
1059     assert!(result.is_ok());
1060     assert_pending_java_exception_detailed(
1061         &mut env,
1062         Some(RUNTIME_EXCEPTION_CLASS),
1063         Some("Test Exception"),
1064     );
1065 }
1066 
1067 #[test]
throw_new_fail()1068 pub fn throw_new_fail() {
1069     let mut env = attach_current_thread();
1070 
1071     let result = env.throw_new("java/lang/NonexistentException", "Test Exception");
1072     assert!(result.is_err());
1073     // Just to clear the java.lang.NoClassDefFoundError
1074     assert_pending_java_exception(&mut env);
1075 }
1076 
1077 #[test]
throw_defaults()1078 pub fn throw_defaults() {
1079     let mut env = attach_current_thread();
1080 
1081     test_throwable_descriptor_with_default_type(&mut env, TEST_EXCEPTION_MESSAGE);
1082     test_throwable_descriptor_with_default_type(&mut env, TEST_EXCEPTION_MESSAGE.to_owned());
1083     test_throwable_descriptor_with_default_type(&mut env, JNIString::from(TEST_EXCEPTION_MESSAGE));
1084 }
1085 
1086 #[test]
test_conversion()1087 pub fn test_conversion() {
1088     let env = attach_current_thread();
1089     let orig_obj: JObject = env.new_string("Hello, world!").unwrap().into();
1090 
1091     let obj: JObject = unwrap(env.new_local_ref(&orig_obj), &env);
1092     let string = JString::from(obj);
1093     let actual = JObject::from(string);
1094     assert!(unwrap(env.is_same_object(&orig_obj, actual), &env));
1095 
1096     let global_ref = env.new_global_ref(&orig_obj).unwrap();
1097     assert!(unwrap(env.is_same_object(&orig_obj, global_ref), &env));
1098 
1099     let weak_ref = unwrap(env.new_weak_ref(&orig_obj), &env).expect("weak ref should not be null");
1100     let actual =
1101         unwrap(weak_ref.upgrade_local(&env), &env).expect("weak ref should not have been GC'd");
1102     assert!(unwrap(env.is_same_object(&orig_obj, actual), &env));
1103 
1104     let obj: JObject = unwrap(env.new_local_ref(&orig_obj), &env);
1105     let auto_local = env.auto_local(obj);
1106     assert!(unwrap(env.is_same_object(&orig_obj, auto_local), &env));
1107 }
1108 
1109 #[test]
test_null_get_string()1110 pub fn test_null_get_string() {
1111     let mut env = attach_current_thread();
1112     let s = unsafe { JString::from_raw(std::ptr::null_mut() as _) };
1113     let ret = env.get_string(&s);
1114     assert!(ret.is_err());
1115 }
1116 
1117 #[test]
test_invalid_list_get_string()1118 pub fn test_invalid_list_get_string() {
1119     let mut env = attach_current_thread();
1120 
1121     let class = env.find_class("java/util/List").unwrap();
1122     let class = JString::from(JObject::from(class));
1123     let class = env.auto_local(class);
1124 
1125     let ret = env.get_string(&class);
1126     assert!(ret.is_err());
1127 }
1128 
test_throwable_descriptor_with_default_type<'local, D>(env: &mut JNIEnv<'local>, descriptor: D) where D: Desc<'local, JThrowable<'local>>,1129 fn test_throwable_descriptor_with_default_type<'local, D>(env: &mut JNIEnv<'local>, descriptor: D)
1130 where
1131     D: Desc<'local, JThrowable<'local>>,
1132 {
1133     let result = descriptor.lookup(env);
1134     assert!(result.is_ok());
1135     let exception = result.unwrap();
1136     let exception = exception.as_ref();
1137 
1138     assert_exception_type(env, exception, RUNTIME_EXCEPTION_CLASS);
1139     assert_exception_message(env, exception, TEST_EXCEPTION_MESSAGE);
1140 }
1141 
1142 // Helper method that asserts that result is Error and the cause is JavaException.
assert_exception(res: &Result<jobject, Error>, expect_message: &str)1143 fn assert_exception(res: &Result<jobject, Error>, expect_message: &str) {
1144     assert!(res.is_err());
1145     assert!(res
1146         .as_ref()
1147         .map_err(|error| matches!(error, Error::JavaException))
1148         .expect_err(expect_message));
1149 }
1150 
1151 // Shortcut to `assert_pending_java_exception_detailed()` without checking for expected  type and
1152 // message of exception.
assert_pending_java_exception(env: &mut JNIEnv)1153 fn assert_pending_java_exception(env: &mut JNIEnv) {
1154     assert_pending_java_exception_detailed(env, None, None)
1155 }
1156 
1157 // Helper method that asserts there is a pending Java exception of `expected_type` with
1158 // `expected_message` and clears it if any.
assert_pending_java_exception_detailed( env: &mut JNIEnv, expected_type: Option<&str>, expected_message: Option<&str>, )1159 fn assert_pending_java_exception_detailed(
1160     env: &mut JNIEnv,
1161     expected_type: Option<&str>,
1162     expected_message: Option<&str>,
1163 ) {
1164     assert!(env.exception_check().unwrap());
1165     let exception = env.exception_occurred().expect("Unable to get exception");
1166     env.exception_clear().unwrap();
1167 
1168     if let Some(expected_type) = expected_type {
1169         assert_exception_type(env, &exception, expected_type);
1170     }
1171 
1172     if let Some(expected_message) = expected_message {
1173         assert_exception_message(env, &exception, expected_message);
1174     }
1175 }
1176 
1177 // Asserts that exception is of `expected_type` type.
assert_exception_type(env: &mut JNIEnv, exception: &JThrowable, expected_type: &str)1178 fn assert_exception_type(env: &mut JNIEnv, exception: &JThrowable, expected_type: &str) {
1179     assert!(env.is_instance_of(exception, expected_type).unwrap());
1180 }
1181 
1182 // Asserts that exception's message is `expected_message`.
assert_exception_message(env: &mut JNIEnv, exception: &JThrowable, expected_message: &str)1183 fn assert_exception_message(env: &mut JNIEnv, exception: &JThrowable, expected_message: &str) {
1184     let message = env
1185         .call_method(exception, "getMessage", "()Ljava/lang/String;", &[])
1186         .unwrap()
1187         .l()
1188         .unwrap();
1189     let msg_rust: String = env.get_string(&message.into()).unwrap().into();
1190     assert_eq!(msg_rust, expected_message);
1191 }
1192