1use alloc::boxed::Box;
4
5use crate::JsValue;
6use crate::encode::{BinaryEncode, EncodeTypeDef};
7use crate::ipc::{DecodeError, DecodedData, EncodedData};
8use crate::object_store::insert_object;
9use wry_bindgen_core::{CallbackKey, ObjectHandle, RustCallback};
10
11pub struct ScopedClosure<'a, T: ?Sized> {
13 pub(crate) _phantom: core::marker::PhantomData<(&'a (), crate::alloc::boxed::Box<T>)>,
16 pub(crate) callback: CallbackOwnership,
17 pub(crate) value: crate::JsValue,
18}
19
20#[derive(Clone, Copy)]
25pub(crate) enum CallbackOwnership {
26 None,
28 Owned,
30 Detached,
33}
34
35impl CallbackOwnership {
36 pub(crate) fn owned(_handle: ObjectHandle) -> Self {
37 Self::Owned
38 }
39
40 pub(crate) fn needs_flush(&self) -> bool {
43 !matches!(self, Self::None)
44 }
45
46 pub(crate) fn detach(&mut self) {
48 if matches!(self, Self::Owned) {
49 *self = Self::Detached;
50 }
51 }
52}
53
54pub type Closure<T> = ScopedClosure<'static, T>;
56
57use crate::__rt::WasmWord;
58
59#[unsafe(no_mangle)]
66pub unsafe extern "C" fn __wbindgen_destroy_closure(a: WasmWord, b: WasmWord) {
67 let _ = (a, b);
68}
69
70impl<T: ?Sized> ScopedClosure<'static, T> {
71 pub(crate) fn wrap_encode_decode<FnPtr>(
73 encode_decode: impl Fn(&mut DecodedData, &mut EncodedData) -> Result<(), DecodeError> + 'static,
74 ) -> Self
75 where
76 CallbackKey<FnPtr>: BinaryEncode + EncodeTypeDef,
77 {
78 let key = insert_object(RustCallback::new_fn(encode_decode));
79 let value =
80 crate::__rt::wbg_cast::<CallbackKey<FnPtr>, crate::JsValue>(CallbackKey::new(key));
81 Self {
82 _phantom: core::marker::PhantomData,
83 callback: crate::closure::CallbackOwnership::owned(key),
84 value,
85 }
86 }
87
88 pub(crate) fn wrap_encode_decode_mut<FnPtr>(
90 encode_decode: impl FnMut(&mut DecodedData, &mut EncodedData) -> Result<(), DecodeError>
91 + 'static,
92 ) -> Self
93 where
94 CallbackKey<FnPtr>: BinaryEncode + EncodeTypeDef,
95 {
96 let key = insert_object(RustCallback::new_fn_mut(encode_decode));
97 let value =
98 crate::__rt::wbg_cast::<CallbackKey<FnPtr>, crate::JsValue>(CallbackKey::new(key));
99 Self {
100 _phantom: core::marker::PhantomData,
101 callback: crate::closure::CallbackOwnership::owned(key),
102 value,
103 }
104 }
105
106 pub(crate) fn wrap_once_encode_decode_mut<FnPtr>(
108 mut encode_decode: impl FnMut(&mut DecodedData, &mut EncodedData) -> Result<(), DecodeError>
109 + 'static,
110 ) -> Self
111 where
112 CallbackKey<FnPtr>: BinaryEncode + EncodeTypeDef,
113 {
114 let handle_cell = alloc::rc::Rc::new(core::cell::Cell::new(None::<ObjectHandle>));
115 let handle_for_callback = handle_cell.clone();
116 let key = insert_object(RustCallback::new_fn_mut(move |decoder, encoder| {
117 let result = encode_decode(decoder, encoder);
118 if let Some(handle) = handle_for_callback.take() {
120 handle.drop_rust_object();
121 }
122 result
123 }));
124 handle_cell.set(Some(key));
125 let value = crate::__rt::wbg_cast::<CallbackKey<FnPtr>, crate::JsValue>(
126 CallbackKey::js_owned_once(key),
127 );
128 Self {
133 _phantom: core::marker::PhantomData,
134 callback: crate::closure::CallbackOwnership::owned(key),
135 value,
136 }
137 }
138}
139
140impl<'a, T> ScopedClosure<'a, T>
141where
142 T: ?Sized + WasmClosure,
143{
144 pub fn as_js_value(&self) -> &JsValue {
146 &self.value
147 }
148}
149
150impl<T: ?Sized> Drop for ScopedClosure<'_, T> {
151 fn drop(&mut self) {
152 if let crate::closure::CallbackOwnership::Owned = self.callback {
153 self.value.js_ref().dispose_js_rust_function();
154 }
155 }
156}
157
158impl<T: ?Sized> Unpin for ScopedClosure<'_, T> {}
159
160#[doc(hidden)]
162pub trait MaybeUnwindSafe {}
163
164impl<T: ?Sized> MaybeUnwindSafe for T {}
165
166#[doc(hidden)]
168pub trait IntoWasmClosure<T: ?Sized> {
169 fn into_closure(self) -> Closure<T>
170 where
171 Self: Sized,
172 {
173 unreachable!("unsized closure objects must be converted from Box<Self>")
174 }
175
176 fn into_closure_box(self: Box<Self>) -> Closure<T>;
177}
178
179#[doc(hidden)]
181pub trait IntoWasmClosureRef<T: ?Sized> {
182 fn into_scoped_closure_ref<'a>(t: &'a Self) -> ScopedClosure<'a, T::Static>
183 where
184 T: WasmClosure;
185}
186
187#[doc(hidden)]
189pub trait IntoWasmClosureRefMut<T: ?Sized> {
190 fn into_scoped_closure_ref_mut<'a>(t: &'a mut Self) -> ScopedClosure<'a, T::Static>
191 where
192 T: WasmClosure;
193}
194
195#[doc(hidden)]
197pub trait WasmClosureFnOnce<T: ?Sized, A, R>: Sized + 'static {
198 fn into_closure(self) -> Closure<T>;
199}
200
201#[doc(hidden)]
203pub trait WasmClosureFnOnceAbort<T: ?Sized, A, R>: Sized + 'static {
204 fn into_closure(self) -> Closure<T>;
205}
206
207impl<T: ?Sized> AsRef<JsValue> for ScopedClosure<'_, T> {
208 fn as_ref(&self) -> &JsValue {
209 &self.value
210 }
211}
212
213impl<T> core::fmt::Debug for ScopedClosure<'_, T>
214where
215 T: ?Sized,
216{
217 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
218 f.write_str("Closure { ... }")
220 }
221}
222
223#[doc(hidden)]
225pub trait WasmClosure {
226 type Static: ?Sized;
228 type AsMut: ?Sized;
230}
231
232#[doc(hidden)]
234pub trait WryWasmClosure<M> {
235 fn into_js_closure(boxed: Box<Self>) -> Closure<Self>;
237}
238
239impl<T> ScopedClosure<'static, T>
240where
241 T: ?Sized + WasmClosure,
242{
243 pub fn new<F>(t: F) -> Self
244 where
245 F: IntoWasmClosure<T> + MaybeUnwindSafe + 'static,
246 {
247 Self::own(t)
248 }
249
250 pub fn own<F>(t: F) -> Self
251 where
252 F: IntoWasmClosure<T> + MaybeUnwindSafe + 'static,
253 {
254 <F as IntoWasmClosure<T>>::into_closure(t)
255 }
256
257 pub fn own_aborting<F>(t: F) -> Self
258 where
259 F: IntoWasmClosure<T> + 'static,
260 {
261 <F as IntoWasmClosure<T>>::into_closure(t)
262 }
263
264 pub fn own_assert_unwind_safe<F>(t: F) -> Self
265 where
266 F: IntoWasmClosure<T> + 'static,
267 {
268 <F as IntoWasmClosure<T>>::into_closure(t)
269 }
270
271 pub fn wrap<F>(data: Box<F>) -> Self
272 where
273 F: IntoWasmClosure<T> + ?Sized + MaybeUnwindSafe,
274 {
275 <F as IntoWasmClosure<T>>::into_closure_box(data)
276 }
277
278 pub fn wrap_aborting<F>(data: Box<F>) -> Self
279 where
280 F: IntoWasmClosure<T> + ?Sized,
281 {
282 <F as IntoWasmClosure<T>>::into_closure_box(data)
283 }
284
285 pub fn wrap_assert_unwind_safe<F>(data: Box<F>) -> Self
286 where
287 F: IntoWasmClosure<T> + ?Sized,
288 {
289 <F as IntoWasmClosure<T>>::into_closure_box(data)
290 }
291
292 pub fn borrow<'a, F>(t: &'a F) -> ScopedClosure<'a, T::Static>
293 where
294 F: IntoWasmClosureRef<T> + MaybeUnwindSafe + ?Sized,
295 {
296 F::into_scoped_closure_ref(t)
297 }
298
299 pub fn borrow_aborting<'a, F>(t: &'a F) -> ScopedClosure<'a, T::Static>
300 where
301 F: IntoWasmClosureRef<T> + ?Sized,
302 {
303 F::into_scoped_closure_ref(t)
304 }
305
306 pub fn borrow_assert_unwind_safe<'a, F>(t: &'a F) -> ScopedClosure<'a, T::Static>
307 where
308 F: IntoWasmClosureRef<T> + ?Sized,
309 {
310 F::into_scoped_closure_ref(t)
311 }
312
313 pub fn borrow_mut<'a, F>(t: &'a mut F) -> ScopedClosure<'a, T::Static>
314 where
315 F: IntoWasmClosureRefMut<T> + MaybeUnwindSafe + ?Sized,
316 {
317 F::into_scoped_closure_ref_mut(t)
318 }
319
320 pub fn borrow_mut_aborting<'a, F>(t: &'a mut F) -> ScopedClosure<'a, T::Static>
321 where
322 F: IntoWasmClosureRefMut<T> + ?Sized,
323 {
324 F::into_scoped_closure_ref_mut(t)
325 }
326
327 pub fn borrow_mut_assert_unwind_safe<'a, F>(t: &'a mut F) -> ScopedClosure<'a, T::Static>
328 where
329 F: IntoWasmClosureRefMut<T> + ?Sized,
330 {
331 F::into_scoped_closure_ref_mut(t)
332 }
333
334 pub fn once<F, A, R>(fn_once: F) -> Self
340 where
341 F: WasmClosureFnOnce<T, A, R> + MaybeUnwindSafe,
342 {
343 <F as WasmClosureFnOnce<T, A, R>>::into_closure(fn_once)
344 }
345
346 pub fn once_aborting<F, A, R>(fn_once: F) -> Self
347 where
348 F: WasmClosureFnOnceAbort<T, A, R>,
349 {
350 <F as WasmClosureFnOnceAbort<T, A, R>>::into_closure(fn_once)
351 }
352
353 pub fn once_assert_unwind_safe<F, A, R>(fn_once: F) -> Self
354 where
355 F: WasmClosureFnOnceAbort<T, A, R>,
356 {
357 Self::once_aborting(fn_once)
358 }
359
360 pub fn forget(self) {
362 core::mem::forget(self);
363 }
364
365 pub fn into_js_value(self) -> JsValue {
367 let value = core::mem::ManuallyDrop::new(self);
368 value.value.clone()
370 }
371
372 pub fn once_into_js<F, A, R>(fn_once: F) -> JsValue
377 where
378 F: WasmClosureFnOnce<T, A, R> + MaybeUnwindSafe,
379 {
380 Self::once(fn_once).into_js_value()
381 }
382}
383
384#[cfg(test)]
385mod tests {
386 use core::panic::{RefUnwindSafe, UnwindSafe};
387
388 use super::ScopedClosure;
389
390 fn assert_auto_traits<T: Send + Sync + UnwindSafe + RefUnwindSafe>() {}
391
392 #[test]
393 fn scoped_closure_preserves_auto_traits() {
394 assert_auto_traits::<
395 ScopedClosure<'static, dyn Fn() + Send + Sync + UnwindSafe + RefUnwindSafe>,
396 >();
397 assert_auto_traits::<
398 ScopedClosure<'static, dyn FnMut() + Send + Sync + UnwindSafe + RefUnwindSafe>,
399 >();
400 }
401}