Skip to main content

boa_engine/interop/
into_js_function_impls.rs

1//! Implementations of the `IntoJsFunction` trait for various function signatures.
2
3use super::private::IntoJsFunctionSealed;
4use super::{IntoJsFunctionCopied, UnsafeIntoJsFunction};
5use crate::interop::{JsRest, TryFromJsArgument};
6use crate::{Context, NativeFunction, TryIntoJsResult};
7use std::cell::RefCell;
8
9/// A token to represent the context argument in the function signature.
10/// This should not be used directly and has no external meaning.
11#[derive(Debug, Copy, Clone)]
12pub struct ContextArgToken;
13
14macro_rules! impl_into_js_function {
15    ($($id: ident: $t: ident),*) => {
16        impl<$($t,)* R, T> IntoJsFunctionSealed<($($t,)*), R> for T
17        where
18            $($t: for<'a> TryFromJsArgument<'a> + 'static,)*
19            R: TryIntoJsResult,
20            T: FnMut($($t,)*) -> R + 'static
21        {}
22
23        impl<$($t,)* R, T> IntoJsFunctionSealed<($($t,)* ContextArgToken,), R> for T
24        where
25            $($t: for<'a> TryFromJsArgument<'a> + 'static,)*
26            R: TryIntoJsResult,
27            T: FnMut($($t,)* &mut Context) -> R + 'static
28        {}
29
30        impl<$($t,)* R, T> IntoJsFunctionSealed<($($t,)* JsRest<'_>,), R> for T
31        where
32            $($t: for<'a> TryFromJsArgument<'a> + 'static,)*
33            R: TryIntoJsResult,
34            T: FnMut($($t,)* JsRest<'_>) -> R + 'static
35        {}
36
37        impl<$($t,)* R, T> IntoJsFunctionSealed<($($t,)* JsRest<'_>, ContextArgToken), R> for T
38        where
39            $($t: for<'a> TryFromJsArgument<'a> + 'static,)*
40            R: TryIntoJsResult,
41            T: FnMut($($t,)* JsRest<'_>, &mut Context) -> R + 'static
42        {}
43
44        impl<$($t,)* R, T> UnsafeIntoJsFunction<($($t,)*), R> for T
45        where
46            $($t: for<'a> TryFromJsArgument<'a> + 'static,)*
47            R: TryIntoJsResult,
48            T: FnMut($($t,)*) -> R + 'static,
49        {
50            #[allow(unused_variables)]
51            unsafe fn into_js_function_unsafe(self, _context: &mut Context) -> NativeFunction {
52                let s = RefCell::new(self);
53                unsafe {
54                    NativeFunction::from_closure(move |this, args, ctx| {
55                        let rest = args;
56                        $(
57                            let ($id, rest) = $t::try_from_js_argument(this, rest, ctx)?;
58                        )*
59                        match s.try_borrow_mut() {
60                            Ok(mut r) => r( $($id,)* ).try_into_js_result(ctx),
61                            Err(_) => {
62                                Err($crate::js_error!("recursive calls to this function not supported"))
63                            }
64                        }
65                    })
66                }
67            }
68        }
69
70        impl<$($t,)* R, T> UnsafeIntoJsFunction<($($t,)* JsRest<'_>,), R> for T
71        where
72            $($t: for<'a> TryFromJsArgument<'a> + 'static,)*
73            R: TryIntoJsResult,
74            T: FnMut($($t,)* JsRest<'_>) -> R + 'static,
75        {
76            #[allow(unused_variables)]
77            unsafe fn into_js_function_unsafe(self, _context: &mut Context) -> NativeFunction {
78                let s = RefCell::new(self);
79                unsafe {
80                    NativeFunction::from_closure(move |this, args, ctx| {
81                        let rest = args;
82                        $(
83                            let ($id, rest) = $t::try_from_js_argument(this, rest, ctx)?;
84                        )*
85                        match s.try_borrow_mut() {
86                            Ok(mut r) => r( $($id,)* rest.into() ).try_into_js_result(ctx),
87                            Err(_) => {
88                                Err($crate::js_error!("recursive calls to this function not supported"))
89                            }
90                        }
91                    })
92                }
93            }
94        }
95
96        impl<$($t,)* R, T> UnsafeIntoJsFunction<($($t,)* ContextArgToken,), R> for T
97        where
98            $($t: for<'a> TryFromJsArgument<'a> + 'static,)*
99            R: TryIntoJsResult,
100            T: FnMut($($t,)* &mut Context) -> R + 'static,
101        {
102            #[allow(unused_variables)]
103            unsafe fn into_js_function_unsafe(self, _context: &mut Context) -> NativeFunction {
104                let s = RefCell::new(self);
105                unsafe {
106                    NativeFunction::from_closure(move |this, args, ctx| {
107                        let rest = args;
108                        $(
109                            let ($id, rest) = $t::try_from_js_argument(this, rest, ctx)?;
110                        )*
111                        let r = s.borrow_mut()( $($id,)* ctx);
112                        r.try_into_js_result(ctx)
113                    })
114                }
115            }
116        }
117
118        impl<$($t,)* R, T> UnsafeIntoJsFunction<($($t,)* JsRest<'_>, ContextArgToken), R> for T
119        where
120            $($t: for<'a> TryFromJsArgument<'a> + 'static,)*
121            R: TryIntoJsResult,
122            T: FnMut($($t,)* JsRest<'_>, &mut Context) -> R + 'static,
123        {
124            #[allow(unused_variables)]
125            unsafe fn into_js_function_unsafe(self, _context: &mut Context) -> NativeFunction {
126                let s = RefCell::new(self);
127                unsafe {
128                    NativeFunction::from_closure(move |this, args, ctx| {
129                        let rest = args;
130                        $(
131                            let ($id, rest) = $t::try_from_js_argument(this, rest, ctx)?;
132                        )*
133                        let r = s.borrow_mut()( $($id,)* rest.into(), ctx);
134                        r.try_into_js_result(ctx)
135                    })
136                }
137            }
138        }
139
140        // Safe versions for `Fn(..) -> ...`.
141        impl<$($t,)* R, T> IntoJsFunctionCopied<($($t,)*), R> for T
142        where
143            $($t: for<'a> TryFromJsArgument<'a> + 'static,)*
144            R: TryIntoJsResult,
145            T: Fn($($t,)*) -> R + 'static + Copy,
146        {
147            #[allow(unused_variables)]
148            fn into_js_function_copied(self, _context: &mut Context) -> NativeFunction {
149                let s = self;
150                NativeFunction::from_copy_closure(move |this, args, ctx| {
151                    let rest = args;
152                    $(
153                        let ($id, rest) = $t::try_from_js_argument(this, rest, ctx)?;
154                    )*
155                    let r = s( $($id,)* );
156                    r.try_into_js_result(ctx)
157                })
158            }
159        }
160
161        impl<$($t,)* R, T> IntoJsFunctionCopied<($($t,)* JsRest<'_>,), R> for T
162        where
163            $($t: for<'a> TryFromJsArgument<'a> + 'static,)*
164            R: TryIntoJsResult,
165            T: Fn($($t,)* JsRest<'_>) -> R + 'static + Copy,
166        {
167            #[allow(unused_variables)]
168            fn into_js_function_copied(self, _context: &mut Context) -> NativeFunction {
169                let s = self;
170                NativeFunction::from_copy_closure(move |this, args, ctx| {
171                    let rest = args;
172                    $(
173                        let ($id, rest) = $t::try_from_js_argument(this, rest, ctx)?;
174                    )*
175                    let r = s( $($id,)* rest.into() );
176                    r.try_into_js_result(ctx)
177                })
178            }
179        }
180
181        impl<$($t,)* R, T> IntoJsFunctionCopied<($($t,)* ContextArgToken,), R> for T
182        where
183            $($t: for<'a> TryFromJsArgument<'a> + 'static,)*
184            R: TryIntoJsResult,
185            T: Fn($($t,)* &mut Context) -> R + 'static + Copy,
186        {
187            #[allow(unused_variables)]
188            fn into_js_function_copied(self, _context: &mut Context) -> NativeFunction {
189                let s = self;
190                NativeFunction::from_copy_closure(move |this, args, ctx| {
191                    let rest = args;
192                    $(
193                        let ($id, rest) = $t::try_from_js_argument(this, rest, ctx)?;
194                    )*
195                    let r = s( $($id,)* ctx);
196                    r.try_into_js_result(ctx)
197                })
198            }
199        }
200
201        impl<$($t,)* R, T> IntoJsFunctionCopied<($($t,)* JsRest<'_>, ContextArgToken), R> for T
202        where
203            $($t: for<'a> TryFromJsArgument<'a> + 'static,)*
204            R: TryIntoJsResult,
205            T: Fn($($t,)* JsRest<'_>, &mut Context) -> R + 'static + Copy,
206        {
207            #[allow(unused_variables)]
208            fn into_js_function_copied(self, _context: &mut Context) -> NativeFunction {
209                let s = self;
210                NativeFunction::from_copy_closure(move |this, args, ctx| {
211                    let rest = args;
212                    $(
213                        let ($id, rest) = $t::try_from_js_argument(this, rest, ctx)?;
214                    )*
215                    let r = s( $($id,)* rest.into(), ctx);
216                    r.try_into_js_result(ctx)
217                })
218            }
219        }
220    };
221}
222
223// Currently implemented up to 12 arguments. The empty argument list
224// is implemented separately above.
225// Consider that JsRest and JsThis are part of this list, but Context
226// is not, as it is a special specialization of the template.
227impl_into_js_function!();
228impl_into_js_function!(a: A);
229impl_into_js_function!(a: A, b: B);
230impl_into_js_function!(a: A, b: B, c: C);
231impl_into_js_function!(a: A, b: B, c: C, d: D);
232impl_into_js_function!(a: A, b: B, c: C, d: D, e: E);
233impl_into_js_function!(a: A, b: B, c: C, d: D, e: E, f: F);
234impl_into_js_function!(a: A, b: B, c: C, d: D, e: E, f: F, g: G);
235impl_into_js_function!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H);
236impl_into_js_function!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I);
237impl_into_js_function!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J);
238impl_into_js_function!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K);
239impl_into_js_function!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L);