--- a/xpcom/rust/xpcom/xpcom_macros/src/lib.rs
+++ b/xpcom/rust/xpcom/xpcom_macros/src/lib.rs
@@ -126,31 +126,33 @@
// NOTE: We use some really big quote! invocations, so we need a high recursion
// limit.
#![recursion_limit="256"]
#[macro_use]
extern crate quote;
+#[macro_use]
extern crate syn;
extern crate proc_macro;
#[macro_use]
extern crate lazy_static;
use proc_macro::TokenStream;
use quote::{ToTokens, Tokens};
use syn::*;
+use syn::punctuated::Punctuated;
+
use std::collections::{HashMap, HashSet};
-use std::default::Default;
use std::error::Error;
/* These are the structs generated by the rust_macros.py script */
/// A single parameter to an XPCOM method.
#[derive(Debug)]
struct Param {
name: &'static str,
@@ -169,16 +171,28 @@ struct Method {
/// cannot be implemented in rust code.
#[derive(Debug)]
struct Interface {
name: &'static str,
base: Option<&'static str>,
methods: Result<&'static [Method], &'static str>,
}
+impl Interface {
+ fn base(&self) -> Result<Option<&'static Interface>, Box<Error>> {
+ Ok(if let Some(base) = self.base {
+ Some(*IFACES.get(base).ok_or_else(
+ || format!("Base interface {} does not exist", base)
+ )?)
+ } else {
+ None
+ })
+ }
+}
+
lazy_static! {
/// This item contains the information generated by the procedural macro in
/// the form of a `HashMap` from interface names to their descriptions.
static ref IFACES: HashMap<&'static str, &'static Interface> = {
let lists: &[&[Interface]] =
include!(concat!(env!("MOZ_TOPOBJDIR"), "/dist/xpcrs/bt/all.rs"));
let mut hm = HashMap::new();
@@ -206,170 +220,136 @@ impl ToTokens for RefcntKind {
RefcntKind::Atomic => quote!(xpcom::AtomicRefcnt).to_tokens(tokens),
}
}
}
/// Scans through the attributes on a struct, and extracts the type of the refcount to use.
fn get_refcnt_kind(attrs: &[Attribute]) -> Result<RefcntKind, Box<Error>> {
for attr in attrs {
- if let MetaItem::NameValue(ref name, Lit::Str(ref value, _)) = attr.value {
- if name != "refcnt" {
+ if let Some(Meta::NameValue(ref attr)) = attr.interpret_meta() {
+ if attr.ident.as_ref() != "refcnt" {
continue;
}
+ let value = if let Lit::Str(ref s) = attr.lit {
+ s.value()
+ } else {
+ Err("Unexpected non-string value in #[refcnt]")?
+ };
+
return if value == "nonatomic" {
Ok(RefcntKind::NonAtomic)
} else if value == "atomic" {
Ok(RefcntKind::Atomic)
} else {
Err("Unexpected value in #[refcnt]. \
Expected `nonatomic`, or `atomic`")?
};
}
}
- Err("Expected #[refcnt] attribute".into())
+ Err("Expected #[refcnt] attribute")?
}
/// Scan the attributes looking for an #[xpimplements] attribute. The identifier
/// arguments passed to this attribute are the interfaces which the type wants to
/// directly implement.
-fn get_bases(attrs: &[Attribute]) -> Result<Vec<&str>, Box<Error>> {
+fn get_bases(attrs: &[Attribute]) -> Result<Vec<&'static Interface>, Box<Error>> {
let mut inherits = Vec::new();
for attr in attrs {
- if let MetaItem::List(ref name, ref items) = attr.value {
- if name != "xpimplements" {
+ if let Some(Meta::List(ref attr)) = attr.interpret_meta() {
+ if attr.ident.as_ref() != "xpimplements" {
continue;
}
- for item in items {
- if let NestedMetaItem::MetaItem(MetaItem::Word(ref iface)) = *item {
- inherits.push(iface.as_ref());
+ for item in &attr.nested {
+ if let NestedMeta::Meta(Meta::Word(ref iface)) = *item {
+ if let Some(&iface) = IFACES.get(iface.as_ref()) {
+ inherits.push(iface);
+ } else {
+ Err(format!("Unexpected invalid base interface `{}` in \
+ #[xpimplements(..)]", iface))?
+ }
} else {
- return Err("Unexpected non-identifier in xpimplements \
- attribute list".into());
+ Err("Unexpected non-identifier in #[xpimplements(..)]")?
}
}
}
}
Ok(inherits)
}
/// Extract the fields list from the input struct.
-fn get_fields(di: &DeriveInput) -> Result<&[Field], Box<Error>> {
- match di.body {
- Body::Struct(VariantData::Struct(ref fields)) => Ok(fields),
+fn get_fields(di: &DeriveInput) -> Result<&Punctuated<Field, Token![,]>, Box<Error>> {
+ match di.data {
+ Data::Struct(DataStruct {
+ fields: Fields::Named(ref named), ..
+ }) => Ok(&named.named),
_ => Err("The initializer struct must be a standard \
named value struct definition".into())
}
}
-/// Helper function for building a `syn::Ty` from a list of path segment.s
-fn mk_path_ty(segments: &[&str]) -> Ty {
- Ty::Path(None, Path {
- global: true,
- segments: segments.iter().map(|&seg| {
- PathSegment {
- ident: seg.into(),
- parameters: PathParameters::none(),
- }
- }).collect()
- })
-}
-
/// Takes the `Init*` struct in, and generates a `DeriveInput` for the "real" struct.
-fn gen_real_struct(init: &DeriveInput, bases: &[&str], refcnt_ty: RefcntKind) -> Result<DeriveInput, Box<Error>> {
+fn gen_real_struct(init: &DeriveInput, bases: &[&Interface], refcnt_ty: RefcntKind) -> Result<DeriveInput, Box<Error>> {
// Determine the name for the real struct based on the name of the
// initializer struct's name.
if !init.ident.as_ref().starts_with("Init") {
- return Err("The target struct's name must begin with Init".into());
+ Err("The target struct's name must begin with Init")?
}
let name: Ident = init.ident.as_ref()[4..].into();
+ let vis = &init.vis;
- // Add the vtable and refcnt fields to the struct declaration.
- let mut fields = vec![];
- for base in bases {
- fields.push(Field {
- ident: Some(format!("__base_{}", base).into()),
- vis: Visibility::Inherited,
- attrs: vec![],
- ty: Ty::Ptr(
- Box::new(MutTy {
- ty: mk_path_ty(&["xpcom", "interfaces", &format!("{}VTable", base)]),
- mutability: Mutability::Immutable,
- })
- ),
- });
- }
-
- fields.push(Field {
- ident: Some("__refcnt".into()),
- vis: Visibility::Inherited,
- attrs: vec![],
- ty: syn::parse_type(quote!(#refcnt_ty).as_ref())?,
- });
+ let bases = bases.iter().map(|base| {
+ let ident = Ident::from(format!("__base_{}", base.name));
+ let vtable = Ident::from(format!("{}VTable", base.name));
+ quote!(#ident : *const xpcom::interfaces::#vtable)
+ });
- // Add the data fields from the initializer to the struct declaration.
- fields.extend(get_fields(init)?.iter().cloned());
-
- // Create the real struct definition
- Ok(DeriveInput {
- ident: name,
- vis: init.vis.clone(),
- attrs: vec![
- // #[repr(C)]
- Attribute {
- style: AttrStyle::Outer,
- value: MetaItem::List(
- "repr".into(),
- vec![NestedMetaItem::MetaItem(
- MetaItem::Word("C".into())
- )],
- ),
- is_sugared_doc: false,
- }
- ],
- generics: Generics::default(),
- body: Body::Struct(VariantData::Struct(fields)),
- })
+ let fields = get_fields(init)?;
+ Ok(parse_quote! {
+ #[repr(C)]
+ #vis struct #name {
+ #(#bases,)*
+ __refcnt: #refcnt_ty,
+ #fields
+ }
+ })
}
/// Generates the `extern "system"` methods which are actually included in the
/// VTable for the given interface.
///
/// These methods attempt to invoke the `recover_self` method to translate from
/// the passed-in raw pointer to the actual `&self` value, and it is expected to
/// be in scope.
-fn gen_vtable_methods(base: &str) -> Result<Tokens, Box<Error>> {
- let base_ty = Ident::from(base);
+fn gen_vtable_methods(iface: &Interface) -> Result<Tokens, Box<Error>> {
+ let base_ty = Ident::from(iface.name);
- let iface = IFACES.get(base)
- .ok_or(format!("Interface {} does not exist", base))?;
-
- let base_methods = if let Some(base) = iface.base {
+ let base_methods = if let Some(base) = iface.base()? {
gen_vtable_methods(base)?
} else {
quote!{}
};
let methods = iface.methods
.map_err(|reason| format!("Interface {} cannot be implemented in rust \
- because {} is not supported yet", base, reason))?;
+ because {} is not supported yet", iface.name, reason))?;
let mut method_defs = Vec::new();
for method in methods {
let name = Ident::from(method.name);
- let ret = Ident::from(method.ret);
+ let ret = syn::parse_str::<Type>(method.ret)?;
let mut params = Vec::new();
let mut args = Vec::new();
for param in method.params {
let name = Ident::from(param.name);
- let ty = Ident::from(param.ty);
+ let ty = syn::parse_str::<Type>(param.ty)?;
params.push(quote!{#name : #ty,});
args.push(quote!{#name,});
}
method_defs.push(quote!{
unsafe extern "system" fn #name (this: *const #base_ty, #(#params)*) -> #ret {
let lt = ();
@@ -381,28 +361,25 @@ fn gen_vtable_methods(base: &str) -> Res
Ok(quote!{
#base_methods
#(#method_defs)*
})
}
/// Generates the VTable for a given base interface. This assumes that the
/// implementations of each of the `extern "system"` methods are in scope.
-fn gen_inner_vtable(base: &str) -> Result<Tokens, Box<Error>> {
- let vtable_ty = Ident::from(format!("{}VTable", base));
-
- let iface = IFACES.get(base)
- .ok_or(format!("Interface {} does not exist", base))?;
+fn gen_inner_vtable(iface: &Interface) -> Result<Tokens, Box<Error>> {
+ let vtable_ty = Ident::from(format!("{}VTable", iface.name));
let methods = iface.methods
.map_err(|reason| format!("Interface {} cannot be implemented in rust \
- because {} is not supported yet", base, reason))?;
+ because {} is not supported yet", iface.name, reason))?;
// Generate the vtable for the base interface.
- let base_vtable = if let Some(base) = iface.base {
+ let base_vtable = if let Some(base) = iface.base()? {
let vt = gen_inner_vtable(base)?;
quote!{__base: #vt,}
} else {
quote!{}
};
// Include each of the method definitions for this interface.
let vtable_init = methods.into_iter().map(|method| {
@@ -411,19 +388,19 @@ fn gen_inner_vtable(base: &str) -> Resul
}).collect::<Vec<_>>();
Ok(quote!(#vtable_ty {
#base_vtable
#(#vtable_init)*
}))
}
-fn gen_root_vtable(name: &Ident, base: &str) -> Result<Tokens, Box<Error>> {
- let field = Ident::from(format!("__base_{}", base));
- let vtable_ty = Ident::from(format!("{}VTable", base));
+fn gen_root_vtable(name: &Ident, base: &Interface) -> Result<Tokens, Box<Error>> {
+ let field = Ident::from(format!("__base_{}", base.name));
+ let vtable_ty = Ident::from(format!("{}VTable", base.name));
let methods = gen_vtable_methods(base)?;
let value = gen_inner_vtable(base)?;
// Define the `recover_self` method. This performs an offset calculation to
// recover a pointer to the original struct from a pointer to the given
// VTable field.
Ok(quote!{#field: {
// NOTE: The &'a () dummy lifetime parameter is useful as it easily
@@ -453,41 +430,41 @@ fn gen_root_vtable(name: &Ident, base: &
},})
}
/// Generate the cast implementations. This generates the implementation details
/// for the `Coerce` trait, and the `QueryInterface` method. The first return
/// value is the `QueryInterface` implementation, and the second is the `Coerce`
/// implementation.
fn gen_casts(
- seen: &mut HashSet<String>,
- base: &str,
+ seen: &mut HashSet<&'static str>,
+ iface: &Interface,
name: &Ident,
coerce_name: &Ident,
vtable_field: &Ident,
) -> Result<(Tokens, Tokens), Box<Error>> {
- if !seen.insert(base.to_owned()) {
+ if !seen.insert(iface.name) {
return Ok((quote!{}, quote!{}));
}
// Generate the cast implementations for the base interfaces.
- let (base_qi, base_coerce) = if let Some(base) = IFACES[base].base {
+ let (base_qi, base_coerce) = if let Some(base) = iface.base()? {
gen_casts(
seen,
base,
name,
coerce_name,
vtable_field,
)?
} else {
(quote!{}, quote!{})
};
// Add the if statment to QueryInterface for the base class.
- let base_name = Ident::from(base);
+ let base_name = Ident::from(iface.name);
let qi = quote! {
#base_qi
if *uuid == #base_name::IID {
// Implement QueryInterface in terms of coersions.
self.addref();
*result = self.coerce::<#base_name>()
as *const #base_name
@@ -514,19 +491,18 @@ fn gen_casts(
}
}
};
Ok((qi, coerce))
}
/// The root xpcom procedural macro definition.
-fn xpcom(input: &str) -> Result<Tokens, Box<Error>> {
- let init = syn::parse_derive_input(input)?;
- if init.generics != Generics::default() {
+fn xpcom(init: DeriveInput) -> Result<Tokens, Box<Error>> {
+ if !init.generics.params.is_empty() || !init.generics.where_clause.is_none() {
return Err("Cannot #[derive(xpcom)] on a generic type, due to \
rust limitations. It is not possible to instantiate \
a static with a generic type parameter, meaning that \
generic types cannot have their VTables instantiated \
correctly.".into());
}
let bases = get_bases(&init.attrs)?;
@@ -565,17 +541,17 @@ fn xpcom(input: &str) -> Result<Tokens,
let mut qi_impl = Vec::new();
let mut coerce_impl = Vec::new();
for base in &bases {
let (qi, coerce) = gen_casts(
&mut seen,
base,
name,
&coerce_name,
- &Ident::from(format!("__base_{}", base)),
+ &Ident::from(format!("__base_{}", base.name)),
)?;
qi_impl.push(qi);
coerce_impl.push(coerce);
}
Ok(quote! {
#real
@@ -684,12 +660,11 @@ fn xpcom(input: &str) -> Result<Tokens,
self.Release();
}
}
})
}
#[proc_macro_derive(xpcom, attributes(xpimplements, refcnt))]
pub fn xpcom_internal(input: TokenStream) -> TokenStream {
- let source = input.to_string();
- let out_src = xpcom(&source).unwrap().to_string();
- out_src.parse().unwrap()
+ xpcom(parse(input).expect("Invalid derive input"))
+ .expect("#[derive(xpcom)] failed").into()
}