Add aggr attribute support for new coclass macro design

This commit is contained in:
adrianwithah 2019-09-10 18:43:49 +01:00
Родитель 15f4eeb65d
Коммит fbcbc79cb8
6 изменённых файлов: 98 добавлений и 35 удалений

Просмотреть файл

@ -1,6 +1,7 @@
use proc_macro2::TokenStream as HelperTokenStream;
use quote::quote;
use syn::{Ident, ItemStruct, Fields};
use std::collections::HashMap;
// #[repr(C)]
// pub struct BritishShortHairCat {
@ -17,7 +18,7 @@ use syn::{Ident, ItemStruct, Fields};
/// ..ref count..
/// ..init struct..
/// }
pub fn generate(base_itf_idents: &[Ident], struct_item: &ItemStruct) -> HelperTokenStream {
pub fn generate(aggr_map: &HashMap<Ident, Vec<Ident>>, base_itf_idents: &[Ident], struct_item: &ItemStruct) -> HelperTokenStream {
let struct_ident = &struct_item.ident;
let vis = &struct_item.vis;
@ -33,11 +34,18 @@ pub fn generate(base_itf_idents: &[Ident], struct_item: &ItemStruct) -> HelperTo
_ => panic!("Found non Named fields in struct.")
};
let aggregates = aggr_map.iter().map(|(aggr_field_ident, aggr_base_itf_idents)| {
quote!(
#aggr_field_ident: *mut <dyn com::IUnknown as com::ComInterface>::VPtr
)
});
quote!(
#[repr(C)]
#vis struct #struct_ident {
#(#bases_itf_idents,)*
#ref_count_ident: u32,
#(#aggregates,)*
#fields
}
)

Просмотреть файл

@ -1,6 +1,7 @@
use proc_macro2::TokenStream as HelperTokenStream;
use quote::{format_ident, quote};
use syn::{Ident, ItemStruct, Fields,};
use std::collections::HashMap;
// impl BritishShortHairCat {
// fn allocate(init_struct: InitBritishShortHairCat) -> Box<BritishShortHairCat> {
@ -37,7 +38,7 @@ use syn::{Ident, ItemStruct, Fields,};
/// Generates the allocate and get_class_object function for the COM object.
/// allocate: instantiates the COM fields, such as vpointers for the COM object.
/// get_class_object: Instantiate an instance to the class object.
pub fn generate(base_itf_idents: &[Ident], struct_item: &ItemStruct) -> HelperTokenStream {
pub fn generate(aggr_map: &HashMap<Ident, Vec<Ident>>, base_itf_idents: &[Ident], struct_item: &ItemStruct) -> HelperTokenStream {
let struct_ident = &struct_item.ident;
// Allocate stuff
@ -72,6 +73,14 @@ pub fn generate(base_itf_idents: &[Ident], struct_item: &ItemStruct) -> HelperTo
quote!(#field_ident)
});
let aggregate_inits = aggr_map.iter().map(|(aggr_field_ident, aggr_base_itf_idents)| {
quote!(
#aggr_field_ident: std::ptr::null_mut()
)
});
let set_aggregate_fns = gen_set_aggregate_fns(aggr_map);
quote!(
impl #struct_ident {
fn allocate(#fields) -> Box<#struct_ident> {
@ -80,7 +89,8 @@ pub fn generate(base_itf_idents: &[Ident], struct_item: &ItemStruct) -> HelperTo
let out = #struct_ident {
#(#base_fields,)*
#ref_count_ident: 0,
#(#field_idents),*
#(#aggregate_inits,)*
#(#field_idents)*
};
Box::new(out)
}
@ -88,6 +98,25 @@ pub fn generate(base_itf_idents: &[Ident], struct_item: &ItemStruct) -> HelperTo
pub fn get_class_object() -> Box<#class_factory_ident> {
<#class_factory_ident>::new()
}
#set_aggregate_fns
}
)
}
fn gen_set_aggregate_fns(aggr_map: &HashMap<Ident, Vec<Ident>>) -> HelperTokenStream {
let mut fns = Vec::new();
for (aggr_field_ident, aggr_base_itf_idents) in aggr_map.iter() {
for base in aggr_base_itf_idents {
let set_aggregate_fn_ident = format_ident!("set_aggregate_{}", macro_utils::camel_to_snake(&base.to_string()));
fns.push(quote!(
fn #set_aggregate_fn_ident(&mut self, aggr: *mut <dyn com::IUnknown as com::ComInterface>::VPtr) {
// TODO: What happens if we are overwriting an existing aggregate?
self.#aggr_field_ident = aggr
}
));
}
}
quote!(#(#fns)*)
}

Просмотреть файл

