问题描述
如何从WebAssembly函数返回JavaScript字符串?
How can I return a JavaScript string from a WebAssembly function?
以下模块可以用C(++)编写吗?
Can the following module be written in C(++) ?
export function foo() {
return 'Hello World!';
}
另外:我可以将此传递给JS引擎是垃圾收集?
推荐答案
WebAssembly本身不支持字符串类型,而是支持 i32
/ i64
/ f32
/ f64
以及 i8
/ i16
用于存储。
WebAssembly doesn't natively support a string type, it rather supports i32
/ i64
/ f32
/ f64
value types as well as i8
/ i16
for storage.
您可以使用WebAssembly实例进行交互:
You can interact with a WebAssembly instance using:
- ,从JavaScript调用WebAssembly,WebAssembly返回单个值类型。
- 其中WebAssembly调用JavaScript,w ith尽可能多的值类型(注意:必须在模块编译时知道计数,这不是数组且不是可变参数)。
- ,其中是一个
ArrayBuffer
,可以使用(以及其他)Uint8Array
进行索引。
exports
, where from JavaScript you call into WebAssembly, and WebAssembly returns a single value type.imports
where WebAssembly calls into JavaScript, with as many value types as you want (note: the count must be known at Module compilation time, this isn't an array and isn't variadic).Memory.buffer
, which is anArrayBuffer
that can be indexed using (among others)Uint8Array
.
这取决于你想做什么,但似乎直接访问缓冲区是最简单的:
It depends on what you want to do, but it seems like accessing the buffer directly is the easiest:
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);
如果您的模块有然后它在实例化时执行。否则你可能会有一个你打电话的导出,例如 instance.exports.doIt()
。
If your module had a start
function then it got executed at instantiation time. Otherwise you'll likely have an export which you call, e.g. instance.exports.doIt()
.
一旦完成,你需要在内存中获取字符串大小+索引,您还将通过导出公开:
Once that's done, you need to get string size + index in memory, which you would also expose through an export:
const size = instance.exports.myStringSize();
const index = instance.exports.myStringIndex();
然后你将它从缓冲区中读出来:
You'd then read it out of the buffer:
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
。
Note that I'm reading 8-bit values from the buffer, I'm therefore assuming the strings were ASCII. That's what std::string
would give you (index in memory would be what .c_str()
returns), but to expose something else such as UTF-8 you'd need to use a C++ library supporting UTF-8, and then read UTF-8 yourself from JavaScript, obtain the codepoints, and use String.fromCodePoint
.
你也可以依赖字符串为空终止,我没有在这里做。
You could also rely on the string being null-terminated, which I didn't do here.
您还可以使用一旦在浏览器中更广泛地通过创建进入 WebAssembly.Memory
的缓冲区
(这是 ArrayBuffer
)。
You could also use the TextDecoder
API once it's available more widely in browsers by creating an ArrayBufferView
into the WebAssembly.Memory
's buffer
(which is an ArrayBuffer
).
相反,如果您正在执行从WebAssembly到JavaScript的记录,然后您可以如上所述公开内存
,然后从WebAssembly声明一个调用JavaScript的大小+位置的导入。您可以将模块实例化为:
If, instead, you're doing something like logging from WebAssembly to JavaScript, then you can expose the Memory
as above, and then from WebAssembly declare an import which calls JavaScript with size + position. You could instantiate your module as:
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
获取neutered,你需要重新创建它。
This has the caveat that if you ever grow the memory (either through JavaScript using Memory.prototype.grow
, or using the grow_memory
opcode) then the ArrayBuffer
gets neutered and you need to create it anew.
关于垃圾收集: WebAssembly.Module
/ WebAssembly.Instance
/ WebAssembly.Memory
都是JavaScript引擎收集的垃圾,但那是一把相当大的锤子。您可能想要GC字符串,而这对于居住在 WebAssembly.Memory
中的对象来说是不可能的。我们已经讨论了。
On garbage collection: WebAssembly.Module
/ WebAssembly.Instance
/ WebAssembly.Memory
are all garbage collected by the JavaScript engine, but that's a pretty big hammer. You likely want to GC strings, and that's currently not possible for objects which live inside a WebAssembly.Memory
. We've discussed adding GC support in the future.
这篇关于如何从WebAssembly函数返回JavaScript字符串的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!