如何从WebAssembly函数返回JavaScript字符串?

下列模块可以用C(++)编写吗?

export function foo() {
  return 'Hello World!';
}

另外:我可以将其传递给JS引擎进行垃圾回收吗?

最佳答案

WebAssembly本身不支持字符串类型,而是支持i32/i64/f32/f64 value types以及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 function,则它在实例化时执行。否则,您可能会调用一个导出,例如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终止的字符串,我在这里没有这样做。

    您还可以通过在TextDecoderArrayBufferView(这是WebAssembly.Memory)中创建一个 buffer API来使用 ArrayBuffer (在浏览器中更为广泛地使用)。

    相反,如果您正在执行从WebAssembly到JavaScript的日志记录等操作,则可以如上所述公开Memory,然后从WebAssembly声明一个导入,该导入以size + position调用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);
            }
        }
    });
    

    需要注意的是,如果您增加了内存(通过使用Memory.prototype.grow的JavaScript或使用grow_memory操作码)来增加内存,则ArrayBuffer会被屏蔽,因此您需要重新创建它。

    关于垃圾收集:WebAssembly.Module/WebAssembly.Instance/WebAssembly.Memory都是JavaScript引擎收集的垃圾,但这是一个很大的锤子。您可能希望使用GC字符串,而对于WebAssembly.Memory内部的对象,目前尚无法实现。我们已经讨论了adding GC support in the future

    关于javascript - 如何从WebAssembly函数返回JavaScript字符串,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/41353389/

    10-11 21:13