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