intercom_common/attributes/
com_class.rs1use super::common::*;
2use crate::prelude::*;
3
4use crate::idents;
5use crate::idents::SomeIdent;
6use crate::model;
7use crate::utils;
8
9use crate::tyhandlers::ModelTypeSystem;
10
11use syn::spanned::Spanned;
12
13pub fn expand_com_class(
21 attr_tokens: TokenStreamNightly,
22 item_tokens: TokenStreamNightly,
23) -> Result<TokenStreamNightly, model::ParseError>
24{
25 let mut output = vec![];
27 let cls = model::ComClass::parse(&lib_name(), attr_tokens.into(), item_tokens.clone().into())?;
28 let cls_ident = &cls.name;
29 let cls_name = cls_ident.to_string();
30 let (impl_generics, ty_generics, where_clause) = cls.generics.split_for_impl();
31
32 let support_error_info_vtbl = quote!(
36 <dyn intercom::ISupportErrorInfo as intercom::attributes::ComInterfaceVariant<
37 intercom::type_system::AutomationTypeSystem,
38 >>::VTable
39 );
40 let mut query_interface_match_arms = vec![
41 quote!(
42 if riid == <dyn intercom::IUnknown as intercom::attributes::ComInterfaceVariant<intercom::type_system::AutomationTypeSystem>>::iid() {
43 let ptr = ( &vtables._ISupportErrorInfo )
44 as *const &#support_error_info_vtbl
45 as *mut &#support_error_info_vtbl
46 as intercom::raw::RawComPtr;
47 intercom::logging::trace(|l| l(module_path!(), format_args!(
48 "[{:p}] {}::query_interface({:-X}) -> IUnknown [{:p}]",
49 vtables, #cls_name, riid, ptr)));
50 ptr
51 } else
52 ),
53 quote!(
54 if riid == <dyn intercom::ISupportErrorInfo as intercom::attributes::ComInterfaceVariant<intercom::type_system::AutomationTypeSystem>>::iid() {
55 let ptr = ( &vtables._ISupportErrorInfo )
56 as *const &#support_error_info_vtbl
57 as *mut &#support_error_info_vtbl
58 as intercom::raw::RawComPtr;
59 intercom::logging::trace(|l| l(module_path!(), format_args!(
60 "[{:p}] {}::query_interface({:-X}) -> ISupportErrorInfo [{:p}]",
61 vtables, #cls_name, riid, ptr)));
62 ptr
63 } else
64 ),
65 ];
66 let mut support_error_info_match_arms = vec![];
67
68 output.push(quote!(
69 impl #impl_generics intercom::IUnknown for #cls_ident #ty_generics #where_clause {}
70 ));
71
72 let mut vtable_list_field_defs = vec![];
82 let mut vtable_list_field_decls = vec![quote!(
83 _ISupportErrorInfo:
84 &'static <dyn intercom::ISupportErrorInfo as intercom::attributes::ComInterfaceVariant<
85 intercom::type_system::AutomationTypeSystem,
86 >>::VTable
87 )];
88 let mut vtable_list_field_values = vec![];
89 let mut vtable_list_field_ptrs = vec![quote!(
90 _ISupportErrorInfo :
91 &<dyn intercom::ISupportErrorInfo as intercom::attributes::ComInterfaceVTableFor<
92 dyn intercom::ISupportErrorInfo,
93 #cls_ident #ty_generics,
94 intercom::type_system::AutomationTypeSystem>>::VTABLE
95 )];
96
97 for itf in &cls.interfaces {
101 let maybe_dyn = match cls.is_self_path(itf) {
102 true => quote!(),
103 false => quote_spanned!(itf.span() => dyn),
104 };
105 output.push(quote_spanned!(itf.span() =>
106 impl #impl_generics intercom::attributes::HasInterface<#maybe_dyn #itf> for #cls_ident #ty_generics #where_clause {}
107 ));
108
109 for &ts in &[ModelTypeSystem::Automation, ModelTypeSystem::Raw] {
110 let itf_ident = itf.get_some_ident().expect("#[com_interface] had no ident");
112 let itf_variant = Ident::new(&format!("{}_{:?}", itf_ident, ts), itf.span());
113 let ts_type = ts.as_typesystem_type(itf.span());
114
115 output.push(quote!(
117 #[allow(non_snake_case)]
118 impl #impl_generics intercom::attributes::ComClassInterface<
119 #maybe_dyn #itf, #ts_type> for #cls_ident #ty_generics #where_clause {
120
121 #[inline(always)]
122 fn offset() -> usize {
123 unsafe {
124 &intercom::ComBoxData::< #cls_ident #ty_generics >::null_vtable().#itf_variant
125 as *const _ as usize
126 }
127 }
128 }
129 ));
130
131 let itf_attrib_data = quote!(
135 <<#maybe_dyn #itf as intercom::attributes::ComInterface>::TSelf
136 as intercom::attributes::ComInterfaceVariant<#ts_type>>);
137 let itf_vtable_for = quote!(
138 <#maybe_dyn #itf as intercom::attributes::ComInterfaceVTableFor<#maybe_dyn #itf, #cls_ident #ty_generics, #ts_type>>);
139
140 vtable_list_field_defs.push(quote!( #itf_variant : #itf_attrib_data::VTable));
142 vtable_list_field_decls.push(quote!( #itf_variant : &'static #itf_attrib_data::VTable));
143 vtable_list_field_values.push(quote!( #itf_variant : #itf_vtable_for::VTABLE));
144 vtable_list_field_ptrs.push(quote!( #itf_variant : &#itf_vtable_for::VTABLE));
145
146 let itf_name = itf_ident.to_string();
150 let ts_name = format!("{:?}", ts);
151 query_interface_match_arms.push(quote!(
152 if riid == #itf_attrib_data::iid() {
153 let ptr = &vtables.#itf_variant
154 as *const &#itf_attrib_data::VTable
155 as *mut &#itf_attrib_data::VTable
156 as intercom::raw::RawComPtr;
157 intercom::logging::trace(|l| l(module_path!(), format_args!(
158 "[{:p}] {}::query_interface({:-X}) -> {} ({}) [{:p}]",
159 vtables, #cls_name, riid, #itf_name, #ts_name, ptr)));
160 ptr
161 } else
162 ));
163
164 support_error_info_match_arms.push(quote!(
166 if riid == #itf_attrib_data::iid() {
167 true
168 } else
169 ));
170 }
171 }
172
173 output.push(quote!(
178 #[allow(non_snake_case)]
179 impl #impl_generics intercom::attributes::ComClassInterface<
180 dyn intercom::ISupportErrorInfo,
181 intercom::type_system::AutomationTypeSystem>
182 for #cls_ident #ty_generics #where_clause {
183
184 #[inline(always)]
185 fn offset() -> usize { 0 }
186 }
187 ));
188
189 output.push(quote!(
191 impl #impl_generics intercom::attributes::HasInterface< dyn intercom::IUnknown > for #cls_ident #ty_generics #where_clause {}
192 ));
193
194 let vtable_list_ident = Ident::new(
201 &format!("__intercom_vtable_for_{}", cls_ident),
202 Span::call_site(),
203 );
204 let visibility = &cls.visibility;
205 output.push(quote!(
206 #[allow(non_snake_case)]
207 #[doc(hidden)]
208 #[derive(Clone, Copy)]
209 #visibility struct #vtable_list_ident {
210 #( #vtable_list_field_decls ),*
211 }
212 ));
213
214 let vtable_static_ident = Ident::new(
216 &format!("Static{}", vtable_list_ident),
217 vtable_list_ident.span(),
218 );
219 output.push(quote!(
220 #[allow(non_snake_case)]
221 #visibility struct #vtable_static_ident {
222 #( #vtable_list_field_defs ),*
223 }
224
225 #[allow(clippy::all)]
226 impl #impl_generics intercom::attributes::ComClass for #cls_ident #ty_generics #where_clause {
227 type VTableList = #vtable_list_ident;
228 const VTABLE : Self::VTableList = #vtable_list_ident {
229 #( #vtable_list_field_ptrs ),*
230 };
231 fn query_interface(
232 vtables : &Self::VTableList,
233 riid : intercom::REFIID,
234 ) -> intercom::RawComResult< intercom::raw::RawComPtr > {
235 if riid.is_null() {
236 intercom::logging::error(|l| l(module_path!(), format_args!(
237 "[{:p}] {}::query_interface(NULL)", vtables, #cls_name)));
238 return Err( intercom::raw::E_NOINTERFACE );
239 }
240 unsafe {
241 let riid = &*riid;
242 intercom::logging::trace(|l| l(module_path!(), format_args!(
243 "[{:p}] {}::query_interface({:-X})", vtables, #cls_name, riid)));
244 Ok(
245 #( #query_interface_match_arms )*
246 {
247 intercom::logging::trace(|l| l(module_path!(), format_args!(
248 "[{:p}] {}::query_interface({:-X}) -> E_NOINTERFACE", vtables, #cls_name, riid)));
249 return Err( intercom::raw::E_NOINTERFACE )
250 }
251 )
252 }
253 }
254
255 fn interface_supports_error_info(
256 riid : intercom::REFIID
257 ) -> bool
258 {
259 if riid.is_null() { return false; }
260 unsafe {
261 let riid = &*riid;
262 #( #support_error_info_match_arms )*
263 { false }
264 }
265 }
266 }
267 ));
268
269 let clsid_ident = idents::clsid(cls_ident);
271 if let Some(ref guid) = cls.clsid {
272 let clsid_guid_tokens = utils::get_guid_tokens(guid, Span::call_site());
273 let clsid_doc = format!("`{}` class ID.", cls_ident);
274 let clsid_const = quote!(
275 #[allow(non_upper_case_globals)]
276 #[doc = #clsid_doc ]
277 pub const #clsid_ident : intercom::CLSID = #clsid_guid_tokens;
278 );
279 output.push(clsid_const);
280 }
281
282 output.push(create_get_typeinfo_function(&cls));
283
284 Ok(tokens_to_tokenstream(item_tokens, output))
285}
286
287fn create_get_typeinfo_function(cls: &model::ComClass) -> TokenStream
288{
289 let fn_name = Ident::new(
290 &format!("get_intercom_coclass_info_for_{}", cls.name),
291 Span::call_site(),
292 );
293 let cls_ident = &cls.name;
294 let cls_name = cls.name.to_string();
295 let clsid = match &cls.clsid {
296 Some(guid) => guid,
297 None => {
298 return quote!(
299 pub(crate) fn #fn_name() -> Vec<intercom::typelib::TypeInfo>
300 { vec![] }
301 )
302 }
303 };
304 let clsid_tokens = utils::get_guid_tokens(clsid, Span::call_site());
305 let (impl_generics, ty_generics, where_clause) = cls.generics.split_for_impl();
306 let (interfaces, interface_info): (Vec<_>, Vec<_>) = cls
307 .interfaces
308 .iter()
309 .map(|itf_path| {
310 let itf_name = itf_path.get_some_ident().expect("#[com_interface] had no ident").to_string();
311 let maybe_dyn = match cls.is_self_path(itf_path) {
312 true => quote!(),
313 false => quote_spanned!(itf_path.span() => dyn),
314 };
315
316 let attr_cominterfacevariant = quote!(
320 <#maybe_dyn #itf_path as intercom::attributes::ComInterface>::TSelf
321 as intercom::attributes::ComInterfaceVariant
322 );
323 (
324 quote!( intercom::typelib::InterfaceRef {
325 name: #itf_name.into(),
326 iid_automation: <#attr_cominterfacevariant<intercom::type_system::AutomationTypeSystem>>::iid().clone(),
327 iid_raw: <#attr_cominterfacevariant<intercom::type_system::RawTypeSystem>>::iid().clone(),
328 } ),
329 quote!(
330 r.extend(<#maybe_dyn #itf_path as intercom::attributes::ComInterfaceTypeInfo>::gather_type_info());
331 ),
332 )
333 })
334 .unzip();
335 quote!(
336 impl #impl_generics intercom::attributes::ComClassTypeInfo for #cls_ident #ty_generics #where_clause
337 {
338 fn gather_type_info() -> Vec<intercom::typelib::TypeInfo>
339 {
340 let mut r = vec![ intercom::typelib::TypeInfo::Class(
341 intercom::ComBox::new( intercom::typelib::CoClass::__new(
342 #cls_name.into(),
343 #clsid_tokens,
344 vec![ #( #interfaces ),* ]
345 ) ) )
346 ];
347 #( #interface_info )*
348 r
349 }
350 }
351 )
352}