Skip to content

Commit

Permalink
Merge #1028
Browse files Browse the repository at this point in the history
1028: feat(wasi) Introduce strict/non-strict modes for `get_wasi_version` r=Hywan a=Hywan

Sequel of #957.

If a module has multiple import namespaces, `get_wasi_version` is
broken because it assumes a module must only have a single namespace.

This patch updates `get_wasi_version` to introduce a `strict` flag:

* In strict mode, the previous behavior applies; only one namespace is expected to be found, and this namespace must be a WASI namespace to detect the version,
* In non-strict mode, at least one WASI namespace must be declared, and the first one is used to detect the version if any.

The non-strict mode is slower because it compares namespace strings.

2 new private constants have been declared: `SNAPSHOT0_NAMESPACE` and `SNAPSHOT1_NAMESPACE` to avoid repetition and bugs.

Co-authored-by: Ivan Enderlin <ivan.enderlin@hoa-project.net>
  • Loading branch information
bors[bot] and Hywan authored Dec 6, 2019
2 parents bf04864 + dc418a7 commit 799666e
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 20 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## **[Unreleased]**

- [#1028](https://github.com/wasmerio/wasmer/pull/1028) Introduce strict/non-strict modes for `get_wasi_version`
- [#1029](https://github.com/wasmerio/wasmer/pull/1029) Add the “floating” `WasiVersion::Latest` version.
- [#1006](https://github.com/wasmerio/wasmer/pull/1006) Fix minor panic issue when `wasmer::compile_with` called with llvm backend
- [#1009](https://github.com/wasmerio/wasmer/pull/1009) Enable LLVM verifier for all tests, add new llvm-backend-tests crate.
Expand Down
67 changes: 47 additions & 20 deletions lib/wasi/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use wasmer_runtime_core::module::Module;
/// Check if a provided module is compiled for some version of WASI.
/// Use [`get_wasi_version`] to find out which version of WASI the module is.
pub fn is_wasi_module(module: &Module) -> bool {
get_wasi_version(module).is_some()
get_wasi_version(module, false).is_some()
}

/// The version of WASI. This is determined by the imports namespace
Expand All @@ -29,26 +29,53 @@ pub enum WasiVersion {
Latest,
}

/// Detect the version of WASI being used from the namespace
pub fn get_wasi_version(module: &Module) -> Option<WasiVersion> {
let mut import_iter = module
.info()
.imported_functions
.iter()
.map(|(_, import_name)| import_name.namespace_index);

// returns None if empty
let first = import_iter.next()?;
if import_iter.all(|idx| idx == first) {
// once we know that all the namespaces are the same, we can use it to
// detect which version of WASI this is
match module.info().namespace_table.get(first) {
"wasi_unstable" => Some(WasiVersion::Snapshot0),
"wasi_snapshot_preview1" => Some(WasiVersion::Snapshot1),
_ => None,
/// Namespace for the `Snapshot0` version.
const SNAPSHOT0_NAMESPACE: &'static str = "wasi_unstable";

/// Namespace for the `Snapshot1` version.
const SNAPSHOT1_NAMESPACE: &'static str = "wasi_snapshot_preview1";

/// Detect the version of WASI being used based on the import
/// namespaces.
///
/// A strict detection expects that all imports live in a single WASI
/// namespace. A non-strict detection expects that at least one WASI
/// namespace exits to detect the version. Note that the strict
/// detection is faster than the non-strict one.
pub fn get_wasi_version(module: &Module, strict: bool) -> Option<WasiVersion> {
let module_info = &module.info();
let mut imports = module_info.imported_functions.iter();

if strict {
let mut imports = imports.map(|(_, import_name)| import_name.namespace_index);

// Returns `None` if empty.
let first = imports.next()?;

// If there is only one namespace…
if imports.all(|index| index == first) {
// … and that this namespace is a WASI one.
match module_info.namespace_table.get(first) {
SNAPSHOT0_NAMESPACE => Some(WasiVersion::Snapshot0),
SNAPSHOT1_NAMESPACE => Some(WasiVersion::Snapshot1),
_ => None,
}
} else {
None
}
} else {
// not all funcs have the same namespace, therefore it's not WASI
None
let namespace_table = &module_info.namespace_table;

// Check that at least a WASI namespace exists, and use the
// first one in the list to detect the WASI version.
imports.find_map(|(_, import_name)| {
let namespace_index = import_name.namespace_index;

match namespace_table.get(namespace_index) {
SNAPSHOT0_NAMESPACE => Some(WasiVersion::Snapshot0),
SNAPSHOT1_NAMESPACE => Some(WasiVersion::Snapshot1),
_ => None,
}
})
}
}

0 comments on commit 799666e

Please sign in to comment.