From c84cea995bd5d1596eaffe041b479ba1081a50fe Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Tue, 14 Feb 2017 14:13:43 +0100 Subject: [PATCH] Derive DomObject with a proc macro --- Cargo.lock | 9 +++ components/domobject_derive/Cargo.toml | 14 ++++ components/domobject_derive/lib.rs | 53 +++++++++++++ components/plugins/jstraceable.rs | 4 +- components/plugins/lib.rs | 6 -- components/plugins/reflector.rs | 82 --------------------- components/script/Cargo.toml | 1 + components/script/dom/bindings/reflector.rs | 12 +++ components/script/lib.rs | 2 + 9 files changed, 92 insertions(+), 91 deletions(-) create mode 100644 components/domobject_derive/Cargo.toml create mode 100644 components/domobject_derive/lib.rs delete mode 100644 components/plugins/reflector.rs diff --git a/Cargo.lock b/Cargo.lock index d4bd99511e9..35473330695 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -650,6 +650,14 @@ dependencies = [ "time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "domobject_derive" +version = "0.0.1" +dependencies = [ + "quote 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.10.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "dtoa" version = "0.2.2" @@ -2255,6 +2263,7 @@ dependencies = [ "cookie 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "cssparser 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", "devtools_traits 0.0.1", + "domobject_derive 0.0.1", "encoding 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/components/domobject_derive/Cargo.toml b/components/domobject_derive/Cargo.toml new file mode 100644 index 00000000000..0fc9b7f2124 --- /dev/null +++ b/components/domobject_derive/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "domobject_derive" +version = "0.0.1" +authors = ["The Servo Project Developers"] +license = "MPL-2.0" +publish = false + +[lib] +path = "lib.rs" +proc-macro = true + +[dependencies] +syn = "0.10" +quote = "0.3" diff --git a/components/domobject_derive/lib.rs b/components/domobject_derive/lib.rs new file mode 100644 index 00000000000..037ae597e8b --- /dev/null +++ b/components/domobject_derive/lib.rs @@ -0,0 +1,53 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +extern crate proc_macro; +#[macro_use] extern crate quote; +extern crate syn; + +#[proc_macro_derive(DomObject)] +pub fn expand_token_stream(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + expand_string(&input.to_string()).parse().unwrap() +} + +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() + } else { + panic!("#[derive(DomObject)] should only be applied on proper structs") + }; + + let name = &type_.ident; + let (impl_generics, ty_generics, where_clause) = type_.generics.split_for_impl(); + + let tokens = quote! { + impl #impl_generics ::js::conversions::ToJSValConvertible for #name #ty_generics #where_clause { + #[allow(unsafe_code)] + unsafe fn to_jsval(&self, + cx: *mut ::js::jsapi::JSContext, + rval: ::js::jsapi::MutableHandleValue) { + let object = ::dom::bindings::reflector::DomObject::reflector(self).get_jsobject(); + object.to_jsval(cx, rval) + } + } + + impl #impl_generics ::dom::bindings::reflector::DomObject for #name #ty_generics #where_clause { + #[inline] + fn reflector(&self) -> &::dom::bindings::reflector::Reflector { + self.#first_field_name.reflector() + } + } + + impl #impl_generics ::dom::bindings::reflector::MutDomObject for #name #ty_generics #where_clause { + fn init_reflector(&mut self, obj: *mut ::js::jsapi::JSObject) { + self.#first_field_name.init_reflector(obj); + } + } + }; + + tokens.to_string() +} diff --git a/components/plugins/jstraceable.rs b/components/plugins/jstraceable.rs index 233f90b3bb7..498f4006010 100644 --- a/components/plugins/jstraceable.rs +++ b/components/plugins/jstraceable.rs @@ -15,9 +15,7 @@ pub fn expand_dom_struct(cx: &mut ExtCtxt, sp: Span, _: &MetaItem, anno: Annotat item2.attrs.push(quote_attr!(cx, #[repr(C)])); item2.attrs.push(quote_attr!(cx, #[derive(JSTraceable)])); item2.attrs.push(quote_attr!(cx, #[derive(HeapSizeOf)])); - - // The following attributes are only for internal usage - item2.attrs.push(quote_attr!(cx, #[_generate_reflector])); + item2.attrs.push(quote_attr!(cx, #[derive(DomObject)])); // #[dom_struct] gets consumed, so this lets us keep around a residue // Do NOT register a modifier/decorator on this attribute item2.attrs.push(quote_attr!(cx, #[_dom_struct_marker])); diff --git a/components/plugins/lib.rs b/components/plugins/lib.rs index a8efe9ab1fb..eca3feef5f1 100644 --- a/components/plugins/lib.rs +++ b/components/plugins/lib.rs @@ -34,8 +34,6 @@ use syntax::symbol::Symbol; /// Handles the auto-deriving for `#[derive(JSTraceable)]` pub mod jstraceable; pub mod lints; -/// Autogenerates implementations of DomObject on DOM structs -pub mod reflector; /// Utilities for writing plugins mod utils; @@ -45,10 +43,6 @@ pub fn plugin_registrar(reg: &mut Registry) { Symbol::intern("dom_struct"), MultiModifier(box jstraceable::expand_dom_struct)); - reg.register_syntax_extension( - Symbol::intern("_generate_reflector"), - MultiDecorator(box reflector::expand_reflector)); - reg.register_late_lint_pass(box lints::unrooted_must_root::UnrootedPass::new()); reg.register_late_lint_pass(box lints::privatize::PrivatizePass); reg.register_late_lint_pass(box lints::inheritance_integrity::InheritancePass); diff --git a/components/plugins/reflector.rs b/components/plugins/reflector.rs deleted file mode 100644 index 768e65e59eb..00000000000 --- a/components/plugins/reflector.rs +++ /dev/null @@ -1,82 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -use syntax::ast::{ItemKind, MetaItem}; -use syntax::codemap::Span; -use syntax::ext::base::{Annotatable, ExtCtxt}; -use utils::match_ty_unwrap; - - -pub fn expand_reflector(cx: &mut ExtCtxt, span: Span, _: &MetaItem, annotatable: &Annotatable, - push: &mut FnMut(Annotatable)) { - if let &Annotatable::Item(ref item) = annotatable { - if let ItemKind::Struct(ref def, _) = item.node { - if def.fields().is_empty() { - cx.span_err(span, "#[dom_struct] should have a reflector or \ - parent dom struct as its first field"); - return; - } - let struct_name = item.ident; - // This path has to be hardcoded, unfortunately, since we can't resolve paths at expansion time - match def.fields().iter().find( - |f| match_ty_unwrap(&*f.ty, &["dom", "bindings", "reflector", "Reflector"]).is_some()) { - // If it has a field that is a Reflector, use that - Some(f) => { - let field_name = f.ident; - let impl_item = quote_item!(cx, - impl ::dom::bindings::reflector::DomObject for $struct_name { - fn reflector<'a>(&'a self) -> &'a ::dom::bindings::reflector::Reflector { - &self.$field_name - } - } - ); - let impl_item_mut = quote_item!(cx, - impl ::dom::bindings::reflector::MutDomObject for $struct_name { - fn init_reflector(&mut self, obj: *mut ::js::jsapi::JSObject) { - self.$field_name.set_jsobject(obj); - } - } - ); - impl_item.map(|it| push(Annotatable::Item(it))); - impl_item_mut.map(|it| push(Annotatable::Item(it))) - }, - // Or just call it on the first field (supertype). - None => { - let field_name = def.fields()[0].ident; - let impl_item = quote_item!(cx, - impl ::dom::bindings::reflector::DomObject for $struct_name { - fn reflector<'a>(&'a self) -> &'a ::dom::bindings::reflector::Reflector { - self.$field_name.reflector() - } - } - ); - let impl_item_mut = quote_item!(cx, - impl ::dom::bindings::reflector::MutDomObject for $struct_name { - fn init_reflector(&mut self, obj: *mut ::js::jsapi::JSObject) { - self.$field_name.init_reflector(obj); - } - } - ); - impl_item.map(|it| push(Annotatable::Item(it))); - impl_item_mut.map(|it| push(Annotatable::Item(it))) - } - }; - - let impl_item = quote_item!(cx, - impl ::js::conversions::ToJSValConvertible for $struct_name { - #[allow(unsafe_code)] - unsafe fn to_jsval(&self, - cx: *mut ::js::jsapi::JSContext, - rval: ::js::jsapi::MutableHandleValue) { - let object = ::dom::bindings::reflector::DomObject::reflector(self).get_jsobject(); - object.to_jsval(cx, rval) - } - } - ); - impl_item.map(|it| push(Annotatable::Item(it))); - } else { - cx.span_err(span, "#[dom_struct] seems to have been applied to a non-struct"); - } - } -} diff --git a/components/script/Cargo.toml b/components/script/Cargo.toml index 57ea9b4b648..6e77113094b 100644 --- a/components/script/Cargo.toml +++ b/components/script/Cargo.toml @@ -36,6 +36,7 @@ caseless = "0.1.0" cookie = {version = "0.2.5", features = ["serialize-rustc"]} cssparser = {version = "0.7", features = ["heap_size", "serde-serialization"]} devtools_traits = {path = "../devtools_traits"} +domobject_derive = {path = "../domobject_derive"} encoding = "0.2" euclid = "0.10.1" fnv = "1.0" diff --git a/components/script/dom/bindings/reflector.rs b/components/script/dom/bindings/reflector.rs index 4f342070707..40d894ccd00 100644 --- a/components/script/dom/bindings/reflector.rs +++ b/components/script/dom/bindings/reflector.rs @@ -83,8 +83,20 @@ pub trait DomObject { } } +impl DomObject for Reflector { + fn reflector(&self) -> &Self { + self + } +} + /// A trait to initialize the `Reflector` for a DOM object. pub trait MutDomObject: DomObject { /// Initializes the Reflector fn init_reflector(&mut self, obj: *mut JSObject); } + +impl MutDomObject for Reflector { + fn init_reflector(&mut self, obj: *mut JSObject) { + self.set_jsobject(obj) + } +} diff --git a/components/script/lib.rs b/components/script/lib.rs index 7126d53653e..3242e0ccf01 100644 --- a/components/script/lib.rs +++ b/components/script/lib.rs @@ -39,6 +39,8 @@ extern crate core; #[macro_use] extern crate cssparser; extern crate devtools_traits; +#[macro_use] +extern crate domobject_derive; extern crate encoding; extern crate euclid; extern crate fnv;