jni/wrapper/objects/
auto_local.rs

1use std::{
2    mem::ManuallyDrop,
3    ops::{Deref, DerefMut},
4    ptr,
5};
6
7use log::debug;
8
9use 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)]
27pub struct AutoLocal<'local, T>
28where
29    T: Into<JObject<'local>>,
30{
31    obj: ManuallyDrop<T>,
32    env: JNIEnv<'local>,
33}
34
35impl<'local, T> AutoLocal<'local, T>
36where
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.
47    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.
65    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
97impl<'local, T> Drop for AutoLocal<'local, T>
98where
99    T: Into<JObject<'local>>,
100{
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
120impl<'local, T, U> AsRef<U> for AutoLocal<'local, T>
121where
122    T: AsRef<U> + Into<JObject<'local>>,
123{
124    fn as_ref(&self) -> &U {
125        self.obj.as_ref()
126    }
127}
128
129impl<'local, T, U> AsMut<U> for AutoLocal<'local, T>
130where
131    T: AsMut<U> + Into<JObject<'local>>,
132{
133    fn as_mut(&mut self) -> &mut U {
134        self.obj.as_mut()
135    }
136}
137
138impl<'local, T> Deref for AutoLocal<'local, T>
139where
140    T: Into<JObject<'local>>,
141{
142    type Target = T;
143
144    fn deref(&self) -> &Self::Target {
145        &self.obj
146    }
147}
148
149impl<'local, T> DerefMut for AutoLocal<'local, T>
150where
151    T: Into<JObject<'local>>,
152{
153    fn deref_mut(&mut self) -> &mut Self::Target {
154        &mut self.obj
155    }
156}