In the MVP, only functions, global variables, table
and memory
can be imported or exported.
All imports include two opaque names: a module name and an import name. When organizing the imported object, the first level should be an object containing properties that might be imported. To some extent, it's like the role that exports
of WebAssembly.Instance
plays.
For example, if there's a foo
module:
(module
(func $foo1 (export "foo1") (result i32)
i32.const 1
)
(func $foo2 (export "foo2") (result i32)
i32.const 2
)
)
Here you can see the other fashion when exporting. Just write export
when defining a function. In fact, this module doesn't invoke any function so function names can be ignored.
If the other module requires exported functions from the foo
module:
(module
(import "env" "log" (func $log (param i32)))
(import "foo" "foo1" (func $foo1 (result i32)))
(import "foo" "foo2" (func $foo2 (result i32)))
(func $main
call $foo1
call $log
call $foo2
call $log
)
(start $main)
)
At the time of writing, browsers don't integrate loading and initializing WebAssembly modules automatically so you have to do it by yourself.
(async () => {
const foo = await WebAssembly.instantiateStreaming(fetch('foo.wasm'));
const importObj = {
env : {
log(n) {
console.log(n);
}
},
foo : foo.instance.exports
};
WebAssembly.instantiateStreaming(fetch('program.wasm'), importObj);
})();
Of course, you can write code like above because “program.wasm” imports functions from the foo
module. The problem here is, how to know what a module imports in advance?
WebAssembly.Module
has an imports
function which accepts WebAssembly.Module
and returns import
information declared in a module. The returned value is an array. Each element is an object containing kind
, module
and name
properties. You can know what kind of the imported object, such as 'function'
, the module name and import name.
You can use WebAssembly.Module.imports
to rewrite the above program as follow:
function moduleNames(mod, importObj) {
return Array.from(
new Set(
WebAssembly.Module.imports(mod)
.map(impt => impt.module)
.filter(name => !(name in importObj))
)
);
}
(async () => {
const importObj = {
env : {
log(n) {
console.log(n);
}
}
};
const progModule = await WebAssembly.compileStreaming(fetch('program.wasm'));
const names = moduleNames(progModule, importObj);
const results = await Promise.all(
names.map(name => WebAssembly.instantiateStreaming(fetch(`${name}.wasm`)))
);
for(let i = 0; i < names.length; i++) {
importObj[names[i]] = results[i].instance.exports;
}
WebAssembly.instantiate(progModule, importObj);
})();
If you want to know information about exported objects from a module, WebAssembly.Module
provides an exports
function.
Of course, the example here is simple. Dependencies between modules are more complex. Modern browsers might solve import and export problems in the future. If not, there might be module loader libraries to do things as above.