A WebAssembly Module is the foundational unit of WebAssembly, acting as a self-contained and reusable block of code that can be executed in a web environment.
What is a WebAssembly Module?
A WebAssembly Module is a binary or text file that contains WebAssembly bytecode, representing a compiled version of a program. This module can be executed by WebAssembly runtime environments, such as browsers or Node.js.
The most common file format for a module is .wasm (binary format), but developers can also use the .wat format (text format) for writing and debugging.
Structure of a WebAssembly Module
A WebAssembly module contains several components:
- Imports: Allows the module to use functionality provided by the host environment or other modules.
- Functions: Encodes the program’s logic as callable functions.
- Exports: Makes specific functions, memory or variables accessible to the host environment (like JavaScript).
- Memory: Defines memory storage that the module can use for data.
- Tables: Stores function references or other indexable elements.
- Global Variables: Encapsulates constant or mutable values accessible across the module.
Here’s how a basic module is structured:
(module
;; Import a JavaScript function
(import "js" "log" (func $log (param i32)))
;; Define an exported function
(func $add (param $x i32) (param $y i32) (result i32)
local.get $x
local.get $y
i32.add
)
(export "add" (func $add))
)
In this example:
- The module imports a function named log from JavaScript.
- It defines a function add that takes two integers and returns their sum.
- The add function is exported, making it accessible from outside the module.
Importing and Exporting in WebAssembly Modules
Importing Functions and Memory
WebAssembly modules can import functions, memory, or global variables from the host environment (e.g., JavaScript) or other modules. This allows developers to leverage external libraries and APIs.
Example:
(module
;; Import a memory object from JavaScript
(import "js" "memory" (memory 1))
)
Exporting Functions and Variables
Modules can export functions or memory for use by the host environment. Exporting is essential for interacting with JavaScript or other modules.
Example:
(module
;; Define a function
(func $double (param $x i32) (result i32)
local.get $x
i32.const 2
i32.mul
)
;; Export the function
(export "double" (func $double))
)
In this example, the double function multiplies a number by 2 and is exported for use in JavaScript.
Loading and Using WebAssembly Modules in JavaScript
To use a WebAssembly module in a web application, it must be instantiated using the WebAssembly API in JavaScript. Here’s how you can load and interact with a module:
Loading a Module
fetch('module.wasm')
.then(response => response.arrayBuffer())
.then(bytes => WebAssembly.instantiate(bytes))
.then(({ instance }) => {
console.log(instance.exports.add(10, 20)); // Outputs: 30
});
Steps:
- Fetch the .wasm file from the server.
- Convert the response into an array buffer.
- Instantiate the module using WebAssembly.instantiate.
- Call the exported add function from the module.
Practical Example: A Calculator Module
Let’s build a simple calculator module:
WebAssembly Text Format (WAT):
(module
(func $add (param $a i32) (param $b i32) (result i32)
local.get $a
local.get $b
i32.add
)
(func $subtract (param $a i32) (param $b i32) (result i32)
local.get $a
local.get $b
i32.sub
)
(export "add" (func $add))
(export "subtract" (func $subtract))
)
Using the Module in JavaScript:
fetch('calculator.wasm')
.then(response => response.arrayBuffer())
.then(bytes => WebAssembly.instantiate(bytes))
.then(({ instance }) => {
console.log("Addition:", instance.exports.add(15, 10)); // Outputs: 25
console.log("Subtraction:", instance.exports.subtract(15, 10)); // Outputs: 5
});
Benefits of WebAssembly Modules
- Performance: Modules are compiled into machine code, offering near-native execution speed.
- Security: Modules run in a sandboxed environment, ensuring that they cannot access or modify unauthorized system resources.
- Cross-Language Compatibility: Modules can be written in various languages like C, Rust and Go and then compiled to WebAssembly.
- Interoperability: Modules seamlessly integrate with JavaScript, allowing developers to combine the strengths of both technologies.