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



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

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

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

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

WebAssembly 本身不支持字符串类型,而是支持i32/i64/f32/f64值类型以及用于存储的i8/i16

您可以使用以下方法与 WebAssembly 实例进行交互:

  • exports,从 JavaScript 调用 WebAssembly,WebAssembly 返回单个值类型。
  • importsWebAssembly 调用 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]);

请注意,我正在从缓冲区读取 8 位值,因此我假设字符串是 ASCII。这就是std::string会给你的(内存中的索引将是.c_str()返回的),但是要公开其他东西,例如UTF-8,您需要使用支持UTF-8的C++库,然后自己从JavaScript中读取UTF-8,获取代码点并使用String.fromCodePoint

你也可以依赖以 null 结尾的字符串,我在这里没有这样做。

一旦TextDecoderAPI在浏览器中更广泛地可用,您也可以通过在WebAssembly.Memorybuffer(这是一个ArrayBuffer)中创建ArrayBufferView来使用它。


相反,如果你正在做一些类似从 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/WebAssembly.Instance/WebAssembly.Memory都是由JavaScript引擎收集的垃圾,但这是一个相当大的锤子。您可能想要 GC 字符串,而对于位于WebAssembly.Memory中的对象,这目前是不可能的。我们已经讨论过将来添加 GC 支持。

2020 年更新

自从其他答案发布以来,情况发生了变化。

今天我会打赌 WebAssembly 接口类型 - 见下文。

由于您专门询问了有关C++,请参阅:

  • Nbind:

nbind - 神奇的标头,使您的C++库可从 JavaScript 访问

nbind 是一组标头,可让您的 C++11 库从 JavaScript 访问。使用单个 #include 语句,C++编译器无需任何其他工具即可生成必要的绑定。然后,您的库可以用作 Node.js 插件,或者,如果使用 Emscripten 编译为 asm.js则直接在网页中使用,而无需任何插件。

  • 恩宾德:

Embind 用于将C++函数和类绑定到 JavaScript,以便编译后的代码可以被"普通"JavaScript 以自然的方式使用。Embind 还支持从 C++ 调用 JavaScript 类。

请参阅以下 WebAssembly 提案:

  • JS类型
  • 引用类型
  • 接口类型

该提案向 WebAssembly 添加了一组新的接口类型,用于描述高级值(如字符串、序列、记录和变体),而无需提交单个内存表示或共享方案。接口类型只能在模块的接口中使用,并且只能由声明性接口适配器生成或使用。

有关详细信息和精彩说明,请参阅:

  • WebAssembly 接口类型:与所有事物互操作! 作者:Lin Clark。

您已经可以将它与一些实验性功能一起使用,请参阅:

  • https://www.youtube.com/watch?v=Qn_4F3foB3Q

有关使用另一种方法的良好实际示例,请参阅:

  • 利布钠.js

libsodium.js - 使用Emscripten编译为WebAssembly和纯JavaScript的钠加密库,并自动生成包装器,使其易于在Web应用程序中使用。

另请参阅:

  • 瓦斯默:

Wasmer 是一个开源运行时,用于在服务器上执行 WebAssembly。我们的使命是使所有软件普遍可用。我们支持在运行时独立运行 Wasm 模块,但也可以使用我们的语言集成嵌入多种语言。

特别是Wasmer-JS:

Wasmer-JS允许在Node.js和浏览器中使用服务器端编译的WebAssembly模块。该项目被设置为多个JavaScript包的单存储库。

在这篇文章中也有一些关于黑客新闻的好信息。

给定:

  • memWebAssembly.Memory对象(来自模块导出)
  • p,字符串第一个字符的地址
  • len,字符串的长度(以字节为单位),

您可以使用以下命令读取字符串:

let str = (new TextDecoder()).decode(new Uint8Array(mem.buffer, p, len));

这假定字符串是 UTF-8 编码的。

我找到了一种黑客方法,就像我们在 hybird 应用方式中所做的一样,而且很容易。

只需注入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++传递函数,您将看到输出。对于这种情况,请查看此库。

相关内容

  • 没有找到相关文章

最新更新