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:
Alex Crichton
2018-11-08 10:58:55 -08:00
parent d646b29bb7
commit 5b76a6291e
10 changed files with 146 additions and 81 deletions

View File

@@ -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 {

View File

@@ -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);
}

View File

@@ -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);
}
}
}