Skip to content

Commit

Permalink
implement "part 1" of WIT Templates
Browse files Browse the repository at this point in the history
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 taken a "minimalist" approach to this, such that the host binding
generator just skips wildcards entirely, leaving it to the host to use
dynamic APIs to reflect on the component and do the necessary
func_wrap (for imports) and typed_func (for exports) calls directly.

This adds two new functions to the public API:

- `Component::names`: provides an iterator over the names of the
  functions imported by the specified instance.
- `ExportInstance::funcs`: provides an iterator over the names of the
  functions exported by this instance.

In both cases, I'm open to alternative API designs for getting that
information.

Note that 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 <[email protected]>

generate code for wildcards in `bindgen!`

This improves upon the raw `func_wrap` and `typed_func` APIs by
providing static type checking.

Signed-off-by: Joel Dice <[email protected]>

address review feedback

- Add TODO comment to `Component::function_import_names` (renamed from `names`)
- Add `Component::function_export_names` for symmetry (since an embedder may want both sets of names)
- Add a couple of codegen tests for the bindgen macro
- Use `Func` instead of `TypedFunc` for `__wildcard_matches` to avoid lifetime issues
- Update `wit-parser` and use the new `Interface::wildcard` field where applicable
- Add `name: &str` parameter to `WildcardMatch::call` trait function

Signed-off-by: Joel Dice <[email protected]>
  • Loading branch information
dicej committed Mar 20, 2023
1 parent b5a2d53 commit 3f8f14b
Show file tree
Hide file tree
Showing 10 changed files with 536 additions and 43 deletions.
3 changes: 1 addition & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -261,3 +261,6 @@ harness = false
[[bench]]
name = "wasi"
harness = false

[patch.crates-io]
wit-parser = { git = "https://github.com/dicej/wasm-tools", branch = "wit-templates" }
12 changes: 12 additions & 0 deletions crates/component-macro/tests/codegen/wildcards-mixed.wit
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
default world wildcards-mixed {
import imports: interface {
*: func(s: string, v: u32, l: list<u8>) -> tuple<string, u32, list<u8>>
foo: func(s: string, v: u32, l: list<u8>) -> tuple<string, u32, list<u8>>
bar: func(v: u32) -> u32
}
export exports: interface {
*: func(s: string, v: u32, l: list<u8>) -> tuple<string, u32, list<u8>>
foo: func(s: string, v: u32, l: list<u8>) -> tuple<string, u32, list<u8>>
bar: func(v: u32) -> u32
}
}
8 changes: 8 additions & 0 deletions crates/component-macro/tests/codegen/wildcards.wit
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
default world wildcards {
import imports: interface {
*: func(s: string, v: u32, l: list<u8>) -> tuple<string, u32, list<u8>>
}
export exports: interface {
*: func(s: string, v: u32, l: list<u8>) -> tuple<string, u32, list<u8>>
}
}
62 changes: 60 additions & 2 deletions crates/wasmtime/src/component/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ use std::path::Path;
use std::ptr::NonNull;
use std::sync::Arc;
use wasmtime_environ::component::{
ComponentTypes, GlobalInitializer, LoweredIndex, RuntimeAlwaysTrapIndex,
RuntimeTranscoderIndex, StaticModuleIndex, Translator,
ComponentTypes, Export, GlobalInitializer, LoweredIndex, RuntimeAlwaysTrapIndex,
RuntimeTranscoderIndex, StaticModuleIndex, Translator, TypeDef,
};
use wasmtime_environ::{EntityRef, FunctionLoc, ObjectKind, PrimaryMap, ScopeVec, SignatureIndex};
use wasmtime_jit::{CodeMemory, CompiledModuleInfo};
Expand Down Expand Up @@ -527,4 +527,62 @@ impl Component {
pub fn serialize(&self) -> Result<Vec<u8>> {
Ok(self.code_object().code_memory().mmap().to_vec())
}

/// Get the names of all the functions exported from the specified instance.
//
// TODO: Eventually we'll want a public API for reflecting the entire
// component type, including the names and types of all its instances,
// imports, and exports. This function is currently only intended for
// discovering wildcard imports.
pub fn function_import_names<'a>(
&'a self,
instance_name: &'a str,
) -> impl Iterator<Item = &str> + 'a {
let env_component = self.env_component();

env_component
.imports
.values()
.filter_map(move |(import, names)| {
let (name, typedef) = &env_component.import_types[*import];
if let (TypeDef::ComponentInstance(_), true) = (typedef, instance_name == name) {
Some(names.iter().map(String::as_str))
} else {
None
}
})
.flatten()
}

/// Get the names of all the functions exported from the specified instance.
//
// TODO: Eventually we'll want a public API for reflecting the entire
// component type, including the names and types of all its instances,
// imports, and exports. This function is currently only intended for
// discovering wildcard exports.
pub fn function_export_names<'a>(
&'a self,
instance_name: &'a str,
) -> impl Iterator<Item = &str> + 'a {
let env_component = self.env_component();

env_component
.exports
.get(instance_name)
.and_then(|export| {
if let Export::Instance(map) = export {
Some(map.iter().filter_map(|(n, e)| {
if let Export::LiftedFunction { .. } = e {
Some(n.as_str())
} else {
None
}
}))
} else {
None
}
})
.into_iter()
.flatten()
}
}
23 changes: 23 additions & 0 deletions crates/wasmtime/src/component/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,29 @@ impl<'a, 'store> ExportInstance<'a, 'store> {
})
}

/// Returns an iterator of all of the exported functions that this instance
/// contains.
//
// See FIXME in above `modules` method, which also applies here.
pub fn funcs(&mut self) -> impl Iterator<Item = (&'a str, Func)> + '_ {
self.exports
.iter()
.filter_map(|(name, export)| match export {
Export::LiftedFunction { ty, func, options } => Some((
name.as_str(),
Func::from_lifted_func(
self.store,
self.instance,
self.data,
*ty,
func,
options,
),
)),
_ => None,
})
}

fn as_mut(&mut self) -> ExportInstance<'a, '_> {
ExportInstance {
exports: self.exports,
Expand Down
Loading

0 comments on commit 3f8f14b

Please sign in to comment.