Advanced WebAssembly Programming Techniques
Complex Data Structures (Structs, Unions, Arrays)
Struct Definition and Usage:
;; WAT definition of structs (via memory layout)
(module
;; Define a memory export
(memory (export "memory") 1)
;; Struct layout: [i32, i32] (8 bytes total)
(func $create_struct (param $a i32) (param $b i32) (result i32)
local.get $a
local.get $b
local.tee $a
i32.add ;; Simple example: return address of a+b (should allocate memory in practice)
;; Actual implementation requires memory allocation
)
;; More complete struct example
(func $create_person (param $name_ptr i32) (param $name_len i32) (param $age i32) (result i32)
;; Allocate memory: assuming Person struct is name_ptr(i32) + name_len(i32) + age(i32) = 12 bytes
(local $ptr i32)
;; Call memory allocation function (must be predefined)
call $alloc
local.set $ptr
;; Store fields
local.get $ptr
local.get $name_ptr
i32.store ;; Store name_ptr
local.get $ptr
i32.const 4
local.get $name_len
i32.store ;; Store name_len (offset 4 bytes)
local.get $ptr
i32.const 8
local.get $age
i32.store ;; Store age (offset 8 bytes)
local.get $ptr ;; Return struct pointer
)
)Union (Variant Type) Simulation:
;; Simulating unions in WASM (accessing same memory with different views)
(module
(memory (export "memory") 1)
;; Define union: can be i32 or f32 (4 bytes)
(func $create_union (param $value i32) (result i32)
;; Allocate 4 bytes of memory
call $alloc
local.set $ptr
;; Store as i32
local.get $ptr
local.get $value
i32.store
local.get $ptr ;; Return union pointer
)
;; Read as i32
(func $read_union_as_i32 (param $ptr i32) (result i32)
local.get $ptr
i32.load
)
;; Read as f32 (requires JavaScript cooperation for interpretation)
(func $read_union_as_f32 (param $ptr i32) (result f32)
;; WASM lacks f32.load from arbitrary pointers
;; Ensure pointer is properly aligned before using f32.load
local.get $ptr
f32.load ;; Assumes pointer is properly aligned
)
)Dynamic Array Implementation:
;; Dynamic array implementation (simplified)
(module
(memory (export "memory") 1)
;; Array structure: [length(i32), capacity(i32), data(pointer)]
(func $create_array (param $initial_capacity i32) (result i32)
;; Allocate memory: 8 bytes header + capacity*i32 data
local.get $initial_capacity
i32.const 4 ;; Each element is 4 bytes
i32.mul
i32.add
i32.const 8 ;; Header is 8 bytes
i32.add
call $alloc
local.set $ptr
;; Initialize header
local.get $ptr
i32.const 0 ;; length=0
i32.store
local.get $ptr
i32.const 4
local.get $initial_capacity
i32.store ;; capacity=initial_capacity
local.get $ptr
i32.const 8 ;; data pointer points to data area
local.get $initial_capacity
i32.const 4
i32.mul
i32.add
i32.store ;; data pointer=ptr+8
local.get $ptr ;; Return array pointer
)
;; Append element to array
(func $array_push (param $array_ptr i32) (param $value i32)
;; Get current length and capacity
local.get $array_ptr
i32.load ;; length
local.get $array_ptr
i32.const 4
i32.load ;; capacity
i32.ge_u
if
;; Need to resize (simplified: double capacity)
local.get $array_ptr
i32.const 4
i32.load
i32.const 2
i32.mul
call $resize_array
end
;; Store new element
local.get $array_ptr
i32.const 8
i32.load ;; data pointer
local.get $array_ptr
i32.load ;; length
i32.mul
i32.const 4
i32.mul
i32.add
local.get $value
i32.store
;; Update length
local.get $array_ptr
local.get $array_ptr
i32.load
i32.const 1
i32.add
i32.store
)
;; Resize array (simplified implementation)
(func $resize_array (param $array_ptr i32) (param $new_capacity i32)
;; Actual implementation requires new memory allocation and data copying
;; Simplified here
local.get $array_ptr
local.get $new_capacity
i32.const 4
i32.mul
i32.add
i32.const 8
i32.add
call $alloc
;; ... copy data ...
)
)Function Pointers and Dynamic Calls (Tables and Indirect Calls)
Function Table Definition and Usage:
(module
;; Define function table (containing 3 functions)
(table (export "table") 3 anyfunc)
;; Define three functions
(func $add (param $a i32) (param $b i32) (result i32)
local.get $a
local.get $b
i32.add)
(func $sub (param $a i32) (param $b i32) (result i32)
local.get $a
local.get $b
i32.sub)
(func $mul (param $a i32) (param $b i32) (result i32)
local.get $a
local.get $b
i32.mul)
;; Add functions to table
(elem (i32.const 0) $add $sub $mul)
;; Indirectly call function via table
(func $call_via_table (param $index i32) (param $a i32) (param $b i32) (result i32)
local.get $index
local.get $a
local.get $b
call_indirect (type 0) ;; Assumes type 0 matches add/sub/mul signature
)
;; Define function type (for call_indirect)
(type (func (param i32 i32) (result i32)))
)Dynamic Function Registration and Calls:
(module
;; Extensible function table
(table (export "table") 10 anyfunc) ;; Initially 10 slots
;; Count of registered functions
(global $registered_count (mut i32) (i32.const 0))
;; Register new function to table
(func $register_function (param $func anyfunc)
local.get $registered_count
local.tee $index
i32.const 10
i32.ge_u
if
unreachable ;; Table is full
end
;; Store function in table
local.get $index
local.get $func
table.set
;; Increment count
local.get $registered_count
i32.const 1
i32.add
global.set $registered_count
)
;; Call registered function
(func $call_registered (param $index i32) (param $a i32) (param $b i32) (result i32)
local.get $index
local.get $a
local.get $b
call_indirect (type 0) ;; Use same function type
)
;; Define function type
(type (func (param i32 i32) (result i32)))
)JavaScript-Side Dynamic Function Registration:
// Load WASM module
WebAssembly.instantiateStreaming(fetch('dynamic_call.wasm'))
.then(obj => {
const wasm = obj.instance;
const table = wasm.exports.table;
// Define JavaScript function
function jsAdd(a, b) {
return a + b;
}
// Convert JavaScript function to WASM-callable format
const jsFunc = new WebAssembly.Function(
{ parameters: ['i32', 'i32'], results: ['i32'] },
jsAdd
);
// Add JavaScript function to WASM table
table.set(3, jsFunc); // Add to 4th slot (index 3)
// Call JavaScript function via WASM
const result = wasm.exports.call_via_table(3, 5, 7);
console.log('JS function call result:', result); // 12
});Multi-Threading Support (SharedArrayBuffer, Atomic Operations)
WASM Multi-Threading Basics:
;; WASM multi-threading example (conceptual code)
(module
;; Shared memory (must be marked as shared)
(memory (export "memory") 1 shared)
;; Atomic operation example
(func $atomic_add (param $ptr i32) (param $value i32) (result i32)
local.get $ptr
local.get $value
i32.atomic.rmw.add ;; Atomic addition
)
;; Atomic compare-and-swap
(func $atomic_cas (param $ptr i32) (param $expected i32) (param $replacement i32) (result i32)
local.get $ptr
local.get $expected
local.get $replacement
i32.atomic.rmw.cmpxchg ;; Atomic compare-and-swap
)
;; Thread synchronization example (simplified)
(func $worker (param $id i32)
;; Wait for start signal
(loop $wait
i32.atomic.load (i32.const 0) ;; Read start flag
i32.const 1
i32.eq
if
br $done_wait
end
;; Busy waiting (should use more efficient sync primitives in practice)
end)
(block $done_wait
;; Perform work...
local.get $id
call $do_work
;; Set completion flag
local.get $id
i32.const 1
i32.atomic.store (i32.const 1024) ;; Assume offset 1024 is completion flags array
)
)
)JavaScript-Side Multi-Threading Example:
// Main thread code
const workerCount = 4;
const workers = [];
const sharedMemory = new WebAssembly.Memory({
initial: 1,
maximum: 1,
shared: true
});
// Load WASM module
WebAssembly.instantiateStreaming(fetch('threaded.wasm'), {
env: {
memory: sharedMemory
}
}).then(obj => {
const wasm = obj.instance;
// Create shared data
const sharedArray = new Int32Array(sharedMemory.buffer);
sharedArray[0] = 0; // Start flag
// Create worker threads
for (let i = 0; i < workerCount; i++) {
const worker = new Worker('wasm_worker.js');
worker.postMessage({
wasmModule: wasm,
workerId: i,
memory: sharedMemory
});
workers.push(worker);
}
// Set start flag
setTimeout(() => {
sharedArray[0] = 1; // Notify all worker threads to start
}, 100);
});
// wasm_worker.js
self.onmessage = function(e) {
const { wasmModule, workerId, memory } = e.data;
// Run WASM function in Worker
wasmModule.exports.worker(workerId);
// Report completion
self.postMessage({ workerId, done: true });
};Atomic Operations Example:
(module
;; Shared counter
(memory (export "memory") 1 shared)
;; Atomic counter operations
(func $atomic_counter_increment (param $ptr i32) (result i32)
local.get $ptr
i32.atomic.rmw.add (i32.const 1) ;; Atomically add 1
)
;; Atomic counter read
(func $atomic_counter_get (param $ptr i32) (result i32)
local.get $ptr
i32.atomic.load
)
;; More complex synchronization example
(func $producer_consumer (param $data_ptr i32) (param $ready_flag_ptr i32) (param $consume_flag_ptr i32)
;; Producer logic
(loop $produce
;; Produce data (simplified)
local.get $data_ptr
i32.const 42 ;; Example data
i32.store
;; Set ready flag
local.get $ready_flag_ptr
i32.const 1
i32.atomic.store
;; Wait for consume flag
(loop $wait_consume
local.get $consume_flag_ptr
i32.atomic.load
i32.const 1
i32.eq
if
br $done_wait_consume
end
end)
(block $done_wait_consume
;; Reset consume flag
local.get $consume_flag_ptr
i32.const 0
i32.atomic.store
)
)
)
)Exception Handling (Exception Mechanism and Error Propagation)
WASM Exception Handling Proposal Implementation:
;; Exception handling example (based on current proposal)
(module
;; Define exception type (simplified)
(tag $runtime_error (param i32)) ;; Error code
;; Function that may throw an exception
(func $might_throw (param $value i32) (result i32)
local.get $value
i32.const 0
i32.eq
if
;; Throw exception
(throw $runtime_error (i32.const 42)) ;; Error code 42
end
local.get $value
i32.mul ;; Normal return
)
;; Call function that may throw
(func $safe_call (param $value i32) (result i32)
(try
local.get $value
call $might_throw
(catch $runtime_error
;; Handle exception
local.get $value
i32.const -1 ;; Return error indicator value
)
)
)
)JavaScript and WASM Exception Interaction:
// Load WASM module with exception support
WebAssembly.instantiateStreaming(fetch('exceptions.wasm'))
.then(obj => {
const wasm = obj.instance;
try {
// Call function that may throw
const result = wasm.exports.safe_call(0);
console.log('Result:', result);
} catch (e) {
if (e instanceof WebAssembly.Exception) {
console.error('WASM exception:', e);
// Handle exception...
} else {
console.error('Other error:', e);
}
}
});Error Code Propagation Pattern:
;; Error handling without exception mechanism (more compatible with current WASM)
(module
;; Define error codes
(global $ERROR_NONE i32 (i32.const 0))
(global $ERROR_DIV_BY_ZERO i32 (i32.const 1))
(global $ERROR_INVALID_ARG i32 (i32.const 2))
;; Function that may fail
(func $safe_divide (param $a i32) (param $b i32) (result i32)
local.get $b
i32.eqz
if
global.get $ERROR_DIV_BY_ZERO
return
end
local.get $a
local.get $b
i32.div_s
)
;; Call and check for errors
(func $call_with_error_check (param $a i32) (param $b i32) (result i32)
local.get $a
local.get $b
call $safe_divide
local.tee $result
global.get $ERROR_NONE
i32.ne
if
;; Handle error
local.get $result
return ;; Return error code
end
local.get $result
)
)Custom Memory Management (Memory Allocators, Garbage Collection)
Simple Memory Allocator Implementation:
(module
;; Memory pool management
(memory (export "memory") 1)
;; Memory block header structure: [size(i32), next(i32)]
(global $heap_start i32 (i32.const 8)) ;; Heap starts at offset 8 (skip header)
(global $free_list i32 (i32.const 0)) ;; Initial free list is empty
;; Initialize memory pool
(func $init_memory_pool (param $size i32)
;; Allocate initial memory block
local.get $size
i32.const 8 ;; Header size
i32.add
call $alloc_raw ;; Low-level allocation (not through allocator)
;; Set heap start
local.get $size
i32.const 8
i32.add
global.set $heap_start
;; Initialize free list
local.get $size
i32.const 8
i32.add
global.set $free_list
)
;; Low-level memory allocation (directly from WASM memory)
(func $alloc_raw (param $size i32) (result i32)
;; Simplified: assumes enough memory
local.get $size
i32.const 0 ;; Fixed address (simplified)
)
;; Allocate memory (with header)
(func $alloc (param $size i32) (result i32)
;; Calculate total size (including header)
local.get $size
i32.const 8
i32.add
;; Find free block
(local $ptr i32)
(local $found i32)
global.get $free_list
local.set $ptr
(loop $search
local.get $ptr
i32.eqz
if
;; No suitable block found (simplified: allocate new memory)
local.get $size
i32.const 8
i32.add
call $alloc_raw
local.set $ptr
br $found
end
;; Check block size
local.get $ptr
i32.load ;; Load block size
local.get $size
i32.ge_u
if
;; Found suitable block
br $found
end
;; Move to next block
local.get $ptr
i32.const 4
i32.load
local.set $ptr
br $search
end)
(block $found
;; Allocate memory (from found block or newly allocated)
local.get $ptr
local.tee $block_ptr
;; Set block header
local.get $size
i32.store ;; Store requested size
;; Calculate next free block
local.get $block_ptr
i32.const 8
i32.add
local.get $size
i32.add
;; ... update free list ...
;; Return user-usable pointer (skip header)
local.get $block_ptr
i32.const 8
i32.add
)
)
;; Free memory
(func $free (param $ptr i32)
;; Get block header
local.get $ptr
i32.const -8
i32.add
local.set $header_ptr
;; Add block to free list (simplified implementation)
local.get $header_ptr
i32.load ;; Block size
;; ... update free list ...
)
)Integration with JavaScript Garbage Collection:
// Manage WASM memory with GC in JavaScript
class WasmMemoryManager {
constructor(wasmInstance) {
this.wasm = wasmInstance;
this.allocatedObjects = new Set();
}
// Allocate memory and track
alloc(size) {
const ptr = this.wasm.exports.alloc(size);
this.allocatedObjects.add(ptr);
return ptr;
}
// Free memory and untrack
free(ptr) {
if (this.allocatedObjects.has(ptr)) {
this.wasm.exports.free(ptr);
this.allocatedObjects.delete(ptr);
}
}
// Create object and automatically manage memory
createObject(createFunc, ...args) {
const ptr = createFunc(...args);
this.allocatedObjects.add(ptr);
// Return a proxy with auto-free functionality
return new Proxy({}, {
get(target, prop) {
if (prop === 'free') {
return () => wasmMemoryManager.free(ptr);
}
// Other property access...
}
});
}
// Periodic cleanup (example)
periodicCleanup() {
// Implement reference counting or other GC strategy
}
}
// Usage example
const manager = new WasmMemoryManager(wasmInstance);
const objPtr = manager.alloc(100);
// ...use objPtr...
manager.free(objPtr);More Advanced Memory Management Strategies:
;; Generational garbage collection simulation (conceptual code)
(module
;; Memory region definitions
(memory (export "memory") 1)
;; Young generation (eden space)
(global $eden_start i32 (i32.const 8))
(global $eden_end i32 (i32.const 1024)) ;; 1KB eden space
;; Old generation
(global $old_start i32 (i32.const 1024))
(global $old_end i32 (i32.const 65536)) ;; 64KB old generation
;; Allocation counter
(global $alloc_count i32 (i32.const 0))
(global $gc_threshold i32 (i32.const 100)) ;; Trigger GC every 100 allocations
;; Allocate in eden space
(func $alloc_in_eden (param $size i32) (result i32)
local.get $alloc_count
local.tee $count
global.get $gc_threshold
i32.ge_u
if
call $collect_garbage
global.set $alloc_count (i32.const 0)
else
local.get $count
i32.const 1
i32.add
global.set $alloc_count
end
;; Check eden space
global.get $eden_start
local.get $size
i32.add
global.get $eden_end
i32.le_u
if
;; Enough space
local.get $eden_start
local.tee $ptr
local.get $size
i32.store ;; Store size (simplified)
global.get $eden_start
local.get $size
i32.add
global.set $eden_start
local.get $ptr
else
;; Eden space full, try allocating in old generation
call $alloc_in_old
end
)
;; Allocate in old generation
(func $alloc_in_old (param $size i32) (result i32)
;; Similar to eden allocation logic...
)
;; Garbage collection
(func $collect_garbage
;; Marking phase (requires knowledge of root references)
;; Scan JavaScript references...
;; Sweeping phase
;; Reclaim unmarked memory...
)
)WebAssembly Performance Optimization
Compiler Optimization Options (Emscripten Optimization Levels)
Emscripten Optimization Levels Explained:
# Comparison of different optimization levels
emcc -O0 input.c -o output.js # No optimization (debug-friendly)
emcc -O1 input.c -o output.js # Basic optimization
emcc -O2 input.c -o output.js # More optimization
emcc -O3 input.c -o output.js # Aggressive optimization (default)
emcc -Os input.c -o output.js # Optimize for code size
emcc -Oz input.c -o output.js # Maximize code size optimization
# Special optimization options
emcc -flto input.c -o output.js # Link-time optimization
emcc -fno-exceptions input.c -o output.js # Disable exceptions
emcc -fno-rtti input.c -o output.js # Disable RTTIImpact of Optimization Levels on Performance:
| Optimization Level | Compilation Time | Code Size | Execution Speed | Debugging Capability |
|---|---|---|---|---|
| -O0 | Fastest | Largest | Slowest | Best |
| -O1 | Fast | Large | Slower | Good |
| -O2 | Medium | Medium | Medium | Average |
| -O3 | Slow | Small | Fastest | Poor |
| -Os | Medium | Smallest | Medium | Average |
| -Oz | Slow | Smallest | Slower | Poor |
Advanced Optimization Example:
# Combining multiple optimization options
emcc \
-O3 \ # Highest optimization level
-flto \ # Link-time optimization
-s WASM=1 \ # Enable WASM
-s AGGRESSIVE_VARIABLE_ELIMINATION=1 \ # Aggressive variable elimination
-s ELIMINATE_DUPLICATE_FUNCTIONS=1 \ # Eliminate duplicate functions
-s DEAD_CODE_ELIMINATION=1 \ # Dead code elimination
-s SAFE_HEAP=0 \ # Disable safe heap checks (improve performance)
input.c -o output.jsMemory Access Optimization (Reducing Bounds Checks, Memory Alignment)
Reducing Bounds Checks:
;; Original code (with bounds checks)
(func $unsafe_access (param $ptr i32) (result i32)
local.get $ptr
i32.load ;; Implicit bounds check
)
;; Optimized (remove checks after ensuring safety)
(func $safe_access (param $ptr i32) (result i32)
;; Assume ptr is valid
local.get $ptr
i32.load ;; Compiler may optimize out check
)
;; Better approach: use explicit memory region
(module
;; Define explicit memory region
(memory (export "memory") 1)
(global $array_start i32 (i32.const 8))
(global $array_end i32 (i32.const 1024)) ;; 1KB array
;; Optimized access (compiler knows range)
(func $optimized_access (param $index i32) (result i32)
global.get $array_start
local.get $index
i32.const 4 ;; Element size
i32.mul
i32.add
i32.load ;; May optimize out bounds check
)
)Memory Alignment Optimization:
;; Unaligned access (may cause performance penalty)
(func $unaligned_access (param $ptr i32) (result i32)
local.get $ptr
i32.load ;; Assume ptr is unaligned
)
;; Aligned access (better performance)
(func $aligned_access (param $ptr i32) (result i32)
;; Ensure ptr is 4-byte aligned
local.get $ptr
i32.const 3
i32.and ;; Check lowest 2 bits
i32.eqz
if
local.get $ptr
i32.load ;; Aligned access
else
;; Handle unaligned case (or assert)
unreachable
end
)
;; Use aligned load instructions
(func $aligned_load (param $ptr i32) (result i32)
local.get $ptr
i32.load ;; Compiler knows ptr is aligned
;; Or explicitly use aligned load (if WASM supports)
;; local.get $ptr
;; i32.load align=4 ;; Explicitly specify alignment
)Data Structure Layout Optimization:
;; Before optimization (may cause padding)
(struct $bad_layout
(field $a i8) ;; 1 byte
(field $b i32) ;; 4 bytes
(field $c i16) ;; 2 bytes
) ;; Total size might be 8 bytes (with padding)
;; After optimization (reduce padding)
(struct $good_layout
(field $a i8) ;; 1 byte
(field $c i16) ;; 2 bytes
(field $b i32) ;; 4 bytes
) ;; Total size 7 bytes (may still have some padding)
;; Best layout (by descending size)
(struct $best_layout
(field $b i32) ;; 4 bytes
(field $c i16) ;; 2 bytes
(field $a i8) ;; 1 byte
) ;; Total size 7 bytes (possibly more compact)Function Inlining and Code Optimization
Function Inlining Example:
;; Original code (function call)
(func $add (param $a i32) (param $b i32) (result i32)
local.get $a
local.get $b
i32.add)
(func $compute (param $x i32) (param $y i32) (result i32)
local.get $x
local.get $y
call $add ;; Function call
)
;; After inlining (may be optimized to)
(func $compute (param $x i32) (param $y i32) (result i32)
local.get $x
local.get $y
local.get $x
local.get $y
i32.add ;; Directly inline add operation
)Emscripten Inlining Control:
# Control inlining behavior
emcc \
-O3 \ # Enable inlining
-fno-inline-functions \ # Disable all function inlining
-finline-limit=100 \ # Set inlining threshold (bytes)
input.c -o output.jsManual Inlining Hints:
// C code using inlining hints
__attribute__((always_inline))
int add(int a, int b) {
return a + b;
}
// Or
__attribute__((noinline))
int expensive_op(int a, int b) {
// Complex operation, should not be inlined
}Loop Optimization:
;; Original loop
(func $sum_array (param $ptr i32) (param $len i32) (result i32)
(local $i i32)
(local $sum i32)
local.get $i
i32.const 0
local.set $i
(loop $loop
local.get $i
local.get $len
i32.ge_u
br_if $end
local.get $ptr
local.get $i
i32.add
i32.load
local.get $sum
i32.add
local.set $sum
local.get $i
i32.const 1
i32.add
local.set $i
br $loop
)
(block $end
local.get $sum
)
)
;; Optimized (loop unrolling)
(func $sum_array_unrolled (param $ptr i32) (param $len i32) (result i32)
;; Unroll 4 iterations
(local $i i32)
(local $sum i32)
local.get $i
i32.const 0
local.set $i
(loop $loop
local.get $i
local.get $len
i32.ge_u
br_if $end
;; First iteration
local.get $ptr
local.get $i
i32.add
i32.load
local.get $sum
i32.add
local.set $sum
local.get $i
i32.const 1
i32.add
local.set $i
local.get $i
local.get $len
i32.ge_u
br_if $end
;; Second iteration
local.get $ptr
local.get $i
i32.add
i32.load
local.get $sum
i32.add
local.set $sum
local.get $i
i32.const 1
i32.add
local.set $i
;; ... more iterations ...
)Parallel Computing and SIMD Instructions
Complete Example Combining SIMD and Multi-Threading:
;; WASM module combining SIMD and multi-threading (conceptual code)
(module
;; Shared memory (must be marked as shared)
(memory (export "memory") 1 shared)
;; Define thread parameters structure
(struct $thread_params
(field $start_idx i32) ;; Start index
(field $end_idx i32) ;; End index
(field $data_ptr i32) ;; Data pointer
(field $result_ptr i32) ;; Result pointer
)
;; SIMD processing function (single-threaded version)
(func $simd_process_segment (param $start_idx i32) (param $end_idx i32) (param $data_ptr i32) (param $result_ptr i32)
(local $i i32)
local.get $i
local.set $i
(loop $process_loop
local.get $i
local.get $end_idx
i32.ge_u
br_if $process_done
;; Calculate current data block pointer
local.get $data_ptr
local.get $i
i32.const 16 ;; Assume each SIMD block is 16 bytes (4 f32)
i32.mul
i32.add
local.set $data_block_ptr
;; Calculate current result block pointer
local.get $result_ptr
local.get $i
i32.const 16
i32.mul
i32.add
local.set $result_block_ptr
;; Load data into SIMD register
local.get $data_block_ptr
v128() ;; Assume v128.load loads 128 bits (4*f32)
;; SIMD processing (e.g., vector addition)
;; Simplified to direct copy (actual should perform SIMD operations)
;; local.get $data_block_ptr
;; v128.load
;; v128.add ;; Example SIMD operation
;; Actual should load two vectors and add them
;; Store result
local.get $result_block_ptr
v128.store
local.get $i
i32.const 1
i32.add
local.set $i
br $process_loop
end)
(block $process_done)
)
;; Worker thread function
(func $worker_thread (param $params_ptr i32)
;; Extract parameters from struct
local.get $params_ptr
i32.const 0
i32.add
i32.load ;; start_idx
local.get $params_ptr
i32.const 4
i32.add
i32.load ;; end_idx
local.get $params_ptr
i32.const 8
i32.add
i32.load ;; data_ptr
local.get $params_ptr
i32.const 12
i32.add
i32.load ;; result_ptr
call $simd_process_segment
)
;; Main function (starts multiple threads)
(func $parallel_simd_process (param $data_ptr i32) (param $result_ptr i32) (param $total_elements i32)
;; Determine thread count (simplified: fixed number)
i32.const 4
local.set $thread_count
;; Calculate data per thread
local.get $total_elements
local.get $thread_count
i32.div_u
local.set $elements_per_thread
;; Create thread parameters array (simplified: fixed-size array)
i32.const 4
i32.const 16 ;; Each parameter struct is 16 bytes (4*i32)
i32.mul
call $alloc_raw ;; Allocate thread parameters memory
local.get $params_ptr
local.set $params_array_ptr
;; Initialize thread parameters
(local $i i32)
local.get $i
i32.const 0
local.set $i
(loop $init_loop
local.get $i
local.get $thread_count
i32.ge_u
br_if $init_done
;; Calculate start and end indices for current thread
local.get $i
local.get $elements_per_thread
i32.mul
local.set $start_idx
local.get $i
local.get $elements_per_thread
i32.mul
local.get $elements_per_thread
i32.add
local.get $total_elements
i32.min
local.set $end_idx
recognizer: local.get $params_array_ptr
local.get $i
i32.const 16
i32.mul
i32.add
local.set $current_params_ptr
;; start_idx
local.get $current_params_ptr
local.get $start_idx
i32.store
;; end_idx
local.get $current_params_ptr
i32.const 4
i32.add
local.get $end_idx
i32.store
;; data_ptr
local.get $current_params_ptr
i32.const 8
i32.add
local.get $data_ptr
i32.store
;; result_ptr
local.get $current_params_ptr
i32.const 12
i32.add
local.get $result_ptr
i32.store
local.get $i
i32.const 1
i32.add
local.set $i
br $init_loop
end)
(block $init_done)
;; Create and start threads (simplified: actual requires Web Workers)
(local $thread_id i32)
local.get $thread_id
i32.const 0
local.set $thread_id
(loop $thread_loop
local.get $thread_id
local.get $thread_count
i32.ge_u
br_if $thread_done
;; In practice, create Web Worker and pass WASM module and parameters
;; Simplified: assume some way to start thread
local.get $thread_id
local.get $params_array_ptr
local.get $thread_id
i32.const 16
i32.mul
i32.add
call $worker_thread
local.get $thread_id
i32.const 1
i32.add
local.set $thread_id
br $thread_loop
)
(block $thread_done)
)
)SIMD Optimization Best Practices:
- Data Alignment: Ensure SIMD data is 16 bytes aligned (for v128 types)
- Avoid Branching: SIMD instructions typically don’t support conditional branches; use masks instead
- Batch Processing: Process multiple data elements at once
- Reduce Memory Accesses: Perform calculations in registers as much as possible
- Mix SIMD and Scalar: Not all operations are suitable for SIMD
Performance Analysis and Debugging Tools (Continued)
Wasmtime Advanced Profiling Features:
# Detailed performance profiling with Wasmtime
wasmtime \
--profile=cpu=cpu_profile.json \ # CPU profiling
--profile=memory=memory_profile.json \ # Memory usage profiling
--profile=block:block_profile.json \ # Execution block profiling
--profile=cache=cache_profile.json \ # Cache performance profiling
optimized.wasm
# Generate interactive flame graph
wasmtime \
--profile=flamegraph=flamegraph.html \ # Interactive flame graph
optimized.wasm
# Detailed memory profiling options
wasmtime \
--profile=memory=memory_profile.json \
--memory-profiling-sampling-rate=100 \ # Sampling rate (percentage)
optimized.wasmChrome DevTools Advanced Profiling Techniques:
- Memory Snapshots Comparison:
- Take memory snapshots before and after WASM operations
- Use “Comparison” view to inspect memory changes
- Allocation Timeline:
- Enable “Allocation instrumentation on timeline”
- Record WASM memory allocation patterns
- Performance Markers:
// Add performance markers before and after WASM calls performance.mark('wasm-start'); wasmInstance.exports.compute(); performance.mark('wasm-end'); performance.measure('wasm-compute', 'wasm-start', 'wasm-end'); // View measurement results const measures = performance.getEntriesByName('wasm-compute'); console.log('WASM computation time:', measures[0].duration);
WASM-Specific Performance Metrics:
- WASM Instruction Count: Obtained via Wasmtime block profiling
- Memory Access Patterns: Analyze proportion of memory load/store instructions
- Function Call Overhead: Measure cost of WASM-to-WASM or WASM-to-JS calls
- SIMD Utilization: Check proportion of SIMD instructions
Performance Optimization Checklist (Continued):
- Parallelization:
- Identify computationally intensive tasks that can be parallelized
- Use Web Workers and SharedArrayBuffer
- Reasonably divide workloads
- Cache Friendliness:
- Optimize data locality
- Reduce cache misses
- Use data structures suited for CPU cache
- Algorithm Optimization:
- Choose algorithms with lower time complexity
- Avoid unnecessary computations
- Use lookup tables instead of complex calculations
- Reduce WASM-JS Boundary Overhead:
- Batch data transfers
- Minimize cross-boundary calls
- Use shared memory
- Compiler-Specific Optimization:
- Leverage Emscripten-specific optimization flags
- Optimize for target platform (e.g., SIMD instruction set)
- Use latest WASM features
WebAssembly Integration with High-Level Languages
Advanced Rust and WASM Integration
Advanced Rust Lifecycle Management Example:
// Rust WASM module with advanced lifecycle management
use wasm_bindgen::prelude::*;
use std::rc::Rc;
use std::cell::RefCell;
// Manage resources with reference counting
#[wasm_bindgen]
pub struct SharedResource {
data: Rc<RefCell<Vec<u8>>>,
}
#[wasm_bindgen]
impl SharedResource {
#[wasm_bindgen(constructor)]
pub fn new(size: usize) -> SharedResource {
SharedResource {
data: Rc::new(RefCell::new(vec![0; size])),
}
}
// Clone resource (increment ref count)
#[wasm_bindgen]
pub fn clone_resource(&self) -> SharedResource {
SharedResource {
data: Rc::clone(&self.data),
}
}
// Modify resource data
#[wasm_bindgen]
pub fn modify_data(&self, offset: usize, value: u8) {
let mut data = self.data.borrow_mut();
if offset < data.len() {
data[offset] = value;
}
}
// Read resource data
#[wasm_bindgen]
pub fn read_data(&self, offset: usize) -> u8 {
let data = self.data.borrow();
if offset < data.len() {
data[offset]
} else {
0
}
}
}
// Use weak references to avoid cycles
#[wasm_bindgen]
pub struct Node {
value: i32,
parent: Option<WeakRef<Node>>,
children: Vec<Rc<RefCell<Node>>>,
}
#[wasm_bindgen]
impl Node {
#[wasm_bindgen(constructor)]
pub fn new(value: i32) -> Node {
Node {
value,
parent: None,
children: Vec::new(),
}
}
// Add child node
#[wasm_bindgen]
pub fn add_child(&mut self, child: Node) {
let child_rc = Rc::new(RefCell::new(child));
let weak_parent = Rc::downgrade(&Rc::new(RefCell::new(self.clone())));
child_rc.borrow_mut().parent = Some(weak_parent);
self.children.push(child_rc);
}
// Get parent node (if exists)
#[wasm_bindgen]
pub fn get_parent(&self) -> Option<Node> {
self.parent.as_ref().and_then(|weak| weak.upgrade().ok()).map(|rc| rc.borrow().clone())
}
}
// Note: The above Node requires implementing Clone trait in practiceRust Trait Objects and Dynamic Dispatch:
// Rust WASM module with trait objects example
use wasm_bindgen::prelude::*;
// Define trait
pub trait DataProcessor {
fn process(&self, data: &[u8]) -> Vec<u8>;
fn name(&self) -> String;
}
// Implement trait for struct 1
pub struct UppercaseProcessor;
impl DataProcessor for UppercaseProcessor {
fn process(&self, data: &[u8]) -> Vec<u8> {
data.iter().map(|&b| b.to_ascii_uppercase()).collect()
}
fn name(&self) -> String {
"Uppercase Processor".to_string()
}
}
// Implement trait for struct 2
pub struct ReverseProcessor;
impl DataProcessor for ReverseProcessor {
fn process(&self, data: &[u8]) -> Vec<u8> {
let mut result = data.to_vec();
result.reverse();
result
}
fn name(&self) -> String {
"Reverse Processor".to_string()
}
}
// Use trait object
#[wasm_bindgen]
pub struct ProcessorWrapper {
processor: Box<dyn DataProcessor>,
}
#[wasm_bindgen]
impl ProcessorWrapper {
#[wasm_bindgen(constructor)]
pub fn new(processor_type: &str) -> ProcessorWrapper {
let processor: Box<dyn DataProcessor> = match processor_type {
"uppercase" => Box::new(UppercaseProcessor),
"reverse" => Box::new(ReverseProcessor),
_ => panic!("Unknown processor type"),
};
ProcessorWrapper { processor }
}
pub fn process(&self, data: &[u8]) -> Vec<u8> {
self.processor.process(data)
}
pub fn name(&self) -> String {
self.processor.name()
}
}
// Dynamically create trait object (more flexible factory pattern)
#[wasm_bindgen]
pub fn create_processor(processor_type: &str) -> Box<dyn DataProcessor> {
match processor_type {
"uppercase" => Box::new(UppercaseProcessor),
"reverse" => Box::new(ReverseProcessor),
_ => panic!("Unknown processor type"),
}
}Rust with Complex JavaScript Interaction:
// Advanced JavaScript interaction in Rust WASM module
use wasm_bindgen::prelude::*;
use js_sys::{Promise, Array, Object, Reflect};
use web_sys::{console, Window, Document, HtmlElement};
// Complex data structure interaction
#[wasm_bindgen]
pub fn process_complex_js_object(js_obj: &JsValue) -> Result<JsValue, JsValue> {
// Convert JsValue to more specific type
let obj = js_sys::Object::from(js_obj);
// Get nested properties
let user = Reflect::get(&obj, &"user".into())?;
let user_obj = js_sys::Object::from(user);
let name = Reflect::get(&user_obj, &"name".into())?
.as_string()
.ok_or_else(|| JsValue::from_str("name must be a string"))?;
let age = Reflect::get(&user_obj, &"age".into())?
.as_f64()
.ok_or_else(|| JsValue::from_str("age must be a number"))? as u32;
// Process data
let processed_name = format!("Processed: {}", name);
let processed_age = age + 1;
// Create return JavaScript object
let result = Object::new();
Reflect::set(&result, &"name".into(), &JsValue::from(processed_name))?;
Reflect::set(&result, &"age".into(), &JsValue::from(processed_age))?;
// Add processing timestamp
let timestamp = js_sys::Date::now();
Reflect::set(&result, &"timestamp".into(), ×tamp.into())?;
Ok(result.into())
}
// Async operation example
#[wasm_bindgen]
pub async fn fetch_and_process(url: &str) -> Result<JsValue, JsValue> {
// Get Window object
let window = web_sys::window().expect("no global `window` exists");
// Initiate fetch request
let resp_value = wasm_bindgen_futures::JsFuture::from(
window.fetch_with_str(url)
).await?;
// Convert to Response object
let resp: web_sys::Response = resp_value.dyn_into()?;
// Get JSON data
let json = wasm_bindgen_futures::JsFuture::from(resp.json()?).await?;
// Process JSON data
let processed_data = process_complex_js_object(&json)?;
Ok(processed_data)
}
// DOM manipulation example
#[wasm_bindgen]
pub fn manipulate_dom(element_id: &str, new_text: &str) -> Result<(), JsValue> {
// Get Window object
let window = web_sys::window().expect("no global `window` exists");
// Get Document object
let document = window.document().expect("should have a document on window");
// Get element
let element = document.get_element_by_id(element_id)
.ok_or_else(|| JsValue::from_str(&format!("Element with id {} not found", element_id)))?;
// Set text content
element.set_text_content(Some(new_text));
// Add click event listener
let closure = Closure::<dyn FnMut()>::new(move || {
console::log_1(&"Element clicked!".into());
});
element.add_event_listener_with_callback(
"click",
closure.as_ref().unchecked_ref()
)?;
// Prevent closure from being dropped
closure.forget();
Ok(())
}
// Complex Web API usage example
#[wasm_bindgen]
pub async fn complex_web_api_interaction() -> Result<JsValue, JsValue> {
// Get Window object
let window = web_sys::window().expect("no global `window` exists");
// Get Geolocation
let navigator = window.navigator();
let geolocation = navigator.geolocation().ok_or_else(|| {
JsValue::from_str("Geolocation is not supported by this browser")
})?;
// Create Promise to wrap geolocation API
let promise = Promise::new(&mut |resolve, reject| {
let success_callback = Closure::<dyn FnMut(web_sys::Position)>::new(move |position| {
let latitude = position.coords().latitude();
let longitude = position.coords().longitude();
let result = Object::new();
Reflect::set(&result, &"latitude".into(), &latitude.into())?;
Reflect::set(&result, &"longitude".into(), &longitude.into())?;
resolve.call1(&JsValue::UNDEFINED, &result.into()).unwrap();
});
let error_callback = Closure::<dyn FnMut(web_sys::PositionError)>::new(move |error| {
let message = match error.code() {
1 => "Permission denied",
2 => "Position unavailable",
3 => "Timeout",
_ => "Unknown error",
};
let error_obj = Object::new();
Reflect::set(&error_obj, &"message".into(), &message.into())?;
reject.call1(&JsValue::UNDEFINED, &error_obj.into()).unwrap();
});
geolocation.get_current_position(
success_callback.as_ref().unchecked_ref(),
Some(error_callback.as_ref().unchecked_ref())
).unwrap();
// Prevent closures from being dropped
success_callback.forget();
error_callback.forget();
});
// Await Promise resolution
let result = wasm_bindgen_futures::JsFuture::from(promise).await?;
Ok(result)
}C++ and WASM Advanced Integration
C++ Templates and WASM Interfaces:
// C++ WASM module with template specialization example
#include <emscripten/bind.h>
#include <vector>
#include <string>
#include <map>
using namespace emscripten;
// Template class definition
template <typename T>
class DataContainer {
public:
void add(const T& value) {
data.push_back(value);
}
T get(size_t index) const {
if (index < data.size()) {
return data[index];
}
return T(); // Default value
}
size_t size() const {
return data.size();
}
private:
std::vector<T> data;
};
// Explicitly instantiate templates (required for WASM interfaces)
template class DataContainer<int>;
template class DataContainer<float>;
template class DataContainer<std::string>;
// Bind explicitly instantiated templates
EMSCRIPTEN_BINDINGS(template_module) {
// Bind int specialization
class_<DataContainer<int>>("IntDataContainer")
.constructor<>()
.function("add", &DataContainer<int>::add)
.function("get", &DataContainer<int>::get)
.function("size", &DataContainer<int>::size);
// Bind float specialization
class_<DataContainer<float>>("FloatDataContainer")
.constructor<>()
.function("add", &DataContainer<float>::add)
.function("get", &DataContainer<float>::get)
.function("size", &DataContainer<float>::size);
// Bind string specialization
class_<DataContainer<std::string>>("StringDataContainer")
.constructor<>()
.function("add", &DataContainer<std::string>::add)
.function("get", &DataContainer<std::string>::get)
.function("size", &DataContainer<std::string>::size);
}
// More complex template example (with strategy)
template <typename T, typename Allocator = std::allocator<T>>
class AdvancedContainer {
public:
void add(const T& value) {
data.push_back(value);
}
T get(size_t index) const {
if (index < data.size()) {
return data[index];
}
return T();
}
size_t size() const {
return data.size();
}
private:
std::vector<T, Allocator> data;
};
// Explicitly instantiate common combinations
template class AdvancedContainer<int>;
template class AdvancedContainer<float, std::allocator<float>>;C++ Exception Handling and WASM:
// C++ WASM module with exception handling
#include <emscripten/bind.h>
#include <stdexcept>
#include <string>
using namespace emscripten;
// Function that may throw exceptions
std::string process_data(int value) {
if (value < 0) {
throw std::runtime_error("Negative values are not allowed");
}
if (value > 100) {
throw std::out_of_range("Value exceeds maximum limit");
}
return "Processed: " + std::to_string(value);
}
// Wrapper function to catch exceptions
std::string safe_process_data(int value) {
try {
return process_data(value);
} catch (const std::runtime_error& e) {
return "Runtime error: " + std::string(e.what());
} catch (const std::out_of_range& e) {
return "Out of range error: " + std::string(e.what());
} catch (...) {
return "Unknown error occurred";
}
}
// Bind functions
EMSCRIPTEN_BINDINGS(exception_module) {
function("process_data", &process_data); // Directly expose function that may throw
function("safe_process_data", &safe_process_data); // Expose safe wrapper
}
// More complex exception handling example
class DataProcessor {
public:
DataProcessor(int max_value) : max_value(max_value) {}
std::string process(int value) {
if (value < 0) {
throw std::invalid_argument("Value cannot be negative");
}
if (value > max_value) {
throw std::runtime_error("Value exceeds processor's maximum");
}
// Simulate processing
return "Processed value: " + std::to_string(value * 2);
}
private:
int max_value;
};
// Wrapper to catch exceptions
std::string safe_process(DataProcessor& processor, int value) {
try {
return processor.process(value);
} catch (const std::exception& e) {
return "Error: " + std::string(e.what());
}
}
EMSCRIPTEN_BINDINGS(class_handler_exception_module) {
class_<DataProcessor>("DataProcessor")
.constructor(int)
.function("process", &DataProcessor::process); // Directly expose method that may throw
function("safe_process", &safe_process); // Expose safe wrapper
}C++ and JavaScript Object Interaction:
// C++ WASM module interacting with JavaScript objects
#include <emscripten/bind.h>
#include <js/js.hpp> // Assume js-binding library exists
using namespace emscripten;
// Class to manipulate JavaScript objects
class JsObjectWrapper {
public:
JsObjectWrapper() {
// Create new JavaScript object
js_object = emscripten::val::object();
}
void setProperty(const std::string& name, const std::string& value) {
js_object.set(name, value);
}
void setProperty(const std::string& name, int value) {
js_object.set(name, value);
}
void setProperty(const std::string& name, double value) {
js_object.set(name, value);
}
std::string getPropertyAsString(const std::string& name) {
return js_object[name].as<std::string>();
}
int getPropertyAsInt(const std::string& name) {
return js_object[name].as<int>();
}
double getPropertyAsDouble(const std::string& name) {
return js_object[name].as<double>();
}
emscripten::val getJsObject() {
return js_object;
}
private:
emscripten::val js_object;
};
// More complex JavaScript object interaction
class JsArrayProcessor {
public:
// Create and process JavaScript array
emscripten::val processArray(const emscripten::val& js_array) {
// Check if input is array
if (!js_array.isArray()) {
throw std::runtime_error("Input must be a JavaScript array");
}
// Get array length
unsigned length = js_array["length"].as<unsigned>();
// Create result array
emscripten::val result = emscripten::val::array();
// Process each element
for (unsigned i = 0; i < length; ++i) {
// Get element
emscripten::val element = js_array[i];
// Process based on type
if (element.isNumber()) {
// Number element: add 1
double value = element.as<double>();
result.call<void>("push", value + 1);
} else if (element.isString()) {
// String element: convert to uppercase
std::string str = element.as<std::string>();
// Note: actually need to call JavaScript toUpperCase()
emscripten::val upperStr = element.call<emscripten::val>("toUpperCase");
result.call<void>("push", upperStr);
} else {
// Other types: copy directly
result.call<void>("push", element);
}
}
return result;
}
};
EMSCRIPTEN_BINDINGS(module_js_interaction) {
class_<JsObjectWrapper>("JsObjectWrapper")
.constructor<>()
.function("setProperty", (void (JsObjectWrapper::*)(const std::string&, const std::string&)) &JsObjectWrapper::setProperty)
.function("setProperty", (void (JsObjectWrapper::*)(const std::string&, int)) &JsObjectWrapper::setProperty)
.function("setProperty", (void (JsObjectWrapper::*)(const std::string&, double)) &JsObjectWrapper::setProperty)
.function("getPropertyAsString", &JsObjectWrapper::getPropertyAsString)
.function("getPropertyAsInt", &JsObjectWrapper::getPropertyAsInt)
.function("getPropertyAsDouble", &JsObjectWrapper::getPropertyAsDouble)
.function("getJsObject", &JsObjectWrapper::getJsObject);
class_<JsArrayProcessor>("JsArrayProcessor")
.constructor<>()
.function("processArray", &JsArrayProcessor::processArray);
}Go and WASM Advanced Integration
Go Concurrency with WASM:
// Go WASM module with goroutines example
package main
import (
"syscall/js"
"time"
)
// Async task with goroutine
func asyncTask(this js.Value, args []js.Value) interface{} {
// Get callback function
callback := args[0]
// Start goroutine for async task
go func() {
// Simulate time-consuming operation
time.Sleep(2 * time.Second)
// Call JavaScript callback
callback.Invoke("Task completed after 2 seconds")
}()
// Return immediate result
return "Task started"
}
// Multiple goroutines with channel communication
func parallelTasks(this js.Value, args []js.Value) interface{} {
// Get task count
taskCount := args[0].Int()
// Create channel for results
resultChan := make(chan string, taskCount)
// Start multiple goroutines for tasks
for i := 0; i < taskCount; i++ {
go func(taskID int) {
// Simulate task processing
time.Sleep(time.Duration(taskID+1) * 500 * time.Millisecond)
// Send result to channel
resultChan <- js.Global().Get("String").New("Task "+js.Global().Get("String").New(taskID).String()+" completed").String()
}(i)
}
// Collect all results
results := make([]string, taskCount)
for i := 0; i < taskCount; i++ {
results[i] = <-resultChan
}
// Convert results to JavaScript array
jsResults := js.Global().Get("Array").New()
for _, result := range results {
jsResults.Call("push", result)
}
return jsResults
}
func main() {
// Register functions to JavaScript global object
js.Global().Set("asyncTask", js.FuncOf(asyncTask))
js.Global().Set("parallelTasks", js.FuncOf(parallelTasks))
// Keep program running
select {}
}Go Garbage Collection and WASM:
// Go WASM module with memory management example
package main
import (
"syscall/js"
)
// Global variable to store JavaScript callbacks
var callbacks = make(map[int]js.Func)
// Counter for generating IDs
var callbackIDCounter = 0
// Register JavaScript callback
func registerCallback(this js.Value, args []js.Value) interface{} {
// Create new callback function
callback := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
// Handle callback
js.Global().Get("console").Call("log", "Callback invoked with:", args)
// Can execute Go code here
return nil
})
// Generate unique ID
id := callbackIDCounter
callbackIDCounter++
// Store callback
callbacks[id] = callback
// Return ID to JavaScript
return id
}
// Invoke registered callback
func invokeCallback(this js.Value, args []js.Value) interface{} {
// Get callback ID
id := args[0].Int()
// Find callback
callback, exists := callbacks[id]
if !exists {
return "Callback not found"
}
// Call callback
callback.Invoke(args[1:]...)
// Note: Don’t delete here as JavaScript may still need it
// In practice, may need complex lifecycle management
return "Callback invoked"
}
// Release callback
func releaseCallback(this js.Value, args []js.Value) interface{} {
// Get callback ID
id := args[0].Int()
// Find and delete callback
if callback, exists := callbacks[id]; exists {
callback.Release() // Release resources
delete(callbacks, id)
return "Callback released"
}
return "Callback not found"
}
func main() {
// Register functions to JavaScript global object
js.Global().Set("registerCallback", js.FuncOf(registerCallback))
js.Global().Set("invokeCallback", js.FuncOf(invokeCallback))
js.Global().Set("releaseCallback", js.FuncOf(releaseCallback))
// Keep program running
select {}
}Go with Complex JavaScript Data Interaction:
// Go WASM module with complex data interaction example
package main
import (
"encoding/json"
"syscall/js"
)
// Define Go struct
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Address struct {
Street string `json:"street"`
City string `json:"city"`
Country string `json:"country"`
} `json:"address"`
}
// Convert Go struct to JSON string
func personToJSON(this js.Value, args []js.Value) interface{} {
// Create Go struct instance
person := Person{
Name: "John Doe",
Age: 30,
}
// Set nested struct fields
person.Address.Street = "123 Main St"
person.Address.City = "New York"
person.Address.Country = "USA"
// Convert to JSON
jsonData, err := json.Marshal(person)
if err != nil {
return "Error: " + err.Error()
}
// Return parsed JSON string to JavaScript
return js.Global().Get("JSON").Call("parse", string(jsonData))
}
// Parse JSON string to Go struct
func jsonToPerson(this js.Value, args []js.Value) interface{} {
// Get JSON string
jsonStr := args[0].String()
// Parse JSON
var person Person
err := json.Unmarshal([]byte(jsonStr), &person)
if err != nil {
return "Error: " + err.Error()
}
// Convert Go struct to JavaScript object
jsObj := js.Global().Get("Object").New()
// Set simple fields
jsObj.Set("name", person.Name)
jsObj.Set("age", person.Age)
// Set nested object
addressObj := js.Global().Get("Object").New()
addressObj.Set("street", person.Address.Street)
addressObj.Set("city", person.Address.City)
addressObj.Set("country", person.Address.Country)
jsObj.Set("address", addressObj)
return jsObj
}Go Concurrency Model and WASM:
// Go WASM module with advanced concurrency example
package main
import (
"syscall/js"
"time"
)
// Global variables to store callbacks
var (
callbacks = make(map[int]js.Func)
callbackIDs = make(chan int, 100)
)
// Initialize buffered channel for IDs
func init() {
// Pre-generate IDs
for i := 0; i < 100; i++ {
callbackIDs <- i
}
}
// Get next available ID
func getNextID() int {
return <-callbackIDs
}
// Release ID back to pool
func releaseID(id int) {
callbackIDs <- id
}
// Async task processor
func asyncProcessor(this js.Value, args []js.Value) interface{} {
// Get callback ID
id := getNextID()
defer releaseID(id)
// Create callback function
cb := js.FuncOf(func(this js.Value, args []js.Value)) interface{} {
// Handle callback
js.Global().Get("console").Call("log", "Async task completed with ID:", id)
return nil
})
// Store callback
callbacks[id] = cb
// Start goroutine for async task
go func(taskID int) {
// Simulate time-consuming operation
time.Sleep(time.Duration(taskID%3+1) * time.Second)
// Call callback
if cb, exists := callbacks[taskID]; exists {
cb.Invoke(js.Global().Get("JSON").Call("stringify", map[string]interface{}{
"taskId": taskID,
"time": time.Now().Format(time.RFC3339),
}))
}
// Release callback
cb.Release()
delete(callbacks, taskID)
}(id)
// Return task ID to JavaScript
return id
}
// Parallel task processor
func parallelProcessor(this js.Value, args []js.Value)) interface{} {
// Get tasks count
taskCount := args[0].Int()
if taskCount <= 0 {
return "Error: taskCount must be positive"
}
// Create results array
resultArray := js.Global().Get("Array").New()
// Create completion channel
doneChan := make(chan bool, taskCount)
// Start multiple goroutines
for i := 0; i < taskCount; i++ {
go func(taskID int) {
// Simulate task processing
time.Sleep(time.Duration(taskID%5+1) * time.Millisecond * 500)
// Prepare result
result := map[string]interface{}{
"taskId": taskID,
"result": "success",
"duration": (taskID % 5 + 1) * 500,
}
// Send result to JavaScript
js.Global().Call("processParallelResult", result)
// Notify completion
doneChan <- true
}(i)
}
// Wait for all tasks to complete
go func() {
for i := 0; i < taskCount; i++ {
<-doneChan
}
}()
return "Started " + js.Global().Get("String").New(taskCount).String()) + " parallel tasks"
)
func main() {
// Register functions to global object
js.Global().Set("asyncProcessor", js.FuncOf(asyncProcessor))
js.Global().Set("parallelProcessor", js.FuncOf(parallelProcessor))
// Provide JavaScript callable result handler
js.Global().Set("processParallelResult", js.FuncOf(func(this js.Value, args []js.Value)) interface{} {
// Handle parallel task result
js.Global().Get("console").Call("log", "Parallel task result:", args[0])
return nil
}))
// Keep program running
select {}
}



