mirror of https://github.com/boa-dev/boa.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
122 lines
4.1 KiB
122 lines
4.1 KiB
3 years ago
|
// This example goes into the details on how to pass closures as functions
|
||
|
// inside Rust and call them from Javascript.
|
||
|
|
||
3 years ago
|
use boa_engine::{
|
||
3 years ago
|
object::{FunctionBuilder, JsObject},
|
||
|
property::{Attribute, PropertyDescriptor},
|
||
|
Context, JsString, JsValue,
|
||
|
};
|
||
3 years ago
|
use boa_gc::{Finalize, Trace};
|
||
3 years ago
|
|
||
3 years ago
|
fn main() -> Result<(), JsValue> {
|
||
3 years ago
|
// We create a new `Context` to create a new Javascript executor.
|
||
3 years ago
|
let mut context = Context::default();
|
||
3 years ago
|
|
||
3 years ago
|
// We make some operations in Rust that return a `Copy` value that we want
|
||
|
// to pass to a Javascript function.
|
||
|
let variable = 128 + 64 + 32 + 16 + 8 + 4 + 2 + 1;
|
||
3 years ago
|
|
||
|
// We register a global closure function that has the name 'closure' with length 0.
|
||
|
context.register_global_closure("closure", 0, move |_, _, _| {
|
||
3 years ago
|
println!("Called `closure`");
|
||
|
// `variable` is captured from the main function.
|
||
3 years ago
|
println!("variable = {variable}");
|
||
3 years ago
|
println!();
|
||
3 years ago
|
|
||
|
// We return the moved variable as a `JsValue`.
|
||
3 years ago
|
Ok(JsValue::new(variable))
|
||
3 years ago
|
})?;
|
||
|
|
||
3 years ago
|
assert_eq!(context.eval("closure()")?, 255.into());
|
||
|
|
||
|
// We have created a closure with moved variables and executed that closure
|
||
|
// inside Javascript!
|
||
|
|
||
|
// This struct is passed to a closure as a capture.
|
||
|
#[derive(Debug, Clone, Trace, Finalize)]
|
||
|
struct BigStruct {
|
||
|
greeting: JsString,
|
||
|
object: JsObject,
|
||
|
}
|
||
|
|
||
|
// We create a new `JsObject` with some data
|
||
|
let object = context.construct_object();
|
||
|
object.define_property_or_throw(
|
||
|
"name",
|
||
|
PropertyDescriptor::builder()
|
||
|
.value("Boa dev")
|
||
|
.writable(false)
|
||
|
.enumerable(false)
|
||
|
.configurable(false),
|
||
|
&mut context,
|
||
|
)?;
|
||
|
|
||
|
// Now, we execute some operations that return a `Clone` type
|
||
|
let clone_variable = BigStruct {
|
||
3 years ago
|
greeting: JsString::from("Hello!"),
|
||
3 years ago
|
object,
|
||
|
};
|
||
|
|
||
|
// We can use `FunctionBuilder` to define a closure with additional
|
||
|
// captures.
|
||
|
let js_function = FunctionBuilder::closure_with_captures(
|
||
|
&mut context,
|
||
3 years ago
|
|_, _, captures, context| {
|
||
3 years ago
|
println!("Called `createMessage`");
|
||
|
// We obtain the `name` property of `captures.object`
|
||
|
let name = captures.object.get("name", context)?;
|
||
|
|
||
|
// We create a new message from our captured variable.
|
||
|
let message = JsString::concat_array(&[
|
||
|
"message from `",
|
||
|
name.to_string(context)?.as_str(),
|
||
|
"`: ",
|
||
|
captures.greeting.as_str(),
|
||
|
]);
|
||
|
|
||
3 years ago
|
// We can also mutate the moved data inside the closure.
|
||
|
captures.greeting = format!("{} Hello!", captures.greeting).into();
|
||
|
|
||
3 years ago
|
println!("{message}");
|
||
3 years ago
|
println!();
|
||
3 years ago
|
|
||
|
// We convert `message` into `Jsvalue` to be able to return it.
|
||
|
Ok(message.into())
|
||
|
},
|
||
|
// Here is where we move `clone_variable` into the closure.
|
||
|
clone_variable,
|
||
|
)
|
||
|
// And here we assign `createMessage` to the `name` property of the closure.
|
||
|
.name("createMessage")
|
||
|
// By default all `FunctionBuilder`s set the `length` property to `0` and
|
||
|
// the `constructable` property to `false`.
|
||
|
.build();
|
||
|
|
||
|
// We bind the newly constructed closure as a global property in Javascript.
|
||
|
context.register_global_property(
|
||
|
// We set the key to access the function the same as its name for
|
||
|
// consistency, but it may be different if needed.
|
||
|
"createMessage",
|
||
|
// We pass `js_function` as a property value.
|
||
|
js_function,
|
||
|
// We assign to the "createMessage" property the desired attributes.
|
||
|
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
|
||
|
);
|
||
|
|
||
3 years ago
|
assert_eq!(
|
||
3 years ago
|
context.eval("createMessage()")?,
|
||
3 years ago
|
"message from `Boa dev`: Hello!".into()
|
||
|
);
|
||
|
|
||
|
// The data mutates between calls
|
||
|
assert_eq!(
|
||
|
context.eval("createMessage(); createMessage();")?,
|
||
|
"message from `Boa dev`: Hello! Hello! Hello!".into()
|
||
3 years ago
|
);
|
||
|
|
||
3 years ago
|
// We have moved `Clone` variables into a closure and executed that closure
|
||
|
// inside Javascript!
|
||
|
|
||
3 years ago
|
Ok(())
|
||
|
}
|