Use Rust to implement JS API

    Check out the Github repo and change to the folder to follow along.

    git clone https://github.com/second-state/wasmedge-quickjs cd examples/embed_js

    The embed_js demo showcases several different examples on how to embed JavaScript inside Rust. You can build and run all the examples as follows.

    cargo build --target wasm32-wasi --release wasmedge --dir .:. target/wasm32-wasi/release/embed_js.wasm

    The following code snippet shows how to add this Rust function into the JavaScript interpreter, give a name hi() as its JavaScript API, and then call it from JavaScript code.

    1. #![allow(unused)]
    2. fn main() {
    3. fn run_rust_function(ctx: &mut Context) {
    4. ...
    5. let f = ctx.new_function::<HelloFn>("hello");
    6. ctx.get_global().set("hi", f.into());
    7. let code = r#"hi(1,2,3)"#;
    8. let r = ctx.eval_global_str(code);
    9. println!("return value:{:?}", r);
    10. }
    11. }

    The execution result is as follows.

    hello from rust argv=[Int(1), Int(2), Int(3)] return value:UnDefined

    Using this approach, you can create a JavaScript interpreter with customized API functions. The interpreter runs inside WasmEdge, and can execute JavaScript code, which calls such API functions, from CLI or the network.

    In the JavaScript API design, we sometimes need to provide an object that encapsulates both data and function. In the following example, we define a Rust function for the JavaScript API.

    We then create an “object” on the Rust side, set its data fields, and then register the Rust function as a JavaScript function associated with the objects.

    1. #![allow(unused)]
    2. fn main() {
    3. let mut obj = ctx.new_object();
    4. let f = ctx.new_function::<ObjectFn>("anything");
    5. obj.set("f", f.into());
    6. }

    Next, we make the Rust “object” available as JavaScript object test_obj in the JavaScript interpreter.

    1. #![allow(unused)]
    2. fn main() {
    3. let code = r#"
    4. print('test_obj keys=',Object.keys(test_obj))
    5. print('test_obj.a=',test_obj.a)
    6. print('test_obj.b=',test_obj.b)
    7. test_obj.f(1,2,3,"hi")
    8. "#;
    9. ctx.eval_global_str(code);
    10. }

    The execution result is as follows.

    test_obj keys= a,b,f test_obj.a= 1 test_obj.b= abc hello from rust argv=[Int(1), Int(2), Int(3), String(JsString(hi))] this=Ok( { "a": Int( 1, ), "b": String( JsString( abc, ), ), "f": Function( JsFunction( function anything() { [native code] }, ), ), }, )

    In the previous example, we demonstrated simple examples to create JavaScript APIs from Rust. In this example, we will create a complete Rust module and make it available as a JavaScript object API. The project is in the examples/embed_rust_module folder. You can build and run it as a standard Rust application in WasmEdge.

    cargo build --target wasm32-wasi --release wasmedge --dir .:. target/wasm32-wasi/release/embed_rust_module.wasm

    The Rust implementation of the object is a module as follows. It has data fields, constructor, getters and setters, and functions.

    In the interpreter implementation, we call point::init_point_module first to register the Rust module with the JavaScript context, and then we can run a JavaScript program that simply use the point object.

    1. use wasmedge_quickjs::*;
    2. fn main() {
    3. let code = r#"
    4. import('point').then((point)=>{
    5. let p0 = new point.Point(1,2)
    6. print("js->",p0.x,p0.y)
    7. p0.pprint()
    8. try{
    9. let p = new point.Point()
    10. print("js-> p:",p)
    11. print("js->",p.x,p.y)
    12. p.x=2
    13. p.pprint()
    14. } catch(e) {
    15. print("An error has been caught");
    16. print(e)
    17. }
    18. })
    19. "#;
    20. ctx.eval_global_str(code);
    21. }

    The execution result from the above application is as follows.

    Next, you can see the Rust code in the folder for more examples on how to implement common JavaScript build-in functions including Node.js APIs.