mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-03-16 02:00:51 +00:00
Implement Deref
for all imported JS types
This commit implements the first half of [RFC #5] where the `Deref` trait is implemented for all imported types. The target of `Deref` is either the first entry of the list of `extends` attribute or `JsValue`. All examples using `.as_ref()` with various `web-sys` types have been updated to the more ergonomic deref casts now. Additionally the `web-sys` generation of the `extends` array has been fixed slightly to explicitly list implementatoins in the hierarchy order to ensure the correct target for `Deref` is chosen. [RFC #5]: https://github.com/rustwasm/rfcs/blob/master/text/005-structural-and-deref.md
This commit is contained in:
parent
d646b29bb7
commit
5b76a6291e
@ -535,7 +535,7 @@ impl ToTokens for ast::ImportType {
|
||||
use wasm_bindgen::convert::RefFromWasmAbi;
|
||||
use wasm_bindgen::describe::WasmDescribe;
|
||||
use wasm_bindgen::{JsValue, JsCast};
|
||||
use wasm_bindgen::__rt::core::mem::ManuallyDrop;
|
||||
use wasm_bindgen::__rt::core;
|
||||
|
||||
impl WasmDescribe for #rust_name {
|
||||
fn describe() {
|
||||
@ -589,13 +589,13 @@ impl ToTokens for ast::ImportType {
|
||||
|
||||
impl RefFromWasmAbi for #rust_name {
|
||||
type Abi = <JsValue as RefFromWasmAbi>::Abi;
|
||||
type Anchor = ManuallyDrop<#rust_name>;
|
||||
type Anchor = core::mem::ManuallyDrop<#rust_name>;
|
||||
|
||||
#[inline]
|
||||
unsafe fn ref_from_abi(js: Self::Abi, extra: &mut Stack) -> Self::Anchor {
|
||||
let tmp = <JsValue as RefFromWasmAbi>::ref_from_abi(js, extra);
|
||||
ManuallyDrop::new(#rust_name {
|
||||
obj: ManuallyDrop::into_inner(tmp),
|
||||
core::mem::ManuallyDrop::new(#rust_name {
|
||||
obj: core::mem::ManuallyDrop::into_inner(tmp),
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -657,6 +657,20 @@ impl ToTokens for ast::ImportType {
|
||||
};
|
||||
}).to_tokens(tokens);
|
||||
|
||||
let deref_target = match self.extends.first() {
|
||||
Some(target) => quote! { #target },
|
||||
None => quote! { JsValue },
|
||||
};
|
||||
(quote! {
|
||||
impl core::ops::Deref for #rust_name {
|
||||
type Target = #deref_target;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &#deref_target {
|
||||
self.as_ref()
|
||||
}
|
||||
}
|
||||
}).to_tokens(tokens);
|
||||
for superclass in self.extends.iter() {
|
||||
(quote! {
|
||||
impl From<#rust_name> for #superclass {
|
||||
|
@ -7,7 +7,8 @@ extern crate wasm_bindgen_futures;
|
||||
extern crate wasm_bindgen_test;
|
||||
extern crate web_sys;
|
||||
|
||||
use wasm_bindgen_test::wasm_bindgen_test_configure;
|
||||
use wasm_bindgen::{JsValue, JsCast};
|
||||
use wasm_bindgen_test::*;
|
||||
|
||||
wasm_bindgen_test_configure!(run_in_browser);
|
||||
|
||||
@ -56,3 +57,11 @@ pub mod table_element;
|
||||
pub mod title_element;
|
||||
pub mod xpath_result;
|
||||
pub mod indexeddb;
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn deref_works() {
|
||||
let x = JsValue::from(3);
|
||||
let x = x.unchecked_into::<web_sys::XmlHttpRequestUpload>();
|
||||
let y: &web_sys::XmlHttpRequestEventTarget = &x;
|
||||
drop(y);
|
||||
}
|
||||
|
@ -735,11 +735,17 @@ impl<'src> FirstPass<'src, ()> for weedle::CallbackInterfaceDefinition<'src> {
|
||||
impl<'a> FirstPassRecord<'a> {
|
||||
pub fn all_superclasses<'me>(&'me self, interface: &str) -> impl Iterator<Item = String> + 'me {
|
||||
let mut set = BTreeSet::new();
|
||||
self.fill_superclasses(interface, &mut set);
|
||||
set.into_iter()
|
||||
let mut list = Vec::new();
|
||||
self.fill_superclasses(interface, &mut set, &mut list);
|
||||
list.into_iter()
|
||||
}
|
||||
|
||||
fn fill_superclasses(&self, interface: &str, set: &mut BTreeSet<String>) {
|
||||
fn fill_superclasses(
|
||||
&self,
|
||||
interface: &str,
|
||||
set: &mut BTreeSet<&'a str>,
|
||||
list: &mut Vec<String>,
|
||||
) {
|
||||
let data = match self.interfaces.get(interface) {
|
||||
Some(data) => data,
|
||||
None => return,
|
||||
@ -749,8 +755,9 @@ impl<'a> FirstPassRecord<'a> {
|
||||
None => return,
|
||||
};
|
||||
if self.interfaces.contains_key(superclass) {
|
||||
if set.insert(camel_case_ident(superclass)) {
|
||||
self.fill_superclasses(superclass, set);
|
||||
if set.insert(superclass) {
|
||||
list.push(camel_case_ident(superclass));
|
||||
self.fill_superclasses(superclass, set, list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,10 +16,7 @@ pub fn run() -> Result<(), JsValue> {
|
||||
let val = document.create_element("p")?;
|
||||
val.set_inner_html("Hello from Rust!");
|
||||
|
||||
// Right now the class inheritance hierarchy of the DOM isn't super
|
||||
// ergonomic, so we manually cast `val: Element` to `&Node` to call the
|
||||
// `append_child` method.
|
||||
AsRef::<web_sys::Node>::as_ref(&body).append_child(val.as_ref())?;
|
||||
body.append_child(&val)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -8,29 +8,19 @@ use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen::JsCast;
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn main() {
|
||||
pub fn main() -> Result<(), JsValue> {
|
||||
let document = web_sys::window().unwrap().document().unwrap();
|
||||
let canvas = document
|
||||
.create_element("canvas")
|
||||
.unwrap()
|
||||
.dyn_into::<web_sys::HtmlCanvasElement>()
|
||||
.map_err(|_| ())
|
||||
.unwrap();
|
||||
(document.body().unwrap().as_ref() as &web_sys::Node)
|
||||
.append_child(canvas.as_ref() as &web_sys::Node)
|
||||
.unwrap();
|
||||
.create_element("canvas")?
|
||||
.dyn_into::<web_sys::HtmlCanvasElement>()?;
|
||||
document.body().unwrap().append_child(&canvas)?;
|
||||
canvas.set_width(640);
|
||||
canvas.set_height(480);
|
||||
(canvas.as_ref() as &web_sys::HtmlElement)
|
||||
.style()
|
||||
.set_property("border", "solid")
|
||||
.unwrap();
|
||||
canvas.style().set_property("border", "solid")?;
|
||||
let context = canvas
|
||||
.get_context("2d")
|
||||
.get_context("2d")?
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.dyn_into::<web_sys::CanvasRenderingContext2d>()
|
||||
.unwrap();
|
||||
.dyn_into::<web_sys::CanvasRenderingContext2d>()?;
|
||||
let context = Rc::new(context);
|
||||
let pressed = Rc::new(Cell::new(false));
|
||||
{
|
||||
@ -41,9 +31,10 @@ pub fn main() {
|
||||
context.move_to(event.offset_x() as f64, event.offset_y() as f64);
|
||||
pressed.set(true);
|
||||
}) as Box<FnMut(_)>);
|
||||
(canvas.as_ref() as &web_sys::EventTarget)
|
||||
.add_event_listener_with_callback("mousedown", closure.as_ref().unchecked_ref())
|
||||
.unwrap();
|
||||
canvas.add_event_listener_with_callback(
|
||||
"mousedown",
|
||||
closure.as_ref().unchecked_ref(),
|
||||
)?;
|
||||
closure.forget();
|
||||
}
|
||||
{
|
||||
@ -57,9 +48,10 @@ pub fn main() {
|
||||
context.move_to(event.offset_x() as f64, event.offset_y() as f64);
|
||||
}
|
||||
}) as Box<FnMut(_)>);
|
||||
(canvas.as_ref() as &web_sys::EventTarget)
|
||||
.add_event_listener_with_callback("mousemove", closure.as_ref().unchecked_ref())
|
||||
.unwrap();
|
||||
canvas.add_event_listener_with_callback(
|
||||
"mousemove",
|
||||
closure.as_ref().unchecked_ref(),
|
||||
)?;
|
||||
closure.forget();
|
||||
}
|
||||
{
|
||||
@ -70,9 +62,12 @@ pub fn main() {
|
||||
context.line_to(event.offset_x() as f64, event.offset_y() as f64);
|
||||
context.stroke();
|
||||
}) as Box<FnMut(_)>);
|
||||
(canvas.as_ref() as &web_sys::EventTarget)
|
||||
.add_event_listener_with_callback("mouseup", closure.as_ref().unchecked_ref())
|
||||
.unwrap();
|
||||
canvas.add_event_listener_with_callback(
|
||||
"mouseup",
|
||||
closure.as_ref().unchecked_ref(),
|
||||
)?;
|
||||
closure.forget();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -139,7 +139,7 @@ impl WorkerPool {
|
||||
// thread?
|
||||
// * Need to handle the `?` on `post_message` as well.
|
||||
array.push(&wasm_bindgen::memory());
|
||||
worker.post_message(array.as_ref())?;
|
||||
worker.post_message(&array)?;
|
||||
worker.set_onmessage(Some(callback.as_ref().unchecked_ref()));
|
||||
worker.set_onerror(Some(callback.as_ref().unchecked_ref()));
|
||||
workers.push(worker);
|
||||
@ -355,10 +355,10 @@ impl Shared {
|
||||
self.scene.height as f64,
|
||||
)?;
|
||||
let arr = Array::new();
|
||||
arr.push(data.as_ref());
|
||||
arr.push(&data);
|
||||
arr.push(&JsValue::from(done));
|
||||
arr.push(&JsValue::from(self.id as f64));
|
||||
global.post_message(arr.as_ref())?;
|
||||
global.post_message(&arr)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ extern crate wasm_bindgen;
|
||||
extern crate web_sys;
|
||||
|
||||
use wasm_bindgen::prelude::*;
|
||||
use web_sys::{AudioContext, AudioNode, OscillatorType};
|
||||
use web_sys::{AudioContext, OscillatorType};
|
||||
|
||||
/// Converts a midi note to frequency
|
||||
///
|
||||
@ -60,34 +60,24 @@ impl FmOsc {
|
||||
fm_osc.set_type(OscillatorType::Sine);
|
||||
fm_osc.frequency().set_value(0.0);
|
||||
|
||||
// Create base class references:
|
||||
{
|
||||
let primary_node: &AudioNode = primary.as_ref();
|
||||
let gain_node: &AudioNode = gain.as_ref();
|
||||
let fm_osc_node: &AudioNode = fm_osc.as_ref();
|
||||
let fm_gain_node: &AudioNode = fm_gain.as_ref();
|
||||
let destination = ctx.destination();
|
||||
let destination_node: &AudioNode = destination.as_ref();
|
||||
// Connect the nodes up!
|
||||
|
||||
// Connect the nodes up!
|
||||
// The primary oscillator is routed through the gain node, so that
|
||||
// it can control the overall output volume.
|
||||
primary.connect_with_audio_node(&gain)?;
|
||||
|
||||
// The primary oscillator is routed through the gain node, so that
|
||||
// it can control the overall output volume.
|
||||
primary_node.connect_with_audio_node(gain.as_ref())?;
|
||||
// Then connect the gain node to the AudioContext destination (aka
|
||||
// your speakers).
|
||||
gain.connect_with_audio_node(&ctx.destination())?;
|
||||
|
||||
// Then connect the gain node to the AudioContext destination (aka
|
||||
// your speakers).
|
||||
gain_node.connect_with_audio_node(destination_node)?;
|
||||
|
||||
// The FM oscillator is connected to its own gain node, so it can
|
||||
// control the amount of modulation.
|
||||
fm_osc_node.connect_with_audio_node(fm_gain.as_ref())?;
|
||||
// The FM oscillator is connected to its own gain node, so it can
|
||||
// control the amount of modulation.
|
||||
fm_osc.connect_with_audio_node(&fm_gain)?;
|
||||
|
||||
|
||||
// Connect the FM oscillator to the frequency parameter of the main
|
||||
// oscillator, so that the FM node can modulate its frequency.
|
||||
fm_gain_node.connect_with_audio_param(&primary.frequency())?;
|
||||
}
|
||||
// Connect the FM oscillator to the frequency parameter of the main
|
||||
// oscillator, so that the FM node can modulate its frequency.
|
||||
fm_gain.connect_with_audio_param(&primary.frequency())?;
|
||||
|
||||
// Start the oscillators!
|
||||
primary.start()?;
|
||||
|
@ -8,20 +8,16 @@ use web_sys::{WebGlProgram, WebGlRenderingContext, WebGlShader};
|
||||
use js_sys::{WebAssembly};
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn draw() {
|
||||
pub fn draw() -> Result<(), JsValue> {
|
||||
let document = web_sys::window().unwrap().document().unwrap();
|
||||
let canvas = document.get_element_by_id("canvas").unwrap();
|
||||
let canvas: web_sys::HtmlCanvasElement = canvas
|
||||
.dyn_into::<web_sys::HtmlCanvasElement>()
|
||||
.map_err(|_| ())
|
||||
.unwrap();
|
||||
.dyn_into::<web_sys::HtmlCanvasElement>()?;
|
||||
|
||||
let context = canvas
|
||||
.get_context("webgl")
|
||||
.get_context("webgl")?
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.dyn_into::<WebGlRenderingContext>()
|
||||
.unwrap();
|
||||
.dyn_into::<WebGlRenderingContext>()?;
|
||||
|
||||
let vert_shader = compile_shader(
|
||||
&context,
|
||||
@ -32,7 +28,7 @@ pub fn draw() {
|
||||
gl_Position = position;
|
||||
}
|
||||
"#,
|
||||
).unwrap();
|
||||
)?;
|
||||
let frag_shader = compile_shader(
|
||||
&context,
|
||||
WebGlRenderingContext::FRAGMENT_SHADER,
|
||||
@ -41,23 +37,23 @@ pub fn draw() {
|
||||
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
|
||||
}
|
||||
"#,
|
||||
).unwrap();
|
||||
let program = link_program(&context, [vert_shader, frag_shader].iter()).unwrap();
|
||||
)?;
|
||||
let program = link_program(&context, [vert_shader, frag_shader].iter())?;
|
||||
context.use_program(Some(&program));
|
||||
|
||||
let vertices: [f32; 9] = [-0.7, -0.7, 0.0, 0.7, -0.7, 0.0, 0.0, 0.7, 0.0];
|
||||
let memory_buffer = wasm_bindgen::memory().dyn_into::<WebAssembly::Memory>().unwrap().buffer();
|
||||
let memory_buffer = wasm_bindgen::memory().dyn_into::<WebAssembly::Memory>()?.buffer();
|
||||
let vertices_location = vertices.as_ptr() as u32 / 4;
|
||||
let vert_array = js_sys::Float32Array::new(&memory_buffer).subarray(
|
||||
vertices_location,
|
||||
vertices_location + vertices.len() as u32,
|
||||
);
|
||||
|
||||
let buffer = context.create_buffer().unwrap();
|
||||
let buffer = context.create_buffer().ok_or("failed to create buffer")?;
|
||||
context.bind_buffer(WebGlRenderingContext::ARRAY_BUFFER, Some(&buffer));
|
||||
context.buffer_data_with_array_buffer_view(
|
||||
WebGlRenderingContext::ARRAY_BUFFER,
|
||||
vert_array.as_ref(),
|
||||
&vert_array,
|
||||
WebGlRenderingContext::STATIC_DRAW,
|
||||
);
|
||||
context.vertex_attrib_pointer_with_i32(0, 3, WebGlRenderingContext::FLOAT, false, 0, 0);
|
||||
@ -71,6 +67,7 @@ pub fn draw() {
|
||||
0,
|
||||
(vertices.len() / 3) as i32,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn compile_shader(
|
||||
|
@ -83,6 +83,7 @@
|
||||
- [Cargo Features](./web-sys/cargo-features.md)
|
||||
- [Function Overloads](./web-sys/function-overloads.md)
|
||||
- [Type Translations](./web-sys/type-translations.md)
|
||||
- [Inheritance](./web-sys/inheritance.md)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
55
guide/src/web-sys/inheritance.md
Normal file
55
guide/src/web-sys/inheritance.md
Normal file
@ -0,0 +1,55 @@
|
||||
# Inheritance in `web-sys`
|
||||
|
||||
Inheritance between JS classes is the bread and butter of how the DOM works on
|
||||
the web, and as a result it's quite important for `web-sys` to provide access to
|
||||
this inheritance hierarchy as well! There are few ways you can access the
|
||||
inheritance hierarchy when using `web-sys`.
|
||||
|
||||
### Accessing parent classes using `Deref`
|
||||
|
||||
Like smart pointers in Rust, all types in `web_sys` implement `Deref` to their
|
||||
parent JS class. This means, for example, if you have a `web_sys::Element` you
|
||||
can create a `web_sys::Node` from that implicitly:
|
||||
|
||||
```rust
|
||||
let element: &Element = ...;
|
||||
|
||||
element.append_child(..); // call a method on `Node`
|
||||
|
||||
method_expecting_a_node(&element); // coerce to `&Node` implicitly
|
||||
|
||||
let node: &Node = &element; // explicitly coerce to `&Node`
|
||||
```
|
||||
|
||||
Using `Deref` allows ergonomic transitioning up the inheritance hierarchy to the
|
||||
parent class and beyond, giving you access to all the methods using the `.`
|
||||
operator.
|
||||
|
||||
### Accessing parent classes using `AsRef`
|
||||
|
||||
In addition to `Deref`, the `AsRef` trait is implemented for all types in
|
||||
`web_sys` for all types in the inheritance hierarchy. For example for the
|
||||
`HtmlAnchorElement` type you'll find:
|
||||
|
||||
```rust
|
||||
impl AsRef<HtmlElement> for HtmlAnchorElement
|
||||
impl AsRef<Element> for HtmlAnchorElement
|
||||
impl AsRef<Node> for HtmlAnchorElement
|
||||
impl AsRef<EventTarget> for HtmlAnchorElement
|
||||
impl AsRef<Object> for HtmlAnchorElement
|
||||
impl AsRef<JsValue> for HtmlAnchorElement
|
||||
```
|
||||
|
||||
You can use `.as_ref()` to explicitly get a reference to any parent class from
|
||||
from a type in `web_sys`. Note that because of the number of `AsRef`
|
||||
implementations you'll likely need to have type inference guidance as well.
|
||||
|
||||
### Accessing child clases using `JsCast`
|
||||
|
||||
Finally the `wasm_bindgen::JsCast` trait can be used to implement all manner of
|
||||
casts between types. It supports static unchecked casts between types as well as
|
||||
dynamic runtime-checked casts (using `instanceof`) between types.
|
||||
|
||||
More documentation about this can be found [on the trait itself][jscast]
|
||||
|
||||
[jscast]: https://docs.rs/wasm-bindgen/0.2/wasm_bindgen/trait.JsCast.html
|
Loading…
x
Reference in New Issue
Block a user