Browse Source

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`
pull/541/head
Alex Crichton 5 years ago
committed by Dan Gohman
parent
commit
d9edb95218
  1. 1
      crates/interface-types/Cargo.toml
  2. 44
      crates/interface-types/src/lib.rs
  3. 1
      crates/misc/py/Cargo.toml
  4. 14
      crates/misc/py/src/lib.rs
  5. 3
      crates/wasi-common/winx/Cargo.toml
  6. 7
      crates/wasi/src/lib.rs

1
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" }

44
crates/interface-types/src/lib.rs

@ -32,6 +32,7 @@ pub use value::Value;
/// appropriate for bound functions.
pub struct ModuleData {
inner: Option<Inner>,
wasi_module_name: Option<String>,
}
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<String> {
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

1
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"

14
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::<PySet>().unwrap(),
None => {

3
crates/wasi-common/winx/Cargo.toml

@ -18,12 +18,13 @@ features = [
"errhandlingapi",
"handleapi",
"processthreadsapi",
"profileapi",
"securitybaseapi",
"winbase",
"winerror",
"ws2def",
"fileapi",
"aclapi"
"aclapi",
]
[badges]

7
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")
}

Loading…
Cancel
Save