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"
|
wasm-webidl-bindings = "0.6"
|
||||||
wasmtime-jit = { path = '../jit', default-features = false }
|
wasmtime-jit = { path = '../jit', default-features = false }
|
||||||
wasmtime-runtime = { path = '../runtime', default-features = false }
|
wasmtime-runtime = { path = '../runtime', default-features = false }
|
||||||
|
wasmtime-wasi = { path = '../wasi' }
|
||||||
|
|
||||||
[badges]
|
[badges]
|
||||||
maintenance = { status = "actively-developed" }
|
maintenance = { status = "actively-developed" }
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ pub use value::Value;
|
|||||||
/// appropriate for bound functions.
|
/// appropriate for bound functions.
|
||||||
pub struct ModuleData {
|
pub struct ModuleData {
|
||||||
inner: Option<Inner>,
|
inner: Option<Inner>,
|
||||||
|
wasi_module_name: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Inner {
|
struct Inner {
|
||||||
@@ -65,17 +66,38 @@ impl ModuleData {
|
|||||||
// find the right section.
|
// find the right section.
|
||||||
let mut reader = wasmparser::ModuleReader::new(wasm)?;
|
let mut reader = wasmparser::ModuleReader::new(wasm)?;
|
||||||
let mut found = false;
|
let mut found = false;
|
||||||
|
let mut wasi_module_name = None;
|
||||||
while !reader.eof() {
|
while !reader.eof() {
|
||||||
let section = reader.read()?;
|
let section = reader.read()?;
|
||||||
if let wasmparser::SectionCode::Custom { name, .. } = section.code {
|
|
||||||
|
match section.code {
|
||||||
|
wasmparser::SectionCode::Custom { name, .. } => {
|
||||||
if name == "webidl-bindings" {
|
if name == "webidl-bindings" {
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
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 {
|
if !found {
|
||||||
return Ok(ModuleData { inner: None });
|
return Ok(ModuleData {
|
||||||
|
inner: None,
|
||||||
|
wasi_module_name,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ok, perform the more expensive parsing. WebAssembly interface types
|
// Ok, perform the more expensive parsing. WebAssembly interface types
|
||||||
@@ -96,21 +118,13 @@ impl ModuleData {
|
|||||||
|
|
||||||
Ok(ModuleData {
|
Ok(ModuleData {
|
||||||
inner: Some(Inner { module }),
|
inner: Some(Inner { module }),
|
||||||
|
wasi_module_name,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Detects if WASI support is needed: returns module name that is requested.
|
/// Detects if WASI support is needed: returns module name that is requested.
|
||||||
pub fn find_wasi_module_name(&self) -> Option<String> {
|
pub fn find_wasi_module_name(&self) -> Option<String> {
|
||||||
self.inner.as_ref().and_then(|Inner { module }| {
|
self.wasi_module_name.clone()
|
||||||
module
|
|
||||||
.imports
|
|
||||||
.iter()
|
|
||||||
.find(|walrus::Import { module, .. }| match module.as_str() {
|
|
||||||
"wasi" | "wasi_unstable" => true,
|
|
||||||
_ => false,
|
|
||||||
})
|
|
||||||
.map(|walrus::Import { module, .. }| module.clone())
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Same as `Context::invoke` except that this works with a `&[Value]` list
|
/// 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-interface-types = { path = "../../interface-types" }
|
||||||
wasmtime-jit = { path = "../../jit" }
|
wasmtime-jit = { path = "../../jit" }
|
||||||
wasmtime-runtime = { path = "../../runtime" }
|
wasmtime-runtime = { path = "../../runtime" }
|
||||||
|
wasmtime-wasi = { path = "../../wasi" }
|
||||||
target-lexicon = { version = "0.9.0", default-features = false }
|
target-lexicon = { version = "0.9.0", default-features = false }
|
||||||
anyhow = "1.0.19"
|
anyhow = "1.0.19"
|
||||||
region = "2.0.0"
|
region = "2.0.0"
|
||||||
|
|||||||
@@ -73,6 +73,15 @@ pub fn instantiate(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let data = Rc::new(ModuleData::new(wasm_data).map_err(err2py)?);
|
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
|
let instance = context
|
||||||
.instantiate_module(None, wasm_data)
|
.instantiate_module(None, wasm_data)
|
||||||
.map_err(|e| err2py(e.into()))?;
|
.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();
|
let reader = section.get_import_section_reader().unwrap();
|
||||||
for import in reader {
|
for import in reader {
|
||||||
let import = import.unwrap();
|
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) {
|
let set = match dict.get_item(import.module) {
|
||||||
Some(set) => set.downcast_ref::<PySet>().unwrap(),
|
Some(set) => set.downcast_ref::<PySet>().unwrap(),
|
||||||
None => {
|
None => {
|
||||||
|
|||||||
@@ -18,12 +18,13 @@ features = [
|
|||||||
"errhandlingapi",
|
"errhandlingapi",
|
||||||
"handleapi",
|
"handleapi",
|
||||||
"processthreadsapi",
|
"processthreadsapi",
|
||||||
|
"profileapi",
|
||||||
"securitybaseapi",
|
"securitybaseapi",
|
||||||
"winbase",
|
"winbase",
|
||||||
"winerror",
|
"winerror",
|
||||||
"ws2def",
|
"ws2def",
|
||||||
"fileapi",
|
"fileapi",
|
||||||
"aclapi"
|
"aclapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[badges]
|
[badges]
|
||||||
|
|||||||
@@ -4,3 +4,10 @@ mod instantiate;
|
|||||||
mod syscalls;
|
mod syscalls;
|
||||||
|
|
||||||
pub use instantiate::{instantiate_wasi, instantiate_wasi_with_context};
|
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