• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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