What Are Imports in WebAssembly?
Imports enable a WebAssembly module to access functionalities or resources defined outside the module. These resources can include:
- Functions: For performing operations such as logging or mathematical computations.
- Memory: For sharing a memory buffer between WebAssembly and the host.
- Tables: For storing references like function pointers.
- Global Variables: For accessing constants or mutable variables.
Import Declaration Syntax in .wat:
(import "<module_name>" "<field_name>" (<type> ...))
- <module_name>: The name of the external module providing the resource.
- <field_name>: The specific resource or function name.
- <type>: Specifies the type of the resource (e.g., func, memory, global).
Example 1: Importing a Function
A WebAssembly module imports a function from the host environment to perform addition.
WebAssembly Text Format:
(module
(import "env" "add" (func $add (param i32 i32) (result i32)))
(func $useAdd (param $x i32) (param $y i32) (result i32)
local.get $x ;; Push $x onto the stack
local.get $y ;; Push $y onto the stack
call $add ;; Call the imported function
)
(export "useAdd" (func $useAdd))
)
Explanation:
- The add function is imported from the env module.
- useAdd calls the imported add function with parameters $x and $y.
JavaScript Code to Provide add:
const importObject = {
env: {
add: (x, y) => x + y,
},
};
WebAssembly.instantiate(wasmCode, importObject).then((module) => {
const useAdd = module.instance.exports.useAdd;
console.log(useAdd(3, 5)); // Output: 8
});
What Are Exports in WebAssembly?
Exports allow a WebAssembly module to expose its internal functions, memory, or variables for use by the host environment. These exports are accessible to JavaScript or other languages interfacing with WebAssembly.
Export Declaration Syntax in .wat:
(export "<export_name>" (<type> <name>))
- <export_name>: The name used to reference the exported item.
- <type>: Specifies the type being exported (func, memory, etc.).
- <name>: The internal name of the item being exported.
Example 2: Exporting a Function
A simple function that doubles an integer and exports it.
WebAssembly Text Format:
(module
(func $double (param $x i32) (result i32)
local.get $x ;; Push $x onto the stack
i32.const 2 ;; Push the value 2 onto the stack
i32.mul ;; Multiply $x by 2
)
(export "double" (func $double))
)
Explanation:
- The double function is exported with the name double.
JavaScript Code to Use the Export:
WebAssembly.instantiate(wasmCode).then((module) => {
const double = module.instance.exports.double;
console.log(double(4)); // Output: 8
});
Combining Imports and Exports
WebAssembly modules can both import and export resources. This allows them to interact seamlessly with the host environment.
Example 3: Import and Export in One Module
A WebAssembly module that imports a function for addition and exports a function for calculating the sum of two numbers plus a constant.
WebAssembly Text Format:
(module
(import "env" "add" (func $add (param i32 i32) (result i32)))
(func $addWithConstant (param $x i32) (param $y i32) (result i32)
local.get $x ;; Push $x onto the stack
local.get $y ;; Push $y onto the stack
call $add ;; Call the imported 'add' function
i32.const 10 ;; Push the constant 10 onto the stack
i32.add ;; Add the constant to the result
)
(export "addWithConstant" (func $addWithConstant))
)
Explanation:
- The module imports add and uses it inside addWithConstant.
- addWithConstant is exported for use by the host environment.
JavaScript Code:
const importObject = {
env: {
add: (x, y) => x + y,
},
};
WebAssembly.instantiate(wasmCode, importObject).then((module) => {
const addWithConstant = module.instance.exports.addWithConstant;
console.log(addWithConstant(2, 3)); // Output: 15
});
Importing and Exporting Memory
WebAssembly allows importing and exporting memory to share data between the module and its host.
Example 4: Shared Memory
A module imports memory for storing data and exports a function to modify it.
WebAssembly Text Format:
(module
(import "env" "memory" (memory $sharedMemory 1))
(func $writeToMemory (param $index i32) (param $value i32)
local.get $index ;; Get memory index
local.get $value ;; Get value
i32.store ;; Store the value at the index
)
(export "writeToMemory" (func $writeToMemory))
(export "memory" (memory $sharedMemory))
)
JavaScript Code:
const memory = new WebAssembly.Memory({ initial: 1 });
const importObject = {
env: {
memory: memory,
},
};
WebAssembly.instantiate(wasmCode, importObject).then((module) => {
const writeToMemory = module.instance.exports.writeToMemory;
writeToMemory(0, 42); // Write 42 to index 0
const dataView = new DataView(memory.buffer);
console.log(dataView.getInt32(0, true)); // Output: 42
});
Best Practices for Imports and Exports
- Keep Functions Modular:
- Import only what is necessary to maintain efficiency and security.
- Optimize Data Sharing:
- Use memory imports/exports for large datasets instead of individual calls.
- Use Clear Names:
- Choose descriptive names for imports and exports to improve readability.