feat: works
This commit is contained in:
@@ -0,0 +1,154 @@
|
||||
// NOTE: this example requires the `console` feature to run correctly.
|
||||
use boa_engine::{
|
||||
class::{Class, ClassBuilder},
|
||||
property::Attribute,
|
||||
Context, JsResult, JsValue,
|
||||
};
|
||||
|
||||
use boa_gc::{Finalize, Trace};
|
||||
|
||||
// We create a new struct that is going to represent a person.
|
||||
//
|
||||
// We derive `Debug`, `Trace` and `Finalize`, it automatically implements `NativeObject`
|
||||
// so we can pass it as an object in Javascript.
|
||||
//
|
||||
// The fields of the struct are not accessible by Javascript unless we create accessors for them.
|
||||
/// Represents a `Person` object.
|
||||
#[derive(Debug, Trace, Finalize)]
|
||||
struct Person {
|
||||
/// The name of the person.
|
||||
name: String,
|
||||
/// The age of the person.
|
||||
age: u32,
|
||||
}
|
||||
|
||||
// Here we implement a static method for Person that matches the `NativeFunction` signature.
|
||||
//
|
||||
// NOTE: The function does not have to be implemented inside Person, it can be a free function,
|
||||
// or any function that matches the required signature.
|
||||
impl Person {
|
||||
/// Says hello if `this` is a `Person`
|
||||
fn say_hello(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
|
||||
// We check if this is an object.
|
||||
if let Some(object) = this.as_object() {
|
||||
// If it is we downcast the type to type `Person`.
|
||||
if let Some(person) = object.downcast_ref::<Person>() {
|
||||
// and print a message to stdout.
|
||||
println!(
|
||||
"Hello my name is {}, I'm {} years old",
|
||||
person.name,
|
||||
person.age // Here we can access the native rust fields of the struct.
|
||||
);
|
||||
return Ok(JsValue::undefined());
|
||||
}
|
||||
}
|
||||
// If `this` was not an object or the type of `this` was not a native object `Person`,
|
||||
// we throw a `TypeError`.
|
||||
context.throw_type_error("'this' is not a Person object")
|
||||
}
|
||||
}
|
||||
|
||||
impl Class for Person {
|
||||
// We set the binding name of this function to `"Person"`.
|
||||
// It does not have to be `"Person"`, it can be any string.
|
||||
const NAME: &'static str = "Person";
|
||||
// We set the length to `2` since we accept 2 arguments in the constructor.
|
||||
//
|
||||
// This is the same as `Object.length`.
|
||||
// NOTE: The default value of `LENGTH` is `0`.
|
||||
const LENGTH: usize = 2;
|
||||
|
||||
// This is what is called when we construct a `Person` with the expression `new Person()`.
|
||||
fn constructor(_this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<Self> {
|
||||
// We get the first argument. If it is unavailable we default to `undefined`,
|
||||
// and then we call `to_string()`.
|
||||
//
|
||||
// This is equivalent to `String(arg)`.
|
||||
let name = args
|
||||
.get(0)
|
||||
.cloned()
|
||||
.unwrap_or_default()
|
||||
.to_string(context)?;
|
||||
// We get the second argument. If it is unavailable we default to `undefined`,
|
||||
// and then we call `to_u32`.
|
||||
//
|
||||
// This is equivalent to `arg | 0`.
|
||||
let age = args.get(1).cloned().unwrap_or_default().to_u32(context)?;
|
||||
|
||||
// We construct a new native struct `Person`
|
||||
let person = Person {
|
||||
name: name.to_string(),
|
||||
age,
|
||||
};
|
||||
|
||||
Ok(person) // and we return it.
|
||||
}
|
||||
|
||||
/// Here is where the class is initialized.
|
||||
fn init(class: &mut ClassBuilder) -> JsResult<()> {
|
||||
// We add a inheritable method `sayHello` with `0` arguments of length.
|
||||
//
|
||||
// This function is added to the `Person` prototype.
|
||||
class.method("sayHello", 0, Self::say_hello);
|
||||
// We add a static method `is` using a closure, but it must be convertible
|
||||
// to a NativeFunction.
|
||||
// This means it must not contain state, or the code won't compile.
|
||||
//
|
||||
// This function is added to the `Person` class.
|
||||
class.static_method("is", 1, |_this, args, _ctx| {
|
||||
if let Some(arg) = args.get(0) {
|
||||
if let Some(object) = arg.as_object() {
|
||||
// We check if the type of `args[0]` is `Person`
|
||||
if object.is::<Person>() {
|
||||
return Ok(true.into()); // and return `true` if it is.
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(false.into()) // Otherwise we return `false`.
|
||||
});
|
||||
|
||||
// We add an `"inheritedProperty"` property to the prototype of `Person` with
|
||||
// a value of `10` and default attribute flags `READONLY`, `NON_ENUMERABLE` and `PERMANENT`.
|
||||
class.property("inheritedProperty", 10, Attribute::default());
|
||||
|
||||
// Finally, we add a `"staticProperty"` property to `Person` with a value
|
||||
// of `"Im a static property"` and attribute flags `WRITABLE`, `ENUMERABLE` and `PERMANENT`.
|
||||
class.static_property(
|
||||
"staticProperty",
|
||||
"Im a static property",
|
||||
Attribute::WRITABLE | Attribute::ENUMERABLE | Attribute::PERMANENT,
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// First we need to create a Javascript context.
|
||||
let mut context = Context::default();
|
||||
|
||||
// Then we need to register the global class `Person` inside `context`.
|
||||
context.register_global_class::<Person>().unwrap();
|
||||
|
||||
// Having done all of that, we can execute Javascript code with `eval`,
|
||||
// and access the `Person` class defined in Rust!
|
||||
context
|
||||
.eval(
|
||||
r"
|
||||
let person = new Person('John', 19);
|
||||
person.sayHello();
|
||||
|
||||
if (Person.is(person)) {
|
||||
console.log('person is a Person class instance.');
|
||||
}
|
||||
if (!Person.is('Hello')) {
|
||||
console.log('\'Hello\' string is not a Person class instance.');
|
||||
}
|
||||
|
||||
console.log(Person.staticProperty);
|
||||
console.log(person.inheritedProperty);
|
||||
console.log(Person.prototype.inheritedProperty === person.inheritedProperty);
|
||||
",
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
// 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(())
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
// This example shows how to manipulate a Javascript array using Rust code.
|
||||
|
||||
use boa_engine::{
|
||||
object::{FunctionBuilder, JsArray},
|
||||
Context, JsResult, JsValue,
|
||||
};
|
||||
|
||||
fn main() -> JsResult<()> {
|
||||
// We create a new `Context` to create a new Javascript executor.
|
||||
let context = &mut Context::default();
|
||||
|
||||
// Create an empty array.
|
||||
let array = JsArray::new(context);
|
||||
|
||||
assert!(array.is_empty(context)?);
|
||||
|
||||
array.push("Hello, world", context)?; // [ "Hello, world" ]
|
||||
array.push(true, context)?; // [ "Hello, world", true ]
|
||||
|
||||
assert!(!array.is_empty(context)?);
|
||||
|
||||
assert_eq!(array.pop(context)?, JsValue::new(true)); // [ "Hello, world" ]
|
||||
assert_eq!(array.pop(context)?, JsValue::new("Hello, world")); // [ ]
|
||||
assert_eq!(array.pop(context)?, JsValue::undefined()); // [ ]
|
||||
|
||||
array.push(1, context)?; // [ 1 ]
|
||||
|
||||
assert_eq!(array.pop(context)?, JsValue::new(1)); // [ ]
|
||||
assert_eq!(array.pop(context)?, JsValue::undefined()); // [ ]
|
||||
|
||||
array.push_items(
|
||||
&[
|
||||
JsValue::new(10),
|
||||
JsValue::new(11),
|
||||
JsValue::new(12),
|
||||
JsValue::new(13),
|
||||
JsValue::new(14),
|
||||
],
|
||||
context,
|
||||
)?; // [ 10, 11, 12, 13, 14 ]
|
||||
|
||||
array.reverse(context)?; // [ 14, 13, 12, 11, 10 ]
|
||||
|
||||
assert_eq!(array.index_of(12, None, context)?, Some(2));
|
||||
|
||||
// We can also use JsObject method `.get()` through the Deref trait.
|
||||
let element = array.get(2, context)?; // array[ 0 ]
|
||||
assert_eq!(element, JsValue::new(12));
|
||||
// Or we can use the `.at(index)` method.
|
||||
assert_eq!(array.at(0, context)?, JsValue::new(14)); // first element
|
||||
assert_eq!(array.at(-1, context)?, JsValue::new(10)); // last element
|
||||
|
||||
// Join the array with an optional separator (default ",").
|
||||
let joined_array = array.join(None, context)?;
|
||||
assert_eq!(joined_array, "14,13,12,11,10");
|
||||
|
||||
array.fill(false, Some(1), Some(4), context)?;
|
||||
|
||||
let joined_array = array.join(Some("::".into()), context)?;
|
||||
assert_eq!(joined_array, "14::false::false::false::10");
|
||||
|
||||
let filter_callback = FunctionBuilder::native(context, |_this, args, _context| {
|
||||
Ok(args.get(0).cloned().unwrap_or_default().is_number().into())
|
||||
})
|
||||
.build();
|
||||
|
||||
let map_callback = FunctionBuilder::native(context, |_this, args, context| {
|
||||
args.get(0)
|
||||
.cloned()
|
||||
.unwrap_or_default()
|
||||
.pow(&JsValue::new(2), context)
|
||||
})
|
||||
.build();
|
||||
|
||||
let mut data = Vec::new();
|
||||
for i in 1..=5 {
|
||||
data.push(JsValue::new(i));
|
||||
}
|
||||
let another_array = JsArray::from_iter(data, context); // [ 1, 2, 3, 4, 5]
|
||||
|
||||
let chained_array = array // [ 14, false, false, false, 10 ]
|
||||
.filter(filter_callback, None, context)? // [ 14, 10 ]
|
||||
.map(map_callback, None, context)? // [ 196, 100 ]
|
||||
.sort(None, context)? // [ 100, 196 ]
|
||||
.concat(&[another_array.into()], context)? // [ 100, 196, 1, 2, 3, 4, 5 ]
|
||||
.slice(Some(1), Some(5), context)?; // [ 196, 1, 2, 3 ]
|
||||
|
||||
assert_eq!(chained_array.join(None, context)?, "196,1,2,3");
|
||||
|
||||
let reduce_callback = FunctionBuilder::native(context, |_this, args, context| {
|
||||
let accumulator = args.get(0).cloned().unwrap_or_default();
|
||||
let value = args.get(1).cloned().unwrap_or_default();
|
||||
|
||||
accumulator.add(&value, context)
|
||||
})
|
||||
.build();
|
||||
|
||||
assert_eq!(
|
||||
chained_array.reduce(reduce_callback, Some(JsValue::new(0)), context)?,
|
||||
JsValue::new(202)
|
||||
);
|
||||
|
||||
context
|
||||
.global_object()
|
||||
.clone()
|
||||
.set("myArray", array, true, context)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// This example shows how to manipulate a Javascript array using Rust code.
|
||||
|
||||
use boa_engine::{
|
||||
object::{JsArrayBuffer, JsUint32Array, JsUint8Array},
|
||||
property::Attribute,
|
||||
Context, JsResult, JsValue,
|
||||
};
|
||||
|
||||
fn main() -> JsResult<()> {
|
||||
// We create a new `Context` to create a new Javascript executor.
|
||||
let context = &mut Context::default();
|
||||
|
||||
// This create an array buffer of byte length 4
|
||||
let array_buffer = JsArrayBuffer::new(4, context)?;
|
||||
|
||||
// We can now create an typed array to access the data.
|
||||
let uint32_typed_array = JsUint32Array::from_array_buffer(array_buffer, context)?;
|
||||
|
||||
let value = 0x12345678u32;
|
||||
uint32_typed_array.set(0, value, true, context)?;
|
||||
|
||||
assert_eq!(uint32_typed_array.get(0, context)?, JsValue::new(value));
|
||||
|
||||
// We can also create array buffers from a user defined block of data.
|
||||
//
|
||||
// NOTE: The block data will not be cloned.
|
||||
let blob_of_data: Vec<u8> = (0..=255).collect();
|
||||
let array_buffer = JsArrayBuffer::from_byte_block(blob_of_data, context)?;
|
||||
|
||||
// This the byte length of the new array buffer will be the length of block of data.
|
||||
let byte_length = array_buffer.byte_length(context);
|
||||
assert_eq!(byte_length, 256);
|
||||
|
||||
// We can now create an typed array to access the data.
|
||||
let uint8_typed_array = JsUint8Array::from_array_buffer(array_buffer.clone(), context)?;
|
||||
|
||||
for i in 0..byte_length {
|
||||
assert_eq!(uint8_typed_array.get(i, context)?, JsValue::new(i));
|
||||
}
|
||||
|
||||
// We can also register it as a global property
|
||||
context.register_global_property(
|
||||
"myArrayBuffer",
|
||||
array_buffer,
|
||||
Attribute::WRITABLE | Attribute::ENUMERABLE | Attribute::CONFIGURABLE,
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
use boa_engine::{
|
||||
object::{JsArray, JsMap},
|
||||
Context, JsResult, JsValue,
|
||||
};
|
||||
|
||||
fn main() -> JsResult<()> {
|
||||
// Create a `Context` for the Javascript executor.
|
||||
let context = &mut Context::default();
|
||||
|
||||
// Create a new empty map.
|
||||
let map = JsMap::new(context);
|
||||
|
||||
// Set a key-value for the map.
|
||||
map.set("Key-1", "Value-1", context)?;
|
||||
|
||||
let map_check = map.has("Key-1", context)?;
|
||||
assert_eq!(map_check, true.into()); // true
|
||||
|
||||
// Set a second key-value to the same map.
|
||||
map.set(2, 4, context)?;
|
||||
|
||||
assert_eq!(map.get_size(context)?, 2.into()); //true
|
||||
|
||||
assert_eq!(map.get("Key-1", context)?, "Value-1".into());
|
||||
assert_eq!(map.get(2, context)?, 4.into());
|
||||
// Delete an entry with a provided key.
|
||||
map.delete("Key-1", context)?;
|
||||
assert_eq!(map.get_size(context)?, 1.into());
|
||||
|
||||
let deleted_key_one = map.get("Key-1", context)?;
|
||||
|
||||
assert_eq!(deleted_key_one, JsValue::undefined());
|
||||
|
||||
// Retrieve a MapIterator for all entries in the Map.
|
||||
let entries = map.entries(context)?;
|
||||
|
||||
let _first_value = entries.next(context)?;
|
||||
|
||||
// Create a multidimensional array with key value pairs -> [[first-key, first-value], [second-key, second-value]]
|
||||
let js_array = JsArray::new(context);
|
||||
|
||||
let vec_one = vec![JsValue::new("first-key"), JsValue::new("first-value")];
|
||||
let vec_two = vec![JsValue::new("second-key"), JsValue::new("second-value")];
|
||||
|
||||
js_array.push(JsArray::from_iter(vec_one, context), context)?;
|
||||
js_array.push(JsArray::from_iter(vec_two, context), context)?;
|
||||
|
||||
// Create a map from the JsArray using it's iterable property.
|
||||
let iter_map = JsMap::from_js_iterable(&js_array.into(), context)?;
|
||||
|
||||
assert_eq!(iter_map.get("first-key", context)?, "first-value".into());
|
||||
|
||||
iter_map.set("third-key", "third-value", context)?;
|
||||
|
||||
assert_eq!(iter_map.get_size(context)?, JsValue::new(3));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// This example shows how to manipulate a Javascript Set using Rust code.
|
||||
#![allow(clippy::bool_assert_comparison)]
|
||||
use boa_engine::{object::JsSet, Context, JsValue};
|
||||
|
||||
fn main() -> Result<(), JsValue> {
|
||||
// New `Context` for a new Javascript executor.
|
||||
let context = &mut Context::default();
|
||||
|
||||
// Create an empty set.
|
||||
let set = JsSet::new(context);
|
||||
|
||||
assert_eq!(set.size(context)?, 0);
|
||||
set.add(5, context)?;
|
||||
assert_eq!(set.size(context)?, 1);
|
||||
set.add(10, context)?;
|
||||
assert_eq!(set.size(context)?, 2);
|
||||
set.clear(context)?;
|
||||
assert_eq!(set.size(context)?, 0);
|
||||
|
||||
set.add("one", context)?;
|
||||
set.add("two", context)?;
|
||||
set.add("three", context)?;
|
||||
|
||||
assert!(set.has("one", context)?);
|
||||
assert_eq!(set.has("One", context)?, false);
|
||||
|
||||
set.delete("two", context)?;
|
||||
|
||||
assert_eq!(set.has("two", context)?, false);
|
||||
|
||||
set.clear(context)?;
|
||||
|
||||
assert_eq!(set.has("one", context)?, false);
|
||||
assert_eq!(set.has("three", context)?, false);
|
||||
assert_eq!(set.size(context)?, 0);
|
||||
|
||||
// Add a slice into a set;
|
||||
set.add_items(
|
||||
&[JsValue::new(1), JsValue::new(2), JsValue::new(3)],
|
||||
context,
|
||||
)?;
|
||||
// Will return 1, as one slice was added.
|
||||
assert_eq!(set.size(context)?, 1);
|
||||
|
||||
// Make a new set from a slice
|
||||
let slice_set = JsSet::from_iter([JsValue::new(1), JsValue::new(2), JsValue::new(3)], context);
|
||||
// Will return 3, as each element of slice was added into the set.
|
||||
assert_eq!(slice_set.size(context)?, 3);
|
||||
|
||||
set.clear(context)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
// This example shows how to manipulate a Javascript array using Rust code.
|
||||
|
||||
use boa_engine::{
|
||||
object::{FunctionBuilder, JsUint8Array},
|
||||
property::Attribute,
|
||||
Context, JsResult, JsValue,
|
||||
};
|
||||
|
||||
fn main() -> JsResult<()> {
|
||||
// We create a new `Context` to create a new Javascript executor.
|
||||
let context = &mut Context::default();
|
||||
|
||||
let data: Vec<u8> = (0..=255).collect();
|
||||
|
||||
let array = JsUint8Array::from_iter(data, context)?;
|
||||
|
||||
assert_eq!(array.get(0, context)?, JsValue::new(0));
|
||||
|
||||
let mut sum = 0;
|
||||
|
||||
for i in 0..=255 {
|
||||
assert_eq!(array.at(i, context)?, JsValue::new(i));
|
||||
sum += i;
|
||||
}
|
||||
|
||||
let callback = FunctionBuilder::native(context, |_this, args, context| {
|
||||
let accumulator = args.get(0).cloned().unwrap_or_default();
|
||||
let value = args.get(1).cloned().unwrap_or_default();
|
||||
|
||||
accumulator.add(&value, context)
|
||||
})
|
||||
.build();
|
||||
|
||||
assert_eq!(
|
||||
array.reduce(callback, Some(JsValue::new(0)), context)?,
|
||||
JsValue::new(sum)
|
||||
);
|
||||
|
||||
context.register_global_property(
|
||||
"myUint8Array",
|
||||
array,
|
||||
Attribute::WRITABLE | Attribute::ENUMERABLE | Attribute::CONFIGURABLE,
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// This example shows how to load, parse and execute JS code from a source file
|
||||
// (./scripts/helloworld.js)
|
||||
|
||||
use std::fs::read_to_string;
|
||||
|
||||
use boa_engine::Context;
|
||||
|
||||
fn main() {
|
||||
let js_file_path = "./scripts/helloworld.js";
|
||||
|
||||
match read_to_string(js_file_path) {
|
||||
Ok(src) => {
|
||||
// Instantiate the execution context
|
||||
let mut context = Context::default();
|
||||
// Parse the source code
|
||||
match context.eval(src) {
|
||||
Ok(res) => {
|
||||
println!("{}", res.to_string(&mut context).unwrap());
|
||||
}
|
||||
Err(e) => {
|
||||
// Pretty print the error
|
||||
eprintln!("Uncaught {}", e.display());
|
||||
}
|
||||
};
|
||||
}
|
||||
Err(msg) => eprintln!("Error: {}", msg),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// This example loads, parses and executes a JS code string
|
||||
|
||||
use boa_engine::Context;
|
||||
|
||||
fn main() {
|
||||
let js_code = "console.log('Hello World from a JS code string!')";
|
||||
|
||||
// Instantiate the execution context
|
||||
let mut context = Context::default();
|
||||
|
||||
// Parse the source code
|
||||
match context.eval(js_code) {
|
||||
Ok(res) => {
|
||||
println!("{}", res.to_string(&mut context).unwrap());
|
||||
}
|
||||
Err(e) => {
|
||||
// Pretty print the error
|
||||
eprintln!("Uncaught {}", e.display());
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
// This example implements a custom module handler which mimics
|
||||
// the require/module.exports pattern
|
||||
|
||||
use boa_engine::{prelude::JsObject, property::Attribute, Context, JsResult, JsValue};
|
||||
use std::fs::read_to_string;
|
||||
|
||||
fn main() {
|
||||
let js_file_path = "./scripts/calctest.js";
|
||||
let buffer = read_to_string(js_file_path);
|
||||
|
||||
if buffer.is_err() {
|
||||
println!("Error: {}", buffer.unwrap_err());
|
||||
return;
|
||||
}
|
||||
|
||||
// Creating the execution context
|
||||
let mut ctx = Context::default();
|
||||
|
||||
// Adding custom implementation that mimics 'require'
|
||||
ctx.register_global_function("require", 0, require);
|
||||
|
||||
// Adding custom object that mimics 'module.exports'
|
||||
let moduleobj = JsObject::default();
|
||||
moduleobj
|
||||
.set("exports", JsValue::from(" "), false, &mut ctx)
|
||||
.unwrap();
|
||||
ctx.register_global_property("module", JsValue::from(moduleobj), Attribute::default());
|
||||
|
||||
// Instantiating the engine with the execution context
|
||||
// Loading, parsing and executing the JS code from the source file
|
||||
ctx.eval(&buffer.unwrap()).unwrap();
|
||||
}
|
||||
|
||||
// Custom implementation that mimics the 'require' module loader
|
||||
fn require(_: &JsValue, args: &[JsValue], ctx: &mut Context) -> JsResult<JsValue> {
|
||||
let arg = args.get(0).unwrap();
|
||||
|
||||
// BUG: Dev branch seems to be passing string arguments along with quotes
|
||||
let libfile = arg
|
||||
.to_string(ctx)
|
||||
.expect("Failed to convert to string")
|
||||
.to_string();
|
||||
|
||||
// Read the module source file
|
||||
println!("Loading: {}", libfile);
|
||||
let buffer = read_to_string(libfile);
|
||||
if let Err(..) = buffer {
|
||||
println!("Error: {}", buffer.unwrap_err());
|
||||
Ok(JsValue::Rational(-1.0))
|
||||
} else {
|
||||
// Load and parse the module source
|
||||
ctx.eval(&buffer.unwrap()).unwrap();
|
||||
|
||||
// Access module.exports and return as ResultValue
|
||||
let global_obj = ctx.global_object().to_owned();
|
||||
let module = global_obj.get("module", ctx).unwrap();
|
||||
module.as_object().unwrap().get("exports", ctx)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user