用 Rust 实现 JS API

    在开始之前,克隆 GitHub 仓库 ,并切换到目录 。该操作如下所示。

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

    对于如何将 JavaScript 嵌入到 Rust 中,embed_js 列出了几个不同的例子。你可以按如下的命令构建并运行这些示例。

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

    注:命令行中的 --dir .:. 给予了 wasmedge 读取文件系统的本地目录的权限。

    下面这个代码片段定义了一个 Rust 函数,该函数可以作为一个 API 结合到 JavaScript 解释器中。

    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. }

    执行的结果如下所示。

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

    通过这个方法,你可以创建出一个带有自定义 API 函数的 JavaScript 解释器。这个解释器是运行在 WasmEdge 里的,并且可以通过 CLI 或者网络来执行调用了这些 API 函数的 JavaScript 代码。

    在 JavaScript API 的设计中,我们有时需要提供一个封装了数据以及函数的对象。在下面的示例中,我们为 JavaScript API 定义了一个 Rust 函数。

    然后,我们在 Rust 端创建一个”对象”,设置它的数据域,然后将该 Rust 函数注册为关联到该对象的 JavaScript 函数。

    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. }

    接下来,我们使这个 Rust “对象” 能在 JavaScript 解释器中作为 JavaScript 对象 test_obj 使用。

    在 JavaScript 代码中,你可以直接将 test_obj 作为 API 的一部分使用了。

    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. }

    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] }, ), ), }, )

    在前面的示例中,我们简单地演示了如何用 Rust 创建 JavaScript API。在这个例子中,我们将会创建一个完整的 Rust 模块并让它成为一个 JavaScript 对象 API。这个项目在文件夹 examples/embed_rust_module 中。你可以构建并将其当做一个标准的 Rust 应用,在 WasmEdge 中运行它。

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

    该对象的 Rust 实现,如下面这个模块所示。它含有数据域、构造函数、访问器、设置器以及函数。

    在解释器的实现中,我们先调用 point::init_point_module 以将 Rust 模块注册到 JavaScript 的上下文中,然后运行一个使用了对象 point 的 JavaScript 程序。

    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. }

    上面这个应用的执行结果如下所示。

    rust-> new Point [Int(1), Int(2)] rust-> get x rust-> get y js-> 1 2 rust-> pprint: Point(1, 2) rust-> new Point [] js-> p: undefined An error has been caught TypeError: cannot read property 'x' of undefined