Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Features/wasm components #630

Merged
merged 45 commits into from
Dec 1, 2024
Merged

Conversation

superchris
Copy link
Collaborator

This is a draft PR to show how the progress is coming along for supporting web assembly components. Check out wasm_components_test.exs to see where things are at

@superchris
Copy link
Collaborator Author

So I've hit a bit of wall here where it looks I'll need to do something drastic to get component support working the way I think it should. A little conext: A wasm component has, as it's definition, a .wit file which specifies the high level, typed, interface to the component. The wasmtime rust crate provides a bindgen macro which generates the rust bindings for a component. This means that for a component to be used, their will need to be some project specific rust code which depends on the the wasmex rust code. Trying to have one rustler nif depend on another rustler nif did not seem to go very well, which makes me think what would need to happen is the rustler nif would need to be specific to the components being used. In order for this to be viable, there would need to be considerable change to the wasmex codebase methinks, probably involving allowing the use to specify the Native module via configuration so that they could sub in their own, and then maybe a use Wasmex.Native macro to bring in all the wasmex stuff. I don't know if I've described this well enough to make any sense at all, but I'm trying to understand if I should keep marching in this direction or if this is so radical that I should fork and do component specific hex package. Love to hear any thoughts you might have @tessi etc

@tessi
Copy link
Owner

tessi commented Sep 20, 2024

Hey @superchris

first things first: it's awesome that you start tackling this. Having good component support is the next most pressing thing for wasmex to have. thank you 💜

I started looking into components but didn't had the time yet to start a real prototype - so I'm afraid my understanding of how components work isn't perfect yet. My (maybe naïve?) understanding of components was that they are more or less normal wasm modules with added .wit type definitions. Those type definitions could be "used" by wasmex to read/write function results/params. I wasn't sure how that "using" of the WIT types could be done - my hope was to maybe have an elixir macro that defines proper elixir-structs, which wasmex then knows how to (de-)serialise to what the bytes the wasm module needs. Basically, like building our own bindgen.

Coming from that understanding, I didn't get the part on why we need to link other NIFs or use the wasmtime bindgen. Can you elaborate there, please? I probably just don't know enough to fully understand the problem - so just dropping a link to the relevant docs for me to educate myself also works.

If you're up for it, we could also just have a call and discuss/brainstorm possible approaches synchronous. :)

@superchris
Copy link
Collaborator Author

Hey @tessi! Thanks for wasmex :) So the issue I am facing is that I will need to generate two kinds of bindings, one in rust, and another in Elixir. In my branch I the rust bindings I need are in native/wasmex/src/todo_list.rs and the elixir bindings are the todo_* functions in Native. These will need to exist for each component, at least the way I understand things right now. This means each project will have rust code of it's own that will depend on the core wasmex rust code, which is the wall I hit. I'd be delighted to do a brainstorm call (and maybe try to better explain things), feel free to suggest some times that are good for you. I'm UTC-4 (EDT).

@superchris
Copy link
Collaborator Author

@tessi I think after reading your comments again I understand what you are saying. Having our own Elixir bindgen would be amazing, I was trying to do a lazier approach because I was intimidated by making to do the lowering and lifting of elixir types into wasm memory. I probably am not facile enough with rust and the internals of wasm to be able to tackle the elixir bindgen idea just yet. Anyways, happy to talk if you'd like 😄

@tessi
Copy link
Owner

tessi commented Sep 20, 2024

yay, i'm on UTC+2 so that isn't horribly far off. I'm currently on watch for the barely sleeping, feverish baby so today is not a good day to talk sync. But we'll find a time :)

Not being very into wrangling bytes in rust shouldn't stop us. Maybe we can somehow expose the raw memory to elixir and write/read it from there. Not sure what's best, but we have options. I'll do some reading into wasmtimes bindgen and see how heavy it is. From what you wrote here, writing our own bindgen/wrapper/wit-type-serialiser still sounds like a good approach, if we can make it work - much easier to use than having to build a separate NIF and somehow link it 🤔

