java_oxide/refs/
ref_.rs

1use crate::{AssignableTo, Env, Global, JavaDebug, JavaDisplay, Local, ReferenceType};
2use jni_sys::jobject;
3use std::{
4    fmt::{self, Debug, Display, Formatter},
5    marker::PhantomData,
6    mem::transmute,
7    ops::Deref,
8};
9
10/// A non-null, [reference](https://www.ibm.com/docs/en/sdk-java-technology/8?topic=collector-overview-jni-object-references)
11/// to a Java object (+ [Env]).  This may refer to a [Local](crate::Local), [Global](crate::Global), local [Arg](crate::Arg), etc.
12///
13/// `Ref` guarantees not to have a `Drop` implementation. It does not own the JNI reference.
14///
15/// **Not FFI Safe:**  `#[repr(rust)]`, and exact layout is likely to change - depending on exact features used - in the
16/// future.  Specifically, on Android, since we're guaranteed to only have a single ambient VM, we can likely store the
17/// `*const JNIEnv` in thread local storage instead of lugging it around in every [Ref].  Of course, there's no
18/// guarantee that's actually an *optimization*...
19#[repr(C)] // this is NOT for FFI-safety, this is to ensure `cast` methods are sound.
20pub struct Ref<'env, T: ReferenceType> {
21    object: jobject,
22    env: Env<'env>,
23    _class: PhantomData<T>,
24}
25
26impl<'env, T: ReferenceType> std::ops::Receiver for Ref<'env, T> {
27    type Target = T;
28}
29
30impl<'env, T: ReferenceType> Ref<'env, T> {
31    /// Wraps an raw JNI reference.
32    ///
33    /// # Safety
34    ///
35    /// - `object` is a non-null JNI reference, and it must keep valid under `env` before the created `Ref` goes out of scope.
36    ///   This means it should not be a raw pointer managed by [Local] or any other wrapper that deletes it on dropping.
37    /// - `object` references an instance of type `T`.
38    pub unsafe fn from_raw(env: Env<'env>, object: jobject) -> Self {
39        Self {
40            object,
41            env,
42            _class: PhantomData,
43        }
44    }
45
46    /// Gets the [Env] under which the JNI reference is valid.
47    pub fn env(&self) -> Env<'env> {
48        self.env
49    }
50
51    /// Returns the raw JNI reference pointer.
52    pub fn as_raw(&self) -> jobject {
53        self.object
54    }
55
56    /// Returns a new JNI global reference of the same Java object.
57    pub fn as_global(&self) -> Global<T> {
58        let env = self.env();
59        let jnienv = env.as_raw();
60        let object = unsafe { ((**jnienv).v1_2.NewGlobalRef)(jnienv, self.as_raw()) };
61        assert!(!object.is_null());
62        unsafe { Global::from_raw(env.vm(), object) }
63    }
64
65    /// Returns a new JNI local reference of the same Java object.
66    pub fn as_local(&self) -> Local<'env, T> {
67        let env = self.env();
68        let jnienv = env.as_raw();
69        let object = unsafe { ((**jnienv).v1_2.NewLocalRef)(jnienv, self.as_raw()) };
70        assert!(!object.is_null());
71        unsafe { Local::from_raw(self.env(), object) }
72    }
73
74    /// Enters monitored mode or increments the JNI monitor counter in this thread for the referenced Java object.
75    /// See [Monitor] for the limited locking functionality.
76    pub fn as_monitor(&'env self) -> Monitor<'env, T> {
77        Monitor::new(self)
78    }
79
80    /// Tests whether two JNI references refer to the same Java object.
81    pub fn is_same_object<O: ReferenceType>(&self, other: &Ref<'_, O>) -> bool {
82        let jnienv = self.env.as_raw();
83        unsafe { ((**jnienv).v1_2.IsSameObject)(jnienv, self.as_raw(), other.as_raw()) }
84    }
85
86    /// Checks if the Java object can be safely casted to type `U`.
87    pub(crate) fn check_assignable<U: ReferenceType>(&self) -> Result<(), crate::CastError> {
88        let env = self.env();
89        let jnienv = env.as_raw();
90        let class = U::static_with_jni_type(|t| unsafe { env.require_class(t) });
91        let assignable = unsafe { ((**jnienv).v1_2.IsInstanceOf)(jnienv, self.as_raw(), class) };
92        unsafe {
93            ((**jnienv).v1_2.DeleteLocalRef)(jnienv, class);
94        }
95        if assignable {
96            Ok(())
97        } else {
98            Err(crate::CastError)
99        }
100    }
101
102    /// Casts itself to a JNI reference of type `U` forcefully, without the cost of runtime checking.
103    ///
104    /// # Safety
105    ///
106    /// - `self` references an instance of type `U`.
107    pub unsafe fn cast_unchecked<U: ReferenceType>(self) -> Ref<'env, U> {
108        unsafe { transmute(self) }
109    }
110
111    /// Tries to cast itself to a JNI reference of type `U`.
112    pub fn cast<U: ReferenceType>(self) -> Result<Ref<'env, U>, crate::CastError> {
113        self.check_assignable::<U>()?;
114        Ok(unsafe { self.cast_unchecked() })
115    }
116
117    /// Casts itself towards a super class type, without the cost of runtime checking.
118    pub fn upcast<U: ReferenceType>(self) -> Ref<'env, U>
119    where
120        T: AssignableTo<U>,
121    {
122        unsafe { self.cast_unchecked() }
123    }
124
125    /// Casts the borrowed `Ref` to a JNI reference of type `U` forcefully, without the cost of runtime checking.
126    ///
127    /// # Safety
128    ///
129    /// - `self` references an instance of type `U`.
130    pub unsafe fn cast_ref_unchecked<U: ReferenceType>(&self) -> &Ref<'env, U> {
131        unsafe { transmute(self) }
132    }
133
134    /// Tries to cast the borrowed `Ref` to a JNI reference of type `U`.
135    pub fn cast_ref<U: ReferenceType>(&self) -> Result<&Ref<'env, U>, crate::CastError> {
136        self.check_assignable::<U>()?;
137        Ok(unsafe { self.cast_ref_unchecked() })
138    }
139
140    /// Casts the borrowed `Ref` towards a super class type, without the cost of runtime checking.
141    pub fn upcast_ref<U: ReferenceType>(&self) -> &Ref<'env, U>
142    where
143        T: AssignableTo<U>,
144    {
145        unsafe { self.cast_ref_unchecked() }
146    }
147}
148
149impl<'env, T: JavaDebug> Debug for Ref<'env, T> {
150    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
151        T::fmt(self, f)
152    }
153}
154
155impl<'env, T: JavaDisplay> Display for Ref<'env, T> {
156    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
157        T::fmt(self, f)
158    }
159}
160
161/// A borrowed [Ref] of a Java object locked with the JNI monitor mechanism, providing *limited* thread safety.
162///
163/// **It is imposible to be FFI safe.** It is important to drop the monitor or call [Monitor::unlock()] when appropriate.
164///
165/// Limitations:
166///
167/// - It merely blocks other native functions from owning the JNI monitor of the same object before it drops.
168/// - It may not block other native functions from using the corresponding object without entering monitored mode.
169/// - It may not prevent any Java method or block from using this object, even if it is marked as `synchronized`.
170/// - While it is a reentrant lock for the current thread, dead lock is still possible under multi-thread conditions.
171pub struct Monitor<'env, T: ReferenceType> {
172    inner: &'env Ref<'env, T>,
173}
174
175impl<'env, T: ReferenceType> Monitor<'env, T> {
176    fn new(reference: &'env Ref<'env, T>) -> Self {
177        let jnienv = reference.env.as_raw();
178        let result = unsafe { ((**jnienv).v1_2.MonitorEnter)(jnienv, reference.as_raw()) };
179        assert!(result == jni_sys::JNI_OK);
180        Self { inner: reference }
181    }
182
183    /// Decrements the JNI monitor counter indicating the number of times it has entered this monitor.
184    /// If the value of the counter becomes zero, the current thread releases the monitor.
185    pub fn unlock(self) -> &'env Ref<'env, T> {
186        let inner = self.inner;
187        drop(self); // this looks clearer than dropping implicitly
188        inner
189    }
190}
191
192impl<'env, T: ReferenceType> Deref for Monitor<'env, T> {
193    type Target = Ref<'env, T>;
194    fn deref(&self) -> &Self::Target {
195        self.inner
196    }
197}
198
199impl<'env, T: ReferenceType> Drop for Monitor<'env, T> {
200    fn drop(&mut self) {
201        let env = self.inner.env;
202        let jnienv = env.as_raw();
203        let result = unsafe { ((**jnienv).v1_2.MonitorExit)(jnienv, self.inner.as_raw()) };
204        assert!(result == jni_sys::JNI_OK);
205        let exception = unsafe { ((**jnienv).v1_2.ExceptionOccurred)(jnienv) };
206        assert!(
207            exception.is_null(),
208            "exception happened calling JNI MonitorExit, the monitor is probably broken previously"
209        );
210    }
211}