aboutsummaryrefslogtreecommitdiffstats
path: root/components/domobject_derive/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'components/domobject_derive/lib.rs')
-rw-r--r--components/domobject_derive/lib.rs59
1 files changed, 55 insertions, 4 deletions
diff --git a/components/domobject_derive/lib.rs b/components/domobject_derive/lib.rs
index 037ae597e8b..c718b514fc5 100644
--- a/components/domobject_derive/lib.rs
+++ b/components/domobject_derive/lib.rs
@@ -14,17 +14,27 @@ pub fn expand_token_stream(input: proc_macro::TokenStream) -> proc_macro::TokenS
fn expand_string(input: &str) -> String {
let type_ = syn::parse_macro_input(input).unwrap();
- let first_field_name = if let syn::Body::Struct(syn::VariantData::Struct(ref fields)) = type_.body {
- let first_field = fields.first().expect("#[derive(DomObject)] should not be applied on empty structs");
- first_field.ident.as_ref().unwrap()
+ let fields = if let syn::Body::Struct(syn::VariantData::Struct(ref fields)) = type_.body {
+ fields
} else {
panic!("#[derive(DomObject)] should only be applied on proper structs")
};
+ let (first_field, fields) = fields
+ .split_first()
+ .expect("#[derive(DomObject)] should not be applied on empty structs");
+ let first_field_name = first_field.ident.as_ref().unwrap();
+ let mut field_types = vec![];
+ for field in fields {
+ if !field_types.contains(&&field.ty) {
+ field_types.push(&field.ty);
+ }
+ }
+
let name = &type_.ident;
let (impl_generics, ty_generics, where_clause) = type_.generics.split_for_impl();
- let tokens = quote! {
+ let mut items = quote! {
impl #impl_generics ::js::conversions::ToJSValConvertible for #name #ty_generics #where_clause {
#[allow(unsafe_code)]
unsafe fn to_jsval(&self,
@@ -49,5 +59,46 @@ fn expand_string(input: &str) -> String {
}
};
+ let mut params = quote::Tokens::new();
+ params.append_separated(type_.generics.ty_params.iter().map(|param| &param.ident), ", ");
+
+ // For each field in the struct, we implement ShouldNotImplDomObject for a
+ // pair of all the type parameters of the DomObject and and the field type.
+ // This allows us to support parameterized DOM objects
+ // such as IteratorIterable<T>.
+ items.append_all(field_types.iter().map(|ty| {
+ quote! {
+ impl #impl_generics ShouldNotImplDomObject for ((#params), #ty) #where_clause {}
+ }
+ }));
+
+ let bound = syn::TyParamBound::Trait(
+ syn::PolyTraitRef {
+ bound_lifetimes: vec![],
+ trait_ref: syn::parse_path("::dom::bindings::reflector::DomObject").unwrap(),
+ },
+ syn::TraitBoundModifier::None
+ );
+
+ let mut generics = type_.generics.clone();
+ generics.ty_params.push(syn::TyParam {
+ attrs: vec![],
+ ident: "__T".into(),
+ bounds: vec![bound],
+ default: None,
+ });
+ let (impl_generics, _, where_clause) = generics.split_for_impl();
+
+ items.append(quote! {
+ trait ShouldNotImplDomObject {}
+ impl #impl_generics ShouldNotImplDomObject for ((#params), __T) #where_clause {}
+ }.as_str());
+
+ let dummy_const = syn::Ident::new(format!("_IMPL_DOMOBJECT_FOR_{}", name));
+ let tokens = quote! {
+ #[allow(non_upper_case_globals)]
+ const #dummy_const: () = { #items };
+ };
+
tokens.to_string()
}