It can be useful for WASI programs to optionally depend on certain functions in a WASI API. For example, if a WASI API evolves to include a new function a program might want to continue to run on older systems that don't yet support the new function. In this case a optional import mechanism allows the program to run on older systems and detect the presence of the function at runtime. Another use case is an API that is not applicable on certain embedding. In this case optionals imports would allow program to continue to run in such an embedding, albeit with reduced or modified behavior.
Note: In the ELF specification this type of import is known as weak. We chose optional because the term weak is already used other context in WebAssembly and JavaScript, specifically in JavaScript weakrefs proposal where it relates to garbage collection.
WebAssembly itself does not currently provide a mechanism for optional imports. There is some discussion of adding it to the spec, and WASI would likely use such a feature if/when it is added. In the absence of first class optional imports this document describes the mechanism used by WASI to specify optional imports using a custom section. Currently this is only specified for function imports.
Optional function imports are implemented using two imports for each function. The first being the optional function itself and the second being an i32 global which indicates if the function is present at runtime. We call this addition import the guard.
For example, if a module called wasi:fs
added a new statvfs
function to its
interface a program could optionally import this new function in the following
way:
(func $statvfs (import "wasi:fs" "statvfs.optional"))
(global $statvfs_is_present (import "wasm:fs" "statvfs.is_present") i32)
These two imports would also need to be added to the import.optional
custom
section (See below).
On older systems that don't support the new function, $statvfs_is_present
would be set to 0 and calling $statvfs
would result in a trap.
On systems that do support the new function, $statvfs_is_present
is set to
1 and calling $statvfs
would work as expected.
In order to use the above options function its presence should first be tested for. In C code this would look something like this:
if (__wasm_is_present(wasm_fs.statvfs)) {
wasm_fs.statvfs(...)
}
At the WebAssembly level it might look like this:
global.get $statvfs_is_present
if i32
call $statvfs
end
A custom section is used to specify which imports are optional, and for each optional import the name of the corresponding guard (the global import which is used to signal its presence). For each module that contains optional imports the module name is specified once followed by a list of its optional imports along with their corresponding guards.
The name of this custom section is import.optional
and its contents are as
follows:
Field | Type | Description |
---|---|---|
count | varuint32 |
count of mod_optionals to follow |
mod_optionals | optional_import_list* |
sequence if optional_import_list |
Each optional_import_list
has the following content:
Field | Type | Description |
---|---|---|
mod_name_len | varuint32 |
the length of mod_name_data in bytes |
mod_name_data | bytes |
UTF-8 encoding of the module name |
count | varuint32 |
count of opt_import to follow |
opt_import | opt_import* |
sequence of opt_import |
Each opt_import
has the following content:
Field | Type | Description |
---|---|---|
name_len | varuint32 |
the length of name_data in bytes |
name_data | bytes |
UTF-8 encoding of the import name |
guard_name_len | varuint32 |
the length of guard_name_data in bytes |
guard_name_data | bytes |
UTF-8 encoding of the import name |