@ -1,6 +1,7 @@
use proc_macro2::TokenStream as HelperTokenStream;
use quote::quote;
use syn::{Ident, ItemStruct};
use std::collections::HashMap;
// impl std::ops::Drop for BritishShortHairCat {
// fn drop(&mut self) {
@ -14,7 +15,7 @@ use syn::{Ident, ItemStruct};
// }
// }
pub fn generate(base_itf_idents: &[Ident], struct_item: &ItemStruct) -> HelperTokenStream {
pub fn generate(aggr_map: &HashMap<Ident, Vec<Ident>>, base_itf_idents: &[Ident], struct_item: &ItemStruct) -> HelperTokenStream {
let struct_ident = &struct_item.ident;
let box_from_raws = base_itf_idents.iter().map(|base| {
let vptr_field_ident = macro_utils::get_vptr_field_ident(&base);
@ -23,10 +24,23 @@ pub fn generate(base_itf_idents: &[Ident], struct_item: &ItemStruct) -> HelperTo
)
});
let aggregate_drops = aggr_map.iter().map(|(aggr_field_ident, _)| {
quote!(
if !self.#aggr_field_ident.is_null() {
let mut aggr_itf_ptr: com::ComPtr<dyn com::IUnknown> = com::ComPtr::new(self.#aggr_field_ident as *mut winapi::ctypes::c_void);
aggr_itf_ptr.release();
core::mem::forget(aggr_itf_ptr);
}
)
});
quote!(
impl std::ops::Drop for #struct_ident {
fn drop(&mut self) {
use com::IUnknown;
let _ = unsafe {
#(#aggregate_drops)*
#(#box_from_raws)*
};
}

Просмотреть файл

@ -124,9 +124,15 @@ pub fn generate(
quote!(
else if #first_aggr_match_condition #(#rem_aggr_match_conditions)* {
let mut aggr_itf_ptr: ComPtr<dyn IUnknown> = ComPtr::new(self.#aggr_field_ident as *mut winapi::ctypes::c_void);
if self.#aggr_field_ident.is_null() {
*ppv = std::ptr::null_mut::<winapi::ctypes::c_void>();
return winapi::shared::winerror::E_NOINTERFACE;
}
let mut aggr_itf_ptr: com::ComPtr<dyn com::IUnknown> = com::ComPtr::new(self.#aggr_field_ident as *mut winapi::ctypes::c_void);
let hr = aggr_itf_ptr.query_interface(riid, ppv);
if com::failed(hr) {
*ppv = std::ptr::null_mut::<winapi::ctypes::c_void>();
return winapi::shared::winerror::E_NOINTERFACE;
}
@ -135,7 +141,7 @@ pub fn generate(
// outer object.
aggr_itf_ptr.release();
forget(aggr_itf_ptr);
core::mem::forget(aggr_itf_ptr);
}
)
});

Просмотреть файл

@ -20,12 +20,15 @@ pub fn expand_com_class(attr: TokenStream, item: TokenStream) -> TokenStream {
let aggr_itf_idents = macro_utils::get_aggr_map(&input);
let mut out: Vec<TokenStream> = Vec::new();
out.push(com_struct::generate(&base_itf_idents, &input).into());
out.push(com_struct_impl::generate(&base_itf_idents, &input).into());
out.push(com_struct::generate(&aggr_itf_idents, &base_itf_idents, &input).into());
out.push(com_struct_impl::generate(&aggr_itf_idents, &base_itf_idents, &input).into());
out.push(iunknown_impl::generate(&base_itf_idents, &aggr_itf_idents, &input).into());
out.push(drop_impl::generate(&base_itf_idents, &input).into());
out.push(drop_impl::generate(&aggr_itf_idents, &base_itf_idents, &input).into());
out.push(deref_impl::generate(&input).into());
out.push(class_factory::generate(&input).into());
TokenStream::from_iter(out)
// TokenStream::from_iter(out)
let result = TokenStream::from_iter(out);
println!("Result:\n{}", result.to_string());
result
}

Просмотреть файл

@ -60,34 +60,37 @@ pub fn get_base_interface_idents(struct_item: &ItemStruct) -> Vec<Ident> {
pub fn get_aggr_map(struct_item: &ItemStruct) -> HashMap<Ident, Vec<Ident>> {
let mut aggr_map = HashMap::new();
let fields = match &struct_item.fields {
Fields::Named(f) => &f.named,
_ => panic!("Found field other than named fields in struct")
};
for field in fields {
for attr in &field.attrs {
if let Ok(Meta::List(ref attr)) = attr.parse_meta() {
if attr.path.segments.last().unwrap().ident != "aggr" {
continue;
}
let mut aggr_interfaces_idents = Vec::new();
assert!(attr.nested.len() > 0, "Need to expose at least one interface from aggregated COM object.");
for item in &attr.nested {
if let NestedMeta::Meta(Meta::Path(p)) = item {
assert!(p.segments.len() == 1, "Incapable of handling multiple path segments yet.");
aggr_interfaces_idents.push(p.segments.last().unwrap().ident.clone());
}
}
let ident = field.ident.as_ref().unwrap().clone();
aggr_map.insert(ident, aggr_interfaces_idents);
for attr in &struct_item.attrs {
if let Ok(Meta::List(ref attr)) = attr.parse_meta() {
if attr.path.segments.last().unwrap().ident != "aggr" {
continue;
}
let mut aggr_interfaces_idents = Vec::new();
assert!(attr.nested.len() > 0, "Need to expose at least one interface from aggregated COM object.");
for item in &attr.nested {
if let NestedMeta::Meta(Meta::Path(p)) = item {
assert!(p.segments.len() == 1, "Incapable of handling multiple path segments yet.");
aggr_interfaces_idents.push(p.segments.last().unwrap().ident.clone());
}
}
let ident = aggr_interfaces_idents.iter()
.map(|base| {
crate::camel_to_snake(&base.to_string())
})
.fold("aggr".to_owned(), |acc, base| {
format!("{}_{}", acc, base)
});
aggr_map.insert(format_ident!("{}", ident), aggr_interfaces_idents);
}
}
for (ident, _) in aggr_map.iter() {
println!("Ident found: {}", ident);
}
aggr_map
}