use boa_gc::{custom_trace, Finalize, Gc, Trace};
use crate::{object::JsPromise, Context, JsResult, JsValue};
pub type NativeFunctionPointer = fn(&JsValue, &[JsValue], &mut Context<'_>) -> JsResult<JsValue>;
trait TraceableClosure: Trace {
fn call(
&self,
this: &JsValue,
args: &[JsValue],
context: &mut Context<'_>,
) -> JsResult<JsValue>;
}
#[derive(Trace, Finalize)]
struct Closure<F, T>
where
F: Fn(&JsValue, &[JsValue], &T, &mut Context<'_>) -> JsResult<JsValue>,
T: Trace,
{
#[unsafe_ignore_trace]
f: F,
captures: T,
}
impl<F, T> TraceableClosure for Closure<F, T>
where
F: Fn(&JsValue, &[JsValue], &T, &mut Context<'_>) -> JsResult<JsValue>,
T: Trace,
{
fn call(
&self,
this: &JsValue,
args: &[JsValue],
context: &mut Context<'_>,
) -> JsResult<JsValue> {
(self.f)(this, args, &self.captures, context)
}
}
#[derive(Clone)]
pub struct NativeFunction {
inner: Inner,
}
#[derive(Clone)]
enum Inner {
PointerFn(NativeFunctionPointer),
Closure(Gc<dyn TraceableClosure>),
}
impl Finalize for NativeFunction {
fn finalize(&self) {
if let Inner::Closure(c) = &self.inner {
c.finalize();
}
}
}
unsafe impl Trace for NativeFunction {
custom_trace!(this, {
if let Inner::Closure(c) = &this.inner {
mark(c);
}
});
}
impl std::fmt::Debug for NativeFunction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("NativeFunction").finish_non_exhaustive()
}
}
impl NativeFunction {
#[inline]
pub fn from_fn_ptr(function: NativeFunctionPointer) -> Self {
Self {
inner: Inner::PointerFn(function),
}
}
pub fn from_async_fn<Fut>(f: fn(&JsValue, &[JsValue], &mut Context<'_>) -> Fut) -> Self
where
Fut: std::future::IntoFuture<Output = JsResult<JsValue>> + 'static,
{
Self::from_copy_closure(move |this, args, context| {
let future = f(this, args, context);
Ok(JsPromise::from_future(future, context).into())
})
}
pub fn from_copy_closure<F>(closure: F) -> Self
where
F: Fn(&JsValue, &[JsValue], &mut Context<'_>) -> JsResult<JsValue> + Copy + 'static,
{
unsafe { Self::from_closure(closure) }
}
pub fn from_copy_closure_with_captures<F, T>(closure: F, captures: T) -> Self
where
F: Fn(&JsValue, &[JsValue], &T, &mut Context<'_>) -> JsResult<JsValue> + Copy + 'static,
T: Trace + 'static,
{
unsafe { Self::from_closure_with_captures(closure, captures) }
}
pub unsafe fn from_closure<F>(closure: F) -> Self
where
F: Fn(&JsValue, &[JsValue], &mut Context<'_>) -> JsResult<JsValue> + 'static,
{
unsafe {
Self::from_closure_with_captures(
move |this, args, _, context| closure(this, args, context),
(),
)
}
}
pub unsafe fn from_closure_with_captures<F, T>(closure: F, captures: T) -> Self
where
F: Fn(&JsValue, &[JsValue], &T, &mut Context<'_>) -> JsResult<JsValue> + 'static,
T: Trace + 'static,
{
let ptr = Gc::into_raw(Gc::new(Closure {
f: closure,
captures,
}));
unsafe {
Self {
inner: Inner::Closure(Gc::from_raw(ptr)),
}
}
}
#[inline]
pub fn call(
&self,
this: &JsValue,
args: &[JsValue],
context: &mut Context<'_>,
) -> JsResult<JsValue> {
match self.inner {
Inner::PointerFn(f) => f(this, args, context),
Inner::Closure(ref c) => c.call(this, args, context),
}
}
}