从 JavaScript 调用 WebAssembly 中的 C 风格函数指针
Calling a C style function pointer in a WebAssembly from JavaScript
有什么方法可以访问 WebAssembly 模块中的函数指针吗?
例如,给定以下 "module" 编译为 WebAssembly:
extern void set_callback(void (*callback)(void *arg), void *arg);
static void callback(void *arg)
{
/* ... */
}
int main() {
set_callback(&callback, 0);
return 0;
}
JavaScript 中 do_callback
的实现是否可以调用回调而不必依赖中间 C 函数导出来执行实际函数调用?
var instance = new WebAssembly.Instance(module, {
memory: /* ... */
env: {
set_callback: function set_callback(callbackptr, argptr) {
// We only got the pointer, is there any
},
},
});
通过中间函数导出,我的意思是我可以添加一个具有 public 可见性的内部函数。
do_callback(void (*callback)(void *arg), void *arg)
{
callback();
}
然后JavaScriptset_callback
函数可以通过委托do_callback
函数调用函数指针
function set_callback(callbackptr, argptr) {
instance.exports.do_callback(callbackptr, argptr);
}
但是,最好不要通过显式间接寻址来执行此操作,这可能吗,也许可以使用函数表?
您可以从 Javascript 调用函数指针。
函数指针存储在Table中。当函数指针传递给 Javascript 时,您将收到该函数指针的 table 的整数索引。将该索引传递给 Table.prototype.get()
,您可以调用该函数。
...
set_callback: function set_callback(callbackptr, argptr) {
tbl.get(callbackptr)(argptr);
},
...
您可以在 Tables 部分下的 MDN 页面上阅读更多相关信息:
https://developer.mozilla.org/en-US/docs/WebAssembly/Using_the_JavaScript_API#Tables
编辑:这是我用来测试的最小示例。
第一个文件fptr.c
用emcc fptr.c -Os -s WASM=1 -s SIDE_MODULE=1 -o fptr.wasm
编译
typedef int (*fptr_type)(void);
extern void pass_fptr_to_js(fptr_type fptr);
static int callback_0(void)
{
return 26;
}
static int callback_1(void)
{
return 42;
}
void run_test()
{
pass_fptr_to_js(callback_0);
pass_fptr_to_js(callback_1);
}
这里是fptr.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>WebAssembly Experiment</title>
</head>
<body>
<h3>Check the console.</h3>
<script type="text/javascript">
fetch('fptr.wasm').then(function(response) {
response.arrayBuffer().then(function(buffer) {
WebAssembly.compile(buffer).then(function(module) {
var imports = {};
imports.env = {};
imports.env.memoryBase = 0;
imports.env.memory = new WebAssembly.Memory({ initial: 256 });
imports.env.tableBase = 0;
imports.env.table = new WebAssembly.Table({ initial: 4, element: 'anyfunc' });
imports.env["abort"] = function() {
console.error("ABORT");
};
imports.env["_pass_fptr_to_js"] = function(fptr) {
console.log("table index: " + fptr + ", return value: " + imports.env.table.get(fptr)());
};
WebAssembly.instantiate(module, imports).then(function(instance) {
instance.exports["__post_instantiate"]();
instance.exports["_run_test"]();
});
});
});
});
</script>
</body>
</html>
当时的问题是 Clang 基本上没有实现 call_indirect 并且在代码生成期间实际上没有用任何东西填充函数 table。
已在当前版本的 LLVM 中解决。
有什么方法可以访问 WebAssembly 模块中的函数指针吗?
例如,给定以下 "module" 编译为 WebAssembly:
extern void set_callback(void (*callback)(void *arg), void *arg);
static void callback(void *arg)
{
/* ... */
}
int main() {
set_callback(&callback, 0);
return 0;
}
JavaScript 中 do_callback
的实现是否可以调用回调而不必依赖中间 C 函数导出来执行实际函数调用?
var instance = new WebAssembly.Instance(module, {
memory: /* ... */
env: {
set_callback: function set_callback(callbackptr, argptr) {
// We only got the pointer, is there any
},
},
});
通过中间函数导出,我的意思是我可以添加一个具有 public 可见性的内部函数。
do_callback(void (*callback)(void *arg), void *arg)
{
callback();
}
然后JavaScriptset_callback
函数可以通过委托do_callback
函数调用函数指针
function set_callback(callbackptr, argptr) {
instance.exports.do_callback(callbackptr, argptr);
}
但是,最好不要通过显式间接寻址来执行此操作,这可能吗,也许可以使用函数表?
您可以从 Javascript 调用函数指针。
函数指针存储在Table中。当函数指针传递给 Javascript 时,您将收到该函数指针的 table 的整数索引。将该索引传递给 Table.prototype.get()
,您可以调用该函数。
...
set_callback: function set_callback(callbackptr, argptr) {
tbl.get(callbackptr)(argptr);
},
...
您可以在 Tables 部分下的 MDN 页面上阅读更多相关信息: https://developer.mozilla.org/en-US/docs/WebAssembly/Using_the_JavaScript_API#Tables
编辑:这是我用来测试的最小示例。
第一个文件fptr.c
用emcc fptr.c -Os -s WASM=1 -s SIDE_MODULE=1 -o fptr.wasm
typedef int (*fptr_type)(void);
extern void pass_fptr_to_js(fptr_type fptr);
static int callback_0(void)
{
return 26;
}
static int callback_1(void)
{
return 42;
}
void run_test()
{
pass_fptr_to_js(callback_0);
pass_fptr_to_js(callback_1);
}
这里是fptr.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>WebAssembly Experiment</title>
</head>
<body>
<h3>Check the console.</h3>
<script type="text/javascript">
fetch('fptr.wasm').then(function(response) {
response.arrayBuffer().then(function(buffer) {
WebAssembly.compile(buffer).then(function(module) {
var imports = {};
imports.env = {};
imports.env.memoryBase = 0;
imports.env.memory = new WebAssembly.Memory({ initial: 256 });
imports.env.tableBase = 0;
imports.env.table = new WebAssembly.Table({ initial: 4, element: 'anyfunc' });
imports.env["abort"] = function() {
console.error("ABORT");
};
imports.env["_pass_fptr_to_js"] = function(fptr) {
console.log("table index: " + fptr + ", return value: " + imports.env.table.get(fptr)());
};
WebAssembly.instantiate(module, imports).then(function(instance) {
instance.exports["__post_instantiate"]();
instance.exports["_run_test"]();
});
});
});
});
</script>
</body>
</html>
当时的问题是 Clang 基本上没有实现 call_indirect 并且在代码生成期间实际上没有用任何东西填充函数 table。
已在当前版本的 LLVM 中解决。