You should have two compiled Wasm files in the /src/assets folder of your project: main.wasm and memory.wasm. Since we need to utilize the malloc() and free() functions exported from memory.wasm in the main.wasm code, our loading code is going to look different from the earlier examples. Create a file in the /src/store folder named initializeWasm.js and populate it with the following contents:
/**
* Returns an array of compiled (not instantiated!) Wasm modules.
* We need the main.wasm file we created, as well as the memory.wasm file
* that allows us to use C functions like malloc() and free().
*/
const fetchAndCompileModules = () =>
Promise.all(
['../assets/main.wasm', '../assets/memory.wasm'].map(fileName =>
fetch(fileName)
.then(response => {
if (response.ok) return response.arrayBuffer();
throw new Error(`Unable to fetch WebAssembly file: ${fileName}`);
})
.then(bytes => WebAssembly.compile(bytes))
)
);
/**
* Returns an instance of the compiled "main.wasm" file.
*/
const instantiateMain = (compiledMain, memoryInstance, wasmMemory) => {
const memoryMethods = memoryInstance.exports;
return WebAssembly.instantiate(compiledMain, {
env: {
memoryBase: 0,
tableBase: 0,
memory: wasmMemory,
table: new WebAssembly.Table({ initial: 16, element: 'anyfunc' }),
abort: console.log,
_consoleLog: value => console.log(value),
_malloc: memoryMethods.malloc,
_free: memoryMethods.free
}
});
};
/**
* Compiles and instantiates the "memory.wasm" and "main.wasm" files and
* returns the `exports` property from main's `instance`.
*/
export default async function initializeWasm() {
const wasmMemory = new WebAssembly.Memory({ initial: 1024 });
const [compiledMain, compiledMemory] = await fetchAndCompileModules();
const memoryInstance = await WebAssembly.instantiate(compiledMemory, {
env: {
memory: wasmMemory
}
});
const mainInstance = await instantiateMain(
compiledMain,
memoryInstance,
wasmMemory
);
return mainInstance.exports;
}
The file's default export function, initializeWasm(), performs the following steps:
- Create a new WebAssembly.Memory instance (wasmMemory).
- Call the fetchAndCompileModules() function to get a WebAssembly.Module instance for memory.wasm (compiledMemory) and main.wasm (compiledMain).
- Instantiate compiledMemory (memoryInstance) and pass the wasmMemory into the importObj.
- Pass compiledMain, memoryInstance, and wasmMemory into the instantiateMain() function.
- Instantiate compiledMain and pass the exported malloc() and free() functions from memoryInstance along with wasmMemory into the importObj.
- Return the exports property of the Instance returned from instantiateMain (mainInstance).
As you can see, the process is more complex when you have dependencies within Wasm modules.
You may have noticed that the malloc and free methods on the memoryInstance exports property weren't prefixed with an underscore. This is because the memory.wasm file was compiled using LLVM without Emscripten, which doesn't add the _.