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`
This commit is contained in:
committed by
Dan Gohman
parent
0006a2af95
commit
d9edb95218
@@ -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" }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 => {
|
||||
|
||||
@@ -18,12 +18,13 @@ features = [
|
||||
"errhandlingapi",
|
||||
"handleapi",
|
||||
"processthreadsapi",
|
||||
"profileapi",
|
||||
"securitybaseapi",
|
||||
"winbase",
|
||||
"winerror",
|
||||
"ws2def",
|
||||
"fileapi",
|
||||
"aclapi"
|
||||
"aclapi",
|
||||
]
|
||||
|
||||
[badges]
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user