java_oxide/refs/
local.rs

1use crate::{AssignableTo, Env, Global, JavaDebug, JavaDisplay, Ref, ReferenceType, Return};
2use jni_sys::*;
3use std::{
4    fmt::{self, Debug, Display, Formatter},
5    mem::transmute,
6    ops::Deref,
7};
8
9/// A [Local](https://www.ibm.com/docs/en/sdk-java-technology/8?topic=collector-overview-jni-object-references),
10/// non-null, reference to a Java object (+ [Env]) limited to the current thread/stack.
11///
12/// Including the `env` allows for the convenient execution of methods without having to individually pass the `env` as
13/// an argument to each and every one.  Since this is limited to the current thread/stack, these cannot be sanely stored
14/// in any kind of static storage, nor shared between threads - instead use a [Global] if you need to do either.
15///
16/// Will `DeleteLocalRef` when dropped, invalidating the `jobject` but ensuring threads that rarely or never return to
17/// Java may run without being guaranteed to eventually exhaust their local reference limit.  If this is not desired,
18/// convert to a plain [Ref] with:
19///
20/// ```rust,no_run
21/// # use java_oxide::*;
22/// # fn example<T: ReferenceType>(local: Local<T>) {
23/// let local = Local::leak(local);
24/// # }
25/// ```
26///
27/// **Not FFI Safe:**  `#[repr(rust)]`, and exact layout is likely to change - depending on exact features used - in the
28/// future.  Specifically, on Android, since we're guaranteed to only have a single ambient VM, we can likely store the
29/// `*const JNIEnv` in thread local storage instead of lugging it around in every [Local].  Of course, there's no
30/// guarantee that's actually an *optimization*...
31#[repr(C)] // this is NOT for FFI-safety, this is to ensure `cast` methods are sound.
32pub struct Local<'env, T: ReferenceType> {
33    ref_: Ref<'env, T>,
34}
35
36impl<'env, T: ReferenceType> Local<'env, T> {
37    /// Wraps an owned raw JNI local reference, taking the ownership.
38    ///
39    /// # Safety
40    ///
41    /// - `object` must be an owned non-null JNI local reference that belongs to `env`,
42    ///   not to be deleted by another wrapper.
43    /// - `object` references an instance of type `T`.
44    pub unsafe fn from_raw(env: Env<'env>, object: jobject) -> Self {
45        Self {
46            ref_: unsafe { Ref::from_raw(env, object) },
47        }
48    }
49
50    /// Gets the [Env] under which the JNI reference is valid.
51    pub fn env(&self) -> Env<'env> {
52        self.ref_.env()
53    }
54
55    /// Returns the raw JNI reference pointer.
56    pub fn as_raw(&self) -> jobject {
57        self.ref_.as_raw()
58    }
59
60    /// Leaks the `Local` and turns it into a raw pointer, preserving the ownership of one JNI
61    /// local reference; prevents `DeleteLocalRef` from being called on dropping. See [Local::leak].
62    pub fn into_raw(self) -> jobject {
63        let object = self.ref_.as_raw();
64        std::mem::forget(self); // Don't allow `Local` to delete it
65        object
66    }
67
68    /// Leaks the `Local`, prevents `DeleteLocalRef` from being called on dropping.
69    ///
70    /// If the current thread is a Java thread, it will be freed when the control flow returns
71    /// to Java; otherwise it will be freed when the native thread exits.
72    ///
73    /// NOTE: some JVM implementations have a strict limitation of local reference capacity,
74    /// an uncatchable error will be thrown if the capacity is full.
75    pub fn leak(self) -> Ref<'env, T> {
76        unsafe { Ref::from_raw(self.env(), self.into_raw()) }
77    }
78
79    /// Returns a new JNI global reference of the same Java object.
80    pub fn as_global(&self) -> Global<T> {
81        self.as_ref().as_global()
82    }
83
84    /// Creates and leaks a new local reference to be returned from the JNI `extern` callback function.
85    /// It will be freed as soon as the control flow returns to Java.
86    pub fn as_return(&self) -> Return<'env, T> {
87        self.clone().into_return()
88    }
89
90    /// Leaks the local reference to be returned from the JNI `extern` callback function.
91    /// It will be freed as soon as the control flow returns to Java.
92    pub fn into_return(self) -> Return<'env, T> {
93        unsafe { Return::from_raw(self.into_raw()) }
94    }
95
96    /// Tries to cast itself to a JNI reference of type `U`.
97    pub fn cast<U: ReferenceType>(self) -> Result<Local<'env, U>, crate::CastError> {
98        self.as_ref().check_assignable::<U>()?;
99        // Memory layout of the inner `Ref<'env, U>` is the same as `Ref<'env, T>`.
100        Ok(unsafe { transmute::<Local<'_, T>, Local<'_, U>>(self) })
101    }
102
103    /// Casts itself towards a super class type, without the cost of runtime checking.
104    pub fn upcast<U: ReferenceType>(self) -> Local<'env, U>
105    where
106        T: AssignableTo<U>,
107    {
108        // Memory layout of the inner `Ref<'env, U>` is the same as `Ref<'env, T>`.
109        unsafe { transmute(self) }
110    }
111}
112
113impl<'env, T: ReferenceType> From<Ref<'env, T>> for Local<'env, T> {
114    fn from(x: Ref<'env, T>) -> Self {
115        x.as_local()
116    }
117}
118
119impl<'env, T: ReferenceType> From<&Local<'env, T>> for Local<'env, T> {
120    fn from(x: &Local<'env, T>) -> Self {
121        x.clone()
122    }
123}
124
125impl<'env, T: ReferenceType> From<&Ref<'env, T>> for Local<'env, T> {
126    fn from(x: &Ref<'env, T>) -> Self {
127        x.as_local()
128    }
129}
130
131// NOTE: `AsRef` would become **unsound** if `Ref` should implement `Copy` or `Clone`.
132//
133// It is possible to have a safe `pub fn as_ref(&'env self) -> Ref<'env, T>` outside of
134// `AsRef` trait, however a borrowed `Ref` is returned for the convenience of use.
135impl<'env, T: ReferenceType> AsRef<Ref<'env, T>> for Local<'env, T> {
136    fn as_ref(&self) -> &Ref<'env, T> {
137        &self.ref_
138    }
139}
140
141// NOTE: `Deref` would become **unsound** if `Ref` should implement `Copy` or `Clone`.
142impl<'env, T: ReferenceType> Deref for Local<'env, T> {
143    type Target = Ref<'env, T>;
144    fn deref(&self) -> &Self::Target {
145        &self.ref_
146    }
147}
148
149impl<'env, T: ReferenceType> Clone for Local<'env, T> {
150    fn clone(&self) -> Self {
151        let env = self.env().as_raw();
152        let object = unsafe { ((**env).v1_2.NewLocalRef)(env, self.as_raw()) };
153        assert!(!object.is_null());
154        unsafe { Self::from_raw(self.env(), object) }
155    }
156}
157
158impl<'env, T: ReferenceType> Drop for Local<'env, T> {
159    fn drop(&mut self) {
160        let env = self.env().as_raw();
161        unsafe { ((**env).v1_2.DeleteLocalRef)(env, self.as_raw()) }
162    }
163}
164
165impl<'env, T: JavaDebug> Debug for Local<'env, T> {
166    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
167        T::fmt(self, f)
168    }
169}
170
171impl<'env, T: JavaDisplay> Display for Local<'env, T> {
172    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
173        T::fmt(self, f)
174    }
175}