WebAssembly Text Format (WAT)
WAT Syntax Basics
Module Structure:
(module
;; Module content
)Function Definition:
(func $add (param $a i32) (param $b i32) (result i32)
local.get $a
local.get $b
i32.add)Memory Definition:
(memory (export "memory") 1) ;; Initial 1 page (64KB), exportableGlobal Variables:
(global $counter (mut i32) (i32.const 0)) ;; Mutable i32 global variable, initial value 0Basic Instruction Set
Numeric Operations:
i32.add ;; Addition
i32.sub ;; Subtraction
i32.mul ;; Multiplication
i32.div_s ;; Signed division
i32.eq ;; Equality comparison
i32.lt_s ;; Signed less-than comparisonControl Flow:
block
;; Code block
end
loop
;; Loop
end
if
;; Execute if condition is true
else
;; Execute if condition is false (optional)
end
br 1 ;; Branch to label at depth 1
br_if 1 ;; Branch if condition is trueMemory Access:
i32.load (offset=0 align=4) ;; Load i32 from memory, offset 0, 4-byte alignment
i32.store (offset=4 align=4) ;; Store i32 to memory, offset 4, 4-byte alignmentFunction Definition and Invocation
Function Parameters and Return Values:
(func $mul (param $a i32) (param $b i32) (result i32)
local.get $a
local.get $b
i32.mul)Function Invocation:
(func $main
i32.const 2
i32.const 3
call $add ;; Call $add function
)Local Variables:
(func $calc
(local $temp i32)
i32.const 5
local.set $temp
local.get $temp
i32.const 10
i32.add)Memory Management
Memory Allocation:
WebAssembly does not provide dynamic memory allocation instructions, but memory can be managed as follows:
- Pre-allocated Fixed-Size Memory:
(memory (export "memory") 10) ;; Initial 10 pages (640KB)- Memory Management via JavaScript:
const memory = new WebAssembly.Memory({ initial: 1 });
const wasmInstance = new WebAssembly.Instance(module, { env: { memory } });Boundary Checks:
All memory access instructions include implicit boundary checks, and out-of-bounds access triggers an exception:
i32.load (offset=100 align=4) ;; Throws exception if offset 100 exceeds memory rangeGlobal Variables and Imports/Exports
Global Variables:
(global $pi (mut f64) (f64.const 3.1415926)) ;; Mutable f64 global variableImporting Functions:
(import "env" "log" (func $log (param i32))) ;; Import log function from env moduleImporting Memory:
(import "env" "memory" (memory 1)) ;; Import 1 page of memory from env moduleExporting Functions:
(export "add" (func $add)) ;; Export $add function as "add"Exporting Memory:
(memory (export "memory") 1) ;; Export memory as "memory"WebAssembly Binary Format (WASM)
WASM Binary Structure
File Structure:
Magic Number (4 bytes): 0x00 0x61 0x73 0x6D ('\0asm')
Version (4 bytes): 0x01 0x00 0x00 0x00 (Version 1)
Section Sequence...Main Section Types:
- Type Section (1): Function signatures
- Import Section (2): Imported items
- Function Section (3): Function definitions
- Table Section (4): Function tables
- Memory Section (5): Memory definitions
- Global Section (6): Global variables
- Export Section (7): Exported items
- Start Section (8): Startup function
- Element Section (9): Table elements
- Code Section (10): Function bodies
- Data Section (11): Data segments
Module Section Analysis
Type Section Example:
Section Type: 1 (Type)
Section Size: 0x0A (10 bytes)
Content:
- Number of function types: 0x02 (2)
- Type 1: 0x60 0x02 0x7F 0x7F 0x7F (2 i32 parameters, 1 i32 return)
- Type 2: 0x60 0x01 0x7E 0x7F (1 i64 parameter, 1 i32 return)Function Section Example:
Section Type: 3 (Function)
Section Size: 0x04 (4 bytes)
Content:
- Number of functions: 0x02 (2)
- Function Type Index 1: 0x00 (First type)
- Function Type Index 2: 0x01 (Second type)Binary Encoding and Decoding
LEB128 Encoding:
Variable-length integer encoding rules:
- Most significant bit of each byte is the continuation flag (1=continue, 0=end)
- Lower 7 bits are payload data
- Little-endian ordering
Example Encoding:
- Number 624485: 0xE5 0x8E 0x26
- 0xE5: 11100101 (continue=1, value=000101)
- 0x8E: 10001110 (continue=1, value=001110)
- 0x26: 00100110 (continue=0, value=0100110)
WASM and WAT Conversion
Tools Used:
# WAT to WASM
wat2wasm add.wat -o add.wasm
# WASM to WAT
wasm2wat add.wasm -o add.txtConversion Example:
- WAT:
(module
(func $add (param $a i32) (param $b i32) (result i32)
local.get $a
local.get $b
i32.add)
(export "add" (func $add))
)- Corresponding WASM (Hexadecimal):
0061736d0100000001070160027f7f7f017f030201000705010161646400000a09010700200020016a0bWASM File Structure Analysis
Analysis Tools:
# View WASM module information
wasm2wat --debug-names add.wasm
# Visualize analysis
wasm-decompile add.wasmTypical Module Structure:
Module Header
├── Type Section (Function signatures)
├── Import Section (Imported items)
├── Function Section (Function definitions)
├── Memory Section (Memory definitions)
├── Export Section (Exported items)
├── Code Section (Function bodies)
└── Data Section (Initialized data)WebAssembly and JavaScript Interaction
Importing and Exporting Functions
JavaScript Calling WASM Functions:
// Load WASM module
WebAssembly.instantiateStreaming(fetch('math.wasm'))
.then(obj => {
// Call exported add function
console.log(obj.instance.exports.add(2, 3)); // 5
// Call exported mul function
console.log(obj.instance.exports.mul(4, 5)); // 20
});WASM Export Function Definition:
(module
(func $add (param $a i32) (param $b i32) (result i32)
local.get $a
local.get $b
i32.add)
(func $mul (param $a i32) (param $b i32) (result i32)
local.get $a
local.get $b
i32.mul)
(export "add" (func $add))
(export "mul" (func $mul))
)Memory Sharing
JavaScript Accessing WASM Memory:
WebAssembly.instantiateStreaming(fetch('memory.wasm'))
.then(obj => {
const memory = obj.instance.exports.memory;
const buffer = new Uint8Array(memory.buffer);
// Write data
buffer[0] = 65; // 'A'
buffer[1] = 66; // 'B'
// Read data
console.log(String.fromCharCode(buffer[0], buffer[1])); // "AB"
});WASM Memory Definition:
(module
(memory (export "memory") 1) ;; Export 1 page of memory
(func $write (param $offset i32) (param $value i32)
local.get $offset
local.get $value
i32.store (offset=0 align=4))
(export "write" (func $write))
)Data Type Conversion
Numeric Conversion:
// JavaScript numbers passed directly to WASM
const result = wasmInstance.exports.add(2, 3); // Automatic conversion
// Handling large integers
const bigNum = BigInt(2**53);
const wasmResult = wasmInstance.exports.processBigInt(Number(bigNum));String Conversion:
// JavaScript string to WASM
function stringToWasm(str, memory) {
const encoder = new TextEncoder();
const bytes = encoder.encode(str);
const ptr = wasmInstance.exports.alloc(bytes.length);
new Uint8Array(memory.buffer, ptr, bytes.length).set(bytes);
return ptr;
}
// WASM string to JavaScript
function wasmToString(ptr, length, memory) {
const bytes = new Uint8Array(memory.buffer, ptr, length);
return new TextDecoder().decode(bytes);
}Array Conversion:
// JavaScript array to WASM
function arrayToWasm(arr, memory) {
const ptr = wasmInstance.exports.alloc(arr.length * 4); // Assuming i32 array
new Int32Array(memory.buffer, ptr, arr.length).set(arr);
return ptr;
}
// WASM array to JavaScript
function wasmToArray(ptr, length, memory) {
return new Int32Array(memory.buffer, ptr, length);
}Asynchronous Interaction
Promises with WASM:
async function loadAndRunWASM() {
try {
const wasmModule = await WebAssembly.instantiateStreaming(
fetch('async.wasm'),
{ env: { log: console.log } }
);
// Call async-friendly WASM function
const result = wasmModule.instance.exports.computeAsync();
console.log('Result:', result);
} catch (err) {
console.error('WASM loading failed:', err);
}
}
loadAndRunWASM();Asynchronous Pattern in WASM:
;; WASM does not support async natively but can cooperate with JS:
;; 1. WASM calls JS callback function
;; 2. JS completes async operation and calls back to WASM
(func $async_operation
(import "env" "async_callback" (func $callback (param i32)))
;; Set callback and return
call $callback
i32.const 0
)Error Handling and Exception Passing
JavaScript Error Handling:
try {
const wasmInstance = await WebAssembly.instantiateStreaming(fetch('module.wasm'));
// Call WASM function that may throw
wasmInstance.exports.might_fail();
} catch (err) {
if (err instanceof WebAssembly.RuntimeError) {
console.error('WASM runtime error:', err.message);
} else {
console.error('Other error:', err);
}
}WASM Error Handling Pattern:
;; Error handling in WASM typically uses error codes
(func $safe_divide (param $a i32) (param $b i32) (result i32)
local.get $b
i32.eqz
if
i32.const -1 ;; Error code -1 for division by zero
return
end
local.get $a
local.get $b
i32.div_s
)
;; JavaScript checks error code
const result = wasmInstance.exports.safe_divide(10, 0);
if (result === -1) {
console.error('Division by zero error');
} else {
console.log('Result:', result);
}Advanced Error Handling:
// Using WebAssembly.Exception (newer feature)
try {
const wasmInstance = await WebAssembly.instantiateStreaming(fetch('exceptions.wasm'));
wasmInstance.exports.might_throw();
} catch (e) {
if (e instanceof WebAssembly.Exception) {
console.error('WASM exception:', e);
// Access exception data
const tag = e.tag;
const values = e.values;
}
}



