使用 rustwasm 编写简单的 wasm 代码:入参为一个 Uint8Array ,出参为一个字符串。
#[wasm_bindgen]
pub fn echo(input: Vec<u8>) -> String {
String::from("hello world")
}
引入小程序中, Android 和 macOS 微信客户端可以正常返回 hello world 字符串,iOS 微信客户端返回为空。
跟踪调试,发现问题在于
/**
* @param {Uint8Array} input
* @returns {string}
*/
export function echo(input) {
try {
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
const ptr0 = passArray8ToWasm0(input, wasm.__wbindgen_malloc);
const len0 = WASM_VECTOR_LEN;
// 执行到这里时,cachedUint8Memory0 的大小为 wasm.memory.buffer 的大小
wasm.echo(retptr, ptr0, len0);
// 执行到这里时,cachedUint8Memory0 空间被 wasm 回收,大小应为 0
// Android 和 macOS 微信客户端 cachedUint8Memory0 大小为 0 ,符合预期
// iOS 客户端似乎没能回收 cachedUint8Memory0 的空间,大小仍为执行 wasm.echo 前的值
var r0 = getInt32Memory0()[retptr / 4 + 0];
var r1 = getInt32Memory0()[retptr / 4 + 1];
return getStringFromWasm0(r0, r1);
} finally {
wasm.__wbindgen_add_to_stack_pointer(16);
wasm.__wbindgen_free(r0, r1);
}
}
排查要点如上,可以看到 iOS 微信客户端的 WXWebAssembly 表现与主流浏览器不同,此问题可能导致内存访问越界进而造成微信客户端 crash (可稳定复现)。希望客户端这边能够排查,感谢~
同样使用rust wasm存在wasm与js之间字符串传输出现空内容的情况,在rust wasm的胶水js中,有下面这段代码,js和wasm之间的字符串传输应该是使用cachedUint8Memory0(Uint8Array)作为TypedArray进行内存访问,经过调试,在开发者工具中cachedUint8Memory0和wasm.memory.buffer始终保持同步,而在iOS中,通过打印cachedUint8Memory0和wasm.memory.buffer的字节长度发现,后者比前者大,同时打印字符串在Uint8Array中的偏移量,该偏移量在cachedUint8Memory0范围之外,因此读取该字符串时发生了内存越界访问,得到的字符串也就为空了。
let cachedUint8Memory0; function getUint8Memory0() { if (cachedUint8Memory0.byteLength === 0) { cachedUint8Memory0 = new Uint8Array(wasm.memory.buffer); } return cachedUint8Memory0; }
改变上述代码块的判断条件,测试在iOS端可以正常在js和wasm间传输字符串了,可以参考一下。
// cachedUint8Memory0.byteLength === 0 改为下面的判断条件 cachedUint8Memory0.byteLength !== wasm.memory.buffer.byteLength // 这个修改并不算正确的做法,直接去掉判断条件,让cachedUint8Memory0始终重新实例化更为有效,这样cachedUint8Memory0肯定就保持了对wasm.memory.buffer的同步 let cachedUint8Memory0; function getUint8Memory0() { return (cachedUint8Memory0 = new Uint8Array(wasm.memory.buffer)); }
iPhone XR iOS 8.0.39 调试基础库 2.32.3 依然存在view在malloc之后byteLength依然为旧值的情况
你好,反馈已收到,我们核实下,有进展会同步。