如何从 WebAssembly 函数返回 JavaScript 字符串?
以下模块可以用 C(++) 编写吗?
export function foo() {
return 'Hello World!';
}
另外:我可以将它传递给 JS 引擎进行垃圾收集吗?
如何从 WebAssembly 函数返回 JavaScript 字符串?
以下模块可以用 C(++) 编写吗?
export function foo() {
return 'Hello World!';
}
另外:我可以将它传递给 JS 引擎进行垃圾收集吗?
WebAssembly 本身并不支持字符串类型,而是支持i32
/ i64
/ f32
/f64
值类型以及i8
/i16
用于存储。
您可以使用以下方式与 WebAssembly 实例交互:
exports
,从 JavaScript 调用 WebAssembly,WebAssembly 返回一个值类型。imports
其中 WebAssembly 调用 JavaScript,具有任意数量的值类型(注意:计数必须在模块编译时已知,这不是数组,也不是可变参数)。Memory.buffer
,这是一个ArrayBuffer
可以使用(除其他外)索引的Uint8Array
。这取决于你想做什么,但似乎直接访问缓冲区是最简单的:
const bin = ...; // WebAssembly binary, I assume below that it imports a memory from module "imports", field "memory".
const module = new WebAssembly.Module(bin);
const memory = new WebAssembly.Memory({ initial: 2 }); // Size is in pages.
const instance = new WebAssembly.Instance(module, { imports: { memory: memory } });
const arrayBuffer = memory.buffer;
const buffer = new Uint8Array(arrayBuffer);
如果您的模块有一个start
函数,那么它会在实例化时执行。否则,您可能会有一个您调用的导出,例如instance.exports.doIt()
.
完成后,您需要在内存中获取字符串大小 + 索引,您还可以通过导出公开:
const size = instance.exports.myStringSize();
const index = instance.exports.myStringIndex();
然后你会从缓冲区中读取它:
let s = "";
for (let i = index; i < index + size; ++i)
s += String.fromCharCode(buffer[i]);
请注意,我正在从缓冲区中读取 8 位值,因此我假设字符串是 ASCII。这就是std::string
给你的(内存中的索引是.c_str()
返回的),但是要公开其他东西,比如 UTF-8,你需要使用支持 UTF-8 的 C++ 库,然后自己从 JavaScript 中读取 UTF-8,获得代码点,并使用String.fromCodePoint
.
您还可以依赖以 null 结尾的字符串,我在这里没有这样做。
TextDecoder
一旦API在浏览器中更广泛地可用,您也可以通过ArrayBufferView
在WebAssembly.Memory
's中创建一个buffer
(即一个ArrayBuffer
)来使用它。
相反,如果您正在执行类似从 WebAssembly 记录到 JavaScript 的操作,那么您可以公开Memory
上述内容,然后从 WebAssembly 声明一个导入,该导入使用大小 + 位置调用 JavaScript。您可以将模块实例化为:
const memory = new WebAssembly.Memory({ initial: 2 });
const arrayBuffer = memory.buffer;
const buffer = new Uint8Array(arrayBuffer);
const instance = new WebAssembly.Instance(module, {
imports: {
memory: memory,
logString: (size, index) => {
let s = "";
for (let i = index; i < index + size; ++i)
s += String.fromCharCode(buffer[i]);
console.log(s);
}
}
});
需要注意的是,如果您曾经增加内存(通过 JavaScript 使用Memory.prototype.grow
,或使用grow_memory
操作码),那么内存ArrayBuffer
就会被绝育,您需要重新创建它。
关于垃圾收集://WebAssembly.Module
都是JavaScript引擎收集的垃圾,但那是一个相当大的锤子。您可能想要 GC 字符串,而目前对于存在于. 我们已经讨论过在未来添加 GC 支持。WebAssembly.Instance
WebAssembly.Memory
WebAssembly.Memory
自从发布其他答案以来,情况发生了变化。
今天我将赌注在 WebAssembly 接口类型上——见下文。
由于您专门询问了 C++,请参阅:
nbind - 使您的 C++ 库可从 JavaScript 访问的神奇头文件
nbind 是一组头文件,可让您从 JavaScript 访问 C++11 库。使用单个 #include 语句,您的 C++ 编译器无需任何其他工具即可生成必要的绑定。然后,您的库可用作 Node.js 插件,或者,如果使用 Emscripten 编译为 asm.js,则无需任何插件即可直接在网页中使用。
Embind 用于将 C++ 函数和类绑定到 JavaScript,以便“普通”JavaScript 可以自然地使用编译后的代码。Embind 还支持从 C++ 调用 JavaScript 类。
请参阅以下 WebAssembly 提案:
该提案向 WebAssembly 添加了一组新的接口类型,用于描述高级值(如字符串、序列、记录和变体),而无需提交单一的内存表示或共享方案。接口类型只能在模块的接口中使用,并且只能由声明式接口适配器生产或使用。
有关更多信息和很好的解释,请参阅:
您已经可以将它与一些实验性功能一起使用,请参阅:
有关使用另一种方法的真实世界示例,请参阅:
libsodium.js - 使用 Emscripten 编译为 WebAssembly 和纯 JavaScript 的钠加密库,具有自动生成的包装器,使其易于在 Web 应用程序中使用。
也可以看看:
Wasmer 是用于在服务器上执行 WebAssembly 的开源运行时。我们的使命是使所有软件普遍可用。我们支持在运行时独立运行 Wasm 模块,但也可以使用我们的语言集成嵌入多种语言。
特别是Wasmer-JS:
Wasmer-JS 允许在 Node.js 和浏览器中使用服务器端编译的 WebAssembly 模块。该项目设置为多个 JavaScript 包的单一存储库。
这篇关于 Hacker News的文章也有一些很好的信息。
鉴于:
mem
,WebAssembly.Memory
对象(来自模块导出)p
, 字符串第一个字符的地址len
,字符串的长度(以字节为单位),您可以使用以下方法读取字符串:
let str = (new TextDecoder()).decode(new Uint8Array(mem.buffer, p, len));
这假定字符串是 UTF-8 编码的。
我找到了一种 hack 方式,就像我们在 hybird appication 方式中所做的那样,而且非常容易。
只需注入window.alert
函数,然后将其放回去:
let originAlert = window.alert;
window.alert = function(message) {
renderChart(JSON.parse(message))
};
get_data_from_alert();
window.alert = originAlert;
和本地人,只是:
// Import the `window.alert` function from the Web.
#[wasm_bindgen]
extern "C" {
fn alert(s: &str);
}
...
pub fn get_data_from_alert() {
alert(CHART_DATA);
}
您可以在我的 GitHub 示例中看到:https ://github.com/phodal/rust-wasm-d3js-sample
有一种更简单的方法可以做到这一点。首先,您需要二进制文件的实例:
const module = new WebAssembly.Module(bin);
const memory = new WebAssembly.Memory({ initial: 2 });
const instance = new WebAssembly.Instance(module, { imports: { memory: memory } });
然后,如果你运行console.log(instance)
,几乎在这个对象的顶部你会看到 function AsciiToString
。从返回字符串的 C++ 传递您的函数,您将看到输出。对于这种情况,请查看此库。