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

时间:2016-12-27 23:16:21

标签: javascript webassembly

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

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

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

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

3 个答案:

答案 0 :(得分:41)

WebAssembly本身不支持字符串类型,而是支持i32 / i64 / f32 / f64 value types以及{{1 } / i8用于存储。

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

  • exports,您从JavaScript调用WebAssembly,而WebAssembly返回单个值类型。
  • imports其中WebAssembly调用JavaScript,使用尽可能多的值类型(注意:必须在模块编译时知道计数,这不是数组且不是可变参数)。
  • Memory.bufferi16,可以使用ArrayBuffer(等)进行索引。

这取决于你想做什么,但似乎直接访问缓冲区是最简单的:

Uint8Array

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

然后你将它从缓冲区中读出来:

const size = instance.exports.myStringSize();
const index = instance.exports.myStringIndex();

请注意,我正在从缓冲区读取8位值,因此我假设字符串是ASCII。这就是let s = ""; for (let i = index; i < index + size; ++i) s += String.fromCharCode(buffer[i]); 给你的内容(内存中的索引将是std::string返回的内容),但是为了暴露其他东西,例如UTF-8,你需要使用支持UTF-8的C ++库,以及然后自己从JavaScript读取UTF-8,获取代码点,并使用.c_str()

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

通过在String.fromCodePoint的{​​{1}} WebAssembly.Memory buffer中创建TextDecoder API,您也可以在浏览器中更广泛地使用ArrayBufferView })。

相反,如果您正在执行从WebAssembly到JavaScript的操作,那么您可以如上所述公开ArrayBuffer,然后从WebAssembly声明一个调用JavaScript的大小+位置的导入。您可以将模块实例化为:

Memory

这有一点需要注意,如果你增加内存(使用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都是JavaScript引擎收集的垃圾,但这是一个非常大的问题。您可能想要GC字符串,而这对于WebAssembly.Memory内的对象来说是不可能的。我们已经讨论了adding GC support in the future

答案 1 :(得分:3)

给出:

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

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

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

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

答案 2 :(得分:0)

我发现了一种破解方法,就像我们在混合应用程序中所做的一样,而且非常容易。

只需注入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