From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- xpcom/rust/xpcom/xpcom_macros/src/lib.rs | 818 +++++++++++++++++++++++++++++++ 1 file changed, 818 insertions(+) create mode 100644 xpcom/rust/xpcom/xpcom_macros/src/lib.rs (limited to 'xpcom/rust/xpcom/xpcom_macros/src/lib.rs') diff --git a/xpcom/rust/xpcom/xpcom_macros/src/lib.rs b/xpcom/rust/xpcom/xpcom_macros/src/lib.rs new file mode 100644 index 0000000000..2511ba9103 --- /dev/null +++ b/xpcom/rust/xpcom/xpcom_macros/src/lib.rs @@ -0,0 +1,818 @@ +/* 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/. */ + +//! This crate provides the `#[xpcom]` custom attribute. This custom attribute +//! is used in order to implement [`xpcom`] interfaces. +//! +//! # Usage +//! +//! The easiest way to explain this crate is probably with a usage example. I'll +//! show you the example, and then we'll destructure it and walk through what +//! each component is doing. +//! +//! ```ignore +//! // Declaring an XPCOM Struct +//! #[xpcom(implement(nsIRunnable), atomic)] +//! struct ImplRunnable { +//! i: i32, +//! } +//! +//! // Implementing methods on an XPCOM Struct +//! impl ImplRunnable { +//! unsafe fn Run(&self) -> nsresult { +//! println!("{}", self.i); +//! NS_OK +//! } +//! } +//! ``` +//! +//! ## Declaring an XPCOM Struct +//! +//! ```ignore +//! // This derive should be placed on the initialization struct in order to +//! // trigger the procedural macro. +//! #[xpcom( +//! // The implement argument should be passed the names of the IDL +//! // interfaces which you want to implement. These can be separated by +//! // commas if you want to implement multiple interfaces. +//! // +//! // Some methods use types which we cannot bind to in rust. Interfaces +//! // like those cannot be implemented, and a compile-time error will occur +//! // if they are listed in this attribute. +//! implement(nsIRunnable), +//! +//! // The refcount kind can be specified as one of the following values: +//! // * `atomic` == atomic reference count +//! // ~= NS_DECL_THREADSAFE_ISUPPORTS in C++ +//! // * `nonatomic` == non atomic reference count +//! // ~= NS_DECL_ISUPPORTS in C++ +//! atomic, +//! )] +//! +//! // It is a compile time error to put the `#[xpcom]` attribute on +//! // an enum, union, or tuple struct. +//! // +//! // The macro will generate both the named struct, as well as a version with +//! // its name prefixed with `Init` which can be used to initialize the type. +//! struct ImplRunnable { +//! i: i32, +//! } +//! ``` +//! +//! The above example will generate `ImplRunnable` and `InitImplRunnable` +//! structs. The `ImplRunnable` struct will implement the [`nsIRunnable`] XPCOM +//! interface, and cannot be constructed directly. +//! +//! The following methods will be automatically implemented on it: +//! +//! ```ignore +//! // Automatic nsISupports implementation +//! unsafe fn AddRef(&self) -> MozExternalRefCountType; +//! unsafe fn Release(&self) -> MozExternalRefCountType; +//! unsafe fn QueryInterface(&self, uuid: &nsIID, result: *mut *mut libc::c_void) -> nsresult; +//! +//! // Allocates and initializes a new instance of this type. The values will +//! // be moved from the `Init` struct which is passed in. +//! fn allocate(init: InitImplRunnable) -> RefPtr; +//! +//! // Helper for performing the `query_interface` operation to case to a +//! // specific interface. +//! fn query_interface(&self) -> Option>; +//! +//! // Coerce function for cheaply casting to our base interfaces. +//! fn coerce(&self) -> &T; +//! ``` +//! +//! The [`RefCounted`] interface will also be implemented, so that the type can +//! be used within the [`RefPtr`] type. +//! +//! The `coerce` and `query_interface` methods are backed by the generated +//! `*Coerce` trait. This trait is impl-ed for every interface implemented by +//! the trait. For example: +//! +//! ```ignore +//! pub trait ImplRunnableCoerce { +//! fn coerce_from(x: &ImplRunnable) -> &Self; +//! } +//! impl ImplRunnableCoerce for nsIRunnable { .. } +//! impl ImplRunnableCoerce for nsISupports { .. } +//! ``` +//! +//! ## Implementing methods on an XPCOM Struct +//! +//! ```ignore +//! // Methods should be implemented directly on the generated struct. All +//! // methods other than `AddRef`, `Release`, and `QueryInterface` must be +//! // implemented manually. +//! impl ImplRunnable { +//! // The method should have the same name as the corresponding C++ method. +//! unsafe fn Run(&self) -> nsresult { +//! // Fields defined on the template struct will be directly on the +//! // generated struct. +//! println!("{}", self.i); +//! NS_OK +//! } +//! } +//! ``` +//! +//! XPCOM methods implemented in Rust have signatures similar to methods +//! implemented in C++. +//! +//! ```ignore +//! // nsISupports foo(in long long bar, in AString baz); +//! unsafe fn Foo(&self, bar: i64, baz: *const nsAString, +//! _retval: *mut *const nsISupports) -> nsresult; +//! +//! // AString qux(in nsISupports ham); +//! unsafe fn Qux(&self, ham: *const nsISupports, +//! _retval: *mut nsAString) -> nsresult; +//! ``` +//! +//! This is a little tedious, so the `xpcom_method!` macro provides a convenient +//! way to generate wrappers around more idiomatic Rust methods. +//! +//! [`xpcom`]: ../xpcom/index.html +//! [`nsIRunnable`]: ../xpcom/struct.nsIRunnable.html +//! [`RefCounted`]: ../xpcom/struct.RefCounted.html +//! [`RefPtr`]: ../xpcom/struct.RefPtr.html + +use lazy_static::lazy_static; +use proc_macro2::{Span, TokenStream}; +use quote::{format_ident, quote, ToTokens}; +use std::collections::{HashMap, HashSet}; +use syn::punctuated::Punctuated; +use syn::{ + parse_macro_input, parse_quote, AttributeArgs, Field, Fields, Ident, ItemStruct, Meta, + MetaList, NestedMeta, Path, Token, Type, +}; + +macro_rules! bail { + (@($t:expr), $s:expr) => { + return Err(syn::Error::new_spanned(&$t, &$s[..])) + }; + (@($t:expr), $f:expr, $($e:expr),*) => { + return Err(syn::Error::new_spanned(&$t, &format!($f, $($e),*)[..])) + }; + ($s:expr) => { + return Err(syn::Error::new(Span::call_site(), &$s[..])) + }; + ($f:expr, $($e:expr),*) => { + return Err(syn::Error::new(Span::call_site(), &format!($f, $($e),*)[..])) + }; +} + +/* 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, + ty: &'static str, +} + +/// A single method on an XPCOM interface. +#[derive(Debug)] +struct Method { + name: &'static str, + params: &'static [Param], + ret: &'static str, +} + +/// An XPCOM interface. `methods` will be `Err("reason")` if the interface +/// 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) -> Option<&'static Interface> { + Some(IFACES[self.base?]) + } + + fn methods(&self) -> Result<&'static [Method], syn::Error> { + match self.methods { + Ok(methods) => Ok(methods), + Err(reason) => Err(syn::Error::new( + Span::call_site(), + &format!( + "Interface {} cannot be implemented in rust \ + because {} is not supported yet", + self.name, reason + ), + )), + } + } +} + +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!(mozbuild::objdir_path!("dist/xpcrs/bt/all.rs")); + + let mut hm = HashMap::new(); + for &list in lists { + for iface in list { + hm.insert(iface.name, iface); + } + } + hm + }; +} + +/// The type of the reference count to use for the struct. +#[derive(Debug, Eq, PartialEq, Copy, Clone)] +enum RefcntKind { + Atomic, + NonAtomic, +} + +/// Produces the tokens for the type representation. +impl ToTokens for RefcntKind { + fn to_tokens(&self, tokens: &mut TokenStream) { + match *self { + RefcntKind::NonAtomic => quote!(xpcom::Refcnt).to_tokens(tokens), + RefcntKind::Atomic => quote!(xpcom::AtomicRefcnt).to_tokens(tokens), + } + } +} + +/// Extract the fields list from the input struct. +fn get_fields(si: &ItemStruct) -> Result<&Punctuated, syn::Error> { + match si.fields { + Fields::Named(ref named) => Ok(&named.named), + _ => bail!(@(si), "The initializer struct must be a standard named \ + value struct definition"), + } +} + +/// Takes the template struct in, and generates `ItemStruct` for the "real" and +/// "init" structs. +fn gen_structs( + template: &ItemStruct, + bases: &[&Interface], + refcnt_ty: RefcntKind, +) -> Result<(ItemStruct, ItemStruct), syn::Error> { + let real_ident = &template.ident; + let init_ident = format_ident!("Init{}", real_ident); + let vis = &template.vis; + + let bases = bases.iter().map(|base| { + let ident = format_ident!("__base_{}", base.name); + let vtable = format_ident!("{}VTable", base.name); + quote!(#ident : *const xpcom::interfaces::#vtable) + }); + + let fields = get_fields(template)?; + let (impl_generics, _, where_clause) = template.generics.split_for_impl(); + Ok(( + parse_quote! { + #[repr(C)] + #vis struct #real_ident #impl_generics #where_clause { + #(#bases,)* + __refcnt: #refcnt_ty, + #fields + } + }, + parse_quote! { + #vis struct #init_ident #impl_generics #where_clause { + #fields + } + }, + )) +} + +/// Generates the `extern "system"` methods which are actually included in the +/// VTable for the given interface. +/// +/// `idx` must be the offset in pointers of the pointer to this vtable in the +/// struct `real`. This is soundness-critical, as it will be used to offset +/// pointers received from xpcom back to the concrete implementation. +fn gen_vtable_methods( + real: &ItemStruct, + iface: &Interface, + vtable_index: usize, +) -> Result { + let base_ty = format_ident!("{}", iface.name); + + let base_methods = if let Some(base) = iface.base() { + gen_vtable_methods(real, base, vtable_index)? + } else { + quote! {} + }; + + let ty_name = &real.ident; + let (impl_generics, ty_generics, where_clause) = real.generics.split_for_impl(); + + let mut method_defs = Vec::new(); + for method in iface.methods()? { + let ret = syn::parse_str::(method.ret)?; + + let mut params = Vec::new(); + let mut args = Vec::new(); + for param in method.params { + let name = format_ident!("{}", param.name); + let ty = syn::parse_str::(param.ty)?; + + params.push(quote! {#name : #ty,}); + args.push(quote! {#name,}); + } + + let name = format_ident!("{}", method.name); + method_defs.push(quote! { + unsafe extern "system" fn #name #impl_generics ( + this: *const #base_ty, #(#params)* + ) -> #ret #where_clause { + let this: &#ty_name #ty_generics = + ::xpcom::reexports::transmute_from_vtable_ptr(&this, #vtable_index); + this.#name(#(#args)*) + } + }); + } + + 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(real: &ItemStruct, iface: &Interface) -> Result { + let vtable_ty = format_ident!("{}VTable", iface.name); + + // Generate the vtable for the base interface. + let base_vtable = if let Some(base) = iface.base() { + let vt = gen_inner_vtable(real, base)?; + quote! {__base: #vt,} + } else { + quote! {} + }; + + // Include each of the method definitions for this interface. + let (_, ty_generics, _) = real.generics.split_for_impl(); + let turbofish = ty_generics.as_turbofish(); + let vtable_init = iface + .methods()? + .into_iter() + .map(|method| { + let name = format_ident!("{}", method.name); + quote! { #name : #name #turbofish, } + }) + .collect::>(); + + Ok(quote!(#vtable_ty { + #base_vtable + #(#vtable_init)* + })) +} + +fn gen_root_vtable( + real: &ItemStruct, + base: &Interface, + idx: usize, +) -> Result { + let field = format_ident!("__base_{}", base.name); + let vtable_ty = format_ident!("{}VTable", base.name); + + let (impl_generics, ty_generics, where_clause) = real.generics.split_for_impl(); + let turbofish = ty_generics.as_turbofish(); + + let methods = gen_vtable_methods(real, base, idx)?; + let vtable = gen_inner_vtable(real, 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: { + // The method implementations which will be used to build the vtable. + #methods + + // The actual VTable definition. This is in a separate method in order + // to allow it to be generic. + #[inline] + fn get_vtable #impl_generics () -> &'static ::xpcom::reexports::VTableExtra<#vtable_ty> #where_clause { + &::xpcom::reexports::VTableExtra { + #[cfg(not(windows))] + offset: { + // NOTE: workaround required to avoid depending on the + // unstable const expression feature `const {}`. + const OFFSET: isize = -((::std::mem::size_of::() * #idx) as isize); + OFFSET + }, + #[cfg(not(windows))] + typeinfo: 0 as *const _, + vtable: #vtable, + } + } + &get_vtable #turbofish ().vtable + },}) +} + +/// 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<&'static str>, + iface: &Interface, + real: &ItemStruct, + coerce_name: &Ident, + vtable_field: &Ident, +) -> Result<(TokenStream, TokenStream), syn::Error> { + 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) = iface.base() { + gen_casts(seen, base, real, coerce_name, vtable_field)? + } else { + (quote! {}, quote! {}) + }; + + // Add the if statment to QueryInterface for the base class. + let base_name = format_ident!("{}", iface.name); + + let qi = quote! { + #base_qi + if *uuid == #base_name::IID { + // Implement QueryInterface in terms of coercions. + self.addref(); + *result = self.coerce::<#base_name>() + as *const #base_name + as *const ::xpcom::reexports::libc::c_void + as *mut ::xpcom::reexports::libc::c_void; + return ::xpcom::reexports::NS_OK; + } + }; + + // Add an implementation of the `*Coerce` trait for the base interface. + let name = &real.ident; + let (impl_generics, ty_generics, where_clause) = real.generics.split_for_impl(); + let coerce = quote! { + #base_coerce + + impl #impl_generics #coerce_name #ty_generics for ::xpcom::interfaces::#base_name #where_clause { + fn coerce_from(v: &#name #ty_generics) -> &Self { + unsafe { + // Get the address of the VTable field. This should be a + // pointer to a pointer to a vtable, which we can then cast + // into a pointer to our interface. + &*(&(v.#vtable_field) + as *const *const _ + as *const ::xpcom::interfaces::#base_name) + } + } + } + }; + + Ok((qi, coerce)) +} + +fn check_generics(generics: &syn::Generics) -> Result<(), syn::Error> { + for param in &generics.params { + let tp = match param { + syn::GenericParam::Type(tp) => tp, + syn::GenericParam::Lifetime(lp) => bail!( + @(lp), + "Cannot use #[xpcom] on types with lifetime parameters. \ + Implementors of XPCOM interfaces must not contain non-'static \ + lifetimes.", + ), + // XXX: Once const generics become stable, it may be as simple as + // removing this bail! to support them. + syn::GenericParam::Const(cp) => { + bail!(@(cp), "Cannot use #[xpcom] on types with const generics.") + } + }; + + let mut static_lt = false; + for bound in &tp.bounds { + match bound { + syn::TypeParamBound::Lifetime(lt) if lt.ident == "static" => { + static_lt = true; + break; + } + _ => {} + } + } + + if !static_lt { + bail!( + @(param), + "Every generic parameter for xpcom implementation must have a \ + 'static lifetime bound declared in the generics. Implicit \ + lifetime bounds or lifetime bounds in where clauses are not \ + detected by the macro and will be ignored. \ + Implementors of XPCOM interfaces must not contain non-'static \ + lifetimes.", + ); + } + } + Ok(()) +} + +#[derive(Default)] +struct Options { + bases: Vec<&'static Interface>, + refcnt: Option, +} + +impl Options { + fn parse_path_arg(&mut self, path: &Path) -> Result<(), syn::Error> { + if path.is_ident("atomic") || path.is_ident("nonatomic") { + if self.refcnt.is_some() { + bail!(@(path), "Duplicate refcnt atomicity specifier"); + } + self.refcnt = Some(if path.is_ident("atomic") { + RefcntKind::Atomic + } else { + RefcntKind::NonAtomic + }); + return Ok(()); + } + bail!(@(path), "Unexpected path argument to #[xpcom]"); + } + + fn parse_list_arg(&mut self, list: &MetaList) -> Result<(), syn::Error> { + if list.path.is_ident("implement") { + for item in list.nested.iter() { + let iface = match *item { + NestedMeta::Meta(syn::Meta::Path(ref iface)) => iface, + _ => bail!(@(item), "Expected interface name to implement"), + }; + let ident = match iface.get_ident() { + Some(ref iface) => iface.to_string(), + _ => bail!(@(iface), "Interface name must be unqualified"), + }; + if let Some(&iface) = IFACES.get(ident.as_str()) { + self.bases.push(iface); + } else { + bail!(@(item), "Invalid base interface `{}`", ident); + } + } + return Ok(()); + } + bail!(@(list), "Unexpected list argument to #[xpcom]"); + } + + fn parse(&mut self, args: &AttributeArgs) -> Result<(), syn::Error> { + for arg in args { + match arg { + NestedMeta::Meta(Meta::Path(path)) => self.parse_path_arg(path)?, + NestedMeta::Meta(Meta::List(list)) => self.parse_list_arg(list)?, + NestedMeta::Meta(Meta::NameValue(name_value)) => { + bail!(@(name_value), "Unexpected name-value argument to #[xpcom]") + } + NestedMeta::Lit(lit) => bail!(@(lit), "Unexpected literal argument to #[xpcom]"), + } + } + + if self.bases.is_empty() { + bail!( + "Types with #[xpcom(..)] must implement at least one \ + interface. Interfaces can be implemented by adding an \ + implements(nsIFoo, nsIBar) parameter to the #[xpcom] attribute" + ); + } + + if self.refcnt.is_none() { + bail!("Must specify refcnt kind in #[xpcom] attribute"); + } + + Ok(()) + } +} + +/// The root xpcom procedural macro definition. +fn xpcom_impl(args: AttributeArgs, template: ItemStruct) -> Result { + let mut options = Options::default(); + options.parse(&args)?; + + check_generics(&template.generics)?; + + let bases = options.bases; + + // Ensure that all our base interface methods have unique names. + let mut method_names = HashMap::new(); + for base in &bases { + for method in base.methods()? { + if let Some(existing) = method_names.insert(method.name, base.name) { + bail!( + "The method `{0}` is declared on both `{1}` and `{2}`, + but a Rust type cannot implement two methods with the \ + same name. You can add the `[binaryname(Renamed{0})]` \ + XPIDL attribute to one of the declarations to rename it.", + method.name, + existing, + base.name + ); + } + } + } + + // Determine what reference count type to use, and generate the real struct. + let refcnt_ty = options.refcnt.unwrap(); + let (real, init) = gen_structs(&template, &bases, refcnt_ty)?; + + let name_init = &init.ident; + let name = &real.ident; + let coerce_name = format_ident!("{}Coerce", name); + + // Generate a VTable for each of the base interfaces. + let mut vtables = Vec::new(); + for (idx, base) in bases.iter().enumerate() { + vtables.push(gen_root_vtable(&real, base, idx)?); + } + + // Generate the field initializers for the final struct, moving each field + // out of the original __init struct. + let inits = get_fields(&init)?.iter().map(|field| { + let id = &field.ident; + quote! { #id : __init.#id, } + }); + + let vis = &real.vis; + + // Generate the implementation for QueryInterface and Coerce. + let mut seen = HashSet::new(); + let mut qi_impl = Vec::new(); + let mut coerce_impl = Vec::new(); + for base in &bases { + let (qi, coerce) = gen_casts( + &mut seen, + base, + &real, + &coerce_name, + &format_ident!("__base_{}", base.name), + )?; + qi_impl.push(qi); + coerce_impl.push(coerce); + } + + let size_for_logs = if real.generics.params.is_empty() { + quote!(::std::mem::size_of::() as u32) + } else { + // Refcount logging requires all types with the same name to have the + // same size, and generics aren't taken into account when creating our + // name string, so we need to make sure that all possible instantiations + // report the same size. To do that, we fake a size based on the number + // of vtable pointers and the known refcount field. + let fake_size_npointers = bases.len() + 1; + quote!((::std::mem::size_of::() * #fake_size_npointers) as u32) + }; + + let (impl_generics, ty_generics, where_clause) = real.generics.split_for_impl(); + let name_for_logs = quote!( + concat!(module_path!(), "::", stringify!(#name #ty_generics), "\0").as_ptr() + as *const ::xpcom::reexports::libc::c_char + ); + Ok(quote! { + #init + + #real + + impl #impl_generics #name #ty_generics #where_clause { + /// This method is used for + fn allocate(__init: #name_init #ty_generics) -> ::xpcom::RefPtr { + #[allow(unused_imports)] + use ::xpcom::*; + #[allow(unused_imports)] + use ::xpcom::interfaces::*; + #[allow(unused_imports)] + use ::xpcom::reexports::{ + libc, nsACString, nsAString, nsCString, nsString, nsresult + }; + + // Helper for asserting that for all instantiations, this + // object has the 'static lifetime. + fn xpcom_types_must_be_static(t: &T) {} + + unsafe { + // NOTE: This is split into multiple lines to make the + // output more readable. + let value = #name { + #(#vtables)* + __refcnt: #refcnt_ty::new(), + #(#inits)* + }; + let boxed = ::std::boxed::Box::new(value); + xpcom_types_must_be_static(&*boxed); + let raw = ::std::boxed::Box::into_raw(boxed); + ::xpcom::RefPtr::from_raw(raw).unwrap() + } + } + + /// Automatically generated implementation of AddRef for nsISupports. + #vis unsafe fn AddRef(&self) -> ::xpcom::MozExternalRefCountType { + let new = self.__refcnt.inc(); + ::xpcom::trace_refcnt::NS_LogAddRef( + self as *const _ as *mut ::xpcom::reexports::libc::c_void, + new as usize, + #name_for_logs, + #size_for_logs, + ); + new + } + + /// Automatically generated implementation of Release for nsISupports. + #vis unsafe fn Release(&self) -> ::xpcom::MozExternalRefCountType { + let new = self.__refcnt.dec(); + ::xpcom::trace_refcnt::NS_LogRelease( + self as *const _ as *mut ::xpcom::reexports::libc::c_void, + new as usize, + #name_for_logs, + #size_for_logs, + ); + if new == 0 { + // dealloc + ::std::mem::drop(::std::boxed::Box::from_raw(self as *const Self as *mut Self)); + } + new + } + + /// Automatically generated implementation of QueryInterface for + /// nsISupports. + #vis unsafe fn QueryInterface(&self, + uuid: *const ::xpcom::nsIID, + result: *mut *mut ::xpcom::reexports::libc::c_void) + -> ::xpcom::reexports::nsresult { + #[allow(unused_imports)] + use ::xpcom::*; + #[allow(unused_imports)] + use ::xpcom::interfaces::*; + + #(#qi_impl)* + + ::xpcom::reexports::NS_ERROR_NO_INTERFACE + } + + /// Perform a QueryInterface call on this object, attempting to + /// dynamically cast it to the requested interface type. Returns + /// Some(RefPtr) if the cast succeeded, and None otherwise. + #vis fn query_interface(&self) + -> ::std::option::Option<::xpcom::RefPtr> + { + let mut ga = ::xpcom::GetterAddrefs::::new(); + unsafe { + if self.QueryInterface(&XPCOM_InterfaceType::IID, ga.void_ptr()).succeeded() { + ga.refptr() + } else { + None + } + } + } + + /// Coerce this type safely to any of the interfaces which it + /// implements without `AddRef`ing it. + #vis fn coerce(&self) -> &XPCOM_InterfaceType { + XPCOM_InterfaceType::coerce_from(self) + } + } + + /// This trait is implemented on the interface types which this + /// `#[xpcom]` type can be safely ane cheaply coerced to using the + /// `coerce` method. + /// + /// The trait and its method should usually not be used directly, but + /// rather acts as a trait bound and implementation for the `coerce` + /// methods. + #[doc(hidden)] + #vis trait #coerce_name #impl_generics #where_clause { + /// Convert a value of the `#[xpcom]` type into the implementing + /// interface type. + fn coerce_from(v: &#name #ty_generics) -> &Self; + } + + #(#coerce_impl)* + + unsafe impl #impl_generics ::xpcom::RefCounted for #name #ty_generics #where_clause { + unsafe fn addref(&self) { + self.AddRef(); + } + + unsafe fn release(&self) { + self.Release(); + } + } + }) +} + +#[proc_macro_attribute] +pub fn xpcom( + args: proc_macro::TokenStream, + input: proc_macro::TokenStream, +) -> proc_macro::TokenStream { + let args = parse_macro_input!(args as AttributeArgs); + let input = parse_macro_input!(input as ItemStruct); + match xpcom_impl(args, input) { + Ok(ts) => ts.into(), + Err(err) => err.to_compile_error().into(), + } +} -- cgit v1.2.3