use std::{ffi::c_void, marker::PhantomData};
use crate::{
context::Context,
handle::Handle,
object::Object,
sys::{raw, reference},
types::boxed::Finalize,
};
#[cfg(feature = "napi-6")]
use {
crate::{
lifecycle::{DropData, InstanceData, InstanceId},
sys::tsfn::ThreadsafeFunction,
},
std::sync::Arc,
};
#[cfg(not(feature = "napi-6"))]
use std::thread::{self, ThreadId};
#[cfg(not(feature = "napi-6"))]
type InstanceId = ThreadId;
#[repr(transparent)]
#[derive(Clone)]
pub(crate) struct NapiRef(*mut c_void);
impl NapiRef {
pub(crate) unsafe fn unref(self, env: raw::Env) {
reference::unreference(env, self.0.cast());
}
}
unsafe impl Send for NapiRef {}
unsafe impl Sync for NapiRef {}
pub struct Root<T> {
internal: Option<NapiRef>,
instance_id: InstanceId,
#[cfg(feature = "napi-6")]
drop_queue: Arc<ThreadsafeFunction<DropData>>,
_phantom: PhantomData<T>,
}
impl<T> std::fmt::Debug for Root<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Root<{}>", std::any::type_name::<T>())
}
}
unsafe impl<T> Send for Root<T> {}
unsafe impl<T> Sync for Root<T> {}
#[cfg(feature = "napi-6")]
fn instance_id<'a, C: Context<'a>>(cx: &mut C) -> InstanceId {
InstanceData::id(cx)
}
#[cfg(not(feature = "napi-6"))]
fn instance_id<'a, C: Context<'a>>(_: &mut C) -> InstanceId {
thread::current().id()
}
impl<T: Object> Root<T> {
pub fn new<'a, C: Context<'a>>(cx: &mut C, value: &T) -> Self {
let env = cx.env().to_raw();
let internal = unsafe { reference::new(env, value.to_local()) };
Self {
internal: Some(NapiRef(internal as *mut _)),
instance_id: instance_id(cx),
#[cfg(feature = "napi-6")]
drop_queue: InstanceData::drop_queue(cx),
_phantom: PhantomData,
}
}
pub fn clone<'a, C: Context<'a>>(&self, cx: &mut C) -> Self {
let env = cx.env();
let internal = self.as_napi_ref(cx).0 as *mut _;
unsafe {
reference::reference(env.to_raw(), internal);
};
Self {
internal: self.internal.clone(),
instance_id: instance_id(cx),
#[cfg(feature = "napi-6")]
drop_queue: Arc::clone(&self.drop_queue),
_phantom: PhantomData,
}
}
pub fn drop<'a, C: Context<'a>>(self, cx: &mut C) {
let env = cx.env().to_raw();
unsafe {
self.into_napi_ref(cx).unref(env);
}
}
pub fn into_inner<'a, C: Context<'a>>(self, cx: &mut C) -> Handle<'a, T> {
let env = cx.env();
let internal = self.into_napi_ref(cx);
let local = unsafe { reference::get(env.to_raw(), internal.0.cast()) };
unsafe {
internal.unref(env.to_raw());
}
Handle::new_internal(unsafe { T::from_local(env, local) })
}
pub fn to_inner<'a, C: Context<'a>>(&self, cx: &mut C) -> Handle<'a, T> {
let env = cx.env();
let local = unsafe { reference::get(env.to_raw(), self.as_napi_ref(cx).0 as *mut _) };
Handle::new_internal(unsafe { T::from_local(env, local) })
}
fn as_napi_ref<'a, C: Context<'a>>(&self, cx: &mut C) -> &NapiRef {
if self.instance_id != instance_id(cx) {
panic!("Attempted to dereference a `neon::handle::Root` from the wrong module ");
}
self.internal
.as_ref()
.unwrap()
}
fn into_napi_ref<'a, C: Context<'a>>(mut self, cx: &mut C) -> NapiRef {
let reference = self.as_napi_ref(cx).clone();
self.internal = None;
reference
}
}
impl<T: Object> Finalize for Root<T> {
fn finalize<'a, C: Context<'a>>(self, cx: &mut C) {
self.drop(cx);
}
}
impl<T> Drop for Root<T> {
#[cfg(not(feature = "napi-6"))]
fn drop(&mut self) {
if self.internal.is_none() {
return;
}
if std::thread::panicking() {
eprintln!("Warning: neon::handle::Root leaked during a panic");
return;
}
if let Ok(true) = crate::context::internal::IS_RUNNING.try_with(|v| *v.borrow()) {
panic!("Must call `into_inner` or `drop` on `neon::handle::Root`");
}
}
#[cfg(feature = "napi-6")]
fn drop(&mut self) {
if let Some(internal) = self.internal.take() {
let _ = self.drop_queue.call(DropData::Ref(internal), None);
}
}
}