rustwasm / wasm-bindgen

Facilitating high-level interactions between Wasm modules and JavaScript
https://rustwasm.github.io/docs/wasm-bindgen/
Apache License 2.0
7.71k stars 1.06k forks source link

Add ability to import and extend JS classes #210

Open willmruzek opened 6 years ago

willmruzek commented 6 years ago

JS classes are heavily used building block in UI development.

It's common for a library like ReactJS or Polymer to provide a component class to extend from.

Thus, there should be an easy way to interoperate with classes, extend them in Rust, and export them back to JS.

I don't know about Rust to know what's possible yet. But I imagine we could use attributes to annotate when a Rust struct inherits from a JS class?

alexcrichton commented 6 years ago

Agreed this'd be great to have! I'm not 100% sure what this would look like, but would love to see and help explore options here

willmruzek commented 6 years ago

Cool! I'll do some research into how this can be done.

I've already looked at bindgen for C++ and didn't find an immediate solution.

willmruzek commented 6 years ago

Is there a way to expand (procedural) macros to see the text output of wasm_bindgen? It would be helpful as I learn more about how this all works.

jsheard commented 6 years ago

cargo-expand should do the trick.

rustup component add rustfmt-preview
cargo install cargo-expand
cargo expand --target=wasm32-unknown-unknown > expanded.rs
willmruzek commented 6 years ago

Thanks @jsheard. I think I have it working now.

willmruzek commented 6 years ago

This is the first time diving into something like this, so this may be a naive suggestion.

It looks like a class is imported as a struct with a bunch of method impls. Would it be possible to construct a trait via the macro as well? Though looking into it, we can't yet bind trait implementations to JS.

Note I've added #[wasm_bindgen(extendable_as = "FooT")] to allow for backwards compatibility.

JS:

export class Foo {
  print() {
    console.log('Foo.print()');
  }
}

Generated Rust:

trait FooT {
    fn new() -> Self;
    fn print(&self) {
     // wasm-bindgen magic 
    }
}

struct Foo;

impl FooT for Foo {
    fn new() -> Component {
        Component
    }
}

Rust usage:

#[wasm_bindgen]
extern {
    #[wasm_bindgen(js_namespace = console)]
    fn log(s: &str);
}

#[wasm_bindgen(module = "...")]
extern {
    #[wasm_bindgen(extendable_as = "FooT")]
    type Foo;

    #[wasm_bindgen(constructor)]
    fn new() -> Foo;

    #[wasm_bindgen(method)]
    fn print(this: &Foo);
}

#[wasm_bindgen]
pub struct Bar {}

#[wasm_bindgen]
impl FooT for Bar {
  fn print() {
    log("Bar.print()");
  }
}
jhpratt commented 5 years ago

Any updates on this? I was looking to see how feasible it would be to create webcomponents using wasm as an experiment, but it's necessary to extend classes for that (either HTMLElement or a framework-specific class).

fitzgen commented 5 years ago

@jhpratt no updates; you may be interested in reading the discussion in https://github.com/rustwasm/team/issues/162 however.

SephReed commented 4 years ago

A couple links to what led me here.


It would be really amazing to be able to extend the AudioWorkletProcessor! IMO, audio work is one of those things which is very well suited to the web, but has never been possible due to the limitations of JS (single thread, slow).

Each AudioWorkletProcessor is its own thread, and having one of those exist in the wasm sphere would ~10x the audio-synthesis ceiling (+/-).

The issue is that one must extend AudioWorkletProcessor and re-export it in order to use it in wasm.... so web audio will have to wait once more.

maxwalley commented 1 year ago

For anyone still looking for the Audio Worklet functionality there is some documentation on how to do it here: https://rustwasm.github.io/wasm-bindgen/examples/wasm-audio-worklet.html