use std::{borrow::Cow, ops::Deref};
use jni_sys::jobject;
use log::{debug, warn};
use crate::{
AttachConfig, JavaVM,
env::Env,
objects::{JClass, JObject, LoaderContext},
strings::JNIStr,
sys,
};
#[cfg(doc)]
use crate::objects::Weak;
use super::Reference;
#[repr(transparent)]
#[derive(Debug)]
pub struct Global<T>
where
T: Into<JObject<'static>>
+ AsRef<JObject<'static>>
+ Default
+ Reference
+ Send
+ Sync
+ 'static,
{
obj: T,
}
#[deprecated(
since = "0.22.0",
note = r#"Since 0.22, `GlobalRef` has been renamed to `Global`."#
)]
pub type GlobalRef<T> = Global<T>;
unsafe impl<T> Send for Global<T> where
T: Into<JObject<'static>>
+ AsRef<JObject<'static>>
+ Default
+ Reference
+ Send
+ Sync
+ 'static
{
}
unsafe impl<T> Sync for Global<T> where
T: Into<JObject<'static>>
+ AsRef<JObject<'static>>
+ Default
+ Reference
+ Send
+ Sync
+ 'static
{
}
impl<T> Default for Global<T>
where
T: Into<JObject<'static>>
+ AsRef<JObject<'static>>
+ Default
+ Reference
+ Send
+ Sync
+ 'static,
{
fn default() -> Self {
Self::null()
}
}
impl<T, U> AsRef<U> for Global<T>
where
T: AsRef<U>
+ Into<JObject<'static>>
+ AsRef<JObject<'static>>
+ Default
+ Reference
+ Send
+ Sync,
{
fn as_ref(&self) -> &U {
self.obj.as_ref()
}
}
impl<T> Deref for Global<T>
where
T: Into<JObject<'static>>
+ AsRef<JObject<'static>>
+ Default
+ Reference
+ Send
+ Sync
+ 'static,
{
type Target = T;
fn deref(&self) -> &Self::Target {
&self.obj
}
}
impl<T> Global<T>
where
T: Into<JObject<'static>>
+ AsRef<JObject<'static>>
+ Default
+ Reference
+ Send
+ Sync
+ 'static,
{
pub(crate) unsafe fn new(_env: &Env, obj: T) -> Self {
Self { obj }
}
pub fn null() -> Self {
Self { obj: T::default() }
}
pub fn into_raw(mut self) -> sys::jobject {
let obj = std::mem::take(&mut self.obj); std::mem::forget(self); let obj: JObject = obj.into();
obj.into_raw()
}
#[doc(hidden)]
#[deprecated(
since = "0.22.0",
note = r#"Replaced with `into_raw` since it is infallible"#
)]
pub fn try_into_raw(self) -> std::result::Result<sys::jobject, std::convert::Infallible> {
Ok(self.into_raw())
}
pub fn as_obj(&self) -> &JObject<'static> {
self.as_ref()
}
}
impl<T> Drop for Global<T>
where
T: Into<JObject<'static>>
+ AsRef<JObject<'static>>
+ Default
+ Reference
+ Send
+ Sync
+ 'static,
{
fn drop(&mut self) {
let obj = std::mem::take(&mut self.obj);
if !obj.is_null() {
let vm = JavaVM::singleton().expect("JavaVM singleton uninitialized");
let exception_safe_delete = || -> jni::errors::Result<()> {
let mut scope = jni::ScopeToken::default();
let mut guard = unsafe {
vm.attach_current_thread_guard(|| AttachConfig::new().scoped(true), &mut scope)?
};
let env = guard.borrow_env_mut();
if env.owns_attachment() {
warn!(
"A JNI global reference was dropped on a thread that is not attached. This will cause a performance problem if it happens frequently. For more information, see the documentation for `jni::objects::Global`."
);
}
unsafe {
ex_safe_jni_call_no_post_check_ex!(env, v1_1, DeleteGlobalRef, obj.as_raw());
}
Ok(())
};
if let Err(err) = exception_safe_delete() {
debug!("error dropping global ref: {:#?}", err);
}
}
}
}
unsafe impl<T> Reference for Global<T>
where
T: Into<JObject<'static>> + AsRef<JObject<'static>> + Default + Reference + Send + Sync,
{
type Kind<'env> = T::Kind<'env>;
type GlobalKind = T::GlobalKind;
fn as_raw(&self) -> jobject {
self.obj.as_raw()
}
fn class_name() -> Cow<'static, JNIStr> {
T::class_name()
}
fn lookup_class<'caller>(
env: &Env<'_>,
loader_context: &LoaderContext,
) -> crate::errors::Result<impl Deref<Target = Global<JClass<'static>>> + 'caller> {
T::lookup_class(env, loader_context)
}
unsafe fn kind_from_raw<'env>(local_ref: jobject) -> Self::Kind<'env> {
unsafe { T::kind_from_raw::<'env>(local_ref) }
}
unsafe fn global_kind_from_raw(global_ref: jobject) -> Self::GlobalKind {
unsafe { T::global_kind_from_raw(global_ref) }
}
}
#[test]
fn test_global_ref_send() {
fn assert_send<T: Send>() {}
assert_send::<Global<JObject<'static>>>();
}
#[test]
fn test_global_ref_sync() {
fn assert_sync<T: Sync>() {}
assert_sync::<Global<JObject<'static>>>();
}