1 | use super::*; |
2 | |
3 | #[derive (Clone, Debug)] |
4 | pub enum CppMethodOrName { |
5 | Method(CppMethod), |
6 | Name(&'static str), |
7 | } |
8 | |
9 | #[derive (Clone, Debug, Hash, PartialEq, Eq)] |
10 | pub struct CppInterface { |
11 | pub def: TypeDef, |
12 | } |
13 | |
14 | impl Ord for CppInterface { |
15 | fn cmp(&self, other: &Self) -> Ordering { |
16 | self.def.name().cmp(other.def.name()) |
17 | } |
18 | } |
19 | |
20 | impl PartialOrd for CppInterface { |
21 | fn partial_cmp(&self, other: &Self) -> Option<Ordering> { |
22 | Some(self.cmp(other)) |
23 | } |
24 | } |
25 | |
26 | impl CppInterface { |
27 | pub fn type_name(&self) -> TypeName { |
28 | self.def.type_name() |
29 | } |
30 | |
31 | pub fn get_methods(&self, writer: &Writer) -> Vec<CppMethodOrName> { |
32 | let namespace = self.def.namespace(); |
33 | |
34 | self.def |
35 | .methods() |
36 | .map(|def| { |
37 | let method = CppMethod::new(def, namespace); |
38 | if method.dependencies.included(writer.config) { |
39 | CppMethodOrName::Method(method) |
40 | } else { |
41 | CppMethodOrName::Name(method.def.name()) |
42 | } |
43 | }) |
44 | .collect() |
45 | } |
46 | |
47 | pub fn write(&self, writer: &Writer) -> TokenStream { |
48 | let methods = self.get_methods(writer); |
49 | |
50 | let base_interfaces = self.base_interfaces(); |
51 | let has_unknown_base = matches!(base_interfaces.first(), Some(Type::IUnknown)); |
52 | |
53 | let mut dependencies = TypeMap::new(); |
54 | |
55 | if writer.config.package { |
56 | self.dependencies(&mut dependencies); |
57 | } |
58 | |
59 | let cfg = writer.write_cfg(self.def, self.def.namespace(), &dependencies, false); |
60 | let vtbl_name = self.write_vtbl_name(writer); |
61 | |
62 | let vtbl = { |
63 | let core = writer.write_core(); |
64 | |
65 | let base = match base_interfaces.last() { |
66 | Some(Type::IUnknown) => { |
67 | quote! { pub base__: #core IUnknown_Vtbl, } |
68 | } |
69 | Some(Type::Object) => { |
70 | quote! { pub base__: #core IInspectable_Vtbl, } |
71 | } |
72 | Some(Type::CppInterface(ty)) => { |
73 | let name = ty.write_vtbl_name(writer); |
74 | quote! { pub base__: #name, } |
75 | } |
76 | _ => quote! {}, |
77 | }; |
78 | |
79 | let mut names = MethodNames::new(); |
80 | |
81 | let methods = methods.iter().map(|method| match method { |
82 | CppMethodOrName::Method(method) => { |
83 | let mut difference = TypeMap::new(); |
84 | |
85 | if writer.config.package { |
86 | difference = method.dependencies.difference(&dependencies); |
87 | } |
88 | |
89 | let name = names.add(method.def); |
90 | let abi = method.write_abi(writer, false); |
91 | let cfg = writer.write_cfg(self.def, self.def.namespace(), &difference, false); |
92 | |
93 | if cfg.is_empty() { |
94 | quote! { |
95 | pub #name: unsafe extern "system" fn #abi, |
96 | } |
97 | } else { |
98 | let cfg_not = |
99 | writer.write_cfg(self.def, self.def.namespace(), &difference, true); |
100 | |
101 | quote! { |
102 | #cfg |
103 | pub #name: unsafe extern "system" fn #abi, |
104 | #cfg_not |
105 | #name: usize, |
106 | } |
107 | } |
108 | } |
109 | CppMethodOrName::Name(name) => { |
110 | let name = to_ident(name); |
111 | quote! { #name: usize, } |
112 | } |
113 | }); |
114 | |
115 | quote! { |
116 | #cfg |
117 | #[repr(C)] |
118 | pub struct #vtbl_name { |
119 | #base |
120 | #(#methods)* |
121 | } |
122 | } |
123 | }; |
124 | |
125 | if writer.config.sys { |
126 | let mut result = quote! {}; |
127 | |
128 | if !writer.config.package { |
129 | if has_unknown_base { |
130 | if let Some(guid) = self.def.guid_attribute() { |
131 | let name: TokenStream = format!("IID_ {}" , self.def.name()).into(); |
132 | result.combine(writer.write_cpp_const_guid(name, &guid)); |
133 | } |
134 | } |
135 | |
136 | result.combine(vtbl); |
137 | } |
138 | |
139 | result |
140 | } else { |
141 | let name = to_ident(self.def.name()); |
142 | |
143 | let mut result = if has_unknown_base { |
144 | if let Some(guid) = self.def.guid_attribute() { |
145 | let guid = writer.write_guid_u128(&guid); |
146 | |
147 | quote! { |
148 | #cfg |
149 | windows_core::imp::define_interface!(#name, #vtbl_name, #guid); |
150 | } |
151 | } else { |
152 | quote! { |
153 | #cfg |
154 | windows_core::imp::define_interface!(#name, #vtbl_name, 0); |
155 | } |
156 | } |
157 | } else { |
158 | quote! { |
159 | #cfg |
160 | windows_core::imp::define_interface!(#name, #vtbl_name); |
161 | } |
162 | }; |
163 | |
164 | if let Some(Type::CppInterface(base)) = base_interfaces.last() { |
165 | let base = base.write_name(writer); |
166 | |
167 | result.combine(quote! { |
168 | #cfg |
169 | impl core::ops::Deref for #name { |
170 | type Target = #base; |
171 | fn deref(&self) -> &Self::Target { |
172 | unsafe { core::mem::transmute(self) } |
173 | } |
174 | } |
175 | }); |
176 | } |
177 | |
178 | if !base_interfaces.is_empty() { |
179 | let bases = base_interfaces.iter().map(|ty| ty.write_name(writer)); |
180 | result.combine(quote! { |
181 | #cfg |
182 | windows_core::imp::interface_hierarchy!(#name, #(#bases),*); |
183 | }) |
184 | } |
185 | |
186 | let method_names = &mut MethodNames::new(); |
187 | let virtual_names = &mut MethodNames::new(); |
188 | let mut methods_tokens = quote! {}; |
189 | |
190 | for method in methods.iter().filter_map(|method| match &method { |
191 | CppMethodOrName::Method(method) => Some(method), |
192 | _ => None, |
193 | }) { |
194 | let mut difference = TypeMap::new(); |
195 | |
196 | if writer.config.package { |
197 | difference = method.dependencies.difference(&dependencies); |
198 | } |
199 | |
200 | let cfg = writer.write_cfg(self.def, self.def.namespace(), &difference, false); |
201 | |
202 | let method = method.write(writer, method_names, virtual_names); |
203 | |
204 | methods_tokens.combine(quote! { |
205 | #cfg |
206 | #method |
207 | }); |
208 | } |
209 | |
210 | if !methods_tokens.is_empty() { |
211 | result.combine(quote! { |
212 | #cfg |
213 | impl #name { |
214 | #methods_tokens |
215 | } |
216 | }); |
217 | } |
218 | |
219 | result.combine(vtbl); |
220 | |
221 | let impl_name: TokenStream = format!(" {}_Impl" , self.def.name()).into(); |
222 | |
223 | if writer.config.package { |
224 | fn collect(interface: &CppInterface, dependencies: &mut TypeMap, writer: &Writer) { |
225 | for method in interface.get_methods(writer).iter() { |
226 | if let CppMethodOrName::Method(method) = method { |
227 | dependencies.combine(&method.dependencies); |
228 | } |
229 | } |
230 | } |
231 | |
232 | collect(self, &mut dependencies, writer); |
233 | base_interfaces.iter().for_each(|interface| { |
234 | if let Type::CppInterface(ty) = interface { |
235 | collect(ty, &mut dependencies, writer); |
236 | } |
237 | }); |
238 | } |
239 | |
240 | let cfg = writer.write_cfg(self.def, self.def.namespace(), &dependencies, false); |
241 | |
242 | let mut names = MethodNames::new(); |
243 | |
244 | let field_methods: Vec<_> = methods |
245 | .iter() |
246 | .map(|method| match method { |
247 | CppMethodOrName::Method(method) => { |
248 | let name = names.add(method.def); |
249 | if has_unknown_base { |
250 | quote! { #name: #name::<Identity, OFFSET>, } |
251 | } else { |
252 | quote! { #name: #name::<Identity>, } |
253 | } |
254 | } |
255 | CppMethodOrName::Name(name) => { |
256 | let name = to_ident(name); |
257 | quote! { #name: 0, } |
258 | } |
259 | }) |
260 | .collect(); |
261 | |
262 | let mut names = MethodNames::new(); |
263 | |
264 | let impl_methods: Vec<_> = methods.iter().map(|method| match method { |
265 | CppMethodOrName::Method(method) => { |
266 | let name = names.add(method.def); |
267 | let signature = method.write_abi(writer, true); |
268 | let upcall = method.write_upcall(&impl_name, &name); |
269 | |
270 | if has_unknown_base { |
271 | quote! { |
272 | unsafe extern "system" fn #name<Identity: #impl_name, const OFFSET: isize> #signature { |
273 | unsafe { |
274 | let this: &Identity = &*((this as *const *const ()).offset(OFFSET) as *const Identity); |
275 | #upcall |
276 | } |
277 | } |
278 | } |
279 | } else { |
280 | quote! { |
281 | unsafe extern "system" fn #name<Identity: #impl_name> #signature { |
282 | unsafe { |
283 | let this = (this as *mut *mut core::ffi::c_void) as *const windows_core::ScopedHeap; |
284 | let this = &*((*this).this as *const Identity); |
285 | #upcall |
286 | } |
287 | } |
288 | } |
289 | } |
290 | } |
291 | _ => quote! {}, |
292 | }).collect(); |
293 | |
294 | let mut names = MethodNames::new(); |
295 | |
296 | let trait_methods: Vec<_> = methods |
297 | .iter() |
298 | .map(|method| match method { |
299 | CppMethodOrName::Method(method) => { |
300 | let name = names.add(method.def); |
301 | let signature = method.write_impl_signature(writer, true); |
302 | quote! { fn #name #signature; } |
303 | } |
304 | _ => quote! {}, |
305 | }) |
306 | .collect(); |
307 | |
308 | let impl_base = base_interfaces.last().map(|ty| ty.write_impl_name(writer)); |
309 | |
310 | let field_base = base_interfaces.last().map(|ty|{ |
311 | match ty { |
312 | Type::IUnknown => quote! { base__: windows_core::IUnknown_Vtbl::new::<Identity, OFFSET>(), }, |
313 | Type::Object => quote! { base__: windows_core::IInspectable_Vtbl::new::<Identity, #name, OFFSET>(), }, |
314 | Type::CppInterface(ty) => { |
315 | let ty = ty.write_vtbl_name(writer); |
316 | if has_unknown_base { |
317 | quote! { base__: #ty::new::<Identity, OFFSET>(), } |
318 | } else { |
319 | quote! { base__: #ty::new::<Identity>(), } |
320 | } |
321 | } |
322 | rest => panic!(" {rest:?}" ), |
323 | } |
324 | }); |
325 | |
326 | result.combine( if has_unknown_base { |
327 | let matches = base_interfaces.iter().filter_map(|ty|{ |
328 | match ty { |
329 | Type::CppInterface(ty) => { |
330 | let name = ty.write_name(writer); |
331 | Some(quote! { || iid == &<#name as windows_core::Interface>::IID }) |
332 | } |
333 | _ => None, |
334 | } |
335 | }); |
336 | |
337 | quote! { |
338 | #cfg |
339 | pub trait #impl_name: #impl_base { |
340 | #(#trait_methods)* |
341 | } |
342 | #cfg |
343 | impl #vtbl_name { |
344 | pub const fn new<Identity: #impl_name, const OFFSET: isize>() -> Self { |
345 | #(#impl_methods)* |
346 | Self { |
347 | #field_base |
348 | #(#field_methods)* |
349 | } |
350 | } |
351 | pub fn matches(iid: &windows_core::GUID) -> bool { |
352 | iid == &<#name as windows_core::Interface>::IID #(#matches)* |
353 | } |
354 | } |
355 | #cfg |
356 | impl windows_core::RuntimeName for #name {} |
357 | } |
358 | } else { |
359 | let implvtbl_ident = impl_name.join("Vtbl" ); |
360 | |
361 | quote! { |
362 | #cfg |
363 | pub trait #impl_name : #impl_base { |
364 | #(#trait_methods)* |
365 | } |
366 | #cfg |
367 | impl #vtbl_name { |
368 | pub const fn new<Identity: #impl_name>() -> Self { |
369 | #(#impl_methods)* |
370 | Self{ |
371 | #field_base |
372 | #(#field_methods)* |
373 | } |
374 | } |
375 | } |
376 | #cfg |
377 | struct #implvtbl_ident<T: #impl_name> (core::marker::PhantomData<T>); |
378 | #cfg |
379 | impl<T: #impl_name> #implvtbl_ident<T> { |
380 | const VTABLE: #vtbl_name = #vtbl_name::new::<T>(); |
381 | } |
382 | #cfg |
383 | impl #name { |
384 | pub fn new<'a, T: #impl_name>(this: &'a T) -> windows_core::ScopedInterface<'a, Self> { |
385 | let this = windows_core::ScopedHeap { vtable: &#implvtbl_ident::<T>::VTABLE as *const _ as *const _, this: this as *const _ as *const _ }; |
386 | let this = core::mem::ManuallyDrop::new(windows_core::imp::Box::new(this)); |
387 | unsafe { windows_core::ScopedInterface::new(core::mem::transmute(&this.vtable)) } |
388 | } |
389 | } |
390 | } |
391 | }); |
392 | |
393 | if self.def.is_agile() { |
394 | result.combine(quote! { |
395 | #cfg |
396 | unsafe impl Send for #name {} |
397 | #cfg |
398 | unsafe impl Sync for #name {} |
399 | }); |
400 | } |
401 | |
402 | result |
403 | } |
404 | } |
405 | |
406 | pub fn write_name(&self, writer: &Writer) -> TokenStream { |
407 | if writer.config.sys { |
408 | quote! { *mut core::ffi::c_void } |
409 | } else { |
410 | self.type_name().write(writer, &[]) |
411 | } |
412 | } |
413 | |
414 | fn write_vtbl_name(&self, writer: &Writer) -> TokenStream { |
415 | let name: TokenStream = format!(" {}_Vtbl" , self.def.name()).into(); |
416 | let namespace = writer.write_namespace(self.def.type_name()); |
417 | quote! { #namespace #name } |
418 | } |
419 | |
420 | pub fn write_impl_name(&self, writer: &Writer) -> TokenStream { |
421 | let name: TokenStream = format!(" {}_Impl" , self.def.name()).into(); |
422 | let namespace = writer.write_namespace(self.def.type_name()); |
423 | quote! { #namespace #name } |
424 | } |
425 | |
426 | pub fn dependencies(&self, dependencies: &mut TypeMap) { |
427 | let base_interfaces = self.base_interfaces(); |
428 | |
429 | for interface in &base_interfaces { |
430 | interface.dependencies(dependencies); |
431 | } |
432 | |
433 | for method in self.def.methods() { |
434 | for ty in method.signature(self.def.namespace(), &[]).types() { |
435 | if ty.is_core() { |
436 | ty.dependencies(dependencies); |
437 | } |
438 | } |
439 | } |
440 | } |
441 | |
442 | pub fn base_interfaces(&self) -> Vec<Type> { |
443 | let mut bases = vec![]; |
444 | let mut def = self.def; |
445 | |
446 | while let Some(base) = def.interface_impls().map(move |imp| imp.ty(&[])).next() { |
447 | match base { |
448 | Type::CppInterface(ref ty) => { |
449 | def = ty.def; |
450 | bases.insert(0, base); |
451 | } |
452 | Type::Object => { |
453 | bases.insert(0, Type::IUnknown); |
454 | bases.insert(1, Type::Object); |
455 | break; |
456 | } |
457 | Type::IUnknown => { |
458 | bases.insert(0, Type::IUnknown); |
459 | break; |
460 | } |
461 | rest => panic!(" {rest:?}" ), |
462 | } |
463 | } |
464 | |
465 | bases |
466 | } |
467 | } |
468 | |