I want to pass a JavaScript function as an argument to a function that has been exported from WebAssembly with a function pointer as a parameter.
Consider the following example:
JavaScript Code:
function foo() {
console.log("foo");
}
wasmInstance.exports.expects_funcptr(foo);
C Code:
typedef void(*funcptr_type)(void);
void expects_funcptr(funcptr_type my_funcptr)
{
my_funcptr();
}
I'm not using Emscripten, but they have a section on the topic in their "Interacting with code" page here: https://emscripten.org/docs/porting/connecting_cpp_and_javascript/Interacting-with-code.html#interacting-with-code-call-function-pointers-from-c. They have a function called addFunction for this.
I took a look at its implementation here: https://github.com/emscripten-core/emscripten/blob/incoming/src/support.js#L755
And it seems quite... hacky. It looks like they are creating a new wasm module that takes the javascript function as an import and exports it as a wasm function. Only then are they able to add the function to a WebAssembly Table.
Is there a better way to do this?
EDIT:
Here's how I'm currently handling this. By using the following function to convert a JS function to WASM I can pass a JS function to WASM like this:
// How the above example would be called using the converter function.
wasmInstance.exports.expects_funcptr(convertFunction(foo, Types.VOID));
// The actual converter function (minus some details for simplicity)
function convertFunction(func, ret, params) {
// Construct a .wasm binary by hand
const bytes = new Uint8Array([
0x00, 0x61, 0x73, 0x6d, // magic
0x01, 0x00, 0x00, 0x00, // version
// ... generate type, import, export sections as well
]);
const module = new WebAssembly.Module(bytes);
const instance = new WebAssembly.Instance(module, {
a: {
b: func
}
});
const ret = table.length;
table.grow(1);
table.set(ret, instance.exports.f);
return ret;
}
This is a crude example to show the concept. An actual implementation has checks if the function has already been converted, handles errors, etc.