1+ use std:: borrow:: Cow ;
12use std:: collections:: HashMap ;
23use std:: marker:: PhantomData ;
34use std:: ops:: { Bound , RangeBounds } ;
@@ -6,12 +7,12 @@ use std::sync::{LazyLock, OnceLock, RwLock};
67
78use jni_sys:: * ;
89
9- use crate :: { AsArg , Env , JClass , JniType , Local , Ref , ReferenceType , ThrowableType } ;
10+ use crate :: { AsArg , Env , JClass , Local , Ref , ReferenceType , ThrowableType } ;
1011
1112/// A Java Array of some POD-like type such as `bool`, `jbyte`, `jchar`, `jshort`, `jint`, `jlong`, `jfloat`, or `jdouble`.
1213///
1314/// Thread safety of avoiding [race conditions](https://www.ibm.com/docs/en/sdk-java-technology/8?topic=jni-synchronization)
14- /// is not guaranteed.
15+ /// is not guaranteed. JNI `GetPrimitiveArrayCritical` cannot ensure exclusive access to the array, so it is not used here.
1516///
1617/// See also [ObjectArray] for arrays of reference types.
1718///
@@ -29,35 +30,35 @@ pub trait PrimitiveArray<T>: Sized + ReferenceType
2930where
3031 T : Clone + Default ,
3132{
32- /// Uses JNI `New{Type}Array` to create a new java array containing "size" elements.
33+ /// Uses JNI `New{Type}Array` to create a new Java array containing "size" elements.
3334 fn new < ' env > ( env : Env < ' env > , size : usize ) -> Local < ' env , Self > ;
3435
35- /// Uses JNI `GetArrayLength` to get the length of the java array.
36+ /// Uses JNI `GetArrayLength` to get the length of the Java array.
3637 fn len ( self : & Ref < ' _ , Self > ) -> usize ;
3738
38- /// Uses JNI `Get{Type}ArrayRegion` to read the contents of the java array within `[start .. start + elements.len()]`.
39+ /// Uses JNI `Get{Type}ArrayRegion` to read the contents of the Java array within `[start .. start + elements.len()]`.
3940 ///
4041 /// Panics if the index is out of bound.
4142 fn get_region ( self : & Ref < ' _ , Self > , start : usize , elements : & mut [ T ] ) ;
4243
43- /// Uses JNI `Set{Type}ArrayRegion` to set the contents of the java array within `[start .. start + elements.len()]`.
44+ /// Uses JNI `Set{Type}ArrayRegion` to set the contents of the Java array within `[start .. start + elements.len()]`.
4445 ///
4546 /// Panics if the index is out of bound.
4647 fn set_region ( self : & Ref < ' _ , Self > , start : usize , elements : & [ T ] ) ;
4748
48- /// Uses JNI `New{Type}Array` + `Set{Type}ArrayRegion` to create a new java array containing a copy of "elements".
49+ /// Uses JNI `New{Type}Array` + `Set{Type}ArrayRegion` to create a new Java array containing a copy of "elements".
4950 fn new_from < ' env > ( env : Env < ' env > , elements : & [ T ] ) -> Local < ' env , Self > {
5051 let array = Self :: new ( env, elements. len ( ) ) ;
5152 array. set_region ( 0 , elements) ;
5253 array
5354 }
5455
55- /// Uses JNI `GetArrayLength` to get the length of the java array, returns `true` if it is 0.
56+ /// Uses JNI `GetArrayLength` to get the length of the Java array, returns `true` if it is 0.
5657 fn is_empty ( self : & Ref < ' _ , Self > ) -> bool {
5758 self . len ( ) == 0
5859 }
5960
60- /// Uses JNI `GetArrayLength` + `Get{Type}ArrayRegion` to read the contents of the java array within given range
61+ /// Uses JNI `GetArrayLength` + `Get{Type}ArrayRegion` to read the contents of the Java array within given range
6162 /// into a new `Vec`.
6263 ///
6364 /// Panics if the index is out of bound.
8687 vec
8788 }
8889
89- /// Uses JNI `GetArrayLength` + `Get{Type}ArrayRegion` to read the contents of the entire java array into a new `Vec`.
90+ /// Uses JNI `GetArrayLength` + `Get{Type}ArrayRegion` to read the contents of the entire Java array into a new `Vec`.
9091 fn as_vec ( self : & Ref < ' _ , Self > ) -> Vec < T > {
9192 self . get_region_as_vec ( 0 ..self . len ( ) )
9293 }
@@ -98,14 +99,12 @@ macro_rules! primitive_array {
9899 pub enum $name { }
99100
100101 unsafe impl ReferenceType for $name {
102+ fn jni_reference_type_name( ) -> Cow <' static , str > {
103+ Cow :: Borrowed ( $type_str)
104+ }
101105 fn jni_get_class( env: Env ) -> & ' static JClass {
102106 static CLASS_CACHE : OnceLock <JClass > = OnceLock :: new( ) ;
103- CLASS_CACHE . get_or_init( || Self :: static_with_jni_type( |t| unsafe { env. require_class( t) } ) )
104- }
105- }
106- unsafe impl JniType for $name {
107- fn static_with_jni_type<R >( callback: impl FnOnce ( & str ) -> R ) -> R {
108- callback( $type_str)
107+ CLASS_CACHE . get_or_init( || unsafe { env. require_class( & Self :: jni_reference_type_name( ) ) } )
109108 }
110109 }
111110
@@ -116,8 +115,8 @@ macro_rules! primitive_array {
116115 let jnienv = env. as_raw( ) ;
117116 unsafe {
118117 let object = ( ( * * jnienv) . v1_2. $new_array) ( jnienv, size) ;
119- let exception = ( ( * * jnienv ) . v1_2 . ExceptionOccurred ) ( jnienv ) ;
120- assert! ( exception . is_null ( ) ) ; // Only sane exception here is an OOM exception
118+ assert! ( !object . is_null ( ) , "OOM" ) ;
119+ env . exception_check_raw ( ) . expect ( "OOM" ) ; // Only sane exception here is an OOM exception
121120 Local :: from_raw( env, object)
122121 }
123122 }
@@ -203,35 +202,32 @@ primitive_array! { DoubleArray, "[D\0", jdouble { NewDoubleArray SetDoubleArra
203202/// See also [PrimitiveArray] for arrays of reference types.
204203pub struct ObjectArray < T : ReferenceType , E : ThrowableType > ( core:: convert:: Infallible , PhantomData < ( T , E ) > ) ;
205204
206- // NOTE: This is a performance compromise for returning `&'static JClass`,
207- // still faster than non-cached `require_class`.
205+ // NOTE: This is a performance compromise for returning `&'static JClass`, still faster than non-cached `FindClass`.
208206static OBJ_ARR_CLASSES : LazyLock < RwLock < HashMap < String , & ' static JClass > > > =
209207 LazyLock :: new ( || RwLock :: new ( HashMap :: new ( ) ) ) ;
210208
211209unsafe impl < T : ReferenceType , E : ThrowableType > ReferenceType for ObjectArray < T , E > {
212- fn jni_get_class ( env : Env ) -> & ' static JClass {
213- Self :: static_with_jni_type ( |t| {
214- let class_map_reader = OBJ_ARR_CLASSES . read ( ) . unwrap ( ) ;
215- if let Some ( & class) = class_map_reader. get ( t) {
216- class
217- } else {
218- drop ( class_map_reader) ;
219- let class: & ' static JClass = Box :: leak ( Box :: new ( unsafe { env. require_class ( t) } ) ) ;
220- let _ = OBJ_ARR_CLASSES . write ( ) . unwrap ( ) . insert ( t. to_string ( ) , class) ;
221- class
222- }
223- } )
210+ fn jni_reference_type_name ( ) -> Cow < ' static , str > {
211+ let inner = T :: jni_reference_type_name ( ) ;
212+ Cow :: Owned ( format ! ( "[L{};\0 " , inner. trim_end_matches( "\0 " ) ) )
224213 }
225- }
226-
227- unsafe impl < T : ReferenceType , E : ThrowableType > JniType for ObjectArray < T , E > {
228- fn static_with_jni_type < R > ( callback : impl FnOnce ( & str ) -> R ) -> R {
229- T :: static_with_jni_type ( |inner| callback ( format ! ( "[L{};\0 " , inner. trim_end_matches( "\0 " ) ) . as_str ( ) ) )
214+ fn jni_get_class ( env : Env ) -> & ' static JClass {
215+ let t = Self :: jni_reference_type_name ( ) ;
216+ let class_map_reader = OBJ_ARR_CLASSES . read ( ) . unwrap ( ) ;
217+ if let Some ( & class) = class_map_reader. get ( t. as_ref ( ) ) {
218+ class
219+ } else {
220+ drop ( class_map_reader) ;
221+ let class = unsafe { env. require_class ( & t) } ;
222+ let class_leaked: & ' static JClass = Box :: leak ( Box :: new ( class) ) ;
223+ let _ = OBJ_ARR_CLASSES . write ( ) . unwrap ( ) . insert ( t. to_string ( ) , class_leaked) ;
224+ class_leaked
225+ }
230226 }
231227}
232228
233229impl < T : ReferenceType , E : ThrowableType > ObjectArray < T , E > {
234- /// Uses JNI `NewObjectArray` to create a new java object array.
230+ /// Uses JNI `NewObjectArray` to create a new Java object array.
235231 pub fn new < ' env > ( env : Env < ' env > , size : usize ) -> Local < ' env , Self > {
236232 assert ! ( size <= i32 :: MAX as usize ) ; // jsize == jint == i32
237233 let class = T :: jni_get_class ( env) . as_raw ( ) ;
@@ -242,7 +238,7 @@ impl<T: ReferenceType, E: ThrowableType> ObjectArray<T, E> {
242238 let fill = null_mut ( ) ;
243239 ( ( * * env) . v1_2 . NewObjectArray ) ( env, size, class, fill)
244240 } ;
245- // Only sane exception here is an OOM exception
241+ assert ! ( !object . is_null ( ) , " OOM" ) ;
246242 env. exception_check :: < E > ( ) . map_err ( |_| "OOM" ) . unwrap ( ) ;
247243 unsafe { Local :: from_raw ( env, object) }
248244 }
@@ -256,7 +252,7 @@ impl<T: ReferenceType, E: ThrowableType> ObjectArray<T, E> {
256252 }
257253 }
258254
259- /// Uses JNI `NewObjectArray` to create a new java object array of the exact size, then sets its items
255+ /// Uses JNI `NewObjectArray` to create a new Java object array of the exact size, then sets its items
260256 /// with the iterator of JNI (null?) references.
261257 pub fn new_from < ' env > ( env : Env < ' env > , elements : impl ExactSizeIterator < Item = impl AsArg < T > > ) -> Local < ' env , Self > {
262258 let size = elements. len ( ) ;
@@ -269,13 +265,13 @@ impl<T: ReferenceType, E: ThrowableType> ObjectArray<T, E> {
269265 array
270266 }
271267
272- /// Uses JNI `GetArrayLength` to get the length of the java array.
268+ /// Uses JNI `GetArrayLength` to get the length of the Java array.
273269 pub fn len ( self : & Ref < ' _ , Self > ) -> usize {
274270 let env = self . env ( ) . as_raw ( ) ;
275271 unsafe { ( ( * * env) . v1_2 . GetArrayLength ) ( env, self . as_raw ( ) ) as usize }
276272 }
277273
278- /// Uses JNI `GetArrayLength` to get the length of the java array, returns `true` if it is 0.
274+ /// Uses JNI `GetArrayLength` to get the length of the Java array, returns `true` if it is 0.
279275 pub fn is_empty ( self : & Ref < ' _ , Self > ) -> bool {
280276 self . len ( ) == 0
281277 }
0 commit comments