如何从WebAssembly函数返回JavaScript字符串?
以下模块是否可以用C(++)编写?
export function foo() {
return 'Hello World!';
}
还有一个问题:我能把它传递给JS引擎进行垃圾回收吗?
如何从WebAssembly函数返回JavaScript字符串?
以下模块是否可以用C(++)编写?
export function foo() {
return 'Hello World!';
}
还有一个问题:我能把它传递给JS引擎进行垃圾回收吗?
exports
,在JavaScript中调用WebAssembly,并返回单个值类型。imports
,WebAssembly调用JavaScript,可以使用任意多个值类型(注意:计数必须在模块编译时已知,这不是一个数组,也不是可变参数)。Memory.buffer
,它是一个可以使用(包括)Uint8Array
进行索引的ArrayBuffer
。这取决于您想要做什么,但直接访问缓冲区似乎是最简单的方法:
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]);
TextDecoder
API在浏览器中更广泛地可用,您还可以通过创建一个ArrayBufferView
来使用它,该视图指向WebAssembly.Memory
的buffer
(它是一个ArrayBuffer
)。
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);
}
}
});
Memory.prototype.grow
或者使用grow_memory
操作码来增加内存,那么ArrayBuffer
将会被禁用,你需要重新创建它。
自其他答案发布以来,情况已经发生了变化。
今天我会押注于WebAssembly接口类型——请参见下文。
由于您特别询问了C++,请查看:
nbind-神奇的头文件,使您的C++库可以从JavaScript中访问
nbind是一组标头,可使您的C++11库可以从JavaScript中访问。通过单个#include语句,您的C++编译器生成必要的绑定,无需任何其他工具。然后可以将您的库用作Node.js附加组件,或者如果使用Emscripten编译为asm.js,则可以直接在网页中使用,无需任何插件。
Embind用于将C++函数和类绑定到JavaScript,以便“普通”JavaScript可以自然地使用编译后的代码。 Embind还支持从C++调用JavaScript类。
请参阅以下WebAssembly提案:
该提案向WebAssembly添加了一组新的接口类型,用于描述高级值(例如字符串、序列、记录和变体),而不承诺单个内存表示或共享方案。接口类型只能在模块的接口中使用,并且只能由声明性接口适配器生成或消费。
有关更多信息和详细解释,请参见:
您已经可以使用一些实验性功能,详情请参见:
如果需要一个使用另一种方法的好的现实世界示例,请参见:
libsodium.js - 这是使用Emscripten编译为WebAssembly和纯JavaScript的钠加密库,自动生成包装器以便在Web应用程序中轻松使用。给定:
mem
,WebAssembly.Memory
对象(来自模块导出)p
,字符串第一个字符的地址len
,字符串的长度(以字节为单位)您可以使用以下方法读取字符串:
let str = (new TextDecoder()).decode(new Uint8Array(mem.buffer, p, len));
这假定该字符串是 UTF-8 编码的。
我发现了一种像混合应用程序那样的hack方法,而且非常简单。
只需注入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)
,几乎在这个对象的顶部,你会看到函数AsciiToString
。传递一个返回字符串的C++函数,你就会看到输出结果。对于这种情况,查看这个库。
TextDecoder
API将UTF-8的Uint8Array解码为字符串:https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder/decode - MaciejWebAssembly.Memory
是普通的 C++ 堆。你传递的i32
是指向该堆的普通 C++ 指针。 - JF Bastien