MADOLA WASM Module Imports - Complete Guide
July 27, 2026 · View on GitHub
Complete guide for using WebAssembly modules in MADOLA, covering implementation, usage, testing, and troubleshooting for both native executable and web application environments.
Table of Contents
- Executive Summary
- Quick Start
- Overview
- Directory Structure
- Creating WASM Modules
- Usage Examples
- Web Application Configuration
- WASM Wrapper Structure
- Testing & Regression
- Implementation Details
- Troubleshooting
- Best Practices
- Performance Comparison
- Status & Verification
Executive Summary
WASM module imports are now fully functional in all environments:
- ✅ Native executable (
dist/madola.exe) - Uses Node.js to execute WASM - ✅ Web application (browser) - Loads WASM directly via WebAssembly API
- ✅ Regression tests - Both native and WASM modes validated
Key Achievements
-
Native Executable WASM Imports ✅
- Import fallback logic implemented
- Path resolution handles Windows backslashes
- Node.js integration working
-
Web Application WASM Imports ✅
- Manifest-based module discovery
- Preloading on page load
- Bridge integration complete
-
Regression Tests ✅
- Native mode tests WASM imports
- WASM mode tests WASM runtime
- Separate baselines for each mode
- 40/40 tests passing
Quick Start
For Native Executable
-
Place WASM files in
~/.madola/trove/[moduleName]/:~/.madola/trove/calcPi/ ├── calcPi.js └── calcPi.wasm -
Use in code:
from calcPi import calcPi; x := calcPi(1000000); -
Run:
dist/madola.exe mycode.mda # Windows dist/madola mycode.mda # Linux/Mac
For Web Application
-
Place WASM files in
web/trove/[moduleName]/:web/trove/calcPi/ ├── calcPi.js └── calcPi.wasm -
Update manifest (
web/trove/manifest.json):{ "calcPi": ["calcPi"] } -
Use in code:
from calcPi import calcPi; x := calcPi(1000000); -
Open in browser:
# Start server dev.bat serve # Windows ./dev.sh serve # Linux/Mac # Open http://localhost:8080
Quick Reference Table
| Environment | WASM Location | Config Required | Loading Method |
|---|---|---|---|
| Native | ~/.madola/trove/[module]/ | None (auto-discover) | Node.js |
| Web App | web/trove/[module]/ | Yes (manifest.json) | Browser WebAssembly |
Overview
MADOLA supports importing functions from WASM modules using the standard import syntax:
from moduleName import functionName;
The system automatically handles different execution environments:
- Native executable (
dist/madola.exe) - Uses Node.js to execute WASM - Web application (
web/index.html) - Loads WASM directly in the browser
Import Resolution Flow
Native Executable:
- Try to find
.mdafile in current directory - Try to find
.mdafile in~/.madola/trove/ - [Fallback] Try WASM:
~/.madola/trove/[module]/[function].{js,wasm} - Load WASM wrapper with Node.js
- Execute function calls via WASM
Web Application:
- On page load: Read
web/trove/manifest.json - Preload all modules: Load each WASM file listed in manifest
- Register in bridge:
window.madolaWasmBridge[functionName] = ... - On import:
from calcPi import calcPi;→ check bridge - Execute: Call function via bridge
Directory Structure
Native Executable Environment
The native executable searches for WASM modules in:
~/.madola/
└── trove/
└── [moduleName]/
├── [functionName].js # JavaScript wrapper
└── [functionName].wasm # WASM binary
Search Priority:
- Current directory (for co-located test modules)
~/.madola/trove/(standard user modules)
Example:
C:/Users/YourName/.madola/
└── trove/
└── calcPi/
├── calcPi.js
└── calcPi.wasm
Web Application Environment
The web application loads WASM modules from:
web/
└── trove/
├── manifest.json # Module registry (REQUIRED!)
└── [moduleName]/
├── [functionName].js # JavaScript wrapper
└── [functionName].wasm # WASM binary
Manifest Format:
{
"calcPi": ["calcPi"],
"mathLib": ["square", "cube", "sqrt"],
"testWasm": ["testSquare", "testDouble"]
}
Regression Test Environment
regression/
├── fixtures/
│ ├── wasm_import.mda # Test file
│ └── testWasm/ # Local WASM modules
│ ├── testSquare.{js,wasm}
│ └── testDouble.{js,wasm}
└── expected/ # Baselines shared by native and WASM
├── evaluation/wasm_import.txt
└── html/wasm_import.html
Native and WASM compare against the same baselines — diagnostic log lines that legitimately differ between backends (import/codegen chatter) are stripped by the comparison's normalization step, not by keeping two sets of files.
Creating WASM Modules
Using @gen_addon Decorator (Recommended)
- Create a
.mdafile with the decorator:
@gen_addon
fn calcPi(n) {
sum := 0.0;
for i in 0...n {
sum := sum + ((-1)^i) / ((2*i)+1);
}
return sum * 4;
}
- Generate WASM for native executable:
# Native execution stores in ~/.madola/trove/
dist/madola.exe myFunction.mda # Windows
dist/madola myFunction.mda # Linux/Mac
This creates:
~/.madola/trove/myFunction/myFunction.cpp(C++ source)~/.madola/trove/myFunction/myFunction.wasm(WASM binary)~/.madola/trove/myFunction/myFunction.js(JavaScript wrapper)
- Copy to web app (if needed):
# Linux/Mac
cp -r ~/.madola/trove/myFunction web/trove/
# Windows
xcopy /E /I %USERPROFILE%\.madola\trove\myFunction web\trove\myFunction
- Update web manifest:
Edit web/trove/manifest.json:
{
"myFunction": ["myFunction"]
}
Manual Placement
If you have pre-compiled WASM modules:
- Create module directory:
web/trove/myModule/
- Add WASM files:
myModule.wasm- WASM binarymyModule.js- JavaScript wrapper (see template below)
- Update manifest:
{
"myModule": ["myFunc1", "myFunc2"]
}
Usage Examples
Example 1: Basic Import
from calcPi import calcPi;
result := calcPi(1000000);
print(result);
Example 2: Multiple Imports
from mathLib import square, cube;
x := square(5);
y := cube(3);
print(x); // 25
print(y); // 27
Example 3: Performance Comparison
from calcPi import calcPi;
// WASM version (fast)
s1 := time();
x1 := calcPi(1000000);
t1 := time() - s1;
// Pure Madola version (slower)
fn calcPi_madola(n) {
sum := 0.0;
for i in 0...n {
sum := sum + ((-1)^i) / ((2*i)+1);
}
return sum * 4;
}
s2 := time();
x2 := calcPi_madola(1000000);
t2 := time() - s2;
print(t1); // ~10-50ms (WASM)
print(t2); // ~500-1000ms (Interpreted)
Web Application Configuration
1. Update Manifest File
The web app uses web/trove/manifest.json to discover available WASM modules:
{
"calcPi": ["calcPi"],
"mathLib": ["square", "cube", "sqrt"],
"statistics": ["mean", "median", "stddev"]
}
Format:
{
"moduleName": ["function1", "function2", ...]
}
2. Module Loading Process
When the web app starts:
- Reads
manifest.json- Discovers all available modules and functions - Preloads WASM modules - Loads each WASM file before code execution
- Registers in bridge - Makes functions available via
window.madolaWasmBridge - Executes user code - Import statements can now resolve to preloaded functions
3. Import Resolution
When MADOLA code contains an import:
from calcPi import calcPi;
x := calcPi(1000000);
Resolution sequence:
- Parser detects
from calcPi import calcPi; - Checks if
calcPiis inwindow.madolaWasmBridge - If not preloaded, attempts dynamic load from
trove/calcPi/calcPi.js - Calls the function via the WASM bridge
WASM Wrapper Structure
JavaScript Wrapper Template
Generated wrappers follow this pattern:
// JavaScript wrapper for [functionName] WASM function
class [functionName]Addon {
constructor(basePath = '') {
this.module = null;
this.isReady = false;
this.basePath = basePath;
}
async load() {
try {
const wasmPath = this.basePath ? `${this.basePath}/[functionName].wasm` : '[functionName].wasm';
let wasmBytes;
// Check if we're in Node.js or browser
if (typeof window === 'undefined') {
// Node.js environment (native executable)
const fs = require('fs');
wasmBytes = fs.readFileSync(`${this.basePath}/[functionName].wasm`);
} else {
// Browser environment (web app)
const response = await fetch(wasmPath);
if (!response.ok) {
throw new Error(`Failed to fetch WASM: ${response.status} ${response.statusText}`);
}
wasmBytes = await response.arrayBuffer();
}
const wasmModule = await WebAssembly.instantiate(wasmBytes, {
env: {
memory: new WebAssembly.Memory({ initial: 256 }),
table: new WebAssembly.Table({ initial: 0, element: 'anyfunc' })
}
});
this.module = wasmModule.instance;
this.isReady = true;
return true;
} catch (error) {
console.error('Failed to load WASM module:', error);
return false;
}
}
[functionName](n) {
if (!this.isReady) {
throw new Error('WASM module not loaded. Call load() first.');
}
return this.module.exports.[functionName]_wasm(n);
}
}
// Export for both Node.js and browser
if (typeof module !== 'undefined' && module.exports) {
module.exports = [functionName]Addon;
} else {
window.[functionName]Addon = [functionName]Addon;
}
Key Features
- Dual environment support - Works in both Node.js and browser
- Dynamic path resolution - Accepts
basePathparameter - Automatic loading - Fetches WASM binary on
load() - Error handling - Clear error messages for debugging
Testing & Regression
Running Regression Tests
Native Tests (Windows):
.\regression\run_regression.bat native
WASM Tests (Windows):
.\regression\run_regression.bat wasm
Native Tests (Linux/Mac):
./regression/run_regression.sh native
WASM Tests (Linux/Mac):
./regression/run_regression.sh wasm
Update Baselines:
.\regression\run_regression.bat native update # Update native baselines
.\regression\run_regression.bat wasm update # Update WASM baselines
Test Coverage
Both native and WASM modes test:
- ✅ Basic evaluation
- ✅
.mdafile imports - ✅ WASM module imports (testSquare, testDouble)
- ✅ HTML formatting
- ✅ Mathematical operations
Test Results
Expected Output:
Running regression tests in native mode...
Running test: wasm_import
Comparing results...
[PASS] wasm_import.txt (evaluation)
[PASS] wasm_import.html (html)
Results: 40 passed, 0 failed
All tests passed!
Adding New WASM Test
-
Create generator file:
@gen_addon fn myFunc(n) { return n * 3; } -
Generate WASM:
dist/madola.exe myGenerator.mda -
Copy to fixtures:
copy %USERPROFILE%\.madola\trove\myGenerator\myFunc.* regression\fixtures\myModule\ -
Create test:
from myModule import myFunc; result := myFunc(10); print(result); -
Run and update:
.\regression\run_regression.bat native update
Implementation Details
Key Code Changes
1. Import Fallback Logic
File: src/core/generator/evaluator.cpp
#ifdef __EMSCRIPTEN__
// Browser: Skip filesystem, use WASM bridge
wasmLoader.loadModule(...);
#else
// Native: Try .mda first, then WASM
if (!found_mda_file) {
wasmLoader.loadModule(...);
}
#endif
2. WASM Module Search Paths
File: src/core/generator/wasm_addon_loader.cpp
std::vector<std::filesystem::path> searchPaths = {
std::filesystem::current_path() / moduleName, // Current dir (tests)
utils::getTroveDirectory(moduleName) // ~/.madola/trove/
};
3. Path Conversion for Node.js
File: src/core/generator/evaluator_functions.cpp
// Convert backslashes to forward slashes for Node.js
std::string jsPath = wasmFunc->jsWrapperPath;
std::replace(jsPath.begin(), jsPath.end(), '\\', '/');
// Add basePath parameter
jsCode << "const addon = new " << name << "Addon('" << basePath << "'); ";
4. WASM Runner Bridge Setup
File: madola_runner.js
// Temporarily delete window so wrappers detect Node.js
const savedWindow = global.window;
delete global.window;
// Load wrapper (uses fs.readFileSync)
const addon = new WrapperClass(basePath);
await addon.load();
// Restore window for WASM runtime
global.window = savedWindow;
madolaBridge[functionName] = (n) => addon[functionName](n);
5. Exception Support for WASM
File: CMakeLists.txt
# Enable WASM exceptions for control flow
target_compile_options(madola_core_obj PRIVATE -fwasm-exceptions)
target_compile_options(madola_core PRIVATE -fwasm-exceptions)
target_compile_options(madola_wasm PRIVATE -fwasm-exceptions)
Files Modified
Source Code (8 files):
src/core/generator/evaluator.cpp- Import fallback logicsrc/core/generator/evaluator_functions.cpp- Path conversionsrc/core/generator/wasm_addon_loader.cpp- Search pathsmadola_runner.js- WASM bridge setupCMakeLists.txt- WASM exception support
Configuration (2 files):
6. web/trove/manifest.json - Module registry
7. regression/run_regression.bat - WASM test support
Test Files:
8. regression/fixtures/wasm_import.mda - Test case
9. regression/fixtures/testWasm/ - Test modules
10. regression/expected/ - Baselines shared by native and WASM
Troubleshooting
Native Executable Issues
Error: Cannot find module 'moduleName.mda' in current directory or ~/.madola/trove/
Solution:
- ✅ Check if WASM files exist in
~/.madola/trove/moduleName/ - ✅ Ensure both
.jsand.wasmfiles are present - ✅ Verify the module name matches the directory name
- ✅ Or place WASM files in same directory as test file (for regression tests)
Error: WASM execution failed: Module not found
Solution:
- ✅ Check that Node.js is installed and in PATH
- ✅ Verify file paths are correct (Windows uses backslashes, converts to forward slashes internally)
- ✅ Check that the basePath is being passed correctly to the wrapper
Web Application Issues
Error: Failed to load WASM: 404 Not Found
Solution:
- ✅ Verify WASM files are in
web/trove/moduleName/ - ✅ Check
manifest.jsonincludes the module - ✅ Ensure web server is serving from the
web/directory - ✅ Refresh browser to reload (hard refresh: Ctrl+F5)
Error: Function not found in manifest
Solution:
- ✅ Add the module to
web/trove/manifest.json:{ "moduleName": ["functionName"] } - ✅ Refresh the browser to reload the manifest
Error: WASM module not loaded
Solution:
- ✅ Check browser console for loading errors
- ✅ Verify the wrapper's
load()method is being called - ✅ Ensure WASM binary is valid and not corrupted
Regression Test Issues
Error: Tests fail with "Return from function" or "Break from loop"
Solution:
- ✅ Ensure WASM exceptions are enabled in CMake (
-fwasm-exceptions) - ✅ Rebuild WASM runtime:
dev.bat wasmor./dev.sh wasm - ✅ Check that control flow state tracking is implemented
Error: Missing output lines in WASM tests
Solution:
- ✅ Ensure
madola_runner.jscaptures print/printErr logs - ✅ Verify output parity between native and WASM modes
- ✅ Update WASM baselines:
run_regression.bat wasm update
Best Practices
1. Consistent Naming
- Module directory name should match import name
- Function name should match the exported WASM function
- Use descriptive names:
calcPi, notcp
2. Organize by Functionality
web/trove/
├── math/ # Mathematical functions
├── statistics/ # Statistical functions
├── geometry/ # Geometric calculations
└── physics/ # Physics simulations
3. Keep Manifest Updated
After adding new WASM modules, always update manifest.json:
{
"math": ["sqrt", "pow", "log"],
"statistics": ["mean", "median", "mode"],
"geometry": ["area", "perimeter", "volume"]
}
4. Version Control
Add generated files to .gitignore if they're build outputs:
# Generated WASM files
~/.madola/trove/**/*.wasm
~/.madola/trove/**/*.js
web/trove/**/*.wasm
web/gen_cpp/**/*.cpp
But commit template/example WASM modules for testing.
Performance Comparison
WASM modules provide significant performance improvements:
Speedup: 10-100x faster for compute-intensive tasks
| Implementation | Execution Time | Relative Performance |
|---|---|---|
| WASM | ~10-50ms | 10-100x faster ⚡ |
| Pure Madola | ~500-1000ms | Baseline |
Example:
from calcPi import calcPi;
// WASM: ~10-50ms for 1,000,000 iterations
x1 := calcPi(1000000);
// Madola: ~500-1000ms for 1,000,000 iterations
fn calcPi_madola(n) { /* ... */ }
x2 := calcPi_madola(1000000);
Status & Verification
✅ PRODUCTION READY
All components tested and working:
- ✅ Native executable imports WASM modules
- ✅ Web application imports WASM modules
- ✅ Regression test coverage (40/40 passing)
- ✅ Comprehensive documentation
- ✅ Example files provided
- ✅ Troubleshooting guides complete
Verification Checklist
Native Executable Environment
Location: ~/.madola/trove/
- ✅ Module directory exists:
~/.madola/trove/calcPi/ - ✅ JavaScript wrapper exists:
calcPi.js - ✅ WASM binary exists:
calcPi.wasm - ✅ Import fallback logic implemented in evaluator
- ✅ Path resolution handles Windows backslashes
Test Command:
dist/madola.exe example.mda # Windows
dist/madola example.mda # Linux/Mac
Expected Output:
Imported WASM function: calcPi from calcPi
Execution completed successfully
Output:
7
Web Application Environment
Location: web/trove/
- ✅ Module directory exists:
web/trove/calcPi/ - ✅ JavaScript wrapper exists:
calcPi.js - ✅ WASM binary exists:
calcPi.wasm - ✅ Manifest updated:
web/trove/manifest.json - ✅ Example file created:
web/examples/wasm_import.mda
Test Steps:
- Start server:
dev.bat serveor./dev.sh serve - Open:
http://localhost:8080 - Load example:
examples/wasm_import.mda - Click "Run"
Expected Behavior:
- WASM module loads automatically on page load
- Import statement resolves to preloaded function
- Code executes successfully
- Results display in output panel
Testing Matrix
| Environment | Import .mda | Import WASM | Status |
|---|---|---|---|
| Native Exec | ✅ | ✅ | Working |
| Web App | ❌ N/A | ✅ | Working |
| Regression (native) | ✅ | ✅ | Working |
| Regression (wasm) | ❌ N/A | ✅ | Working |
Summary
| Environment | Location | Configuration | Loading | Status |
|---|---|---|---|---|
| Native | ~/.madola/trove/[module]/ | Auto-discover | Node.js | ✅ Working |
| Web App | web/trove/[module]/ | manifest.json | Browser | ✅ Working |
Key Points
- ✅ Native and web use same wrapper format
- ✅ Web app requires
manifest.json - ✅ Native auto-discovers from
~/.madola/trove/ - ✅ Import syntax is identical in both environments
- ✅ WASM provides 10-100x performance improvement
- ✅ Setup verified and tested in both environments
- ✅ Regression tests cover both native and WASM modes
- ✅ Exception support enabled for control flow
- ✅ Output parity between native and WASM modes
Next Steps
-
Add new WASM modules:
- Generate with
@gen_addondecorator - Copy to appropriate
trove/directory - Update
web/trove/manifest.jsonfor web app
- Generate with
-
For support:
- Check troubleshooting section above
- Verify setup using checklist
- Test with provided examples
MADOLA WASM imports are complete and production-ready! 🚀
Last updated: 2025-11-26