Lesson 05-WebAssembly Advanced Programming and Optimization

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 RTTI

Impact of Optimization Levels on Performance:

Optimization LevelCompilation TimeCode SizeExecution SpeedDebugging Capability
-O0FastestLargestSlowestBest
-O1FastLargeSlowerGood
-O2MediumMediumMediumAverage
-O3SlowSmallFastestPoor
-OsMediumSmallestMediumAverage
-OzSlowSmallestSlowerPoor

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.js

Memory 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.js

Manual 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:

  1. Data Alignment: Ensure SIMD data is 16 bytes aligned (for v128 types)
  2. Avoid Branching: SIMD instructions typically don’t support conditional branches; use masks instead
  3. Batch Processing: Process multiple data elements at once
  4. Reduce Memory Accesses: Perform calculations in registers as much as possible
  5. 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.wasm

Chrome DevTools Advanced Profiling Techniques:

  1. Memory Snapshots Comparison:
    • Take memory snapshots before and after WASM operations
    • Use “Comparison” view to inspect memory changes
  2. Allocation Timeline:
    • Enable “Allocation instrumentation on timeline”
    • Record WASM memory allocation patterns
  3. 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:

  1. WASM Instruction Count: Obtained via Wasmtime block profiling
  2. Memory Access Patterns: Analyze proportion of memory load/store instructions
  3. Function Call Overhead: Measure cost of WASM-to-WASM or WASM-to-JS calls
  4. SIMD Utilization: Check proportion of SIMD instructions

Performance Optimization Checklist (Continued):

  1. Parallelization:
    • Identify computationally intensive tasks that can be parallelized
    • Use Web Workers and SharedArrayBuffer
    • Reasonably divide workloads
  2. Cache Friendliness:
    • Optimize data locality
    • Reduce cache misses
    • Use data structures suited for CPU cache
  3. Algorithm Optimization:
    • Choose algorithms with lower time complexity
    • Avoid unnecessary computations
    • Use lookup tables instead of complex calculations
  4. Reduce WASM-JS Boundary Overhead:
    • Batch data transfers
    • Minimize cross-boundary calls
    • Use shared memory
  5. 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 practice

Rust 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 {}
}
Share your love