1 use std::{ 2 mem::ManuallyDrop, 3 ops::{Deref, DerefMut}, 4 ptr, 5 }; 6 7 use log::debug; 8 9 use crate::{objects::JObject, JNIEnv}; 10 11 /// Auto-delete wrapper for local refs. 12 /// 13 /// Anything passed to a foreign method _and_ returned from JNI methods is considered a local ref 14 /// unless it is specified otherwise. 15 /// These refs are automatically deleted once the foreign method exits, but it's possible that 16 /// they may reach the JVM-imposed limit before that happens. 17 /// 18 /// This wrapper provides automatic local ref deletion when it goes out of 19 /// scope. 20 /// 21 /// See also the [JNI specification][spec-references] for details on referencing Java objects 22 /// and some [extra information][android-jni-references]. 23 /// 24 /// [spec-references]: https://docs.oracle.com/en/java/javase/12/docs/specs/jni/design.html#referencing-java-objects 25 /// [android-jni-references]: https://developer.android.com/training/articles/perf-jni#local-and-global-references 26 #[derive(Debug)] 27 pub struct AutoLocal<'local, T> 28 where 29 T: Into<JObject<'local>>, 30 { 31 obj: ManuallyDrop<T>, 32 env: JNIEnv<'local>, 33 } 34 35 impl<'local, T> AutoLocal<'local, T> 36 where 37 // Note that this bound prevents `AutoLocal` from wrapping a `GlobalRef`, which implements 38 // `AsRef<JObject<'static>>` but *not* `Into<JObject<'static>>`. This is good, because trying 39 // to delete a global reference as though it were local would cause undefined behavior. 40 T: Into<JObject<'local>>, 41 { 42 /// Creates a new auto-delete wrapper for a local ref. 43 /// 44 /// Once this wrapper goes out of scope, the `delete_local_ref` will be 45 /// called on the object. While wrapped, the object can be accessed via 46 /// the `Deref` impl. new(obj: T, env: &JNIEnv<'local>) -> Self47 pub fn new(obj: T, env: &JNIEnv<'local>) -> Self { 48 // Safety: The cloned `JNIEnv` will not be used to create any local references, only to 49 // delete one. 50 let env = unsafe { env.unsafe_clone() }; 51 52 AutoLocal { 53 obj: ManuallyDrop::new(obj), 54 env, 55 } 56 } 57 58 /// Forget the wrapper, returning the original object. 59 /// 60 /// This prevents `delete_local_ref` from being called when the `AutoLocal` 61 /// gets 62 /// dropped. You must either remember to delete the local ref manually, or 63 /// be 64 /// ok with it getting deleted once the foreign method returns. forget(self) -> T65 pub fn forget(self) -> T { 66 // We need to move `self.obj` out of `self`. Normally that's trivial, but moving out of a 67 // type with a `Drop` implementation is not allowed. We'll have to do it manually (and 68 // carefully) with `unsafe`. 69 // 70 // This could be done without `unsafe` by adding `where T: Default` and using 71 // `std::mem::replace` to extract `self.obj`, but doing it this way avoids unnecessarily 72 // running the drop routine on `self`. 73 74 // Before we mutilate `self`, make sure its drop code will not be automatically run. That 75 // would cause undefined behavior. 76 let mut self_md = ManuallyDrop::new(self); 77 78 unsafe { 79 // Drop the `JNIEnv` in place. As of this writing, that's a no-op, but if `JNIEnv` 80 // gains any drop code in the future, this will run it. 81 // 82 // Safety: The `&mut` proves that `self_md.env` is valid and not aliased. It is not 83 // accessed again after this point. It is wrapped inside `ManuallyDrop`, and will 84 // therefore not be dropped twice. 85 ptr::drop_in_place(&mut self_md.env); 86 87 // Move `obj` out of `self` and return it. 88 // 89 // Safety: The `&mut` proves that `self_md.obj` is valid and not aliased. It is not 90 // accessed again after this point. It is wrapped inside `ManuallyDrop`, and will 91 // therefore not be dropped after it is moved. 92 ptr::read(&*self_md.obj) 93 } 94 } 95 } 96 97 impl<'local, T> Drop for AutoLocal<'local, T> 98 where 99 T: Into<JObject<'local>>, 100 { drop(&mut self)101 fn drop(&mut self) { 102 // Extract the local reference from `self.obj` so that we can delete it. 103 // 104 // This is needed because it is not allowed to move out of `self` during drop. A safe 105 // alternative would be to wrap `self.obj` in `Option`, but that would incur a run-time 106 // performance penalty from constantly checking if it's `None`. 107 // 108 // Safety: `self.obj` is not used again after this `take` call. 109 let obj = unsafe { ManuallyDrop::take(&mut self.obj) }; 110 111 // Delete the extracted local reference. 112 let res = self.env.delete_local_ref(obj); 113 match res { 114 Ok(()) => {} 115 Err(e) => debug!("error dropping global ref: {:#?}", e), 116 } 117 } 118 } 119 120 impl<'local, T, U> AsRef<U> for AutoLocal<'local, T> 121 where 122 T: AsRef<U> + Into<JObject<'local>>, 123 { as_ref(&self) -> &U124 fn as_ref(&self) -> &U { 125 self.obj.as_ref() 126 } 127 } 128 129 impl<'local, T, U> AsMut<U> for AutoLocal<'local, T> 130 where 131 T: AsMut<U> + Into<JObject<'local>>, 132 { as_mut(&mut self) -> &mut U133 fn as_mut(&mut self) -> &mut U { 134 self.obj.as_mut() 135 } 136 } 137 138 impl<'local, T> Deref for AutoLocal<'local, T> 139 where 140 T: Into<JObject<'local>>, 141 { 142 type Target = T; 143 deref(&self) -> &Self::Target144 fn deref(&self) -> &Self::Target { 145 &self.obj 146 } 147 } 148 149 impl<'local, T> DerefMut for AutoLocal<'local, T> 150 where 151 T: Into<JObject<'local>>, 152 { deref_mut(&mut self) -> &mut Self::Target153 fn deref_mut(&mut self) -> &mut Self::Target { 154 &mut self.obj 155 } 156 } 157