1 use log::error; 2 use std::ptr::NonNull; 3 4 use crate::sys::{jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jshort}; 5 use crate::wrapper::objects::ReleaseMode; 6 use crate::{errors::*, sys, JNIEnv}; 7 8 use super::JPrimitiveArray; 9 10 #[cfg(doc)] 11 use super::JByteArray; 12 13 mod type_array_sealed { 14 use crate::sys::{jarray, jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jshort}; 15 use crate::{errors::*, JNIEnv}; 16 use std::ptr::NonNull; 17 18 /// Trait to define type array access/release 19 /// 20 /// # Safety 21 /// 22 /// The methods of this trait must uphold the invariants described in [`JNIEnv::unsafe_clone`] when 23 /// using the provided [`JNIEnv`]. 24 /// 25 /// The `get` method must return a valid pointer to the beginning of the JNI array. 26 /// 27 /// The `release` method must not invalidate the `ptr` if the `mode` is [`sys::JNI_COMMIT`]. 28 pub unsafe trait TypeArraySealed: Copy { 29 /// getter 30 /// 31 /// # Safety 32 /// 33 /// `array` must be a valid pointer to an `Array` object, or `null` 34 /// 35 /// The caller is responsible for passing the returned pointer to [`release`], along 36 /// with the same `env` and `array` reference (which needs to still be valid) get(env: &mut JNIEnv, array: jarray, is_copy: &mut jboolean) -> Result<*mut Self>37 unsafe fn get(env: &mut JNIEnv, array: jarray, is_copy: &mut jboolean) 38 -> Result<*mut Self>; 39 40 /// releaser 41 /// 42 /// # Safety 43 /// 44 /// `ptr` must have been previously returned by the `get` function. 45 /// 46 /// If `mode` is not [`sys::JNI_COMMIT`], `ptr` must not be used again after calling this 47 /// function. release( env: &mut JNIEnv, array: jarray, ptr: NonNull<Self>, mode: i32, ) -> Result<()>48 unsafe fn release( 49 env: &mut JNIEnv, 50 array: jarray, 51 ptr: NonNull<Self>, 52 mode: i32, 53 ) -> Result<()>; 54 } 55 56 // TypeArray builder 57 macro_rules! type_array { 58 ( $jni_type:ty, $jni_get:tt, $jni_release:tt ) => { 59 /// $jni_type array access/release impl 60 unsafe impl TypeArraySealed for $jni_type { 61 /// Get Java $jni_type array 62 unsafe fn get( 63 env: &mut JNIEnv, 64 array: jarray, 65 is_copy: &mut jboolean, 66 ) -> Result<*mut Self> { 67 let internal = env.get_native_interface(); 68 // Even though this method may throw OoME, use `jni_unchecked` 69 // instead of `jni_non_null_call` to remove (a slight) overhead 70 // of exception checking. An error will still be detected as a `null` 71 // result inside AutoElements ctor. Also, modern Hotspot in case of lack 72 // of memory will return null and won't throw an exception: 73 // https://sourcegraph.com/github.com/openjdk/jdk/-/blob/src/hotspot/share/memory/allocation.hpp#L488-489 74 let res = jni_unchecked!(internal, $jni_get, array, is_copy); 75 Ok(res) 76 } 77 78 /// Release Java $jni_type array 79 unsafe fn release( 80 env: &mut JNIEnv, 81 array: jarray, 82 ptr: NonNull<Self>, 83 mode: i32, 84 ) -> Result<()> { 85 let internal = env.get_native_interface(); 86 jni_unchecked!(internal, $jni_release, array, ptr.as_ptr(), mode as i32); 87 Ok(()) 88 } 89 } 90 }; 91 } 92 93 type_array!(jint, GetIntArrayElements, ReleaseIntArrayElements); 94 type_array!(jlong, GetLongArrayElements, ReleaseLongArrayElements); 95 type_array!(jbyte, GetByteArrayElements, ReleaseByteArrayElements); 96 type_array!( 97 jboolean, 98 GetBooleanArrayElements, 99 ReleaseBooleanArrayElements 100 ); 101 type_array!(jchar, GetCharArrayElements, ReleaseCharArrayElements); 102 type_array!(jshort, GetShortArrayElements, ReleaseShortArrayElements); 103 type_array!(jfloat, GetFloatArrayElements, ReleaseFloatArrayElements); 104 type_array!(jdouble, GetDoubleArrayElements, ReleaseDoubleArrayElements); 105 } 106 107 /// A sealed trait to define type array access/release for primitive JNI types 108 pub trait TypeArray: type_array_sealed::TypeArraySealed {} 109 110 impl TypeArray for jint {} 111 impl TypeArray for jlong {} 112 impl TypeArray for jbyte {} 113 impl TypeArray for jboolean {} 114 impl TypeArray for jchar {} 115 impl TypeArray for jshort {} 116 impl TypeArray for jfloat {} 117 impl TypeArray for jdouble {} 118 119 /// Auto-release wrapper for a mutable pointer to the elements of a [`JPrimitiveArray`] 120 /// (such as [`JByteArray`]) 121 /// 122 /// This type is used to wrap pointers returned by `Get<Type>ArrayElements` 123 /// and ensure the pointer is released via `Release<Type>ArrayElements` when dropped. 124 pub struct AutoElements<'local, 'other_local, 'array, T: TypeArray> { 125 array: &'array JPrimitiveArray<'other_local, T>, 126 len: usize, 127 ptr: NonNull<T>, 128 mode: ReleaseMode, 129 is_copy: bool, 130 env: JNIEnv<'local>, 131 } 132 133 impl<'local, 'other_local, 'array, T: TypeArray> AutoElements<'local, 'other_local, 'array, T> { 134 /// # Safety 135 /// 136 /// `len` must be the correct length (number of elements) of the given `array` new_with_len( env: &mut JNIEnv<'local>, array: &'array JPrimitiveArray<'other_local, T>, len: usize, mode: ReleaseMode, ) -> Result<Self>137 pub(crate) unsafe fn new_with_len( 138 env: &mut JNIEnv<'local>, 139 array: &'array JPrimitiveArray<'other_local, T>, 140 len: usize, 141 mode: ReleaseMode, 142 ) -> Result<Self> { 143 // Safety: The cloned `JNIEnv` will not be used to create any local references. It will be 144 // passed to the methods of the `TypeArray` implementation, but that trait is `unsafe` and 145 // implementations are required to uphold the invariants of `unsafe_clone`. 146 let mut env = unsafe { env.unsafe_clone() }; 147 148 let mut is_copy: jboolean = 0xff; 149 let ptr = unsafe { T::get(&mut env, array.as_raw(), &mut is_copy) }?; 150 Ok(AutoElements { 151 array, 152 len, 153 ptr: NonNull::new(ptr).ok_or(Error::NullPtr("Non-null ptr expected"))?, 154 mode, 155 is_copy: is_copy == sys::JNI_TRUE, 156 env, 157 }) 158 } 159 new( env: &mut JNIEnv<'local>, array: &'array JPrimitiveArray<'other_local, T>, mode: ReleaseMode, ) -> Result<Self>160 pub(crate) fn new( 161 env: &mut JNIEnv<'local>, 162 array: &'array JPrimitiveArray<'other_local, T>, 163 mode: ReleaseMode, 164 ) -> Result<Self> { 165 let len = env.get_array_length(array)? as usize; 166 unsafe { Self::new_with_len(env, array, len, mode) } 167 } 168 169 /// Get a reference to the wrapped pointer as_ptr(&self) -> *mut T170 pub fn as_ptr(&self) -> *mut T { 171 self.ptr.as_ptr() 172 } 173 174 /// Commits the changes to the array, if it is a copy commit(&mut self) -> Result<()>175 pub fn commit(&mut self) -> Result<()> { 176 unsafe { self.release_array_elements(sys::JNI_COMMIT) } 177 } 178 179 /// Calls the release function. 180 /// 181 /// # Safety 182 /// 183 /// `mode` must be a valid parameter to the JNI `Release<PrimitiveType>ArrayElements`' `mode` 184 /// parameter. 185 /// 186 /// If `mode` is not [`sys::JNI_COMMIT`], then `self.ptr` must not have already been released. release_array_elements(&mut self, mode: i32) -> Result<()>187 unsafe fn release_array_elements(&mut self, mode: i32) -> Result<()> { 188 T::release(&mut self.env, self.array.as_raw(), self.ptr, mode) 189 } 190 191 /// Don't copy back the changes to the array on release (if it is a copy). 192 /// 193 /// This has no effect if the array is not a copy. 194 /// 195 /// This method is useful to change the release mode of an array originally created 196 /// with `ReleaseMode::CopyBack`. discard(&mut self)197 pub fn discard(&mut self) { 198 self.mode = ReleaseMode::NoCopyBack; 199 } 200 201 /// Indicates if the array is a copy or not is_copy(&self) -> bool202 pub fn is_copy(&self) -> bool { 203 self.is_copy 204 } 205 206 /// Returns the array length (number of elements) len(&self) -> usize207 pub fn len(&self) -> usize { 208 self.len 209 } 210 211 /// Returns true if the vector contains no elements. is_empty(&self) -> bool212 pub fn is_empty(&self) -> bool { 213 self.len == 0 214 } 215 } 216 217 impl<'local, 'other_local, 'array, T: TypeArray> 218 AsRef<AutoElements<'local, 'other_local, 'array, T>> 219 for AutoElements<'local, 'other_local, 'array, T> 220 { as_ref(&self) -> &AutoElements<'local, 'other_local, 'array, T>221 fn as_ref(&self) -> &AutoElements<'local, 'other_local, 'array, T> { 222 self 223 } 224 } 225 226 impl<'local, 'other_local, 'array, T: TypeArray> Drop 227 for AutoElements<'local, 'other_local, 'array, T> 228 { drop(&mut self)229 fn drop(&mut self) { 230 // Safety: `self.mode` is valid and the array has not yet been released. 231 let res = unsafe { self.release_array_elements(self.mode as i32) }; 232 233 match res { 234 Ok(()) => {} 235 Err(e) => error!("error releasing array: {:#?}", e), 236 } 237 } 238 } 239 240 impl<'local, 'other_local, 'array, T: TypeArray> 241 From<&AutoElements<'local, 'other_local, 'array, T>> for *mut T 242 { from(other: &AutoElements<T>) -> *mut T243 fn from(other: &AutoElements<T>) -> *mut T { 244 other.as_ptr() 245 } 246 } 247 248 impl<'local, 'other_local, 'array, T: TypeArray> std::ops::Deref 249 for AutoElements<'local, 'other_local, 'array, T> 250 { 251 type Target = [T]; 252 deref(&self) -> &Self::Target253 fn deref(&self) -> &Self::Target { 254 unsafe { std::slice::from_raw_parts(self.ptr.as_ptr(), self.len) } 255 } 256 } 257 258 impl<'local, 'other_local, 'array, T: TypeArray> std::ops::DerefMut 259 for AutoElements<'local, 'other_local, 'array, T> 260 { deref_mut(&mut self) -> &mut Self::Target261 fn deref_mut(&mut self) -> &mut Self::Target { 262 unsafe { std::slice::from_raw_parts_mut(self.ptr.as_mut(), self.len) } 263 } 264 } 265