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
57impl<T: ?Sized> ScopedClosure<'static, T> {
58 pub(crate) fn wrap_encode_decode<FnPtr>(
60 encode_decode: impl Fn(&mut DecodedData, &mut EncodedData) -> Result<(), DecodeError> + 'static,
61 ) -> Self
62 where
63 CallbackKey<FnPtr>: BinaryEncode + EncodeTypeDef,
64 {
65 let key = insert_object(RustCallback::new_fn(encode_decode));
66 let value =
67 crate::__rt::wbg_cast::<CallbackKey<FnPtr>, crate::JsValue>(CallbackKey::new(key));
68 Self {
69 _phantom: core::marker::PhantomData,
70 callback: crate::closure::CallbackOwnership::owned(key),
71 value,
72 }
73 }
74
75 pub(crate) fn wrap_encode_decode_mut<FnPtr>(
77 encode_decode: impl FnMut(&mut DecodedData, &mut EncodedData) -> Result<(), DecodeError>
78 + 'static,
79 ) -> Self
80 where
81 CallbackKey<FnPtr>: BinaryEncode + EncodeTypeDef,
82 {
83 let key = insert_object(RustCallback::new_fn_mut(encode_decode));
84 let value =
85 crate::__rt::wbg_cast::<CallbackKey<FnPtr>, crate::JsValue>(CallbackKey::new(key));
86 Self {
87 _phantom: core::marker::PhantomData,
88 callback: crate::closure::CallbackOwnership::owned(key),
89 value,
90 }
91 }
92
93 pub(crate) fn wrap_once_encode_decode_mut<FnPtr>(
95 mut encode_decode: impl FnMut(&mut DecodedData, &mut EncodedData) -> Result<(), DecodeError>
96 + 'static,
97 ) -> Self
98 where
99 CallbackKey<FnPtr>: BinaryEncode + EncodeTypeDef,
100 {
101 let handle_cell = alloc::rc::Rc::new(core::cell::Cell::new(None::<ObjectHandle>));
102 let handle_for_callback = handle_cell.clone();
103 let key = insert_object(RustCallback::new_fn_mut(move |decoder, encoder| {
104 let result = encode_decode(decoder, encoder);
105 if let Some(handle) = handle_for_callback.take() {
107 handle.drop_rust_object();
108 }
109 result
110 }));
111 handle_cell.set(Some(key));
112 let value = crate::__rt::wbg_cast::<CallbackKey<FnPtr>, crate::JsValue>(
113 CallbackKey::js_owned_once(key),
114 );
115 Self {
120 _phantom: core::marker::PhantomData,
121 callback: crate::closure::CallbackOwnership::owned(key),
122 value,
123 }
124 }
125}
126
127impl<'a, T> ScopedClosure<'a, T>
128where
129 T: ?Sized + WasmClosure,
130{
131 pub fn as_js_value(&self) -> &JsValue {
133 &self.value
134 }
135}
136
137impl<T: ?Sized> Drop for ScopedClosure<'_, T> {
138 fn drop(&mut self) {
139 if let crate::closure::CallbackOwnership::Owned = self.callback {
140 self.value.js_ref().dispose_js_rust_function();
141 }
142 }
143}
144
145impl<T: ?Sized> Unpin for ScopedClosure<'_, T> {}
146
147#[doc(hidden)]
149pub trait MaybeUnwindSafe {}
150
151impl<T: ?Sized> MaybeUnwindSafe for T {}
152
153#[doc(hidden)]
155pub trait IntoWasmClosure<T: ?Sized> {
156 fn into_closure(self) -> Closure<T>
157 where
158 Self: Sized,
159 {
160 unreachable!("unsized closure objects must be converted from Box<Self>")
161 }
162
163 fn into_closure_box(self: Box<Self>) -> Closure<T>;
164}
165
166#[doc(hidden)]
168pub trait IntoWasmClosureRef<T: ?Sized> {
169 fn into_scoped_closure_ref<'a>(t: &'a Self) -> ScopedClosure<'a, T::Static>
170 where
171 T: WasmClosure;
172}
173
174#[doc(hidden)]
176pub trait IntoWasmClosureRefMut<T: ?Sized> {
177 fn into_scoped_closure_ref_mut<'a>(t: &'a mut Self) -> ScopedClosure<'a, T::Static>
178 where
179 T: WasmClosure;
180}
181
182#[doc(hidden)]
184pub trait WasmClosureFnOnce<T: ?Sized, A, R>: Sized + 'static {
185 fn into_closure(self) -> Closure<T>;
186}
187
188#[doc(hidden)]
190pub trait WasmClosureFnOnceAbort<T: ?Sized, A, R>: Sized + 'static {
191 fn into_closure(self) -> Closure<T>;
192}
193
194impl<T: ?Sized> AsRef<JsValue> for ScopedClosure<'_, T> {
195 fn as_ref(&self) -> &JsValue {
196 &self.value
197 }
198}
199
200impl<T> core::fmt::Debug for ScopedClosure<'_, T>
201where
202 T: ?Sized,
203{
204 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
205 f.write_str("Closure { ... }")
207 }
208}
209
210#[doc(hidden)]
212pub trait WasmClosure {
213 type Static: ?Sized;
215 type AsMut: ?Sized;
217}
218
219#[doc(hidden)]
221pub trait WryWasmClosure<M> {
222 fn into_js_closure(boxed: Box<Self>) -> Closure<Self>;
224}
225
226impl<T> ScopedClosure<'static, T>
227where
228 T: ?Sized + WasmClosure,
229{
230 pub fn new<F>(t: F) -> Self
231 where
232 F: IntoWasmClosure<T> + MaybeUnwindSafe + 'static,
233 {
234 Self::own(t)
235 }
236
237 pub fn own<F>(t: F) -> Self
238 where
239 F: IntoWasmClosure<T> + MaybeUnwindSafe + 'static,
240 {
241 <F as IntoWasmClosure<T>>::into_closure(t)
242 }
243
244 pub fn own_aborting<F>(t: F) -> Self
245 where
246 F: IntoWasmClosure<T> + 'static,
247 {
248 <F as IntoWasmClosure<T>>::into_closure(t)
249 }
250
251 pub fn own_assert_unwind_safe<F>(t: F) -> Self
252 where
253 F: IntoWasmClosure<T> + 'static,
254 {
255 <F as IntoWasmClosure<T>>::into_closure(t)
256 }
257
258 pub fn wrap<F>(data: Box<F>) -> Self
259 where
260 F: IntoWasmClosure<T> + ?Sized + MaybeUnwindSafe,
261 {
262 <F as IntoWasmClosure<T>>::into_closure_box(data)
263 }
264
265 pub fn wrap_aborting<F>(data: Box<F>) -> Self
266 where
267 F: IntoWasmClosure<T> + ?Sized,
268 {
269 <F as IntoWasmClosure<T>>::into_closure_box(data)
270 }
271
272 pub fn wrap_assert_unwind_safe<F>(data: Box<F>) -> Self
273 where
274 F: IntoWasmClosure<T> + ?Sized,
275 {
276 <F as IntoWasmClosure<T>>::into_closure_box(data)
277 }
278
279 pub fn borrow<'a, F>(t: &'a F) -> ScopedClosure<'a, T::Static>
280 where
281 F: IntoWasmClosureRef<T> + MaybeUnwindSafe + ?Sized,
282 {
283 F::into_scoped_closure_ref(t)
284 }
285
286 pub fn borrow_aborting<'a, F>(t: &'a F) -> ScopedClosure<'a, T::Static>
287 where
288 F: IntoWasmClosureRef<T> + ?Sized,
289 {
290 F::into_scoped_closure_ref(t)
291 }
292
293 pub fn borrow_assert_unwind_safe<'a, F>(t: &'a F) -> ScopedClosure<'a, T::Static>
294 where
295 F: IntoWasmClosureRef<T> + ?Sized,
296 {
297 F::into_scoped_closure_ref(t)
298 }
299
300 pub fn borrow_mut<'a, F>(t: &'a mut F) -> ScopedClosure<'a, T::Static>
301 where
302 F: IntoWasmClosureRefMut<T> + MaybeUnwindSafe + ?Sized,
303 {
304 F::into_scoped_closure_ref_mut(t)
305 }
306
307 pub fn borrow_mut_aborting<'a, F>(t: &'a mut F) -> ScopedClosure<'a, T::Static>
308 where
309 F: IntoWasmClosureRefMut<T> + ?Sized,
310 {
311 F::into_scoped_closure_ref_mut(t)
312 }
313
314 pub fn borrow_mut_assert_unwind_safe<'a, F>(t: &'a mut F) -> ScopedClosure<'a, T::Static>
315 where
316 F: IntoWasmClosureRefMut<T> + ?Sized,
317 {
318 F::into_scoped_closure_ref_mut(t)
319 }
320
321 pub fn once<F, A, R>(fn_once: F) -> Self
327 where
328 F: WasmClosureFnOnce<T, A, R> + MaybeUnwindSafe,
329 {
330 <F as WasmClosureFnOnce<T, A, R>>::into_closure(fn_once)
331 }
332
333 pub fn once_aborting<F, A, R>(fn_once: F) -> Self
334 where
335 F: WasmClosureFnOnceAbort<T, A, R>,
336 {
337 <F as WasmClosureFnOnceAbort<T, A, R>>::into_closure(fn_once)
338 }
339
340 pub fn once_assert_unwind_safe<F, A, R>(fn_once: F) -> Self
341 where
342 F: WasmClosureFnOnceAbort<T, A, R>,
343 {
344 Self::once_aborting(fn_once)
345 }
346
347 pub fn forget(self) {
349 core::mem::forget(self);
350 }
351
352 pub fn into_js_value(self) -> JsValue {
354 let value = core::mem::ManuallyDrop::new(self);
355 value.value.clone()
357 }
358
359 pub fn once_into_js<F, A, R>(fn_once: F) -> JsValue
364 where
365 F: WasmClosureFnOnce<T, A, R> + MaybeUnwindSafe,
366 {
367 Self::once(fn_once).into_js_value()
368 }
369}
370
371#[cfg(test)]
372mod tests {
373 use core::panic::{RefUnwindSafe, UnwindSafe};
374
375 use super::ScopedClosure;
376
377 fn assert_auto_traits<T: Send + Sync + UnwindSafe + RefUnwindSafe>() {}
378
379 #[test]
380 fn scoped_closure_preserves_auto_traits() {
381 assert_auto_traits::<
382 ScopedClosure<'static, dyn Fn() + Send + Sync + UnwindSafe + RefUnwindSafe>,
383 >();
384 assert_auto_traits::<
385 ScopedClosure<'static, dyn FnMut() + Send + Sync + UnwindSafe + RefUnwindSafe>,
386 >();
387 }
388}