Skip to content

Latest commit

 

History

History
99 lines (64 loc) · 4.04 KB

js-ffi.md

File metadata and controls

99 lines (64 loc) · 4.04 KB

commit/042d7f0406d8c16809a6387fcc1f303c61e49a23

JavaScript互操作

导入和导出JS函数

从Rust这边

当在一个JS宿主中使用wasm时,从Rust这边导入导出函数都很直接:它和C的方式非常相似。

WebAssembly模块声明一格导入序列,每个带有一个module name和一个import name。 对于一个extern { ... }块的模块名可以使用#[link(wasm_import_module)] 来进行制定,当前它默认是“env”。

导出只有一个名字。另外对于任何extern函数,WebAssembly示例的默认线性内存被导出为“内存”。 functions the WebAssembly instance's default linear memory is exported as "memory".

// import a JS function called `foo` from the module `mod`
#[link(wasm_import_module = "mod")]
extern { fn foo(); }

// export a Rust function called `bar`
#[no_mangle]
pub extern fn bar() { /* ... */ }

因为wasm限制值的类型,这些函数必须只运行在原始数字类型上。

从JS一边

在JS中,一个wasm二进制文件转换为一个ES6模块。 他必须被使用线性内存实例化并且有一组与预期导入匹配的JS函数。 实例化的细节可在MDN上查看。

生成的ES6模块将包含从Rust导出的所有函数,现在可以作为JS函数使用了。

这里有一个全部设置的简单例子。

超越数字

当在JS中使用wasm时,在JS内存和wasm模块内存之间有一个严重的分离:

  • 每个wasm模块有一个线性内存(在本文档的顶部描述过了),他在实例化的时候被初始化。 JS代码可以自由的读写这块内存。

  • 相比较,wasm代码则不能直接访问JS对象。

因此,复杂的互操作主要以两种方式发生:

  • 将我们的二进制数据拷贝进(出)wasm内存。例如,这是一个提供一个所有权的String到rust一侧的方法。

  • 设置一个显示的JS的对象的堆,这些对象之后被给出“地址”。 这允许wasm代码间接地(使用整形)引用JS对象,然后通过调用其他被导入的JS函数来操作那些对象。

幸运的时,这个互操作的事情非常使用通过一个通用的"bindgen"风格的框架wasm-bindgen来解决。 这个框架使我们可以编写管用的Rust函数签名并自动映射到管用的JS函数。

自定义段

自定义部分允许将命名的任意数据嵌入到wasm模块中。 段数据在编译时设置,直接从wasm模块读取,不能在运行时修改。

在Rust中,自定义段时静态数组([T; size]),使用#[link_section]属性导出:

#[link_section = "hello"]
pub static SECTION: [u8; 24] = *b"This is a custom section";

这添加了一个名为hello的自定义段到wasm文件,Rust的变量名SECTION时随意的, 改变它并不会改变这个行为。在这里内容时文本字节,但是它实际可以是任何数据。

自定义段可以在JS这边通过使用WebAssembly.Module.customSections函数读取, 它获取以恶wasm模块和段名作为参数,返回一个ArrayBuffer的数组。 多个段可以使用相同的名字来指定,这时他们将同时出现在这个数组中。

WebAssembly.compileStreaming(fetch("sections.wasm"))
.then(mod => {
  const sections = WebAssembly.Module.customSections(mod, "hello");

  const decoder = new TextDecoder();
  const text = decoder.decode(sections[0]);

  console.log(text); // -> "This is a custom section"
});