Stream: wasmtime

Topic: Sharing generated code between same interface import/export


view this post on Zulip Jeff Parsons (Nov 04 2023 at 22:23):

I'm trying to use the with and interfaces options to wasmtime::component::bindgen! to share generated code for types in an interface.

I seem to have it working for _imports_ by doing things like this:

pub mod types {
pub mod job_server_api {
    wasmtime::component::bindgen!({
        path: "wit/",
        interfaces: "
            import jeffparsons:job-server/job-server-api;
        ",
    });

    pub use self::jeffparsons::job_server::job_server_api::*;
}
}
pub mod job_worker {
    wasmtime::component::bindgen!({
        world: "jeffparsons:job-worker/job-worker",
        with: {
            "jeffparsons:job-server/job-server-api": crate::types::job_server_api,
        },
    });
}

But I'm also trying to _export_ an implementation of job-server-api from the host. (A kind of proxy that dispatches calls to either local or remote components.) I can't figure out how to share the generated code for types in job-server-api across both imports and exports.

Is this possible somehow? The generated code for the imports and exports appears to be identical (except for borrowing convention in the call_whatever methods which is not really an issue) so I assume there's nothing morally reprehensible about what I want to do. :sweat_smile:

My workaround for now is to hand-write From implementations for one or both directions, depending on how the types are used. But that's getting verbose and tedious pretty quickly, as you might imagine. If the generated Rust types can't be the same for some reason, generating these conversions from bindgen! would also be pretty handy.

Thanks!

view this post on Zulip Jeff Parsons (Nov 05 2023 at 05:01):

I should add that I'm actually thinking of these bindgen!-generated bindings as a stopgap, and ultimately I'd like to do this generically, i.e. without the host program having hard-coded knowledge of most of those guest components. (Full context: https://bytecodealliance.zulipchat.com/#narrow/stream/223391-wasm/topic/Wasm.20Components.20and.20service-oriented.20architecture)

So if there's some way to achieve this using the dynamic type/value stuff in wasmtime::component::{types, Val}, LinkerInstance::func_new, etc. instead then I'd be happy to go down that path instead. I'm just not sure if that's possible. (I'm not familiar with it, don't know what limitations exist).

Also, I don't care much about performance; I don't expect these interfaces to be a bottleneck. So if a super-dynamic approach turns out to be the most ergonomic, that's fine.

view this post on Zulip Alex Crichton (Nov 05 2023 at 21:31):

The generated types today currently try to reflect what's happening on the WIT level with different types for imports/exports largely, but that doesn't mean this couldn't be unified where possible either. Mind filing an issue for this?

view this post on Zulip Jeff Parsons (Nov 05 2023 at 22:47):

Thanks, can do!

Alex Crichton said:

[...] what's happening on the WIT level with different types for imports/exports [...]

I had a feeling I'd read something vaguely related to that somewhere, but I don't understand why a type as defined on an interface (e.g. record) is considered a different type depending on whether the interface is imported or exported. I'm sure it all makes sense — it's just not obvious to me (yet).

I'll definitely be using the generated bindings for now; even with my hand-coded conversions it's good enough to let me focus on more interesting parts of the project. But eventually I'll want to do all this dynamically, so I guess I'd better crack open some of the generated components to try to understand when I can safely treat types from different components as equal, etc.


Last updated: Nov 22 2024 at 16:03 UTC