如何从WebAssembly函数返回JavaScript字符串 [英] How can I return a JavaScript string from a WebAssembly function

查看:338
本文介绍了如何从WebAssembly函数返回JavaScript字符串的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何从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:


  • exports ,从JavaScript调用WebAssembly,WebAssembly返回单个值类型。

  • imports 其中WebAssembly调用JavaScript,w ith尽可能多的值类型(注意:必须在模块编译时知道计数,这不是数组且不是可变参数)。

  • Memory.buffer ,其中是一个 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 an ArrayBuffer 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);

如果您的模块有 start function 然后它在实例化时执行。否则你可能会有一个你打电话的导出,例如 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.

您还可以使用 TextDecoder API 一旦在浏览器中更广泛地通过创建 ArrayBufferView 进入 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 中的对象来说是不可能的。我们已经讨论了将来添加GC支持

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字符串的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