Skip to main content

Overview

This example demonstrates MoFA’s WebAssembly plugin runtime for high-performance, sandboxed plugin execution. It showcases:
  • Creating and compiling WASM modules
  • Plugin lifecycle management
  • Resource limits and sandboxing
  • Math operations and recursive functions
  • Plugin manager for multi-plugin coordination
  • Performance metrics and monitoring

What You’ll Learn

  • Using WasmRuntime for WASM execution
  • Writing WAT (WebAssembly Text) modules
  • Implementing resource limits
  • Plugin manager patterns
  • Memory management in WASM
  • Performance optimization

Prerequisites

  • Rust 1.75 or higher
  • Understanding of WebAssembly concepts
  • Basic knowledge of WAT syntax

Architecture

Source Code

The example contains 393 lines with 4 different demonstrations. Here are the key parts:
use mofa_sdk::plugins::wasm_runtime::{
    RuntimeConfig, WasmRuntime, WasmPluginConfig
};

// Create runtime
let mut rt_config = RuntimeConfig::new();
rt_config.execution_config.fuel_metering = false;
rt_config.execution_config.epoch_interruption = false;
let runtime = WasmRuntime::new(rt_config)?;

// Simple WAT module
let wat = r#"
    (module
        (func (export "answer") (result i32)
            i32.const 42
        )
        (func (export "double") (param i32) (result i32)
            local.get 0
            i32.const 2
            i32.mul
        )
    )
"#;

// Compile module
let compiled = runtime.compile_wat("basic-module", wat).await?;
info!("Compiled: {} bytes in {}ms", 
    compiled.size_bytes, 
    compiled.compile_time_ms
);

// Create plugin
let mut config = WasmPluginConfig::new("basic-plugin");
config.resource_limits.max_fuel = None;
let plugin = runtime.create_plugin(&compiled, config).await?;

// Initialize and execute
plugin.initialize().await?;
let result = plugin.call_i32("answer", &[]).await?;
info!("Result: {}", result);  // 42

let result = plugin.call_i32("double", &[Val::I32(21)]).await?;
info!("Result: {}", result);  // 42

Running the Example

1
Run the Example
2
cd examples/wasm_plugin
cargo run

Expected Output

╔══════════════════════════════════════════════════════════════╗
║           MoFA WASM Plugin Runtime Example                  ║
╚══════════════════════════════════════════════════════════════╝

📦 Example 1: Basic WASM Runtime
─────────────────────────────────────────────
  ✓ Compiled module: basic-module
    Size: 84 bytes
    Compile time: 15ms
  ✓ Created plugin: basic-plugin
    State: Ready
  ✓ Called 'answer' function
    Result: 42
  ✓ Called 'double' function with 21
    Result: 42
  📊 Plugin metrics:
    Calls: 2
    Success: 2
    Avg time: 125ns

🔢 Example 2: Math Operations Plugin
─────────────────────────────────────────────
  ✓ Math plugin initialized
    ✓ add(10, 20) = 30 (expected: 30)
    ✓ sub(50, 20) = 30 (expected: 30)
    ✓ mul(6, 7) = 42 (expected: 42)
    ✓ factorial(5) = 120 (expected: 120)
    ✓ fibonacci(10) = 55 (expected: 55)

🔌 Example 3: Plugin Manager
─────────────────────────────────────────────
  ✓ Loaded plugin: greeter
  ✓ Loaded plugin: counter
  📋 Loaded plugins: ["greeter", "counter"]
  ✓ All plugins initialized
  📢 Greeting length: 13
  🔢 Counter increment 1: 1
  🔢 Counter increment 2: 2
  🔢 Counter increment 3: 3
  🔢 Counter increment 4: 4
  🔢 Counter increment 5: 5
  📊 Manager stats:
      Active plugins: 2
      Total calls: 6
      Total execution time: 2ms

