无需Emscripten:Wasm中向C传递JS函数并调用的问题
问题:在Wasm中调用传入的JS函数无输出
找不到相关解决方案,现有资料几乎都基于Emscripten。尝试在C代码中调用传入的JS函数,预期控制台输出called但实际无输出,相关代码和编译步骤如下:
JS代码
WebAssembly.instantiateStreaming(fetch("./out.wasm")).then((assembly) => { c = assembly.instance.exports; c.passFunc(() => { console.log(`called`); }); });
C代码
void passFunc(void (*f)()) { f(); }
编译步骤
~$ clang -O3 --target=wasm64 -emit-llvm -c -S index.c ~$ llc -march=wasm64 -filetype=obj index.ll ~$ wasm-ld --no-entry --export-all --lto-O3 -O3 -z stack-size=8192 -o out.wasm index.o
解决方案及原因
核心问题
WebAssembly的原始函数指针仅能指向Wasm模块内部的函数,无法直接引用JS函数——两者的调用栈、执行环境完全不同,直接传递JS函数指针会导致Wasm无法正确解析执行。
修正实现
1. 调整C代码:通过函数表索引调用回调
#include <stdint.h> // 声明导入的函数表,用于跨环境调用JS函数 extern void(*callback_table[])(void); void passFunc(uint32_t callback_idx) { // 通过索引调用函数表中的JS函数 callback_table[callback_idx](); }
2. 修改JS代码:创建函数表并注册回调函数
// 创建Wasm函数表,初始容量1,元素为无参无返回值的函数类型 const callbackTable = new WebAssembly.Table({ initial: 1, element: 'anyfunc' }); // 定义要执行的JS回调 const jsCallback = () => { console.log(`called`); }; // 将JS函数加入函数表 callbackTable.set(0, jsCallback); // 准备导入对象,把函数表传给Wasm模块 const importObject = { env: { callback_table: callbackTable } }; WebAssembly.instantiateStreaming(fetch("./out.wasm"), importObject) .then((assembly) => { const c = assembly.instance.exports; // 传递函数表中的索引值调用回调 c.passFunc(0); });
3. 更新编译命令(换用wasm32,添加函数表导入参数)
当前大部分环境对wasm64支持不完善,改用wasm32,并告诉链接器需要导入函数表:
clang -O3 --target=wasm32 -emit-llvm -c -S index.c llc -march=wasm32 -filetype=obj index.ll wasm-ld --no-entry --export-all --lto-O3 -O3 -z stack-size=8192 --import-table -o out.wasm index.o
内容的提问来源于stack exchange,提问作者Bomber87t
相关产品推荐
相关产品推荐

