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}