@tessi
Copy link
Owner

tessi commented Sep 20, 2024

brainstorm idea: if we can implement Lower and Lift for elixir types/structs (see: https://github.com/bytecodealliance/wasmtime/blob/e51a68e64ae2e955b69a6f9e37f5479f8186ae91/crates/wasmtime/src/runtime/component/func/typed.rs#L505), then we have the load+store operations done. 🤔

wasmtimes bindgen! macro basically utilizes the Lift/Lower traits, which we can implement ourselves for elixir-structs instead of deriving it automatically from known rust types.

@superchris
Copy link
Collaborator Author

Hey @tessi I got a little further in my implementation and at this point I've hacked things up to the point its a bit of a mess ;) I had to switch out the wasi library to new ones (wasmtime-wasi and wasmtime-wasi-http) that support WASI p2. As a result, I was able to call into a component in JS that did external http calls using JS native fetch, which was pretty exciting. However, I'm still not quite sure how to move forward with an approach that wouldn't involve per component rust code. I think I might spin up a component specific package, even if just temporarily, to see what using per component rust code would even look like. However, I would be happy to pair on your ideas for wasmex and pursue both approaches.

.vscode/settings.json Outdated Show resolved Hide resolved
native/wasmex/Cargo.toml Outdated Show resolved Hide resolved
@superchris
Copy link
Collaborator Author

I did end up making some amount of progress after I decided to split off to another project at least temporarily. It's still got some hand coded plumbing I need to get rid of, but have several examples working with tests to at least prove out the approach. Code is here: https://github.com/launchscout/wasm_components_ex

However after looking at wasmtime-rb where the beginning of component support has landed, I think their approach is better and would allow us to dynamically invoke functions on components without a separate nif lib per component as my current approach requires. I may try another PR based on it. In short, they convert ruby types back and forth to wasmcomponent val types in rust. This seems like a much smaller lift (pun intended :) ).

I still think the lift/lower idea is a good one, just not quite sure how to start and how ambitious it is compared to the other approaches. Happy to pair some time @tessi if you'd like

@tessi
Copy link
Owner

tessi commented Nov 8, 2024

wow, cool! will have a look this evening - both, your examples as well as wasmtime-rb

actually, i'm not too too set on a single idea as long as users don't have to learn rust/nifs (given they already have a .wasm component). I mean, if that's what's required we have to, but better not :)

re pairing: I'm sure by now you're the subject matter expert on components :) happy to pair though. Let's arrange something in a private channel. How can I reach you best? bluesky/mastodon would be my favourites, mail works too

@superchris
Copy link
Collaborator Author

Hey @tessi I just started trying to use BlueSky more I am @superchrisnelson.bsky.social

@tessi tessi mentioned this pull request Nov 10, 2024
@tessi
Copy link
Owner

tessi commented Nov 10, 2024

@superchris thanks again for the call yesterday! was fun and it fuels more excitement for component support 🔥
I started a parallel PR in #663 to experiment. As I said yesterday, this is not to take away from your great work - I just need to play with things to build an understanding and share ideas with you 💜

@superchris
Copy link
Collaborator Author

That sounds awesome more experiments are the way to go. Eventually the best path forward will emerge :)

@superchris superchris marked this pull request as ready for review November 26, 2024 19:29
native/wasmex/todo-list.wit Outdated Show resolved Hide resolved
native/wasmex/Cargo.toml Outdated Show resolved Hide resolved
@tessi
Copy link
Owner

tessi commented Nov 30, 2024

@superchris this is great and I love to merge this (and iterate in separate PRs) 💜
we just need to get linters happy first :)

@tessi tessi merged commit f0df3e8 into tessi:main Dec 1, 2024
13 checks passed
@tessi
Copy link
Owner

tessi commented Dec 1, 2024

Thanks again @superchris . This was a super cool feature for a first PR. Can't wait to see what comes next :) 💜

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants