Skip to main content

boa_engine/interop/
mod.rs

1//! Interop module containing traits and types to ease integration between Boa
2//! and Rust.
3
4/// Internal module only.
5pub(crate) mod private {
6    /// A sealed trait to prevent users from implementing the `IntoJsModuleFunction`
7    /// and `IntoJsFunctionUnsafe` traits to their own types.
8    pub trait IntoJsFunctionSealed<A, R> {}
9}
10
11/// A trait to convert a type into a JS function.
12/// This trait does not require the implementing type to be `Copy`, which
13/// can lead to undefined behaviour if it contains Garbage Collected objects.
14///
15/// This trait is implemented for functions with various signatures.
16///
17/// For example:
18/// ```
19/// # use boa_engine::{UnsafeIntoJsFunction, Context, JsValue, NativeFunction};
20/// # let mut context = Context::default();
21/// let f = |a: i32, b: i32| a + b;
22/// let f = unsafe { f.into_js_function_unsafe(&mut context) };
23/// let result = f
24///     .call(
25///         &JsValue::undefined(),
26///         &[JsValue::from(1), JsValue::from(2)],
27///         &mut context,
28///     )
29///     .unwrap();
30/// assert_eq!(result, JsValue::new(3));
31/// ```
32///
33/// Since the `IntoJsFunctionUnsafe` trait is implemented for `FnMut`, you can
34/// also use closures directly:
35/// ```
36/// # use boa_engine::{UnsafeIntoJsFunction, Context, JsValue, NativeFunction};
37/// # use std::cell::RefCell;
38/// # use std::rc::Rc;
39/// # let mut context = Context::default();
40/// let mut x = Rc::new(RefCell::new(0));
41/// // Because NativeFunction takes ownership of the closure,
42/// // the compiler cannot be certain it won't outlive `x`, so
43/// // we need to create a `Rc<RefCell>` and share it.
44/// let f = unsafe {
45///     let x = x.clone();
46///     move |a: i32| *x.borrow_mut() += a
47/// };
48/// let f = unsafe { f.into_js_function_unsafe(&mut context) };
49/// f.call(&JsValue::undefined(), &[JsValue::from(1)], &mut context)
50///     .unwrap();
51/// f.call(&JsValue::undefined(), &[JsValue::from(4)], &mut context)
52///     .unwrap();
53/// assert_eq!(*x.borrow(), 5);
54/// ```
55pub trait UnsafeIntoJsFunction<Args, Ret>: private::IntoJsFunctionSealed<Args, Ret> {
56    /// Converts the type into a JS function.
57    ///
58    /// # Safety
59    /// This function is unsafe to ensure the callee knows the risks of using this trait.
60    /// The implementing type must not contain any garbage collected objects.
61    unsafe fn into_js_function_unsafe(self, context: &mut Context) -> NativeFunction;
62}
63
64/// The safe equivalent of the [`UnsafeIntoJsFunction`] trait.
65/// This can only be used on closures that have the `Copy` trait.
66///
67/// Since this function is implemented for `Fn(...)` closures, we can use
68/// it directly when defining a function:
69/// ```
70/// # use boa_engine::{IntoJsFunctionCopied, Context, JsValue, NativeFunction};
71/// # let mut context = Context::default();
72/// let f = |a: i32, b: i32| a + b;
73/// let f = f.into_js_function_copied(&mut context);
74/// let result = f
75///     .call(
76///         &JsValue::undefined(),
77///         &[JsValue::from(1), JsValue::from(2)],
78///         &mut context,
79///     )
80///     .unwrap();
81/// assert_eq!(result, JsValue::new(3));
82/// ```
83pub trait IntoJsFunctionCopied<Args, Ret>: private::IntoJsFunctionSealed<Args, Ret> + Copy {
84    /// Converts the type into a JS function.
85    fn into_js_function_copied(self, context: &mut Context) -> NativeFunction;
86}
87
88mod into_js_arguments;
89use crate::{Context, NativeFunction};
90pub use into_js_arguments::*;
91
92// Implement `IntoJsFunction` for functions with a various list of
93// arguments.
94mod into_js_function_impls;