Skip to content

Commit

Permalink
[WIP] Generic script crate
Browse files Browse the repository at this point in the history
Separation is based on adding types to TypeHolderTrait which contains
associated types with traits that script crate actually needs. This allows
using static methods and Sized types.

Affects servo in two ways:
- generic structs does not compile in script crate, but are left for later monomorphization
- dom_structs itself are moved to separate crate

TODO:
- [] tests
- [] performance
- [] polyshing

Fixes servo#1799

r? @jdm
  • Loading branch information
hrvolapeter committed Aug 9, 2018
1 parent 2f14e33 commit 34bff9f
Show file tree
Hide file tree
Showing 368 changed files with 11,280 additions and 9,639 deletions.
55 changes: 47 additions & 8 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion components/dom_struct/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ publish = false
version = "0.0.1"

[dependencies]
quote = "0.6.3"
quote = "0.6.4"
syn = { version = "0.14.2", features = ["full"] }

[lib]
Expand Down
26 changes: 16 additions & 10 deletions components/dom_struct/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,29 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

extern crate proc_macro;

#[macro_use]
extern crate quote;
extern crate syn;
#[macro_use] extern crate quote;

use proc_macro::TokenStream;
use proc_macro::{TokenStream};
use quote::__rt::Span;
use syn::*;

#[proc_macro_attribute]
pub fn dom_struct(args: TokenStream, input: TokenStream) -> TokenStream {
let mut base = None;
let mut crater = None;
if !args.is_empty() {
panic!("#[dom_struct] takes no arguments");
use quote::ToTokens;

let args = args.to_string();
let crate_name = args.trim_matches(&['(', ')', ' '][..]);
crater = Some(syn::Ident::new(crate_name, Span::call_site()));
base = Some(quote! {#[base = #crate_name]});
println!("{}", base.clone().unwrap());
}
let attributes = quote! {
#[derive(DenyPublicFields, DomObject, JSTraceable, MallocSizeOf)]
#[derive(DomObject, DenyPublicFields, JSTraceable, MallocSizeOf)]
#base
#[must_root]
#[repr(C)]
};
Expand All @@ -32,19 +40,17 @@ pub fn dom_struct(args: TokenStream, input: TokenStream) -> TokenStream {

if let Item::Struct(s) = item {
let s2 = s.clone();
if s.generics.params.len() > 0 {
return quote!(#s2).into();
}
if let Fields::Named(ref f) = s.fields {
let f = f.named.first().expect("Must have at least one field").into_value();
let ident = f.ident.as_ref().expect("Must have named fields");
let name = &s.ident;
let (impl_generics, ty_generics, _) = &s.generics.split_for_impl();
let ty = &f.ty;

quote! (
#s2

impl ::dom::bindings::inheritance::HasParent for #name {
impl#impl_generics #crater::dom::bindings::inheritance::HasParent for #name#ty_generics {
type Parent = #ty;
/// This is used in a type assertion to ensure that
/// the source and webidls agree as to what the parent type is
Expand Down
82 changes: 65 additions & 17 deletions components/domobject_derive/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,36 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#![recursion_limit = "128"]
#![feature(log_syntax)]

extern crate proc_macro;
#[macro_use] extern crate quote;
#[macro_use] extern crate syn;

#[proc_macro_derive(DomObject)]
use quote::ToTokens;

#[proc_macro_derive(DomObject, attributes(base))]
pub fn expand_token_stream(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = syn::parse(input).unwrap();
expand_dom_object(input).into()
}

fn expand_dom_object(input: syn::DeriveInput) -> quote::Tokens {
let attrs = input.attrs.iter().filter(|attr| match attr.interpret_meta().unwrap() {
syn::Meta::NameValue(syn::MetaNameValue { ref ident, .. }) if ident == "base" => {
true
}
_ => false,
}).collect::<Vec<_>>();
let mut base = quote::Tokens::new();
let mut th = quote!{TH};
if attrs.len() > 0 {
if let syn::Meta::NameValue(syn::MetaNameValue { lit: syn::Lit::Str(st), .. }) = attrs[0].interpret_meta().unwrap() {
syn::Ident::from(&st.value()[..]).to_tokens(&mut base);
th = quote!{TypeHolder};
}
}

let fields = if let syn::Data::Struct(syn::DataStruct { ref fields, .. }) = input.data {
fields.iter().collect::<Vec<&syn::Field>>()
} else {
Expand All @@ -41,53 +59,83 @@ fn expand_dom_object(input: syn::DeriveInput) -> quote::Tokens {
unsafe fn to_jsval(&self,
cx: *mut ::js::jsapi::JSContext,
rval: ::js::rust::MutableHandleValue) {
let object = ::dom::bindings::reflector::DomObject::reflector(self).get_jsobject();
let object = #base::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 {
impl #impl_generics #base::dom::bindings::reflector::DomObject for #name #ty_generics #where_clause {
type TypeHolder = #th;

#[inline]
fn reflector(&self) -> &::dom::bindings::reflector::Reflector {
fn reflector(&self) -> &#base::dom::bindings::reflector::Reflector<Self::TypeHolder> {
self.#first_field_name.reflector()
}
}

impl #impl_generics ::dom::bindings::reflector::MutDomObject for #name #ty_generics #where_clause {
impl #impl_generics #base::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);
}
}
};
if name == "IterableIterator" {
items = 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::rust::MutableHandleValue) {
let object = #base::dom::bindings::reflector::DomObject::reflector(self).get_jsobject();
object.to_jsval(cx, rval)
}
}

impl #impl_generics #base::dom::bindings::reflector::DomObject for #name #ty_generics #where_clause {
type TypeHolder = T::TypeHolder;

#[inline]
fn reflector(&self) -> &#base::dom::bindings::reflector::Reflector<Self::TypeHolder> {
self.#first_field_name.reflector()
}
}

impl #impl_generics #base::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);
}
}
};
}

let mut params = quote::Tokens::new();
params.append_separated(input.generics.type_params().map(|param| param.ident), ", ");
params.append_separated(input.generics.type_params().map(|param| param.ident), quote!(,));


// 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 {}
}
}));
// items.append_all(field_types.iter().map(|ty| {
// quote! {
// impl #impl_generics ShouldNotImplDomObject for ((#params), #ty) #where_clause {}
// }
// }));

let mut generics = input.generics.clone();
generics.params.push(parse_quote!(__T: ::dom::bindings::reflector::DomObject));
generics.params.push(parse_quote!(__T: #base::dom::bindings::reflector::DomObject));

let (impl_generics, _, where_clause) = generics.split_for_impl();

items.append_all(quote! {
trait ShouldNotImplDomObject {}
impl #impl_generics ShouldNotImplDomObject for ((#params), __T) #where_clause {}
});
// items.append_all(quote! {
// trait ShouldNotImplDomObject {}
// impl #impl_generics ShouldNotImplDomObject for ((#params), __T) #where_clause {}
// });

let dummy_const = syn::Ident::from(format!("_IMPL_DOMOBJECT_FOR_{}", name));
let tokens = quote! {
#[allow(non_upper_case_globals)]
const #dummy_const: () = { #items };
};

tokens
}
Loading

0 comments on commit 34bff9f

Please sign in to comment.