mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-06-12 12:31:22 +00:00
Implement #[wasm_bindgen(extends = ...)]
This commit implements the `extends` attribute for `#[wasm_bindgen]` to statically draw the inheritance hierarchy in the generated bindings, generating appropriate `AsRef`, `AsMut`, and `From` implementations.
This commit is contained in:
@ -128,6 +128,7 @@ pub struct ImportType {
|
||||
pub attrs: Vec<syn::Attribute>,
|
||||
pub doc_comment: Option<String>,
|
||||
pub instanceof_shim: String,
|
||||
pub extends: Vec<Ident>,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
|
||||
|
@ -656,6 +656,31 @@ impl ToTokens for ast::ImportType {
|
||||
()
|
||||
};
|
||||
}).to_tokens(tokens);
|
||||
|
||||
for superclass in self.extends.iter() {
|
||||
(quote! {
|
||||
impl From<#name> for #superclass {
|
||||
fn from(obj: #name) -> #superclass {
|
||||
use wasm_bindgen::JsCast;
|
||||
#superclass::unchecked_from_js(obj.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<#superclass> for #name {
|
||||
fn as_ref(&self) -> &#superclass {
|
||||
use wasm_bindgen::JsCast;
|
||||
#superclass::unchecked_from_js_ref(self.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl AsMut<#superclass> for #name {
|
||||
fn as_mut(&mut self) -> &mut #superclass {
|
||||
use wasm_bindgen::JsCast;
|
||||
#superclass::unchecked_from_js_mut(self.as_mut())
|
||||
}
|
||||
}
|
||||
}).to_tokens(tokens);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -182,6 +182,16 @@ impl BindgenAttrs {
|
||||
})
|
||||
.next()
|
||||
}
|
||||
|
||||
/// Return the list of classes that a type extends
|
||||
fn extends(&self) -> impl Iterator<Item = &Ident> {
|
||||
self.attrs
|
||||
.iter()
|
||||
.filter_map(|a| match a {
|
||||
BindgenAttr::Extends(s) => Some(s),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl syn::synom::Synom for BindgenAttrs {
|
||||
@ -217,6 +227,7 @@ pub enum BindgenAttr {
|
||||
Readonly,
|
||||
JsName(String),
|
||||
JsClass(String),
|
||||
Extends(Ident),
|
||||
}
|
||||
|
||||
impl syn::synom::Synom for BindgenAttr {
|
||||
@ -295,6 +306,13 @@ impl syn::synom::Synom for BindgenAttr {
|
||||
s: syn!(syn::LitStr) >>
|
||||
(s.value())
|
||||
)=> { BindgenAttr::JsClass }
|
||||
|
|
||||
do_parse!(
|
||||
call!(term, "extends") >>
|
||||
punct!(=) >>
|
||||
ns: call!(term2ident) >>
|
||||
(ns)
|
||||
)=> { BindgenAttr::Extends }
|
||||
));
|
||||
}
|
||||
|
||||
@ -520,10 +538,10 @@ impl<'a> ConvertToAst<(BindgenAttrs, &'a Option<String>)> for syn::ForeignItemFn
|
||||
}
|
||||
}
|
||||
|
||||
impl ConvertToAst<()> for syn::ForeignItemType {
|
||||
impl ConvertToAst<BindgenAttrs> for syn::ForeignItemType {
|
||||
type Target = ast::ImportKind;
|
||||
|
||||
fn convert(self, (): ()) -> Result<Self::Target, Diagnostic> {
|
||||
fn convert(self, attrs: BindgenAttrs) -> Result<Self::Target, Diagnostic> {
|
||||
let shim = format!("__wbg_instanceof_{}_{}", self.ident, ShortHash(&self.ident));
|
||||
Ok(ast::ImportKind::Type(ast::ImportType {
|
||||
vis: self.vis,
|
||||
@ -531,6 +549,7 @@ impl ConvertToAst<()> for syn::ForeignItemType {
|
||||
doc_comment: None,
|
||||
instanceof_shim: shim,
|
||||
name: self.ident,
|
||||
extends: attrs.extends().cloned().collect(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
@ -937,7 +956,7 @@ impl<'a> MacroParse<&'a BindgenAttrs> for syn::ForeignItem {
|
||||
let js_namespace = item_opts.js_namespace().or(opts.js_namespace()).cloned();
|
||||
let kind = match self {
|
||||
syn::ForeignItem::Fn(f) => f.convert((item_opts, &module))?,
|
||||
syn::ForeignItem::Type(t) => t.convert(())?,
|
||||
syn::ForeignItem::Type(t) => t.convert(item_opts)?,
|
||||
syn::ForeignItem::Static(s) => s.convert(item_opts)?,
|
||||
_ => panic!("only foreign functions/types allowed for now"),
|
||||
};
|
||||
|
@ -17,6 +17,7 @@ use weedle;
|
||||
|
||||
use super::Result;
|
||||
use util;
|
||||
use util::camel_case_ident;
|
||||
|
||||
/// Collection of constructs that may use partial.
|
||||
#[derive(Default)]
|
||||
@ -36,6 +37,7 @@ pub(crate) struct InterfaceData<'src> {
|
||||
pub(crate) partial: bool,
|
||||
pub(crate) global: bool,
|
||||
pub(crate) operations: BTreeMap<OperationId<'src>, OperationData<'src>>,
|
||||
pub(crate) superclass: Option<&'src str>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord)]
|
||||
@ -146,6 +148,7 @@ impl<'src> FirstPass<'src, ()> for weedle::InterfaceDefinition<'src> {
|
||||
.entry(self.identifier.0)
|
||||
.or_insert_with(Default::default);
|
||||
interface.partial = false;
|
||||
interface.superclass = self.inheritance.map(|s| s.identifier.0);
|
||||
}
|
||||
|
||||
if util::is_chrome_only(&self.attributes) {
|
||||
@ -176,6 +179,7 @@ impl<'src> FirstPass<'src, ()> for weedle::PartialInterfaceDefinition<'src> {
|
||||
partial: true,
|
||||
operations: Default::default(),
|
||||
global: false,
|
||||
superclass: None,
|
||||
},
|
||||
);
|
||||
|
||||
@ -307,3 +311,27 @@ impl<'src> FirstPass<'src, ()> for weedle::TypedefDefinition<'src> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
fn fill_superclasses(&self, interface: &str, set: &mut BTreeSet<String>) {
|
||||
let data = match self.interfaces.get(interface) {
|
||||
Some(data) => data,
|
||||
None => return,
|
||||
};
|
||||
let superclass = match &data.superclass {
|
||||
Some(class) => class,
|
||||
None => return,
|
||||
};
|
||||
if set.insert(camel_case_ident(superclass)) {
|
||||
self.fill_superclasses(superclass, set);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ use backend::defined::{ImportedTypeDefinitions, RemoveUndefinedImports};
|
||||
use backend::util::{ident_ty, rust_ident, wrap_import_function};
|
||||
use failure::ResultExt;
|
||||
use heck::{ShoutySnakeCase};
|
||||
use proc_macro2::{Ident, Span};
|
||||
use weedle::argument::Argument;
|
||||
use weedle::attribute::{ExtendedAttribute, ExtendedAttributeList};
|
||||
|
||||
@ -246,7 +247,10 @@ impl<'src> WebidlParse<'src, ()> for weedle::InterfaceDefinition<'src> {
|
||||
name: rust_ident(camel_case_ident(self.identifier.0).as_str()),
|
||||
attrs: Vec::new(),
|
||||
doc_comment,
|
||||
instanceof_shim: format!("__widl_instanceof_{}", self.name),
|
||||
instanceof_shim: format!("__widl_instanceof_{}", self.identifier.0),
|
||||
extends: first_pass.all_superclasses(self.identifier.0)
|
||||
.map(|name| Ident::new(&name, Span::call_site()))
|
||||
.collect(),
|
||||
}),
|
||||
});
|
||||
|
||||
|
Reference in New Issue
Block a user