use std::cell::{Cell, RefCell}; use std::collections::HashMap; use std::ffi::c_void; use std::ops::{Deref, DerefMut}; use std::rc::Rc; use crate::{bindgen_runtime::ToNapiValue, check_status, Env, Error, Result, Status}; type RefInformation = ( *mut c_void, crate::sys::napi_ref, *const Cell<*mut dyn FnOnce()>, ); thread_local! { pub(crate) static REFERENCE_MAP: RefCell> = Default::default(); } /// ### Experimental feature /// /// Create a `reference` from `Class` instance. /// Unref the `Reference` when the `Reference` is dropped. pub struct Reference { raw: *mut T, napi_ref: crate::sys::napi_ref, env: *mut c_void, finalize_callbacks: Rc>, } unsafe impl Send for Reference {} unsafe impl Sync for Reference {} impl Drop for Reference { fn drop(&mut self) { let status = unsafe { crate::sys::napi_reference_unref(self.env as crate::sys::napi_env, self.napi_ref, &mut 0) }; debug_assert!( status == crate::sys::Status::napi_ok, "Reference unref failed" ); } } impl Reference { #[doc(hidden)] pub fn add_ref(t: *mut c_void, value: RefInformation) { REFERENCE_MAP.with(|map| { map.borrow_mut().insert(t, value); }); } #[doc(hidden)] pub unsafe fn from_value_ptr(t: *mut c_void, env: crate::sys::napi_env) -> Result { if let Some((wrapped_value, napi_ref, finalize_callbacks_ptr)) = REFERENCE_MAP.with(|map| map.borrow().get(&t).cloned()) { check_status!( unsafe { crate::sys::napi_reference_ref(env, napi_ref, &mut 0) }, "Failed to ref napi reference" )?; let finalize_callbacks_raw = unsafe { Rc::from_raw(finalize_callbacks_ptr) }; let finalize_callbacks = finalize_callbacks_raw.clone(); // Leak the raw finalize callbacks Rc::into_raw(finalize_callbacks_raw); Ok(Self { raw: wrapped_value as *mut T, napi_ref, env: env as *mut c_void, finalize_callbacks, }) } else { Err(Error::new( Status::InvalidArg, format!("Class for Type {:?} not found", t), )) } } } impl ToNapiValue for Reference { unsafe fn to_napi_value(env: crate::sys::napi_env, val: Self) -> Result { let mut result = std::ptr::null_mut(); check_status!( unsafe { crate::sys::napi_get_reference_value(env, val.napi_ref, &mut result) }, "Failed to get reference value" )?; Ok(result) } } impl Reference { pub fn clone(&self, env: Env) -> Result { let mut ref_count = 0; check_status!( unsafe { crate::sys::napi_reference_ref(env.0, self.napi_ref, &mut ref_count) }, "Failed to ref napi reference" )?; Ok(Self { raw: self.raw, napi_ref: self.napi_ref, env: env.0 as *mut c_void, finalize_callbacks: self.finalize_callbacks.clone(), }) } /// Safety to share because caller can provide `Env` pub fn share_with Result>( self, #[allow(unused_variables)] env: Env, f: F, ) -> Result> { let s = f(Box::leak(unsafe { Box::from_raw(self.raw) }))?; let s_ptr = Box::into_raw(Box::new(s)); let prev_drop_fn = unsafe { Box::from_raw(self.finalize_callbacks.get()) }; let drop_fn = Box::new(move || { unsafe { Box::from_raw(s_ptr) }; prev_drop_fn(); }); self.finalize_callbacks.set(Box::into_raw(drop_fn)); Ok(SharedReference { raw: s_ptr, owner: self, }) } } impl Deref for Reference { type Target = T; fn deref(&self) -> &Self::Target { unsafe { Box::leak(Box::from_raw(self.raw)) } } } impl DerefMut for Reference { fn deref_mut(&mut self) -> &mut Self::Target { unsafe { Box::leak(Box::from_raw(self.raw)) } } } /// ### Experimental feature /// /// Create a `SharedReference` from an existed `Reference`. pub struct SharedReference { raw: *mut S, owner: Reference, } unsafe impl Send for SharedReference {} unsafe impl Sync for SharedReference {} impl SharedReference { pub fn clone(&self, env: Env) -> Result { Ok(SharedReference { raw: self.raw, owner: self.owner.clone(env)?, }) } /// Safety to share because caller can provide `Env` pub fn share_with Result>( self, #[allow(unused_variables)] env: Env, f: F, ) -> Result> { let s = f(Box::leak(unsafe { Box::from_raw(self.raw) }))?; let raw = Box::into_raw(Box::new(s)); let prev_drop_fn = unsafe { Box::from_raw(self.owner.finalize_callbacks.get()) }; let drop_fn = Box::new(move || { unsafe { Box::from_raw(raw) }; prev_drop_fn(); }); self.owner.finalize_callbacks.set(Box::into_raw(drop_fn)); Ok(SharedReference { raw, owner: self.owner, }) } } impl ToNapiValue for SharedReference { unsafe fn to_napi_value(env: crate::sys::napi_env, val: Self) -> Result { let mut result = std::ptr::null_mut(); check_status!( unsafe { crate::sys::napi_get_reference_value(env, val.owner.napi_ref, &mut result) }, "Failed to get reference value" )?; Ok(result) } } impl Deref for SharedReference { type Target = S; fn deref(&self) -> &Self::Target { unsafe { Box::leak(Box::from_raw(self.raw)) } } } impl DerefMut for SharedReference { fn deref_mut(&mut self) -> &mut Self::Target { unsafe { Box::leak(Box::from_raw(self.raw)) } } }