diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b5b8a80ad3..266dd165d34 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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. diff --git a/lib/wasi-tests/src/lib.rs b/lib/wasi-tests/src/lib.rs index dcfa5bc8b47..6bf3626dd71 100644 --- a/lib/wasi-tests/src/lib.rs +++ b/lib/wasi-tests/src/lib.rs @@ -20,7 +20,7 @@ fn serializing_works() { .map_err(|e| format!("Can't compile module: {:?}", e)) .unwrap(); - let wasi_version = get_wasi_version(&module).expect("WASI module"); + let wasi_version = get_wasi_version(&module, true).expect("WASI module"); let import_object = generate_import_object_for_version( wasi_version, args.clone(), diff --git a/lib/wasi-tests/tests/wasitests/_common.rs b/lib/wasi-tests/tests/wasitests/_common.rs index 41bf14e44b4..04b830a91dc 100644 --- a/lib/wasi-tests/tests/wasitests/_common.rs +++ b/lib/wasi-tests/tests/wasitests/_common.rs @@ -8,7 +8,7 @@ macro_rules! assert_wasi_output { let module = wasmer_runtime::compile(&wasm_bytes[..]).expect("WASM can't be compiled"); - let wasi_version = get_wasi_version(&module).expect("WASI module"); + let wasi_version = get_wasi_version(&module, true).expect("WASI module"); let import_object = generate_import_object_for_version( wasi_version, diff --git a/lib/wasi/src/utils.rs b/lib/wasi/src/utils.rs index d57510eb5f3..8a72a215a43 100644 --- a/lib/wasi/src/utils.rs +++ b/lib/wasi/src/utils.rs @@ -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 @@ -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 { - 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 { + 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, + } + }) } } diff --git a/src/bin/wasmer.rs b/src/bin/wasmer.rs index b3d82cd44be..652612103ef 100644 --- a/src/bin/wasmer.rs +++ b/src/bin/wasmer.rs @@ -764,7 +764,7 @@ fn execute_wasm(options: &Run) -> Result<(), String> { .map_err(|e| format!("{:?}", e))?; } else { #[cfg(feature = "wasi")] - let wasi_version = wasmer_wasi::get_wasi_version(&module); + let wasi_version = wasmer_wasi::get_wasi_version(&module, true); #[cfg(feature = "wasi")] let is_wasi = wasi_version.is_some(); #[cfg(not(feature = "wasi"))]