Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(wasi) Introduce strict/non-strict modes for get_wasi_version #1028

Merged
merged 9 commits into from
Dec 6, 2019
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
2 changes: 1 addition & 1 deletion lib/wasi-tests/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
2 changes: 1 addition & 1 deletion lib/wasi-tests/tests/wasitests/_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
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,
}
})
}
}
2 changes: 1 addition & 1 deletion src/bin/wasmer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"))]
Expand Down