use std::{borrow::Cow, ops::Deref};
use jni_sys::jobject;
use log::{debug, warn};
use crate::{
AttachConfig, JavaVM,
env::Env,
errors::{Error, Result},
objects::{Global, JClass, JObject, LoaderContext},
strings::JNIStr,
sys,
};
use super::Reference;
#[repr(transparent)]
#[derive(Debug)]
pub struct Weak<T>
where
T: Into<JObject<'static>> + AsRef<JObject<'static>> + Default + Reference + Send + Sync,
{
obj: T,
}
#[deprecated(
since = "0.22.0",
note = r#"Since 0.22, `WeakRef` has been renamed to `Weak`."#
)]
pub type WeakRef<T> = Weak<T>;
unsafe impl<T> Send for Weak<T> where
T: Into<JObject<'static>> + AsRef<JObject<'static>> + Default + Reference + Send + Sync
{
}
unsafe impl<T> Sync for Weak<T> where
T: Into<JObject<'static>> + AsRef<JObject<'static>> + Default + Reference + Send + Sync
{
}
impl<T> Default for Weak<T>
where
T: Into<JObject<'static>> + AsRef<JObject<'static>> + Default + Reference + Send + Sync,
{
fn default() -> Self {
Self::null()
}
}
impl<T, U> AsRef<U> for Weak<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 Weak<T>
where
T: Into<JObject<'static>> + AsRef<JObject<'static>> + Default + Reference + Send + Sync,
{
type Target = T;
fn deref(&self) -> &Self::Target {
&self.obj
}
}
impl<T> Weak<T>
where
T: Into<JObject<'static>> + AsRef<JObject<'static>> + Default + Reference + Send + Sync,
{
pub(crate) unsafe fn new(_env: &Env, obj: T) -> Self {
Self { obj }
}
pub fn null() -> Self {
Self { obj: T::default() }
}
pub fn as_raw(&self) -> sys::jweak {
self.obj.as_raw()
}
pub fn upgrade_local<'local>(&self, env: &mut Env<'local>) -> Result<Option<T::Kind<'local>>> {
match env.new_local_ref(self) {
Ok(local_ref) => Ok(Some(local_ref)),
Err(Error::ObjectFreed) => Ok(None),
Err(err) => Err(err),
}
}
pub fn upgrade_global(&self, env: &Env) -> Result<Option<Global<T::GlobalKind>>> {
match env.new_global_ref(self) {
Err(Error::ObjectFreed) => Ok(None),
Err(err) => Err(err),
Ok(global_ref) => Ok(Some(global_ref)),
}
}
pub fn is_garbage_collected(&self, env: &Env) -> Result<bool> {
env.is_same_object(self, JObject::null())
}
#[deprecated = "Use Env::is_same_object"]
pub fn is_same_object<'local, O>(&self, env: &Env<'local>, object: O) -> Result<bool>
where
O: AsRef<JObject<'local>>,
{
env.is_same_object(self, object)
}
#[deprecated = "Use Env::is_same_object"]
pub fn is_weak_ref_to_same_object(&self, env: &Env, other: &Self) -> Result<bool> {
env.is_same_object(self, other)
}
pub fn clone_in_jvm(&self, env: &mut Env<'_>) -> Result<Option<Weak<T::GlobalKind>>> {
match env.new_weak_ref(self) {
Err(Error::ObjectFreed) => Ok(None),
Err(err) => Err(err),
Ok(weak_ref) => Ok(Some(weak_ref)),
}
}
}
impl<T> Drop for Weak<T>
where
T: Into<JObject<'static>> + AsRef<JObject<'static>> + Default + Reference + Send + Sync,
{
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!(
"Dropping a Weak in a detached thread. Fix your code if this message appears frequently (see the Weak docs)."
);
}
unsafe {
ex_safe_jni_call_no_post_check_ex!(
env,
v1_2,
DeleteWeakGlobalRef,
obj.as_raw()
);
}
Ok(())
};
if let Err(err) = exception_safe_delete() {
debug!("error dropping weak ref: {:#?}", err);
}
}
}
}
unsafe impl<T> Reference for Weak<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(local_ref) }
}
unsafe fn global_kind_from_raw(global_ref: jobject) -> Self::GlobalKind {
unsafe { T::global_kind_from_raw(global_ref) }
}
}
#[test]
fn test_weak_ref_send() {
fn assert_send<T: Send>() {}
assert_send::<Weak<JObject<'static>>>();
}
#[test]
fn test_weak_ref_sync() {
fn assert_sync<T: Sync>() {}
assert_sync::<Weak<JObject<'static>>>();
}