bytecodealliance / wasmtime

A fast and secure runtime for WebAssembly
https://wasmtime.dev/
Apache License 2.0
15.2k stars 1.28k forks source link

Help wanted: Accessing interfaces from other components #9309

Open LorDawid opened 5 days ago

LorDawid commented 5 days ago

Is there any way to access interfaces of other WASM components?

I've created a plugin1 with the following .wit representation:

package test:plugin1;

world plugin1 {
    import engine: interface {
        print: func(msg: string);
    }

    export plugin1i: interface {
        initialize-p1: func();
    }
}

And a plugin2 with the following .wit representation:

package test:plugin2;

world plugin2 {
    import engine: interface {
        print: func(msg: string);
    }

    import plugin1i: interface {
        initialize-p1: func();
    }

    export initialize-p2: func();
}

Which is supposed to use the plugin1's interface and call that method, but I haven't been able to find any documentation regarding this, so I'm wondering if it is even possible.

This does not work as the message component imports instance 'plugin1i', but a matching implementation was not found in the linker appears.

The only thing I seem to be able to do is:

    linker
        .instance("plugin1i")
        .unwrap()
        .func_wrap("initialize-p1", |_, (): ()| Ok(println!("Call")))
        .unwrap();

But the goal is not to define the functions myself, but rather to import them from other instances, and I'm also kinda curious about why this even works (are interfaces like instances under the hood? Why do I use .instance() to access a given interface?).

I could not find any documentation regarding this, so I hope that someone could help me here.

I'm trying to make a plugin system where dynamically loaded plugins can depend on each other (without having access to each other's source code), and I'm wondering if the component model is even the right thing to use here.

alexcrichton commented 2 days ago

Thanks for the report! At this time linking like in the same manner as core wasm is not supported. That means that you can't take an export from one component and use it as the import of another component. To work around that you'll be required to use the host as you're doing here. Effectively you're on the right track and the goal is to fill out Ok(println!("Call")).

How exactly you fill that in depends on your use case. If you statically know the signatures, you can specialize to that. If you don't statically know the signatures (which I suspect is the case due to the plugin nature of this) you'll want to use func_new to define the function in LinkerInstance and Func::call. You'll need to do some "reflection" of sorts to iterate the exports of plugin component and to fill out the linker to instantiate the next component as well.

You're right this isn't well documented, and it's something we should improve! In the meantime though let me know if that doesn't work (or perhaps if it's so burdensome you change approaches altogether).