You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

无需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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 14:25:23