Last active
October 22, 2024 07:58
-
-
Save Lohann/209894dc55ed9eb6bd7b3465ad3a81cc to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// -- 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