rustwasm / wasm-bindgen

Facilitating high-level interactions between Wasm modules and JavaScript

Home Page:https://rustwasm.github.io/docs/wasm-bindgen/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Missing WebIDL processing features overview

MOZGIII opened this issue · comments

I was doing a manual implementation for the WebTransport types, and here's case-study-based a list of missing features that makes WebIDL codegen really unusable:

  1. Lack of getters support for dictionaries. A lot of dictionaries are used in return positions: ReadableStream, pretty much everywhere in the getStats for WebTransport, etc.
  2. Dictionaries setters do not allow unsetting values (for the most part) - we might want to either make them all take Option<T> instead of T, or generate an additional accessor to allow unset_....
  3. Untyped async calls - the WebIDL has enough data to properly deduce the type, but that is not done, and an untype js_sys::Promise it returned instead. We should consider adding types support to the js_sys::Promise, and also generating the calls that return promises as real async fns. This is, however, blocked by #3919, as async fn generation is broken.
  4. Well, #3919. The async fn generation is broken.
  5. The catch is not generated correctly. This is an issue with WebIDL definitions, as they lack the machine-readable data on whether a call can throw or not - but nonetheless I count this towards the list of issues of the WebIDL-generated rust code.
  6. ReadableStream/WriteableStream should have a generic indicating what type they produce - they are capable of streaming not just the bytes (i.e. binary data, where they act like Read/Write), but objects too (acting more like a stream / async iterator in rust sense).
  7. Inability to turn certain hot calls final at the codegen level.
  8. I can't just install the latest wasm_bindgen_webidl as a dependency and use it in my crate - as the latest version is not published, and the last published is very old and conflicts with the rest of the crates. This is by far the easiest issue to solve.
  9. Errors types are not properly indicated anywhere, while the WebIDL has relevant info.

Also, some nits I encountered while implementing things manually.

  1. missing_docs lint is triggered for wasm_bindgen macro enums.
  2. No explicit support for attributes as wasm_bindgen macro, as in syntax sugar for generating both getter and setter from the same attribute.
  3. Declaring dictionaries manually is too verbose, they should really be a first-class item in the macro.

Not sure this is the right place for this, and those issue are most definitely reported before - but this is a case study organized in one place, and will hopefully act like a concise roadmap towards a better support for WebIDL. Hopefully if all those are solved somehow we'd get a way nicer codegen.
For now, using WebIDL is much more painful than using custom type declarations.

Dictionaries setters do not allow unsetting values (for the most part) - we might want to either make them all take Option<T> instead of T, or generate an additional accessor to allow unset_....

Just a quick comment on this one: allowing to unset values would effectively void the conditions that should hold for reading the properties. Meaning that all getters would need to be wrapped in an Option.

I guess you could implement the unset_... functions anyway and mark them unsafe?

Not all of them - the attribute ? (with ? at the end) - ones only, those should be unsettable, as they can effectively be not set and read as null (as I understand - null specifically, not undefined). For those values we absolutely need getters to return Option.

UPD: oh, yeah, dictionaries, right. They don't have definitions like that, and thus must absolutely always do the explicit Option<T> for all values. In my opinion, it is not about the ergonomics - but about correctness. In the context of a dictionaries, WebIDL does not actually encode the expected default values - so we can't procedurally generate them - but the spec itself usually does, so it is possible for a developer to properly deduce what value will be in effect if a dictionary value is unset, on a case-by-case basis via reading the docs. Combined with limitations of panics at wasm, and this becomes the only solution to avoid super-fragile apps imo.

In the context of a dictionaries, WebIDL does not actually encode the expected default values

They actually do, so I think your suggestion of allowing to unset optional values only would work great.

E.g. the first dictionary declaration I could find going alphabetically in the enabled WebIDLs:

dictionary AnalyserOptions : AudioNodeOptions {
unsigned long fftSize = 2048;
double maxDecibels = -30;
double minDecibels = -100;
double smoothingTimeConstant = 0.8;
};

It just looks like the code generation for dictionary constructors produces undefined behavior by not setting default values in new:

impl AnalyserOptions {
#[doc = "Construct a new `AnalyserOptions`."]
#[doc = ""]
#[doc = "*This API requires the following crate features to be activated: `AnalyserOptions`*"]
pub fn new() -> Self {
#[allow(unused_mut)]
let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());
ret
}

I'll open a separate issue for that.