用 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 解释器中。
#![allow(unused)]
fn main() {
fn run_rust_function(ctx: &mut Context) {
...
let f = ctx.new_function::<HelloFn>("hello");
ctx.get_global().set("hi", f.into());
let code = r#"hi(1,2,3)"#;
let r = ctx.eval_global_str(code);
println!("return value:{:?}", r);
}
}
执行的结果如下所示。
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 函数。
#![allow(unused)]
fn main() {
let mut obj = ctx.new_object();
let f = ctx.new_function::<ObjectFn>("anything");
obj.set("f", f.into());
}
接下来,我们使这个 Rust “对象” 能在 JavaScript 解释器中作为 JavaScript 对象 test_obj
使用。
在 JavaScript 代码中,你可以直接将 test_obj
作为 API 的一部分使用了。
#![allow(unused)]
fn main() {
let code = r#"
print('test_obj keys=',Object.keys(test_obj))
print('test_obj.a=',test_obj.a)
print('test_obj.b=',test_obj.b)
test_obj.f(1,2,3,"hi")
"#;
ctx.eval_global_str(code);
}
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 程序。
use wasmedge_quickjs::*;
fn main() {
let code = r#"
import('point').then((point)=>{
let p0 = new point.Point(1,2)
print("js->",p0.x,p0.y)
p0.pprint()
try{
let p = new point.Point()
print("js-> p:",p)
print("js->",p.x,p.y)
p.x=2
p.pprint()
} catch(e) {
print("An error has been caught");
print(e)
}
})
"#;
ctx.eval_global_str(code);
}
上面这个应用的执行结果如下所示。
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