summaryrefslogtreecommitdiffstats
path: root/vendor/windows-bindgen/src/rust/implements.rs
blob: d06d8f8f1b27ecebe811ae4a62688ea7acf205cc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
use super::*;

pub fn writer(writer: &Writer, def: TypeDef) -> TokenStream {
    if def.kind() != TypeKind::Interface || (!writer.implement && def.has_attribute("ExclusiveToAttribute")) {
        return quote! {};
    }

    let generics = &type_def_generics(def);
    let type_ident = to_ident(def.name());
    let impl_ident = type_ident.join("_Impl");
    let vtbl_ident = type_ident.join("_Vtbl");
    let implvtbl_ident = impl_ident.join("Vtbl");
    let constraints = writer.generic_constraints(generics);
    let generic_names = writer.generic_names(generics);
    let named_phantoms = writer.generic_named_phantoms(generics);
    let cfg = type_def_cfg_impl(def, generics);
    let doc = writer.cfg_doc(&cfg);
    let features = writer.cfg_features(&cfg);
    let mut requires = quote! {};
    let type_ident = quote! { #type_ident<#generic_names> };
    let vtables = type_def_vtables(def);
    let has_unknown_base = matches!(vtables.first(), Some(Type::IUnknown));

    fn gen_required_trait(writer: &Writer, def: TypeDef, generics: &[Type]) -> TokenStream {
        let name = writer.type_def_name_imp(def, generics, "_Impl");
        quote! {
            + #name
        }
    }

    let mut matches = quote! { *iid == <#type_ident as ::windows_core::ComInterface>::IID };

    if let Some(Type::TypeDef(def, _)) = vtables.last() {
        requires.combine(&gen_required_trait(writer, *def, &[]))
    }

    for def in &vtables {
        if let Type::TypeDef(def, generics) = def {
            let name = writer.type_def_name(*def, generics);

            matches.combine(&quote! {
                || *iid == <#name as ::windows_core::ComInterface>::IID
            })
        }
    }

    if def.flags().contains(TypeAttributes::WindowsRuntime) {
        // TODO: this awkward wrapping of TypeDefs needs fixing
        for interface in type_interfaces(&Type::TypeDef(def, generics.to_vec())) {
            if let Type::TypeDef(def, generics) = interface.ty {
                requires.combine(&gen_required_trait(writer, def, &generics));
            }
        }
    }

    let runtime_name = writer.runtime_name_trait(def, generics, &type_ident, &constraints, &features);

    let mut method_names = MethodNames::new();
    method_names.add_vtable_types(def);

    let method_traits = def.methods().map(|method| {
        let name = method_names.add(method);

        let signature = method_def_signature(def.namespace(), method, generics);

        let signature_tokens = writer.impl_signature(def, &signature);
        quote! { fn #name #signature_tokens; }
    });

    let mut method_names = MethodNames::new();
    method_names.add_vtable_types(def);

    let method_impls = def.methods().map(|method| {
        let name = method_names.add(method);
        let signature = method_def_signature(def.namespace(), method, generics);
        let vtbl_signature = writer.vtbl_signature(def, generics, &signature);

        let invoke_upcall = if def.flags().contains(TypeAttributes::WindowsRuntime) { winrt_methods::gen_upcall(writer, &signature, quote! { this.#name }) } else { com_methods::gen_upcall(writer, &signature, quote! { this.#name }) };

        if has_unknown_base {
            quote! {
                unsafe extern "system" fn #name<#constraints Identity: ::windows_core::IUnknownImpl<Impl = Impl>, Impl: #impl_ident<#generic_names>, const OFFSET: isize> #vtbl_signature {
                    // offset the `this` pointer by `OFFSET` times the size of a pointer and cast it as an IUnknown implementation
                    let this = (this as *const *const ()).offset(OFFSET) as *const Identity;
                    let this = (*this).get_impl();
                    #invoke_upcall
                }
            }
        } else {
            quote! {
                unsafe extern "system" fn #name<Impl: #impl_ident> #vtbl_signature {
                    let this = (this as *mut *mut ::core::ffi::c_void) as *const ::windows_core::ScopedHeap;
                    let this = &*((*this).this as *const Impl);
                    #invoke_upcall
                }
            }
        }
    });

    let mut methods = quote! {};

    match vtables.last() {
        Some(Type::IUnknown) => methods.combine(&quote! { base__: ::windows_core::IUnknown_Vtbl::new::<Identity, OFFSET>(), }),
        Some(Type::IInspectable) => methods.combine(&quote! { base__: ::windows_core::IInspectable_Vtbl::new::<Identity, #type_ident, OFFSET>(), }),
        Some(Type::TypeDef(def, generics)) => {
            let name = writer.type_def_name_imp(*def, generics, "_Vtbl");
            if has_unknown_base {
                methods.combine(&quote! { base__: #name::new::<Identity, Impl, OFFSET>(), });
            } else {
                methods.combine(&quote! { base__: #name::new::<Impl>(), });
            }
        }
        _ => {}
    }

    let mut method_names = MethodNames::new();
    method_names.add_vtable_types(def);

    for method in def.methods() {
        let name = method_names.add(method);
        if has_unknown_base {
            methods.combine(&quote! { #name: #name::<#generic_names Identity, Impl, OFFSET>, });
        } else {
            methods.combine(&quote! { #name: #name::<Impl>, });
        }
    }

    if has_unknown_base {
        quote! {
            #doc
            #features
            pub trait #impl_ident<#generic_names> : Sized #requires where #constraints {
                #(#method_traits)*
            }
            #runtime_name
            #features
            impl<#constraints> #vtbl_ident<#generic_names> {
                pub const fn new<Identity: ::windows_core::IUnknownImpl<Impl = Impl>, Impl: #impl_ident<#generic_names>, const OFFSET: isize>() -> #vtbl_ident<#generic_names> {
                    #(#method_impls)*
                    Self{
                        #methods
                        #(#named_phantoms)*
                    }
                }
                pub unsafe fn matches(iid: *const ::windows_core::GUID) -> bool {
                    #matches
                }
            }
        }
    } else {
        quote! {
            #doc
            #features
            pub trait #impl_ident : Sized #requires {
                #(#method_traits)*
            }
            #features
            impl #vtbl_ident {
                pub const fn new<Impl: #impl_ident>() -> #vtbl_ident {
                    #(#method_impls)*
                    Self{
                        #methods
                        #(#named_phantoms)*
                    }
                }
            }
            #[doc(hidden)]
            #features
            struct #implvtbl_ident<T: #impl_ident> (::std::marker::PhantomData<T>);
            #features
            impl<T: #impl_ident> #implvtbl_ident<T> {
                const VTABLE: #vtbl_ident = #vtbl_ident::new::<T>();
            }
            #features
            impl #type_ident {
                pub fn new<'a, T: #impl_ident>(this: &'a T) -> ::windows_core::ScopedInterface<'a, Self> {
                    let this = ::windows_core::ScopedHeap { vtable: &#implvtbl_ident::<T>::VTABLE as *const _ as *const _, this: this as *const _ as *const _ };
                    let this = ::std::mem::ManuallyDrop::new(::std::boxed::Box::new(this));
                    unsafe { ::windows_core::ScopedInterface::new(::std::mem::transmute(&this.vtable)) }
                }
            }
        }
    }
}