intercom_common/attributes/
com_interface.rs

1use super::common::*;
2use crate::prelude::*;
3
4use std::collections::BTreeMap;
5use std::iter;
6
7use crate::idents;
8use crate::methodinfo::ComMethodInfo;
9use crate::model;
10use crate::tyhandlers::{Direction, ModelTypeSystem};
11use crate::utils;
12
13use syn::spanned::Spanned;
14
15extern crate proc_macro;
16
17/// Interface level output.
18#[derive(Default)]
19struct InterfaceOutput
20{
21    iid_arms: Vec<TokenStream>,
22    method_impls: BTreeMap<String, MethodImpl>,
23}
24
25struct MethodImpl
26{
27    /// _Some_ method info.
28    ///
29    /// This should not be depended upon for anything type system specific.
30    info: ComMethodInfo,
31
32    /// Type system specific implementation for the method.
33    impls: BTreeMap<ModelTypeSystem, TokenStream>,
34}
35
36impl MethodImpl
37{
38    pub fn new(mi: ComMethodInfo) -> Self
39    {
40        MethodImpl {
41            info: mi,
42            impls: Default::default(),
43        }
44    }
45}
46
47/// Expands the `com_interface` attribute.
48///
49/// The attribute expansion results in the following items:
50///
51/// - Global IID for the interface.
52/// - Virtual table struct definition for the interface.
53/// - Implementation for the delegating methods when calling the COM interface
54///   from Rust.
55pub fn expand_com_interface(
56    attr_tokens: TokenStreamNightly,
57    item_tokens: TokenStreamNightly,
58) -> Result<TokenStreamNightly, model::ParseError>
59{
60    // Parse the attribute.
61    let mut output = vec![];
62    let itf =
63        model::ComInterface::from_ast(&lib_name(), attr_tokens.into(), item_tokens.clone().into())?;
64    let itf_path = &itf.path;
65    let itf_name = itf.ident.to_string();
66    let itf_ref = &itf.itf_ref;
67
68    let mut itf_output = InterfaceOutput::default();
69    for (ts, itf_variant) in &itf.variants {
70        process_itf_variant(&itf, *ts, itf_variant, &mut output, &mut itf_output);
71    }
72
73    if itf.item_type == utils::InterfaceType::Trait {
74        let mut impls = vec![];
75        for (_, method) in itf_output.method_impls.iter() {
76            let method_rust_ident = &method.info.name;
77            let method_name = method_rust_ident.to_string();
78            let mut impl_branches = vec![];
79            for (ts, method_ts_impl) in method.impls.iter() {
80                let ts_tokens = ts.as_typesystem_type(method.info.signature_span);
81                let ts_name = format!("{:?}", ts);
82                impl_branches.push(quote_spanned!(method.info.signature_span =>
83                    if let Some( comptr ) = intercom::ComItf::ptr::<#ts_tokens>( self ) {
84                        intercom::logging::trace(|l| l(module_path!(), format_args!(
85                            "[{:p}, with {:p}] Calling {}::{}, type system: {}",
86                            self, comptr.ptr, #itf_name, #method_name, #ts_name)));
87
88                        #method_ts_impl
89                    }
90                ));
91            }
92
93            // Format the method arguments into tokens.
94            let impl_args = method.info.args.iter().map(|ca| {
95                let name = &ca.name;
96                let ty = &ca.ty;
97                quote_spanned!(ca.span => #name : #ty )
98            });
99
100            let unsafety = if method.info.is_unsafe {
101                quote_spanned!(method.info.signature_span => unsafe)
102            } else {
103                quote!()
104            };
105            let self_arg = &method.info.rust_self_arg;
106            let return_ty = &method.info.rust_return_ty;
107
108            // Rust to COM implementation.
109            impls.push(quote_spanned!(method.info.signature_span =>
110                #unsafety fn #method_rust_ident(
111                    #self_arg, #( #impl_args ),*
112                ) -> #return_ty {
113
114                    intercom::logging::trace(|l| l(module_path!(), format_args!(
115                        "[{:p}] Calling {}::{}", self, #itf_name, #method_name)));
116
117                    #[allow(unused_imports)]
118                    use intercom::ErrorValue;
119
120                    // Try the available type systems.
121                    #( #impl_branches )*
122
123                    // The ComItf invariant states it has at least one pointer
124                    // available so we should never get here.
125                    //
126                    // Also since this is Rust-to-COM call we are allowed to
127                    // panic here.
128                    unreachable!();
129                }
130            ));
131        }
132
133        let unsafety = if itf.is_unsafe {
134            quote_spanned!(itf.span => unsafe)
135        } else {
136            quote!()
137        };
138        output.push(quote_spanned!(itf.span =>
139            #[allow(clippy::all)]
140            #[allow(unused_braces)]
141            #unsafety impl<I: intercom::attributes::ComInterface + #itf_path + ?Sized> #itf_path for intercom::ComItf<I> {
142                #( #impls )*
143            }
144        ));
145    }
146
147    // Implement the ComInterface for the trait.
148    let iid_arms = itf_output.iid_arms;
149    let (deref_impl, deref_ret) = if itf.item_type == utils::InterfaceType::Trait {
150        (
151            quote_spanned!(itf.span => com_itf),
152            quote_spanned!(itf.span => &( dyn #itf_path + 'static ) ),
153        )
154    } else {
155        // Note this is _extremely_ dangerous.
156        //
157        // Essentially we are assuming here that every #itf_path pointer represents
158        // a ComBox structure that we have created. This will fail the moment
159        // the user code implements #itf_path interface on their own and passes
160        // that back to us.
161        //
162        // There's no real way to get this to work and we might want to just remove
163        // the possibility to do 'implicit' interfaces by just impling the struct.
164        (
165            quote_spanned!(itf.span =>
166                let some_iunk : &intercom::ComItf<dyn intercom::interfaces::RawIUnknown> = com_itf.as_raw_iunknown();
167                let iunknown_iid = <dyn intercom::IUnknown>::iid(
168                        intercom::type_system::TypeSystemName::Automation )
169                            .expect( "IUnknown must have Automation IID" );
170                let primary_iunk = some_iunk.query_interface( iunknown_iid )
171                        .expect( "All types must implement IUnknown" );
172
173                let combox : *mut intercom::ComBoxData< #itf_path > =
174                        primary_iunk as *mut intercom::ComBoxData< #itf_path >;
175                unsafe {
176
177                    // We are already holding a reference to the 'self', which should
178                    // keep this alive. We don't need to maintain a lifetime of the
179                    // queried interface.
180                    intercom::ComBoxData::release( combox );
181
182                    // Deref.
183                    use std::ops::Deref;
184                    (*combox).deref()
185                }
186            ),
187            quote_spanned!(itf.span => & #itf_path ),
188        )
189    };
190
191    output.push(quote_spanned!(itf.span =>
192        impl intercom::attributes::ComInterface for #itf_ref {
193
194            type TSelf = #itf_ref;
195
196            #[doc = "Returns the IID of the requested interface."]
197            fn iid_ts<TS: intercom::type_system::TypeSystem>() -> &'static intercom::IID
198                where Self: intercom::attributes::ComInterfaceVariant<TS>
199            {
200                <Self as intercom::attributes::ComInterfaceVariant<TS>>::iid()
201            }
202
203            fn iid(
204                ts : intercom::type_system::TypeSystemName
205            ) -> Option< &'static intercom::IID >
206            {
207                match ts {
208                    #( #iid_arms ),*
209                }
210            }
211
212            fn deref(
213                com_itf : &intercom::ComItf<#itf_ref>
214            ) -> #deref_ret {
215                #deref_impl
216            }
217        }
218    ));
219
220    // Implement type info for the interface.
221    output.push(quote_spanned!(itf.span =>
222
223        impl intercom::type_system::ForeignType for #itf_ref {
224
225            /// The name of the type.
226            fn type_name() -> &'static str { stringify!( #itf_path )  }
227        }
228
229    ));
230
231    // Create runtime type info.
232    output.push(create_get_typeinfo_function(&itf));
233
234    Ok(tokens_to_tokenstream(item_tokens, output))
235}
236
237/// Processes the interface type system variant.
238///
239/// # Arguments
240///
241/// * `itf` - Interface details.
242/// * `ts` - Type system the variant represents.
243/// * `itf_variant` - Interface variant details.
244/// * `output` - Direct output emitted for each interface variant.
245/// * `itf_output` - Interface variant data for the interface level output.
246fn process_itf_variant(
247    itf: &model::ComInterface,
248    ts: ModelTypeSystem,
249    itf_variant: &model::ComInterfaceVariant,
250    output: &mut Vec<TokenStream>,
251    itf_output: &mut InterfaceOutput,
252)
253{
254    let itf_path = &itf.path;
255    let itf_ident = &itf.ident;
256    let visibility = &itf.visibility;
257    let ts_value_tokens = ts.as_typesystem_tokens(itf.span);
258    let ts_type_tokens = ts.as_typesystem_type(itf.span);
259    let itf_ref = &itf.itf_ref;
260    let vtable_path = itf.vtable(ts);
261    let attr_cominterfacevariant = quote_spanned!(itf_ident.span() =>
262        intercom::attributes::ComInterfaceVariant<#ts_type_tokens>);
263    let attr_cominterfacevtablefor = quote_spanned!(itf_ident.span() =>
264        intercom::attributes::ComInterfaceVTableFor<I, S, #ts_type_tokens>);
265
266    // Construct the iid(ts) match arm for this type system.
267    itf_output
268        .iid_arms
269        .push(quote_spanned!(itf.span => #ts_value_tokens => Some( <Self as #attr_cominterfacevariant>::iid() ) ));
270
271    // Create a vector for the virtual table fields and insert the base
272    // interface virtual table in it if required.
273    let mut vtbl_fields = vec![];
274    let mut vtbl_values = vec![];
275    if let Some(ref base) = itf.base_interface {
276        vtbl_values.push(quote_spanned!(itf.span =>
277                __base : <dyn #base as #attr_cominterfacevtablefor>::VTABLE));
278        vtbl_fields.push(quote_spanned!(itf.span =>
279                pub __base : <dyn #base as #attr_cominterfacevariant>::VTable));
280    }
281
282    // Gather all the trait methods for the remaining vtable fields.
283    for method_info in &itf_variant.methods {
284        // Create the vtable field and add it to the vector of fields.
285        let (vtbl_field, vtbl_value) = format_method_vtable_entries(itf, method_info, ts);
286        vtbl_fields.push(vtbl_field);
287        vtbl_values.push(vtbl_value);
288
289        // Ensure the MethodImpl exists for the method.
290        // These are shared by type systems so only one type system needs to
291        // add them here.
292        let method_name = method_info.name.to_string();
293        if !itf_output.method_impls.contains_key(&method_name) {
294            itf_output
295                .method_impls
296                .insert(method_name.clone(), MethodImpl::new(method_info.clone()));
297        }
298
299        let method_impl = &mut itf_output
300            .method_impls
301            .get_mut(&method_name)
302            .expect("We just ensured this exists three lines up... ;_;");
303        method_impl.impls.insert(
304            itf_variant.type_system,
305            rust_to_com_delegate(itf, itf_variant, method_info),
306        );
307
308        output.push(create_virtual_method(itf, method_info, ts));
309    }
310
311    // Create the vtable. We've already gathered all the vtable method
312    // pointer fields so defining the struct is simple enough.
313    let itf_bound = match itf.item_type {
314        utils::InterfaceType::Struct => quote!(),
315        utils::InterfaceType::Trait if itf.implemented_by.is_some() => quote!(),
316        utils::InterfaceType::Trait => quote!(+ #itf_ident),
317    };
318    if itf.vtable_of.is_none() {
319        output.push(quote_spanned!(itf.span =>
320            #[allow(non_camel_case_types)]
321            #[allow(non_snake_case)]
322            #[allow(clippy::all)]
323            #[repr(C)]
324            #[doc(hidden)]
325            #[derive(Clone, Copy)]
326            #visibility struct #vtable_path { #( #vtbl_fields, )* }
327
328            #[allow(unused)]
329            impl<I, S> intercom::attributes::ComInterfaceVTableFor<I, S, #ts_type_tokens> for #itf_ref
330            where I: ?Sized,
331                  S: intercom::attributes::ComClassInterface<I, #ts_type_tokens> + intercom::attributes::ComClass #itf_bound,
332            {
333                const VTABLE: #vtable_path = #vtable_path {
334                    #( #vtbl_values, )*
335                };
336            }
337        ));
338    }
339
340    let iid_tokens = utils::get_guid_tokens(&itf_variant.iid, itf.span);
341    output.push(quote_spanned!(itf_path.span() =>
342        #[allow(non_camel_case_types)]
343        #[allow(non_snake_case)]
344        #[allow(clippy::all)]
345        #[doc(hidden)]
346        impl #attr_cominterfacevariant for #itf_ref {
347            type VTable = #vtable_path;
348            fn iid() -> &'static intercom::IID {
349                & #iid_tokens
350            }
351        }
352    ));
353}
354
355/// Creates the functions responsible for delegating calls from Rust to COM
356/// interfaces.
357///
358/// # Arguments
359///
360/// * `itf` - Interface details.
361/// * `itf_variant` - Interface variant details.
362/// * `method_info` - Method to delegate.
363fn rust_to_com_delegate(
364    itf: &model::ComInterface,
365    itf_variant: &model::ComInterfaceVariant,
366    method_info: &ComMethodInfo,
367) -> TokenStream
368{
369    // The COM out-arguments that mirror the Rust return value will
370    // require temporary variables during the COM call. Format their
371    // declarations.
372    let infallible = method_info.returnhandler.is_infallible();
373    let out_arg_declarations = method_info
374        .returnhandler
375        .com_out_args()
376        .iter()
377        .map(|ca| {
378            let ident = &ca.name;
379            let ty = &ca.handler.com_ty(ca.span);
380            let default = ca.handler.default_value();
381            quote_spanned!(ca.span => let mut #ident : #ty = #default; )
382        })
383        .collect::<Vec<_>>();
384
385    // Format the in and out parameters for the COM call.
386    let params: Vec<_> = method_info
387        .raw_com_args()
388        .into_iter()
389        .map(|com_arg| {
390            let name = com_arg.name;
391            match com_arg.dir {
392                Direction::In => {
393                    com_arg
394                        .handler
395                        .rust_to_com(&name, com_arg.span, Direction::In, infallible)
396                }
397                Direction::Out | Direction::Retval => quote_spanned!(com_arg.span => &mut #name ),
398            }
399        })
400        .collect();
401
402    // Combine the parameters into the final parameter list.
403    // This includes the 'this' pointer and both the IN and OUT
404    // parameters.
405    let params =
406        iter::once(quote_spanned!(method_info.rust_self_arg.span() => comptr.ptr.as_ptr()))
407            .chain(params);
408
409    // Create the return statement.
410    let return_ident = Ident::new("__result", Span::call_site());
411    let return_statement = method_info.returnhandler.com_to_rust_return(&return_ident);
412
413    // Resolve some of the fields needed for quote.
414    let method_ident = &method_info.name;
415    let return_ty = &method_info.rust_return_ty;
416    let iid_tokens = utils::get_guid_tokens(&itf_variant.iid, method_info.signature_span);
417    let itf_ref = &itf.itf_ref;
418    let ts_type = itf_variant.type_system.as_typesystem_type(itf.span);
419
420    // Construct the final method.
421    if infallible {
422        quote_spanned!(method_info.signature_span =>
423            #[allow(unused_imports)]
424            let vtbl = comptr.ptr.as_ptr() as *const *const <#itf_ref as
425                intercom::attributes::ComInterfaceVariant<#ts_type>>::VTable;
426
427            #[allow(unused_unsafe)]  // The fn itself _might_ be unsafe.
428            unsafe {
429                #( #out_arg_declarations )*
430                let #return_ident = ((**vtbl).#method_ident)( #( #params ),* );
431
432                let __intercom_iid = #iid_tokens;
433                #[allow(unused_braces)]
434                return { #return_statement };
435            }
436        )
437    } else {
438        quote_spanned!(method_info.signature_span =>
439            #[allow(unused_imports)]
440            let vtbl = comptr.ptr.as_ptr() as *const *const <#itf_ref as
441                intercom::attributes::ComInterfaceVariant<#ts_type>>::VTable;
442
443            // Use an IIFE to act as a try/catch block. The various template
444            // substitutions might end up using ?'s for error handling. The IIFE allows
445            // us to handle the results here immediately.
446            #[allow(unused_unsafe)]  // The fn itself _might_ be unsafe.
447            let __intercom_result : Result< #return_ty, intercom::ComError > = ( || unsafe {
448                #( #out_arg_declarations )*
449                let #return_ident = ((**vtbl).#method_ident)( #( #params ),* );
450
451                let __intercom_iid = #iid_tokens;
452                #[allow(unused_braces)]
453                Ok( { #return_statement } )
454            } )();
455
456            return match __intercom_result {
457                Ok( v ) => v,
458                Err( err ) => < #return_ty as intercom::ErrorValue >::from_error( err ),
459            };
460        )
461    }
462}
463
464fn format_method_vtable_entries(
465    itf: &model::ComInterface,
466    method_info: &ComMethodInfo,
467    ts: ModelTypeSystem,
468) -> (TokenStream, TokenStream)
469{
470    let itf_ident = &itf.ident;
471    let method_ident = &method_info.name;
472    let method_impl_ident = idents::com_to_rust_method_impl(itf_ident, method_ident, ts);
473    let ret_ty = method_info.returnhandler.com_ty();
474    let generics = match itf.item_type {
475        utils::InterfaceType::Struct => quote!(),
476        utils::InterfaceType::Trait => quote!(::<I, S>),
477    };
478    let params = method_info.get_parameters_tokenstream();
479
480    (
481        quote_spanned!(method_info.signature_span =>
482            pub #method_ident : unsafe extern "system" fn(#params) -> #ret_ty),
483        quote_spanned!(method_info.signature_span =>
484            #method_ident: #method_impl_ident #generics),
485    )
486}
487
488fn create_virtual_method(
489    itf: &model::ComInterface,
490    method_info: &ComMethodInfo,
491    ts: ModelTypeSystem,
492) -> TokenStream
493{
494    let itf_ident = &itf.ident;
495    let method_ident = &method_info.name;
496    let method_name = method_ident.to_string();
497    let method_impl_ident = idents::com_to_rust_method_impl(itf_ident, method_ident, ts);
498    let infallible = method_info.returnhandler.is_infallible();
499    let itf_ident = &itf.ident;
500    let itf_path = &itf.path;
501    let ts_type_tokens = ts.as_typesystem_type(itf.span);
502    let params = method_info.get_parameters_tokenstream();
503    let attr_comclassinterface = quote!(intercom::attributes::ComClassInterface);
504
505    // Format the in and out parameters for the Rust call.
506    let in_args: Vec<_> = method_info
507        .args
508        .iter()
509        .map(|ca| {
510            ca.handler
511                .com_to_rust(&ca.name, ca.span, Direction::In, infallible)
512        })
513        .collect();
514
515    let return_ident = Ident::new("__result", Span::call_site());
516    let return_statement = method_info.returnhandler.rust_to_com_return(&return_ident);
517    let ret_ty = method_info.returnhandler.com_ty();
518
519    // Figure out how to get the self struct reference.
520    let self_struct_expr = if itf.implemented_by.is_some() {
521        quote!(&*self_combox)
522    } else if method_info.is_const {
523        quote!(&**self_combox)
524    } else {
525        quote!(&mut **self_combox)
526    };
527
528    // The implemented_by option affects the actual method implementation
529    // as well as the interface bounds. Interfaces implemented manually
530    // do not require the interface as a bound.
531    let (required_itf, call) = match &itf.implemented_by {
532        Some(path) => (
533            quote!(),
534            quote!(#path::#method_ident(self_struct, #( #in_args ),*)),
535        ),
536        None => (
537            quote!(+ #itf_ident),
538            quote!(self_struct.#method_ident( #( #in_args ),* )),
539        ),
540    };
541
542    // Since "+ Struct" is an invalid bound and wouldn't really make sense
543    // anywya, we won't use generic parameters on struct impl based implicit
544    // interfaces.
545    let (generics, bounds, s_ref, i_ref) = match itf.item_type {
546        utils::InterfaceType::Struct => (quote!(), quote!(), quote!(#itf_path), quote!(#itf_path)),
547        utils::InterfaceType::Trait => (
548            quote!(<I, S>),
549            quote!(
550                where I: ?Sized,
551                      S: #attr_comclassinterface<I, #ts_type_tokens> + intercom::attributes::ComClass #required_itf
552            ),
553            quote!(S),
554            quote!(I),
555        ),
556    };
557
558    // Format the payload depending on whether the method is infallible or not.
559    let payload = if infallible {
560        quote!(
561            let self_struct = #self_struct_expr;
562            let #return_ident = #call;
563
564            intercom::logging::trace(|l| l(module_path!(), format_args!(
565                "[{:p}, through {:p}] Serving {}::{}, OK",
566                self_combox, self_vtable, std::any::type_name::<#s_ref>(), #method_name)));
567
568            #return_statement
569        )
570    } else {
571        // Fallible methods require an error-catching closure and error handling.
572        quote!(
573            let result : Result< #ret_ty, intercom::ComError > = ( || {
574                let self_struct = #self_struct_expr;
575                let #return_ident = #call;
576                Ok( { #return_statement } )
577            } )();
578
579            match result {
580                Ok( v ) => {
581                    intercom::logging::trace(|l| l(module_path!(), format_args!(
582                        "[{:p}, through {:p}] Serving {}::{}, OK",
583                        self_combox, self_vtable,
584                        std::any::type_name::<#s_ref>(), #method_name)));
585                    v
586                },
587                Err( err ) => {
588                    intercom::logging::trace(|l| l(module_path!(), format_args!(
589                        "[{:p}, through {:p}] Serving {}::{}, ERROR",
590                        self_combox, self_vtable,
591                        std::any::type_name::<#s_ref>(), #method_name)));
592                    <#ret_ty as intercom::ErrorValue>::from_error(
593                        intercom::store_error(err))
594                },
595            }
596        )
597    };
598
599    quote!(
600        #[allow(non_snake_case)]
601        #[allow(dead_code)]
602        #[doc(hidden)]
603        unsafe extern "system" fn #method_impl_ident #generics(
604            #params
605        ) -> #ret_ty
606        #bounds
607        {
608            // Acquire the reference to the ComBoxData. For this we need
609            // to offset the current 'self_vtable' vtable pointer.
610            let offset = <#s_ref as #attr_comclassinterface<#i_ref, #ts_type_tokens>>::offset();
611            let self_combox = ( self_vtable as usize - offset )
612                    as *mut intercom::ComBoxData<#s_ref>;
613
614            intercom::logging::trace(|l| l(module_path!(), format_args!(
615                "[{:p}, through {:p}] Serving {}::{}",
616                self_combox, self_vtable,
617                std::any::type_name::<#s_ref>(), #method_name)));
618
619            #payload
620        }
621    )
622}
623
624fn create_get_typeinfo_function(itf: &model::ComInterface) -> TokenStream
625{
626    let itf_name = itf.ident.to_string();
627    let itf_ref = &itf.itf_ref;
628    let mut variant_tokens = vec![];
629    for (ts, variant) in &itf.variants {
630        variant_tokens.push(create_typeinfo_for_variant(itf, *ts, variant));
631    }
632    let is_impl_interface = itf.item_type == utils::InterfaceType::Struct;
633
634    quote_spanned!(itf.span =>
635        #[allow(non_snake_case)]
636        #[allow(dead_code)]
637        impl intercom::attributes::ComInterfaceTypeInfo for #itf_ref
638        {
639            fn gather_type_info() -> Vec<intercom::typelib::TypeInfo>
640            {
641                let variants = vec![ #( #variant_tokens ),* ];
642
643                vec![ intercom::typelib::TypeInfo::Interface(
644                    intercom::ComBox::new( intercom::typelib::Interface {
645                        name: #itf_name.into(),
646                        variants,
647                        options: intercom::typelib::InterfaceOptions {
648                            class_impl_interface: #is_impl_interface,
649                            ..Default::default()
650                        }
651                    })
652                ) ]
653            }
654        }
655    )
656}
657
658fn create_typeinfo_for_variant(
659    itf: &model::ComInterface,
660    ts: ModelTypeSystem,
661    itf_variant: &model::ComInterfaceVariant,
662) -> TokenStream
663{
664    let ts_tokens = ts.as_typesystem_tokens(itf.span);
665    let ts_type = ts.as_typesystem_type(itf.span);
666    let iid_tokens = utils::get_guid_tokens(&itf_variant.iid, itf.span);
667    let methods = itf_variant.methods.iter().map( |m| {
668        let method_name = m.name.to_string();
669        let return_type = match &m.return_type {
670            Some(rt) => quote_spanned!(m.signature_span =>
671                intercom::typelib::Arg {
672                    name: "".into(),
673                    ty: <
674                        <#rt as intercom::type_system::ExternType<#ts_type>>::ForeignType
675                        as intercom::type_system::ForeignType>::type_name().into(),
676                    indirection_level: <
677                        <#rt as intercom::type_system::ExternType<#ts_type>>::ForeignType
678                        as intercom::type_system::ForeignType>::indirection_level(),
679                    direction: intercom::typelib::Direction::Return,
680                }),
681            None => quote_spanned!(m.signature_span => intercom::typelib::Arg {
682                name: "".into(),
683                ty: "void".into(),
684                indirection_level: 0,
685                direction: intercom::typelib::Direction::Return,
686            } ),
687        };
688
689        let params = m.raw_com_args().into_iter().map(|arg| {
690            let com_ty = arg.handler.com_ty(arg.span);
691            let arg_name = arg.name.to_string();
692            let dir_ident = Ident::new(match arg.dir {
693                Direction::In => "In",
694                Direction::Out => "Out",
695                Direction::Retval => "Retval"
696            }, arg.span);
697
698            quote_spanned!(arg.span => intercom::typelib::Arg {
699                name: #arg_name.into(),
700                ty: <#com_ty as intercom::type_system::ForeignType>::type_name().into(),
701                indirection_level: <#com_ty as intercom::type_system::ForeignType>::indirection_level(),
702                direction: intercom::typelib::Direction::#dir_ident,
703            })
704        }).collect::<Vec<_>>();
705
706        quote_spanned!(m.signature_span =>
707            intercom::ComBox::new(intercom::typelib::Method {
708                name: #method_name.into(),
709                return_type: #return_type,
710                parameters: vec![ #( #params ),* ],
711            })
712        )
713    }).collect::<Vec<_>>();
714
715    quote_spanned!(itf.span =>
716        intercom::ComBox::new( intercom::typelib::InterfaceVariant {
717            ts: #ts_tokens,
718            iid: #iid_tokens,
719            methods: vec![ #( #methods ),* ],
720        })
721    )
722}