use alloc::boxed::Box;
use crate::JsValue;
use crate::encode::{BinaryEncode, CallbackKey, EncodeTypeDef};
use crate::function::RustCallback;
use crate::ipc::{DecodeError, DecodedData, EncodedData};
use crate::object_store::insert_object;
pub struct ScopedClosure<'a, T: ?Sized> {
pub(crate) _phantom: core::marker::PhantomData<(&'a (), crate::alloc::boxed::Box<T>)>,
pub(crate) callback: CallbackOwnership,
pub(crate) value: crate::JsValue,
}
#[derive(Clone, Copy)]
pub(crate) enum CallbackOwnership {
None,
Owned,
Detached,
}
impl CallbackOwnership {
pub(crate) fn needs_flush(&self) -> bool {
!matches!(self, Self::None)
}
pub(crate) fn detach(&mut self) {
if matches!(self, Self::Owned) {
*self = Self::Detached;
}
}
}
pub type Closure<T> = ScopedClosure<'static, T>;
use crate::__rt::WasmWord;
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __wbindgen_destroy_closure(a: WasmWord, b: WasmWord) {
let _ = (a, b);
}
impl<T: ?Sized> ScopedClosure<'static, T> {
pub(crate) fn wrap_encode_decode<FnPtr>(
encode_decode: impl Fn(&mut DecodedData, &mut EncodedData) -> Result<(), DecodeError> + 'static,
) -> Self
where
CallbackKey<FnPtr>: BinaryEncode + EncodeTypeDef,
{
let key = insert_object(RustCallback::new_fn(encode_decode));
let value =
crate::__rt::wbg_cast::<CallbackKey<FnPtr>, crate::JsValue>(CallbackKey::new(key));
Self {
_phantom: core::marker::PhantomData,
callback: crate::closure::CallbackOwnership::Owned,
value,
}
}
pub(crate) fn wrap_encode_decode_mut<FnPtr>(
encode_decode: impl FnMut(&mut DecodedData, &mut EncodedData) -> Result<(), DecodeError>
+ 'static,
) -> Self
where
CallbackKey<FnPtr>: BinaryEncode + EncodeTypeDef,
{
let key = insert_object(RustCallback::new_fn_mut(encode_decode));
let value =
crate::__rt::wbg_cast::<CallbackKey<FnPtr>, crate::JsValue>(CallbackKey::new(key));
Self {
_phantom: core::marker::PhantomData,
callback: crate::closure::CallbackOwnership::Owned,
value,
}
}
pub(crate) fn wrap_once_encode_decode_mut<FnPtr>(
mut encode_decode: impl FnMut(&mut DecodedData, &mut EncodedData) -> Result<(), DecodeError>
+ 'static,
) -> Self
where
CallbackKey<FnPtr>: BinaryEncode + EncodeTypeDef,
{
let handle_cell = alloc::rc::Rc::new(core::cell::Cell::new(None));
let handle_for_callback = handle_cell.clone();
let key = insert_object(RustCallback::new_fn_mut(move |decoder, encoder| {
let result = encode_decode(decoder, encoder);
if let Some(handle) = handle_for_callback.take() {
crate::batch::queue_rust_object_drop(handle);
}
result
}));
handle_cell.set(Some(key));
let value = crate::__rt::wbg_cast::<CallbackKey<FnPtr>, crate::JsValue>(
CallbackKey::new_with_policy(key, crate::encode::CallbackPolicy::JsOwnedOnce),
);
Self {
_phantom: core::marker::PhantomData,
callback: crate::closure::CallbackOwnership::Owned,
value,
}
}
}
impl<'a, T> ScopedClosure<'a, T>
where
T: ?Sized + WasmClosure,
{
pub fn as_js_value(&self) -> &JsValue {
&self.value
}
}
impl<T: ?Sized> Drop for ScopedClosure<'_, T> {
fn drop(&mut self) {
if let crate::closure::CallbackOwnership::Owned = self.callback {
crate::batch::queue_js_dispose_rust_function(self.value.id());
}
}
}
impl<T: ?Sized> Unpin for ScopedClosure<'_, T> {}
#[doc(hidden)]
pub trait MaybeUnwindSafe {}
impl<T: ?Sized> MaybeUnwindSafe for T {}
#[doc(hidden)]
pub trait IntoWasmClosure<T: ?Sized> {
fn into_closure(self) -> Closure<T>
where
Self: Sized,
{
unreachable!("unsized closure objects must be converted from Box<Self>")
}
fn into_closure_box(self: Box<Self>) -> Closure<T>;
}
#[doc(hidden)]
pub trait IntoWasmClosureRef<T: ?Sized> {
fn into_scoped_closure_ref<'a>(t: &'a Self) -> ScopedClosure<'a, T::Static>
where
T: WasmClosure;
}
#[doc(hidden)]
pub trait IntoWasmClosureRefMut<T: ?Sized> {
fn into_scoped_closure_ref_mut<'a>(t: &'a mut Self) -> ScopedClosure<'a, T::Static>
where
T: WasmClosure;
}
#[doc(hidden)]
pub trait WasmClosureFnOnce<T: ?Sized, A, R>: Sized + 'static {
fn into_closure(self) -> Closure<T>;
}
#[doc(hidden)]
pub trait WasmClosureFnOnceAbort<T: ?Sized, A, R>: Sized + 'static {
fn into_closure(self) -> Closure<T>;
}
impl<T: ?Sized> AsRef<JsValue> for ScopedClosure<'_, T> {
fn as_ref(&self) -> &JsValue {
&self.value
}
}
impl<T> core::fmt::Debug for ScopedClosure<'_, T>
where
T: ?Sized,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Closure")
.field("value", &self.value)
.finish()
}
}
#[doc(hidden)]
pub trait WasmClosure {
type Static: ?Sized;
type AsMut: ?Sized;
}
#[doc(hidden)]
pub trait WryWasmClosure<M> {
fn into_js_closure(boxed: Box<Self>) -> Closure<Self>;
}
impl<T> ScopedClosure<'static, T>
where
T: ?Sized + WasmClosure,
{
pub fn new<F>(t: F) -> Self
where
F: IntoWasmClosure<T> + MaybeUnwindSafe + 'static,
{
Self::own(t)
}
pub fn own<F>(t: F) -> Self
where
F: IntoWasmClosure<T> + MaybeUnwindSafe + 'static,
{
<F as IntoWasmClosure<T>>::into_closure(t)
}
pub fn own_aborting<F>(t: F) -> Self
where
F: IntoWasmClosure<T> + 'static,
{
<F as IntoWasmClosure<T>>::into_closure(t)
}
pub fn own_assert_unwind_safe<F>(t: F) -> Self
where
F: IntoWasmClosure<T> + 'static,
{
<F as IntoWasmClosure<T>>::into_closure(t)
}
pub fn wrap<F>(data: Box<F>) -> Self
where
F: IntoWasmClosure<T> + ?Sized + MaybeUnwindSafe,
{
<F as IntoWasmClosure<T>>::into_closure_box(data)
}
pub fn wrap_aborting<F>(data: Box<F>) -> Self
where
F: IntoWasmClosure<T> + ?Sized,
{
<F as IntoWasmClosure<T>>::into_closure_box(data)
}
pub fn wrap_assert_unwind_safe<F>(data: Box<F>) -> Self
where
F: IntoWasmClosure<T> + ?Sized,
{
<F as IntoWasmClosure<T>>::into_closure_box(data)
}
pub fn borrow<'a, F>(t: &'a F) -> ScopedClosure<'a, T::Static>
where
F: IntoWasmClosureRef<T> + MaybeUnwindSafe + ?Sized,
{
F::into_scoped_closure_ref(t)
}
pub fn borrow_aborting<'a, F>(t: &'a F) -> ScopedClosure<'a, T::Static>
where
F: IntoWasmClosureRef<T> + ?Sized,
{
F::into_scoped_closure_ref(t)
}
pub fn borrow_assert_unwind_safe<'a, F>(t: &'a F) -> ScopedClosure<'a, T::Static>
where
F: IntoWasmClosureRef<T> + ?Sized,
{
F::into_scoped_closure_ref(t)
}
pub fn borrow_mut<'a, F>(t: &'a mut F) -> ScopedClosure<'a, T::Static>
where
F: IntoWasmClosureRefMut<T> + MaybeUnwindSafe + ?Sized,
{
F::into_scoped_closure_ref_mut(t)
}
pub fn borrow_mut_aborting<'a, F>(t: &'a mut F) -> ScopedClosure<'a, T::Static>
where
F: IntoWasmClosureRefMut<T> + ?Sized,
{
F::into_scoped_closure_ref_mut(t)
}
pub fn borrow_mut_assert_unwind_safe<'a, F>(t: &'a mut F) -> ScopedClosure<'a, T::Static>
where
F: IntoWasmClosureRefMut<T> + ?Sized,
{
F::into_scoped_closure_ref_mut(t)
}
pub fn once<F, A, R>(fn_once: F) -> Self
where
F: WasmClosureFnOnce<T, A, R> + MaybeUnwindSafe,
{
<F as WasmClosureFnOnce<T, A, R>>::into_closure(fn_once)
}
pub fn once_aborting<F, A, R>(fn_once: F) -> Self
where
F: WasmClosureFnOnceAbort<T, A, R>,
{
<F as WasmClosureFnOnceAbort<T, A, R>>::into_closure(fn_once)
}
pub fn once_assert_unwind_safe<F, A, R>(fn_once: F) -> Self
where
F: WasmClosureFnOnceAbort<T, A, R>,
{
Self::once_aborting(fn_once)
}
pub fn forget(self) {
core::mem::forget(self);
}
pub fn into_js_value(self) -> JsValue {
let value = core::mem::ManuallyDrop::new(self);
value.value.clone()
}
pub fn once_into_js<F, A, R>(fn_once: F) -> JsValue
where
F: WasmClosureFnOnce<T, A, R> + MaybeUnwindSafe,
{
Self::once(fn_once).into_js_value()
}
}