⚙️  Example 4: Resource Limits
─────────────────────────────────────────────
  ✓ Runtime created with restrictive limits
    Max memory: 1MB (16 pages)
    Max execution time: 1000ms
  📏 Initial memory size: 1 page(s) (64KB)
  ✓ Written 42 to address 0, read back: 42

╔══════════════════════════════════════════════════════════════╗
║                    All examples completed!                   ║
╚══════════════════════════════════════════════════════════════╝

Key Concepts

WAT vs Compiled Plugins

Write plugins in WebAssembly Text format:
(module
  (func (export "my_func") (param i32) (result i32)
    local.get 0
    i32.const 10
    i32.add
  )
)
Pros: Human-readable, easy to write Cons: Must be compiled at runtime

Resource Limits

WASM plugins are sandboxed with configurable limits:
ResourceLimits {
    max_memory_pages: 16,           // Max 1MB memory
    max_table_elements: 1000,       // Max function table size
    max_instances: 5,               // Max plugin instances
    max_execution_time_ms: 1000,    // 1 second timeout
    max_fuel: Some(1_000_000),      // Instruction limit
    max_call_depth: 100,            // Max recursion depth
}

Plugin Lifecycle

// Create
let plugin = runtime.create_plugin(&compiled, config).await?;

// Initialize
plugin.initialize().await?;
// State: Initialized

// Execute
let result = plugin.call_i32("func", &args).await?;
// State: Running

// Stop
plugin.stop().await?;
// State: Stopped

Advanced Features

Memory Management

(module
  (memory (export "memory") 2)  ; 2 pages = 128KB

  (func (export "alloc") (param $size i32) (result i32)
    ;; Simple bump allocator
    ;; Returns pointer to allocated memory
  )

  (func (export "free") (param $ptr i32)
    ;; Free allocated memory
  )
)

Host Functions

// Define host function
let linker = wasmtime::Linker::new(&engine);
linker.func_wrap(
    "env",
    "log",
    |caller: Caller<'_, ()>, ptr: i32, len: i32| {
        // Read string from WASM memory
        let memory = caller.get_export("memory").unwrap();
        // Log the string
    }
)?;

Plugin Events

let mut event_rx = manager.subscribe();

tokio::spawn(async move {
    while let Ok(event) = event_rx.recv().await {
        match event {
            PluginEvent::Loaded { id } => {
                info!("Plugin {} loaded", id);
            }
            PluginEvent::ExecutionCompleted { id, duration } => {
                info!("Plugin {} completed in {:?}", id, duration);
            }
            _ => {}
        }
    }
});

Performance Comparison

Plugin TypeStartupExecutionIsolationHot Reload
RhaiFastSlowPartialYes
WASMMediumFastFullNo
NativeInstantFastestNoneNo
Use WASM for: Performance-critical code, untrusted plugins, cross-platform compatibilityUse Rhai for: Business logic, hot-reloadable rules, rapid iteration

Common Use Cases

Image Processing

High-performance image filters and transformations

Data Validation

Fast validation rules with sandboxing

Cryptography

Secure cryptographic operations

Game Logic

Sandboxed game mechanics

Troubleshooting

Problem: WAT module won’t compileSolution: Validate WAT syntax:
# Use wat2wasm to check syntax
wat2wasm module.wat
Problem: Plugin exceeds memory limitSolution: Increase memory pages:
config.resource_limits.max_memory_pages = 32;  // 2MB
Problem: Cannot call exported functionSolution: Check export name:
(func (export "exact_name") ...)
Problem: Plugin execution is slowSolution: Disable fuel metering for production:
rt_config.execution_config.fuel_metering = false;

Best Practices

Compile Once: Cache compiled modules for reuse
Resource Limits: Always set appropriate limits for security
Error Handling: Trap WASM errors gracefully
Memory Safety: WASM memory is isolated but shared within the instance

Next Steps

Rhai Hot Reload

Compare with runtime plugins

Plugin System

Complete plugin guide

WASM Guide

Advanced WASM techniques

WASM API

WASM runtime API reference

Build docs developers (and LLMs) love