mozilla / uniffi-rs

a multi-language bindings generator for rust
https://mozilla.github.io/uniffi-rs/
Mozilla Public License 2.0
2.52k stars 212 forks source link

WASM support #1424

Open benkuly opened 1 year ago

benkuly commented 1 year ago

For full Kotlin Multiplatform support within uniffi-kotlin-multiplatform-bindings I need some sort of WASM support.

My idea was to expose RustBuffer, RustCallStatus and the scaffolding code with wasm-bindgen, but there are some problems. For example wasm-bindgen does not support MaybeUninit<RustBuffer>. What do you think about this idea? I don't have enough experience with Rust to be able to assess this.

┆Issue is synchronized with this Jira Task ┆Issue Number: UNIFFI-218

arg0d commented 1 year ago

Have you made any progress on this? I'm also investigating WASM JS bindings, and I'm gonna write my proposal for WASM JS implementation in this thread later this week.

benkuly commented 1 year ago

No more progess since opening the issue.

arg0d commented 1 year ago

I have made some experimentation, and here are my findings. Before proceeding with this idea, some feedback would be really helpful @mhammond @rfk

Rationalization

First of all, I have made some rationalization for why this is a good idea.

Proposal

mhammond commented 1 year ago

At a very high abstract level, this makes perfect sense to me and is roughly the approach @bendk took for the Desktop Firefox JS bindings (although there we generate .webidl bindings and implementation etc). The fact these bindings could be external and the of the implementation: "No major changes are needed in uniffi-rs upstream repository" - what's not to like? :)

bendk commented 1 year ago

It makes a lot of sense to me as well and also reminds me of the Firefox JS work. That ended up working out, so I think this effort could too.

So, would each call to Rust would go like this: JS -> secondary scaffolding -> primary scaffolding -> actual rust function?

I wonder if you could skip the primary scaffolding altogether. Most of the existing complexity is defining FfiConverter impls for the types. The actual scaffolding calls are fairly easy to put together. I'm not against going through the extra function, I'd just be interested to know what prevents this and if we could update FfiConverter to fix those issues.

In the secondary scaffolding, use -> Result<(), WasmCallStatus> instead of &mut RustCallStatus. From my research wasm_bindgen does not provide built-in support for passing in arbitrary structs as &mut from JS. However, wasm_bindgen does provide good enough support for returning Result. On Rust side, the function simply returns a Result, where the error is a type convertable to JS ABI. On JS side, when an error is returned, intermediate JS bindings throw the error value. This intermediate error value can be caught, refined into a Uniffi error class, and rethrown.

This is a much nicer API IMO, and it would be nice if the other bindings did something similar. From a quick read, it seems like wasm-bindgen supports Result<T, JsValue>. Is there a way to represent a RustBuffer as a JsValue?

One thing that will probably be tricky is callback interfaces. I think it should be doable, but a pain.

arg0d commented 1 year ago

@bendk you are right, after digging into Rust scaffolding, it looks like it would be better to simply provide an alternate scaffolding implementation for WASM. I will investigate this further.

Yes, RustBuffer can be converted to JsValue, but it needs to be annotated with #[wasm_bindgen]. I would really like to avoid this, because it would mean having to insert WASM code into uniffi-rs upstream. But I think this can be avoided by simply created an identical WasmBuffer struct, and WASM scaffolding could simply map between RustBuffer and WasmBuffer as needed.

Why do you think that callback interfaces are tricky? Rust can easily call JS by using js_sys::JsFunction.

It looks like my department is deprioritizing WASM JS bindings for this quarter, but I have hope to come back to this within the next few months.

bendk commented 1 year ago

Makes sense to me, excited to see how this progresses.

I don't think there's anything fundamentally hard about callback interfaces, it's just felt like they're a lot of work to implement relative to the rest of the features.

matthiasdebernardini commented 1 year ago

what about using wasmer? https://wasmer.io

badboy commented 1 year ago

what about using wasmer? wasmer.io

What about it? wasmer is a Wasm runtime. It can run any wasm you give it. wasm support in UniFFI would mean you can build it in a way that other parts that can interact with wasm can interact with the UniFFI-powered parts.

gyzerok commented 1 year ago

Hello @arg0d! Have you had a chance to come back to this? I'd be highly interested.

arg0d commented 1 year ago

Hey @gyzerok, wasm bindings were deprioritized completely by the library team :( Due to small size of their library, and due to wasm limitations put on Rust code, it will be easier for them to maintain a separate JS version of the library.

gyzerok commented 1 year ago

I see thank you!

@bendk in earlier discussion it is mentioned that Mozilla seems to have some uniffi implementation for WASM used for Firefox desktop. Is there any chance you'd be willing to share those?

badboy commented 1 year ago

I see thank you!

@bendk in earlier discussion it is mentioned that Mozilla seems to have some uniffi implementation for WASM used for Firefox desktop. Is there any chance you'd be willing to share those?

(not ben) we don't have any Wasm implementation. There's a custom JS integration in Gecko, but that's very Gecko-specific.

gogo2464 commented 10 months ago

@arg0d as an user I tested wasm-bindgen and I would like to have uniffi-bindgen module with javascript directly inlcuded in order to avoid to keep my single udl file as required code modification for bindings.

Skaldebane commented 1 month ago

This would definitely be super useful. I'm trying to migrate an Android app to Kotlin Multiplatform, including Wasm support, but I have a Rust shared library. Using UniFFI is pretty great so far for both Android, iOS, and Desktop. It's just the WebAssembly integration that's missing.