From d9edb9521870e086bd7919691beabe719a7dd5d0 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 11 Nov 2019 13:09:45 -0600 Subject: [PATCH] Allow using WASI APIs in the Python extension (#533) * Allow using WASI APIs in the Python extension This commit adds support to the Python extension to load the WASI implementation when a WASI module is seen allowing Python to load WebAssembly modules that use WASI. This is pretty primitive right now because there's no way to configure the environment/args/preopens/etc, but it's hoped to be at least a start! * rustfmt * Refactor checks for the wasi module name * Move the check into `wasmtime-wasi` itself * Make it conservative for now and match anything that says `wasi*` * Leave a `FIXME` for improving this later on * Enable missing feature of winapi for `winx` --- crates/interface-types/Cargo.toml | 1 + crates/interface-types/src/lib.rs | 44 ++++++++++++++++++++---------- crates/misc/py/Cargo.toml | 1 + crates/misc/py/src/lib.rs | 14 ++++++++++ crates/wasi-common/winx/Cargo.toml | 3 +- crates/wasi/src/lib.rs | 7 +++++ 6 files changed, 54 insertions(+), 16 deletions(-) diff --git a/crates/interface-types/Cargo.toml b/crates/interface-types/Cargo.toml index 9b9f8e0a81..c7cc8d243c 100644 --- a/crates/interface-types/Cargo.toml +++ b/crates/interface-types/Cargo.toml @@ -18,6 +18,7 @@ wasmparser = { version = "0.39.2", default-features = false } wasm-webidl-bindings = "0.6" wasmtime-jit = { path = '../jit', default-features = false } wasmtime-runtime = { path = '../runtime', default-features = false } +wasmtime-wasi = { path = '../wasi' } [badges] maintenance = { status = "actively-developed" } diff --git a/crates/interface-types/src/lib.rs b/crates/interface-types/src/lib.rs index af7b13cdd0..27f2973748 100644 --- a/crates/interface-types/src/lib.rs +++ b/crates/interface-types/src/lib.rs @@ -32,6 +32,7 @@ pub use value::Value; /// appropriate for bound functions. pub struct ModuleData { inner: Option, + wasi_module_name: Option, } struct Inner { @@ -65,17 +66,38 @@ impl ModuleData { // find the right section. let mut reader = wasmparser::ModuleReader::new(wasm)?; let mut found = false; + let mut wasi_module_name = None; while !reader.eof() { let section = reader.read()?; - if let wasmparser::SectionCode::Custom { name, .. } = section.code { - if name == "webidl-bindings" { - found = true; - break; + + match section.code { + wasmparser::SectionCode::Custom { name, .. } => { + if name == "webidl-bindings" { + found = true; + break; + } } + + // If we see the import section then see if we can find a wasi + // module import which we can later use to register the wasi + // implementation automatically. + wasmparser::SectionCode::Import => { + let section = section.get_import_section_reader()?; + for import in section { + let import = import?; + if wasmtime_wasi::is_wasi_module(import.module) { + wasi_module_name = Some(import.module.to_string()); + } + } + } + _ => {} } } if !found { - return Ok(ModuleData { inner: None }); + return Ok(ModuleData { + inner: None, + wasi_module_name, + }); } // Ok, perform the more expensive parsing. WebAssembly interface types @@ -96,21 +118,13 @@ impl ModuleData { Ok(ModuleData { inner: Some(Inner { module }), + wasi_module_name, }) } /// Detects if WASI support is needed: returns module name that is requested. pub fn find_wasi_module_name(&self) -> Option { - self.inner.as_ref().and_then(|Inner { module }| { - module - .imports - .iter() - .find(|walrus::Import { module, .. }| match module.as_str() { - "wasi" | "wasi_unstable" => true, - _ => false, - }) - .map(|walrus::Import { module, .. }| module.clone()) - }) + self.wasi_module_name.clone() } /// Same as `Context::invoke` except that this works with a `&[Value]` list diff --git a/crates/misc/py/Cargo.toml b/crates/misc/py/Cargo.toml index 167349dc8e..9acf8ed85b 100644 --- a/crates/misc/py/Cargo.toml +++ b/crates/misc/py/Cargo.toml @@ -24,6 +24,7 @@ wasmtime-environ = { path = "../../environ" } wasmtime-interface-types = { path = "../../interface-types" } wasmtime-jit = { path = "../../jit" } wasmtime-runtime = { path = "../../runtime" } +wasmtime-wasi = { path = "../../wasi" } target-lexicon = { version = "0.9.0", default-features = false } anyhow = "1.0.19" region = "2.0.0" diff --git a/crates/misc/py/src/lib.rs b/crates/misc/py/src/lib.rs index 29d153e67f..4fb278615d 100644 --- a/crates/misc/py/src/lib.rs +++ b/crates/misc/py/src/lib.rs @@ -73,6 +73,15 @@ pub fn instantiate( } let data = Rc::new(ModuleData::new(wasm_data).map_err(err2py)?); + + // If this module expects to be able to use wasi then go ahead and hook + // that up into the imported crates. + if let Some(module_name) = data.find_wasi_module_name() { + let wasi_handle = + wasmtime_wasi::instantiate_wasi("", context.get_global_exports(), &[], &[], &[]) + .map_err(|e| err2py(e.into()))?; + context.name_instance(module_name, wasi_handle); + } let instance = context .instantiate_module(None, wasm_data) .map_err(|e| err2py(e.into()))?; @@ -111,6 +120,11 @@ pub fn imported_modules<'p>(py: Python<'p>, buffer_source: &PyBytes) -> PyResult let reader = section.get_import_section_reader().unwrap(); for import in reader { let import = import.unwrap(); + // Skip over wasi-looking imports since those aren't imported from + // Python but rather they're implemented natively. + if wasmtime_wasi::is_wasi_module(import.module) { + continue; + } let set = match dict.get_item(import.module) { Some(set) => set.downcast_ref::().unwrap(), None => { diff --git a/crates/wasi-common/winx/Cargo.toml b/crates/wasi-common/winx/Cargo.toml index 36bf64abfd..94e9359923 100644 --- a/crates/wasi-common/winx/Cargo.toml +++ b/crates/wasi-common/winx/Cargo.toml @@ -18,12 +18,13 @@ features = [ "errhandlingapi", "handleapi", "processthreadsapi", + "profileapi", "securitybaseapi", "winbase", "winerror", "ws2def", "fileapi", - "aclapi" + "aclapi", ] [badges] diff --git a/crates/wasi/src/lib.rs b/crates/wasi/src/lib.rs index 115bf9ab2e..8a2d141625 100644 --- a/crates/wasi/src/lib.rs +++ b/crates/wasi/src/lib.rs @@ -4,3 +4,10 @@ mod instantiate; mod syscalls; pub use instantiate::{instantiate_wasi, instantiate_wasi_with_context}; + +pub fn is_wasi_module(name: &str) -> bool { + // FIXME: this should be more conservative, but while WASI is in flux and + // we're figuring out how to support multiple revisions, this should do the + // trick. + name.starts_with("wasi") +}