mirror of https://github.com/boa-dev/boa.git
Browse Source
* Allow passing additional `NativeObject` captures to closures * Add test for external closure capturespull/1433/head
jedel1043
3 years ago
committed by
GitHub
6 changed files with 288 additions and 19 deletions
@ -1,21 +1,110 @@
|
||||
use boa::{Context, JsValue}; |
||||
// This example goes into the details on how to pass closures as functions
|
||||
// inside Rust and call them from Javascript.
|
||||
|
||||
use boa::{ |
||||
gc::{Finalize, Trace}, |
||||
object::{FunctionBuilder, JsObject}, |
||||
property::{Attribute, PropertyDescriptor}, |
||||
Context, JsString, JsValue, |
||||
}; |
||||
|
||||
fn main() -> Result<(), JsValue> { |
||||
// We create a new `Context` to create a new Javascript executor.
|
||||
let mut context = Context::new(); |
||||
|
||||
let variable = "I am a captured variable"; |
||||
// 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 |_, _, _| { |
||||
// This value is captured from main function.
|
||||
println!("Called `closure`"); |
||||
// `variable` is captured from the main function.
|
||||
println!("variable = {}", variable); |
||||
|
||||
// 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 from Javascript!"), |
||||
object, |
||||
}; |
||||
|
||||
// We can use `FunctionBuilder` to define a closure with additional
|
||||
// captures.
|
||||
let js_function = FunctionBuilder::closure_with_captures( |
||||
&mut context, |
||||
|_, _, context, captures| { |
||||
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(), |
||||
]); |
||||
|
||||
println!("{}", message); |
||||
|
||||
// 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("closure()")?, |
||||
"I am a captured variable".into() |
||||
context.eval("createMessage()")?, |
||||
"message from `Boa dev`: Hello from Javascript!".into() |
||||
); |
||||
|
||||
// We have moved `Clone` variables into a closure and executed that closure
|
||||
// inside Javascript!
|
||||
|
||||
Ok(()) |
||||
} |
||||
|
Loading…
Reference in new issue