write: write program in a C/C++ runtime language (e.g. C, C++, Rust, etc)
compile: compile program into WebAssembly (i.e. outputting a binary .wasm file)
load & instantiate: have JavaScript to fetch and compile the .wasm binary. Then instantiate it so that JavaScript can use it
using it:
Write
write example C++ code
int squarer(int num) { return num * num;}
Compile
there are 2 ways to compile the C++ code into WebAssembly (NOTE: I can only get the online tool to generate a .wasm file that could be used in JavaScript):
after compilation, any C++ functions that you write will be available in WebAssembly as something called an “export.” Exports are the things that you’ll be able to interact with and use in JavaScript.
online tool compiler
IMPORTANT NOTE: as shown above, the C++ function named squarer has somehow become _z7squareri. This is because the “_Z7” prefix and “i” suffix are debug markers introduced by the C++ compiler
Load & Instantiate
now we have a WebAssembly file, let’s name it squarer.wasm
we will create 2 new files:
scripts.js - takes care of loading and instantiating WebAssembly file
index.html - includes scripts.js
2 Ways to Load & Instantiate a .wasm file
old way
the olderWebAssembly.compile/WebAssembly.instantiate methods require you to create an ArrayBuffer containing your WebAssembly module binary after fetching the raw bytes, and then compile/instantiate it. This is analogous to new Function(string), except that we are substituting a string of characters (JavaScript source code) with an array buffer of bytes (WebAssembly source code)
for basic loading, there are three steps:
get the .wasm bytes into a typed array or ArrayBuffer
compile the bytes into a WebAssembly.Module
instantiate the WebAssembly.Module with imports to get the callable exports
var importObject = { env: { 'memoryBase': 0, 'tableBase': 0, 'memory': new WebAssembly.Memory({initial: 256}), 'table': new WebAssembly.Table({initial: 256, element: 'anyfunc'}), abort: alert,}};fetch('squarer.wasm') .then(response => response.arrayBuffer()) // get the .wasm bytes into a typed array or ArrayBuffer .then(bytes => WebAssembly.instantiate(bytes, importObject)) // compile & instantiate WebAssembly.Module .then(obj => { // Do something with the results! });
var importObject = { env: { 'memoryBase': 0, 'tableBase': 0, 'memory': new WebAssembly.Memory({initial: 256}), 'table': new WebAssembly.Table({initial: 256, element: 'anyfunc'}), abort: alert,}};fetch('squarer.wasm') .then(bytes => WebAssembly.instantiateStreaming(bytes, importObject)) .then(obj => { // Do something with the results! });
Using It
Now, just call your function and pass in an argument from the console. Try something like squarer(9) . Hit return and you’ll see 81 . It works! You’re calling a function written in C++!
IMPORTANT NOTE: make sure the function name matches with the online tool: obj.instance.exports._Z7squareri(9);
var importObject = { env: { 'memoryBase': 0, 'tableBase': 0, 'memory': new WebAssembly.Memory({initial: 256}), 'table': new WebAssembly.Table({initial: 256, element: 'anyfunc'}), abort: alert,}};fetch('squarer.wasm') .then(response => response.arrayBuffer()) // get the .wasm bytes into a typed array or ArrayBuffer .then(bytes => WebAssembly.instantiate(bytes, importObject)) // compile & instantiate WebAssembly.Module .then(obj => { var output = obj.instance.exports._Z7squareri(9); console.log(output); });
Example WebAssembly Object Function Calls
WebAssembly.instantiateStreaming(fetch('myModule.wasm'), importObject) .then(obj => { // Call an exported function: obj.instance.exports.exported_func(); // or access the buffer contents of an exported memory: var i32 = new Uint32Array(obj.instance.exports.memory.buffer); // or access the elements of an exported table: var table = obj.instance.exports.table; console.log(table.get(0)()); })