I want to provide a WebAssembly module with an external JavaScript function that accepts a Rust function pointer.
Once this JS module is initialized, it will call the run() function from the .wasm module, and that will in turn call peekaboo:
window.Module = {};
const imports = {
env: {
memoryBase: 0,
tableBase: 0,
memory: new WebAssembly.Memory({ initial: 256 }),
table: new WebAssembly.Table({ initial: 4, element: 'anyfunc' })
}
};
imports.env.peekaboo = function(f) {
const fn = imports.env.table.get(f);
return fn(2);
};
fetch('game.wasm')
.then(response => response.arrayBuffer())
.then(bytes => WebAssembly.compile(bytes))
.then(mod => WebAssembly.instantiate(mod, imports))
.then(mod => {
mod.exports.run();
Module.memory = imports.env.memory;
Module.dealloc_str = mod.exports.dealloc_str;
});
The examples I've seen indicate that if I import the memory in this way, I should be able to use the table to resolve my function pointer. Here is the Rust code:
#![feature(wasm_import_memory)]
#![wasm_import_memory]
extern "C" {
fn peekaboo(f: fn(u32) -> u32);
}
fn main() {}
#[no_mangle]
pub fn run() {
let plus_one = |x: u32| -> u32 { x + 1 };
unsafe {
peekaboo(plus_one);
}
}
Everything compiles fine but when I execute the peekaboo function, the fn variable is null, indicating that the table was unable to find the function pointer. Therefore executing fn(2) blows up with:
Uncaught (in promise) TypeError: fn is not a function
I more or less followed this example but since I'm working in Rust, the translation is not 1-to-1. I suspect that I've overlooked something that's not obvious to me because I'm new to both Rust and WebAssembly. Can anyone spot my mistake?