Skip to content

Instantly share code, notes, and snippets.

@Lohann
Last active October 22, 2024 07:58
Show Gist options
  • Save Lohann/209894dc55ed9eb6bd7b3465ad3a81cc to your computer and use it in GitHub Desktop.
Save Lohann/209894dc55ed9eb6bd7b3465ad3a81cc to your computer and use it in GitHub Desktop.
// -- package.json --
// "dependencies": {
// "wabt": "^1.0.36"
// }
/**
* The WAT code (WebAssembly Text Format) which must be compiled to WASM (binary).
* - Library used to compile: https://github.com/WebAssembly/wabt
* - WebAssembly instructions set: https://webassembly.github.io/spec/core/text/instructions.html
*/
const WAT_CODE = `
(module
(import "env" "memory" (memory $env.memory 2))
(import "env" "console_log" (func $env.console_log (param i32 i32)))
(import "env" "get_random" (func $env.get_random (result f64)))
(func $add (param $x i32) (param $y i32) (result i32)
i32.const 65536
i32.const 14
call $env.console_log
local.get $x
local.get $y
i32.add)
(func $random (result i32)
call $env.get_random
f64.const 2147483647
f64.mul
f64.ceil
i32.trunc_f64_u)
(export "add" (func $add))
(export "random" (func $random))
(data $d0 (i32.const 65536) "hello, world!\\00"))`;
/**
* Creates an utf-8 encoder/decoder.
* @example
* // Returns Uint8Array([104, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33])
* let bytes = UTF8.encode('hello, world!');
*
* @example
* // Returns 'hello, world!'
* let text = UTF8.decode([104, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33]);
*/
const UTF8 = (() => {
const _utf8Decoder = new TextDecoder('utf-8');
const _utf8Encoder = new TextEncoder('utf-8');
return {
decode: function (bytes) {
if (Array.isArray(bytes)) {
bytes = new Uint8Array(bytes);
}
return _utf8Decoder.decode(bytes);
},
encode: function (text, output) {
return _utf8Encoder.encode(text, output);
},
};
})();
/**
* Main function that runs the WebAssembly module.
*/
async function main() {
// Step 1: Compile the WAT code into WASM code.
const wabt = await require("wabt")();
const compiled = wabt.parseWat('add.wat', WAT_CODE).toBinary({}).buffer;
// Step 2: Allocate the memory that will be imported into the wasm module.
const memory = new WebAssembly.Memory({ initial: 2, maximum: 16 });
/*
* Example 01: How to read the memory
* let memorySlice = new Uint8Array(memory.buffer.slice(offset, offset + length));
*/
/*
* Example 02: How to write bytes 4 bytes to the memory.
* obs: notice 0 is the relative offset in the slice, not the absolute memory offset.
* let memorySlice = new Uint8Array(memory.buffer.slice(offset, offset + length));
* memorySlice.set([1, 2, 3, 4], 0);
*/
// Step 3: Define the imports required by the wasm module (search for "import" in the wat code).
const imports = {
env: {
/**
* The memory object to be imported by the wasm module.
* obs: This memory will be initialized with `data` segments from the wat code after instantiated, example:
* (data $d0 (i32.const 65536) "hello, world!\00") will write "hello, world!\00" to memory at offset 65536.
*
* see: (import "env" "memory" (memory $env.memory 2))
*/
memory: memory,
/**
* Function to be called by the wasm module to log a message to the console.
* @param {Number} offset memory pointer where the utf-8 string starts.
* @param {Number} len length of the string in bytes.
*
* see: (import "env" "console_log" (func $env.console_log (param i32 i32)))
*/
console_log: function (offset, len) {
// Read the memory slice and decode it as utf-8.
const bytes = new Uint8Array(memory.buffer.slice(offset, offset + len));
const message = UTF8.decode(bytes);
console.log(message);
},
/**
* Provides a random number to the wasm module.
* @returns {Number} a random number between 0 and 1.
*
* see: (import "env" "get_random" (func $env.get_random (result f64)))
*/
get_random: function () {
return Math.random();
},
},
};
// Step 4: Instantiate the module with the imported memory and methods.
const wasmInstance = await WebAssembly.instantiate(compiled, imports);
// Example 03: Altering the initialized memory directly.
// The line below overrides the `hello, world!` directly in memory, if you uncomment this line
// the code will print `hello mars!` instead`hello world`.
//
// (new Uint8Array(memory.buffer.slice(65536, 65536 + 14))).set(UTF8.encode('hello, mars! '), 0);
// Step 5: Call the exported function "add" and print the result.
// See: (export "add" (func $add))
let result = wasmInstance.instance.exports.add(7, 11)
console.log('result =', result);
// Step 5: Call the exported function "random" which returns a random unsigned integer.
// See: (export "random" (func $random))
result = wasmInstance.instance.exports.random();
console.log('random =', result);
}
main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment