Stream: git-wasmtime

Topic: wasmtime / Issue #727 Improving `Instance::new` experience


view this post on Zulip Wasmtime GitHub notifications bot (Mar 23 2020 at 16:22):

alexcrichton commented on Issue #727:

I've posted a draft of this to https://github.com/bytecodealliance/wasmtime/pull/1384

view this post on Zulip Wasmtime GitHub notifications bot (Mar 24 2020 at 02:02):

alexcrichton closed Issue #727:

I think this is a large enough proposal and change that I'd like to split this off of https://github.com/bytecodealliance/wasmtime/issues/708. This issue is specifically about improving the experience of using Instance::new, which currently looks like this:

impl Instance {
    pub fn new(
        store: &HostRef<Store>,
        module: &HostRef<Module>,
        externs: &[Extern]
    ) -> Result<Instance>;
}

After #708 that will instead look like:

impl Instance {
    pub fn new(module: &Module, externs: &[Extern]) -> Result<Instance>;
}

Somewhat radically, I would propose that this is actually what we want on Instance! The idea here is that the list of externs is the same length of the list in module.imports(), and externs line up 1:1 with module.imports().

Now that being said this is a pretty bad API to use, and doesn't help instantiating things like WASI or dealing with name resolution at all. I think instead I think we should provide an API similar to the following (bikeshedding everything of course):

impl Instance {
    fn builder(module: &Module) -> InstanceBuilder;
    // ...
}

struct InstanceBuilder { ... }

impl InstanceBuilder {
    // Provide an import by module/name, panics if `module` or `name` is taken
    fn provide(&mut self, module: &str, name: &str, item: impl Into<Extern>) -> &mut Self;

    // Hook up an entire instance under the name `module`. Panics if `module` already used
    fn provide_instance(&mut self, module: &str, instance: &Instance) -> &mut Self;

    // Provides `wasi_unstable` module
    fn provide_wasi_snapshot0(&mut self, config: &WasiSnapshot0Config) -> &mut Self;

    // Provides `wasi_snapshot_preview1` module
    fn provide_wasi_snapshot1(&mut self, config: &WasiSnapshot1Config) -> &mut Self;

    // Does all the name resolution.
    fn build(&mut self) -> Result<Instance>;
}

Generally the idea here is that we keep Instance::new as the "low level" API of instantiating a module. That's for use when name resolution won't cut it for one reason or another. Something like InstanceBuilder will allow providing lots of extensions, as necessary, for hooking into name resolution and such. That way usage might look like:

let module = ...;
let instance = Instance::builder(&module)
    .provide_wasi_snapshot0(&Default::default())
    .provide("my-custom-api", "add", |a: i32, b: i32| a + b)
    .build()?;
// ...

The Into<Extern> part may be pretty tricky and still take some frobbing to get right. Super-long term I think it'd be awesome to do something like:

#[wasmtime::host_module]
trait MyWasmHostModule {
    fn function1(&self);
    fn function2(&self, input: &str) -> String;
}

// generates ...
// trait MyWasmHostModuleExt { ... }
// impl MyWasmHostModuleExt for wasmtime::InstanceBuilder {
//    fn provide_my_wasm_host_module(&mut self, instance: impl MyWasmHostModule)
//       ...
//    }
// }

impl MyWasmHostModule for MyStruct {
    // ...
}

Instace::builder(&module)
    .provide_my_wasm_host_module(&MyStruct)
    .build()?;

(but that's naturally much further in the future)


Last updated: Dec 23 2024 at 12:05 UTC