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