Zig and wasm on the command line
Having just read a short post on compiling zig into WASM, I figured I’d attempt the same. Alas, I didn’t find the instructions particularly clear.
First, write a function in zig.
// main.zig
export fn addInc(a: i32, b: i32) i32 {
return a + inc(b);
}
extern fn inc(a: i32) i32;
now, we compile it:
zig build-lib main.zig -target wasm32-freestanding -dynamic -OReleaseSafe
to produce main.wasm.
Let’s wrap this in a script to run it with node
:
// run.js
const fs = require('fs')
const m = new WebAssembly.Module(fs.readFileSync('main.wasm'))
const env = {
inc(x) { return x+1 }
}
const i = new WebAssembly.Instance(m, {env})
console.log(i.exports.addInc(1,2))
and finally run it:
> node run.js
4
Voila, we’re invoking a wasm function from javascript, which calls back to inc from javascript.
Ok, so let’s add another level of difficulty: pass a string back from wasm.
export fn add(a: i32, b: i32) i32 {
const message = "happy happy joy joy";
consoleLog(message, message.len);
return a + b;
}
extern fn consoleLog(message: [*]const u8, length: u8) void;
const fs = require('fs')
const m = new WebAssembly.Module(fs.readFileSync('main_console.wasm'))
var i;
const env = {
consoleLog(location, size) { consoleLog(i, location, size) }
}
i = new WebAssembly.Instance(m, {env})
consoleLog = (instance, location, size) => {
const buffer = new Uint8Array(instance.exports.memory.buffer, location, size);
const decoder = new TextDecoder();
const string = decoder.decode(buffer);
console.log(string);
}
console.log(i.exports.add(1,2));
This time, we invoke consoleLog
in the wasm binary and delegate to caller which reads the string passed directly from memory.
The consoleLog example can be traced back to this zig-wasm example project orginally.