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.
121 lines
4.1 KiB
121 lines
4.1 KiB
// This example goes into the details on how to pass closures as functions |
|
// inside Rust and call them from Javascript. |
|
|
|
use boa_engine::{ |
|
object::{FunctionBuilder, JsObject}, |
|
property::{Attribute, PropertyDescriptor}, |
|
Context, JsString, JsValue, |
|
}; |
|
use boa_gc::{Finalize, Trace}; |
|
|
|
fn main() -> Result<(), JsValue> { |
|
// We create a new `Context` to create a new Javascript executor. |
|
let mut context = Context::default(); |
|
|
|
// 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; |
|
|
|
// We register a global closure function that has the name 'closure' with length 0. |
|
context.register_global_closure("closure", 0, move |_, _, _| { |
|
println!("Called `closure`"); |
|
// `variable` is captured from the main function. |
|
println!("variable = {variable}"); |
|
println!(); |
|
|
|
// We return the moved variable as a `JsValue`. |
|
Ok(JsValue::new(variable)) |
|
})?; |
|
|
|
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 { |
|
greeting: JsString::from("Hello!"), |
|
object, |
|
}; |
|
|
|
// We can use `FunctionBuilder` to define a closure with additional |
|
// captures. |
|
let js_function = FunctionBuilder::closure_with_captures( |
|
&mut context, |
|
|_, _, captures, context| { |
|
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(), |
|
]); |
|
|
|
// We can also mutate the moved data inside the closure. |
|
captures.greeting = format!("{} Hello!", captures.greeting).into(); |
|
|
|
println!("{message}"); |
|
println!(); |
|
|
|
// 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, |
|
); |
|
|
|
assert_eq!( |
|
context.eval("createMessage()")?, |
|
"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() |
|
); |
|
|
|
// We have moved `Clone` variables into a closure and executed that closure |
|
// inside Javascript! |
|
|
|
Ok(()) |
|
}
|
|
|