use std::{
any::{self, Any},
ops::Deref,
};
use crate::{
context::{internal::Env, Context, FinalizeContext},
handle::{internal::TransparentNoCopyWrapper, Handle, Managed},
object::Object,
sys::{external, raw},
types::{boxed::private::JsBoxInner, private::ValueInternal, Value},
};
type BoxAny = Box<dyn Any + Send + 'static>;
mod private {
pub struct JsBoxInner<T: Send + 'static> {
pub(super) local: crate::sys::raw::Local,
pub(super) raw_data: *const T,
}
}
#[repr(transparent)]
pub struct JsBox<T: Send + 'static>(JsBoxInner<T>);
impl<T: Send + 'static> std::fmt::Debug for JsBoxInner<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "JsBox<{}>", std::any::type_name::<T>())
}
}
impl<T: Send + 'static> std::fmt::Debug for JsBox<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Debug::fmt(&self.0, f)
}
}
unsafe fn maybe_external_deref<'a>(env: Env, local: raw::Local) -> Option<&'a BoxAny> {
external::deref::<BoxAny>(env.to_raw(), local).map(|v| &*v)
}
impl<T: Send + 'static> Clone for JsBoxInner<T> {
fn clone(&self) -> Self {
Self {
local: self.local,
raw_data: self.raw_data,
}
}
}
impl<T: Send + 'static> Object for JsBox<T> {}
impl<T: Send + 'static> Copy for JsBoxInner<T> {}
impl<T: Send + 'static> Value for JsBox<T> {}
unsafe impl<T: Send + 'static> TransparentNoCopyWrapper for JsBox<T> {
type Inner = JsBoxInner<T>;
fn into_inner(self) -> Self::Inner {
self.0
}
}
impl<T: Send + 'static> Managed for JsBox<T> {
fn to_raw(&self) -> raw::Local {
self.0.local
}
#[allow(clippy::not_unsafe_ptr_arg_deref)]
fn from_raw(env: Env, local: raw::Local) -> Self {
let raw_data = unsafe { maybe_external_deref(env, local) }
.expect("Failed to unwrap napi_external as Box<Any>")
.downcast_ref()
.expect("Failed to downcast Any");
Self(JsBoxInner { local, raw_data })
}
}
impl<T: Send + 'static> ValueInternal for JsBox<T> {
fn name() -> String {
any::type_name::<Self>().to_string()
}
fn is_typeof<Other: Value>(env: Env, other: &Other) -> bool {
let data = unsafe { maybe_external_deref(env, other.to_raw()) };
data.map(|v| v.is::<T>()).unwrap_or(false)
}
fn downcast<Other: Value>(env: Env, other: &Other) -> Option<Self> {
let local = other.to_raw();
let data = unsafe { maybe_external_deref(env, local) };
data.and_then(|v| v.downcast_ref())
.map(|raw_data| Self(JsBoxInner { local, raw_data }))
}
}
impl<T: Finalize + Send + 'static> JsBox<T> {
pub fn new<'a, C>(cx: &mut C, value: T) -> Handle<'a, JsBox<T>>
where
C: Context<'a>,
T: Send + 'static,
{
fn finalizer<U: Finalize + 'static>(env: raw::Env, data: BoxAny) {
let data = *data.downcast::<U>().unwrap();
let env = unsafe { std::mem::transmute(env) };
FinalizeContext::with(env, move |mut cx| data.finalize(&mut cx));
}
let v = Box::new(value) as BoxAny;
let raw_data = &*v as *const dyn Any as *const T;
let local = unsafe { external::create(cx.env().to_raw(), v, finalizer::<T>) };
Handle::new_internal(Self(JsBoxInner { local, raw_data }))
}
}
impl<'a, T: Send + 'static> Deref for JsBox<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { &*self.0.raw_data }
}
}
pub trait Finalize: Sized {
fn finalize<'a, C: Context<'a>>(self, _: &mut C) {}
}
impl Finalize for bool {}
impl Finalize for char {}
impl Finalize for i8 {}
impl Finalize for i16 {}
impl Finalize for i32 {}
impl Finalize for i64 {}
impl Finalize for isize {}
impl Finalize for u8 {}
impl Finalize for u16 {}
impl Finalize for u32 {}
impl Finalize for u64 {}
impl Finalize for usize {}
impl Finalize for f32 {}
impl Finalize for f64 {}
impl Finalize for String {}
impl Finalize for std::path::PathBuf {}
macro_rules! finalize_tuple_impls {
($( $name:ident )+) => {
impl<$($name: Finalize),+> Finalize for ($($name,)+) {
fn finalize<'a, C: Context<'a>>(self, cx: &mut C) {
#![allow(non_snake_case)]
let ($($name,)+) = self;
($($name.finalize(cx),)+);
}
}
};
}
impl Finalize for () {}
finalize_tuple_impls! { T0 }
finalize_tuple_impls! { T0 T1 }
finalize_tuple_impls! { T0 T1 T2 }
finalize_tuple_impls! { T0 T1 T2 T3 }
finalize_tuple_impls! { T0 T1 T2 T3 T4 }
finalize_tuple_impls! { T0 T1 T2 T3 T4 T5 }
finalize_tuple_impls! { T0 T1 T2 T3 T4 T5 T6 }
finalize_tuple_impls! { T0 T1 T2 T3 T4 T5 T6 T7 }
impl<T: Finalize> Finalize for Vec<T> {
fn finalize<'a, C: Context<'a>>(self, cx: &mut C) {
for item in self {
item.finalize(cx);
}
}
}
impl<T: Finalize> Finalize for std::boxed::Box<T> {
fn finalize<'a, C: Context<'a>>(self, cx: &mut C) {
(*self).finalize(cx);
}
}
impl<T: Finalize> Finalize for Option<T> {
fn finalize<'a, C: Context<'a>>(self, cx: &mut C) {
if let Some(v) = self {
v.finalize(cx);
}
}
}
impl<T: Finalize> Finalize for std::rc::Rc<T> {
fn finalize<'a, C: Context<'a>>(self, cx: &mut C) {
if let Ok(v) = std::rc::Rc::try_unwrap(self) {
v.finalize(cx);
}
}
}
impl<T: Finalize> Finalize for std::sync::Arc<T> {
fn finalize<'a, C: Context<'a>>(self, cx: &mut C) {
if let Ok(v) = std::sync::Arc::try_unwrap(self) {
v.finalize(cx);
}
}
}
impl<T: Finalize> Finalize for std::sync::Mutex<T> {
fn finalize<'a, C: Context<'a>>(self, cx: &mut C) {
if let Ok(v) = self.into_inner() {
v.finalize(cx);
}
}
}
impl<T: Finalize> Finalize for std::sync::RwLock<T> {
fn finalize<'a, C: Context<'a>>(self, cx: &mut C) {
if let Ok(v) = self.into_inner() {
v.finalize(cx);
}
}
}
impl<T: Finalize> Finalize for std::cell::Cell<T> {
fn finalize<'a, C: Context<'a>>(self, cx: &mut C) {
self.into_inner().finalize(cx);
}
}
impl<T: Finalize> Finalize for std::cell::RefCell<T> {
fn finalize<'a, C: Context<'a>>(self, cx: &mut C) {
self.into_inner().finalize(cx);
}
}