Skip to content

Commit 0d5bd73

Browse files
committed
support worlds which import and/or export "wildcard" interfaces
Per WebAssembly/component-model#172, this implements "part 1" of WIT templates, allowing WIT files to define interfaces which contain a single wildcard function, which worlds may import or export. I've chosen to implement the bindings for host-implemented functions in such a way that the host may delay import resolution until the latest possible moment, i.e. when the guest is actually calling the function. This allows for fully dynamic resolution (e.g. using the function name as a key to be looked up in a remote key-value store) when desired. This does come at a small performance cost compared to doing resolution at e.g. link time instead. In cases where the host wants to do resolution earlier (e.g. at deploy or instantiation time), that's certainly possible, e.g.: ```rust let component = Component::new(&engine, wasm)?; let funcs = component .names("imports") .map(|name| Ok((name.to_owned(), my_resolver(name)?))) .collect::<Result<HashMap<_, _>>>()?; struct MyImports<F> { funcs: HashMap<String, F> } impl <F: Fn() -> Result<u32>> imports::Host for MyImports<F> { fn call(&mut self, name: &str) -> Result<u32> { (self.funcs.get(name).unwrap())() } } let mut store = Store::new(&engine, MyImports { funcs }); ... ``` If we feel that early resolution is the more common case, we could consider adding a configuration option to the binding generator which indicates whether early or late resolution is desired, allowing the generator to optimize (ergonmically and performance-wise) accordingly. Note that the generated `add_to_linker` functions now take a `&Component` parameter as well as a `&mut Linker`. This allows those functions to inspect the component in order to determine how many `func_wrap{_async}` calls to make, and with what names. I'm open to alternatives to this if there's a better way. Finally, I've added a temporary dependency override to Cargo.toml, pointing to our fork of `wasm-tools`, which includes the necessary `wit-parser` changes. We're still iterating on that and will PR those changes separately. We also have a fork of `wit-bindgen` with a new "wildcards" test to verify everything works end-to-end: bytecodealliance/wit-bindgen@main...dicej:wit-templates. I'll PR that last once everything else is stable. Signed-off-by: Joel Dice <joel.dice@fermyon.com>
1 parent 3ff3994 commit 0d5bd73

File tree

7 files changed

+286
-49
lines changed

7 files changed

+286
-49
lines changed

Cargo.lock

+2-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+4-1
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ wasmprinter = "0.2.50"
170170
wasm-encoder = "0.23.0"
171171
wasm-smith = "0.12.1"
172172
wasm-mutate = "0.2.17"
173-
wit-parser = "0.6.1"
173+
wit-parser = "0.6.2"
174174
windows-sys = "0.45.0"
175175
env_logger = "0.9"
176176
rustix = "0.36.7"
@@ -253,3 +253,6 @@ harness = false
253253
[[bench]]
254254
name = "wasi"
255255
harness = false
256+
257+
[patch.crates-io]
258+
wit-parser = { git = "https://github.com/fermyon/wasm-tools", branch = "wit-templates" }

crates/wasmtime/src/component/component.rs

+18
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use serde::{Deserialize, Serialize};
66
use std::collections::{BTreeSet, HashMap};
77
use std::fs;
88
use std::mem;
9+
use std::ops::Deref;
910
use std::path::Path;
1011
use std::ptr::NonNull;
1112
use std::sync::Arc;
@@ -527,4 +528,21 @@ impl Component {
527528
pub fn serialize(&self) -> Result<Vec<u8>> {
528529
Ok(self.code_object().code_memory().mmap().to_vec())
529530
}
531+
532+
/// Get the names of all the imports from the specified instance.
533+
pub fn names<'a>(&'a self, instance_name: &'a str) -> impl Iterator<Item = &str> + 'a {
534+
let env_component = self.env_component();
535+
536+
env_component
537+
.imports
538+
.values()
539+
.filter_map(move |(import, names)| {
540+
if instance_name == &env_component.import_types[*import].0 {
541+
Some(names.iter().map(Deref::deref))
542+
} else {
543+
None
544+
}
545+
})
546+
.flatten()
547+
}
530548
}

crates/wasmtime/src/component/instance.rs

+24
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use crate::{AsContextMut, Module, StoreContextMut};
77
use anyhow::{anyhow, Context, Result};
88
use indexmap::IndexMap;
99
use std::marker;
10+
use std::ops::Deref;
1011
use std::sync::Arc;
1112
use wasmtime_environ::component::{
1213
AlwaysTrap, ComponentTypes, CoreDef, CoreExport, Export, ExportItem, ExtractMemory,
@@ -660,6 +661,29 @@ impl<'a, 'store> ExportInstance<'a, 'store> {
660661
})
661662
}
662663

664+
/// Returns an iterator of all of the exported functions that this instance
665+
/// contains.
666+
//
667+
// See FIXME in above `modules` method, which also applies here.
668+
pub fn funcs(&mut self) -> impl Iterator<Item = (&'a str, Func)> + '_ {
669+
self.exports
670+
.iter()
671+
.filter_map(|(name, export)| match export {
672+
Export::LiftedFunction { ty, func, options } => Some((
673+
name.deref(),
674+
Func::from_lifted_func(
675+
self.store,
676+
self.instance,
677+
self.data,
678+
*ty,
679+
func,
680+
options,
681+
),
682+
)),
683+
_ => None,
684+
})
685+
}
686+
663687
fn as_mut(&mut self) -> ExportInstance<'a, '_> {
664688
ExportInstance {
665689
exports: self.exports,

0 commit comments

Comments
 (0)