Rust编写的JavaScript引擎,该项目是一个试验性质的项目。
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

// 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(())
}