extern crate proc_macro;
extern crate syn;
extern crate synstructure;
#[macro_use]
extern crate quote;
use proc_macro::TokenStream;
use std::iter;
#[proc_macro_derive(JSLifetime)]
pub fn derive_js_rootable(input: TokenStream) -> TokenStream {
let s = input.to_string();
let ast = syn::parse_derive_input(&s).unwrap();
let gen = impl_js_rootable(&ast);
gen.parse().unwrap()
}
fn impl_js_rootable(ast: &syn::DeriveInput) -> quote::Tokens {
let name = &ast.ident;
let (_, ty_generics, _) = ast.generics.split_for_impl();
let impl_generics = ast.generics.ty_params.iter().map(|ty| quote! { #ty });
let impl_generics = quote! { #(#impl_generics),* };
let lifetime_constraints = ast.generics.ty_params.iter().map(|ty| {
let ident = &ty.ident;
quote! { #ident: 'b }
});
let where_clause_predicates = ast.generics.where_clause.predicates.iter().map(|pred| quote! { #pred });
let where_clause_items = lifetime_constraints.chain(where_clause_predicates).collect::<Vec<_>>();
let where_clause = if where_clause_items.is_empty() {
quote! { }
} else {
quote! { where #(#where_clause_items),* }
};
if ast.generics.lifetimes.is_empty() {
return quote! {
#[allow(unsafe_code)]
unsafe impl<'a, #impl_generics> ::josephine::JSLifetime<'a> for #name #ty_generics #where_clause {
type Aged = #name #ty_generics;
}
}
}
assert!(ast.generics.lifetimes.len() == 1, "deriving JSLifetime requires a single lifetime");
let impl_lifetime = &ast.generics.lifetimes[0].lifetime.ident;
assert!(impl_lifetime != "'b", "deriving JSLifetime requires the lifetime to not be named 'b");
let aged_ty_params = ast.generics.ty_params.iter().map(|ty| {
let ident = &ty.ident;
quote! { #ident }
});
let aged_ty_params = quote! { #(#aged_ty_params),* };
quote! {
#[allow(unsafe_code)]
unsafe impl<#impl_lifetime, 'b, #impl_generics> ::josephine::JSLifetime<'b> for #name #ty_generics #where_clause {
type Aged = #name<'b, #aged_ty_params>;
}
}
}
#[proc_macro_derive(JSCompartmental)]
pub fn derive_js_transplantable(input: TokenStream) -> TokenStream {
let s = input.to_string();
let ast = syn::parse_derive_input(&s).unwrap();
let gen = impl_js_transplantable(&ast);
gen.parse().unwrap()
}
fn impl_js_transplantable(ast: &syn::DeriveInput) -> quote::Tokens {
let name = &ast.ident;
let (_, ty_generics, where_clause) = ast.generics.split_for_impl();
let ref lifetimes_and_d: Vec<_> = ast.generics.lifetimes.iter().map(|lifetime| lifetime.lifetime.ident.clone())
.chain(iter::once(syn::Ident::from("D")))
.collect::<Vec<_>>();
if ast.generics.ty_params.is_empty() {
let style = synstructure::BindStyle::Ref.into();
let match_body = synstructure::each_field(&ast, &style, |binding| {
let ty = &binding.field.ty;
quote! {
if !<#ty as ::josephine::JSCompartmental<C, D>>::is_in_compartment(#binding, cx) { return false; }
}
});
return quote! {
#[allow(unsafe_code)]
unsafe impl<#(#lifetimes_and_d),*, C> ::josephine::JSCompartmental<C, D> for #name #ty_generics #where_clause {
type ChangeCompartment = #name #ty_generics;
fn is_in_compartment<S>(&self, cx: &::josephine::JSContext<S>) -> bool where
S: InCompartment<D>
{
match *self {
#match_body
}
true
}
}
}
}
assert!(ast.generics.ty_params.len() == 1, "deriving JSCompartmental requires a single type parameter");
let impl_ty_param = &ast.generics.ty_params[0].ident;
assert!(impl_ty_param != "D", "deriving JSCompartmental requires the type parameter to not be named D");
let style = synstructure::BindStyle::Ref.into();
let match_body = synstructure::each_field(&ast, &style, |binding| {
let ty = &binding.field.ty;
quote! {
if !<#ty as ::josephine::JSCompartmental<#impl_ty_param, D>>::is_in_compartment(#binding, cx) { return false; }
}
});
quote! {
#[allow(unsafe_code)]
unsafe impl<#(#lifetimes_and_d),*, #impl_ty_param> ::josephine::JSCompartmental<#impl_ty_param, D> for #name #ty_generics #where_clause {
type ChangeCompartment = #name<#(#lifetimes_and_d),*>;
fn is_in_compartment<S>(&self, cx: &::josephine::JSContext<S>) -> bool where
S: InCompartment<D>
{
match *self {
#match_body
}
true
}
}
}
}
#[proc_macro_derive(JSInitializable)]
pub fn derive_js_initializable(input: TokenStream) -> TokenStream {
let s = input.to_string();
let ast = syn::parse_derive_input(&s).unwrap();
let gen = impl_js_initializable(&ast);
gen.parse().unwrap()
}
fn impl_js_initializable(ast: &syn::DeriveInput) -> quote::Tokens {
let name = &ast.ident;
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
quote! {
impl #impl_generics ::josephine::JSInitializable for #name #ty_generics #where_clause {}
}
}
#[proc_macro_derive(JSTraceable)]
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 mut type_ = syn::parse_macro_input(input).unwrap();
let style = synstructure::BindStyle::Ref.into();
let match_body = synstructure::each_field(&mut type_, &style, |binding| {
Some(quote! { #binding.trace(tracer); })
});
let name = &type_.ident;
let (impl_generics, ty_generics, where_clause) = type_.generics.split_for_impl();
let mut where_clause = where_clause.clone();
for param in type_.generics.ty_params.iter().skip(1) {
where_clause.predicates.push(syn::WherePredicate::BoundPredicate(syn::WhereBoundPredicate {
bound_lifetimes: Vec::new(),
bounded_ty: syn::Ty::Path(None, param.ident.clone().into()),
bounds: vec![syn::TyParamBound::Trait(
syn::PolyTraitRef {
bound_lifetimes: Vec::new(),
trait_ref: syn::parse_path("::josephine::JSTraceable").unwrap(),
},
syn::TraitBoundModifier::None
)],
}))
}
let tokens = quote! {
#[allow(unsafe_code)]
unsafe impl #impl_generics ::josephine::JSTraceable for #name #ty_generics #where_clause {
#[inline]
#[allow(unused_variables, unused_imports)]
unsafe fn trace(&self, tracer: *mut ::josephine::trace::JSTracer) {
use ::josephine::JSTraceable;
match *self {
#match_body
}
}
}
};
tokens.to_string()
}