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

Wasmtime: Allow importing types that from libraries with wasm-bindgen dependencies #5557

Closed
gbj opened this issue Jan 11, 2023 · 6 comments
Closed

Comments

@gbj
Copy link

gbj commented Jan 11, 2023

Feature

#3937 contains a good discussion of the problems encountered when trying to compile a program that has any wasm-bindgen dependencies in a WASI environment. However, this has significant effects on the ability to use wasmtime, or any environments that use wasmtime, to render HTML for apps created in any of the common Rust frontend frameworks. This is because virtually the whole Rust web ecosystem including web-sys and js-sys depend on wasm-bindgen, and they are the building blocks for all the Rust frontend web frameworks. For example, it's very common f

If you are building a native server binary, these wasm-bindgen ecosystem libraries compile fine, but panic if you try to run them, for obvious reasons — you are not running in the browser, so do not have access to web APIs. Frameworks are built to handle this. For example, it's very common to declare an HTML element event listener that depends on one of the web_sys::Event types. This can be compiled into a native server binary; it simply won't ever be run on the server, so it does not cause an issue.

However, attempting to do the same in wasmtime causes the error in the issue linked above. See e.g., leptos-rs/leptos#295

Benefit

This would allow people to use wasmtime for server-side rendering in frameworks like Yew, Sycamore, Leptos, etc. that depend on web-sys and wasm-bindgen, just as they can use native binaries for that same task.

Implementation

I don't know enough about how wasm-bindgen works internally to make a real proposal, but given that none of the wasm-bindgen code actually needs to run, it seems that the "allow it to compile and panic if called" solution used for building native binaries for other platforms should work just as well. I'm sure the fact that this seems straightforward is a testament to my own ignorance, here, so apologies.

Alternatives

I've laid out a couple alternative approaches for users to this kind of issue in my reply to the issue in the Leptos repo (leptos-rs/leptos#295)

  1. Leptos mocks and reexports most of web-sys, in a way that uses web-sys on the client and stubs on the server. Significant maintenance burden for the framework.
  2. Leptos adds a wasi feature. Opting into it means you lose web-sys types for this and need to do something like #[cfg(not(feature = "wasi"))] on all your event listeners and other code that needs platform types. Significant burden on library users.
  3. Use a Wasm-based serverless platform like Cloudflare Workers that's built on V8, so does support wasm-bindgen.
  4. Use a platform that lets you deploy native binaries so you can build on top of an Actix or Axum server to serve your app.
@alexcrichton
Copy link
Member

Thanks for the report! I definitely agree that the widespread use of wasm-bindgen clashes with Wasmtime and out-of-browser environments. As one of the primary authors of wasm-bindgen, however, I don't think that this would be a good idea.

"Support wasm-bindgen outside of a browser" would basically involve:

  • Hard-code wasm-bindgen's bespoke, nonstandard, and project-specific method of binding to web APIs. This is highly dependent on the wasm-bindgen crate version and changes over time, there's no stability here.
  • Implement all browser APIs in Wasmtime, natively. Naturally this is a sisyphean task for not really all that much benefit, for example what does it mean to create a DOM node in an out-of-browser environment?

Neither of these fundamental pillars of supporting wasm-bindgen make sense to implement in Wasmtime, in my opinion.

Again though I don't disagree that there's pain here in mixing browser and out-of-browser environment but I don't believe unifying the environments is a solution. Instead I think it would be better to be clearer and more consistent about what code compiles where and where it runs. This is also a very difficult question and isn't necessarily all that much easier than the above, but it's more scalable into the future I believe.

@gbj
Copy link
Author

gbj commented Jan 11, 2023

To be clear I agree that it would be a very bad idea to try to implement the browser APIs. The behavior I'd expect is the behavior on any other target: the code compiles and runs but if I actually try to call a wasm-bindgen function it panics. I guess I don't understand why this is possible for arbitrary native targets but not WASI, but again, I'm sure that's my own ignorance.

@gbj
Copy link
Author

gbj commented Jan 11, 2023

Just to expand with a very simple example:

fn get_window() -> Option<web_sys::Window> {
    web_sys::window()
}

fn main() {
    if false {
        get_window();
    }
    println!("Hello, world!");
}

If I cargo run this, it compiles and runs, printing Hello, world!. get_window() is never called, so it does not panic.

If I cargo build --target wasm32-wasi && wasmtime target/wasm32-wasi/debug/wasmtimeexample.wasm, it compiles and then the expected runtime error

Error: failed to run main module `target/wasm32-wasi/debug/wasmtimeexample.wasm`

Caused by:
    0: failed to instantiate "target/wasm32-wasi/debug/wasmtimeexample.wasm"
    1: unknown import: `__wbindgen_placeholder__::__wbindgen_describe` has not been defined

@gbj
Copy link
Author

gbj commented Jan 11, 2023

You know, I wonder if this is just that this check in wasm-bindgen itself is actually insufficient — it only tests whether something is a) Wasm and b) not emscripten.

#[cfg(not(all(target_arch = "wasm32", not(target_os = "emscripten"))))]

@jameysharp
Copy link
Contributor

There may be ways to get wasm-bindgen to generate better code here, I don't know anything about that. But I believe the behavior you expected is available with wasmtime --trap-unknown-imports. Does that help?

@gbj
Copy link
Author

gbj commented Jan 11, 2023

@jameysharp Oh fantastic, that does it! I will close this and consider opening something over in wasm-bindgen for the possible improvement there.

Thanks so much.

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

No branches or pull requests

3 participants