1 | use super::*; |
2 | |
3 | // This is WinRT methods only |
4 | #[derive (Clone, Debug, PartialEq, Eq)] |
5 | pub struct Method { |
6 | pub def: MethodDef, |
7 | pub signature: Signature, |
8 | pub dependencies: TypeMap, |
9 | } |
10 | |
11 | impl Method { |
12 | pub fn new(def: MethodDef, generics: &[Type]) -> Self { |
13 | let signature = def.signature("" , generics); |
14 | |
15 | let mut dependencies = TypeMap::new(); |
16 | signature.dependencies(&mut dependencies); |
17 | |
18 | Self { |
19 | def, |
20 | signature, |
21 | dependencies, |
22 | } |
23 | } |
24 | |
25 | pub fn write_upcall(&self, inner: TokenStream, this: bool) -> TokenStream { |
26 | let noexcept = self.def.has_attribute("NoExceptionAttribute" ); |
27 | |
28 | let invoke_args = self.signature |
29 | .params |
30 | .iter() |
31 | .map(|param| { |
32 | let name = to_ident(¶m.1.name().to_lowercase()); |
33 | let abi_size_name: TokenStream = format!(" {}_array_size" , param.1.name().to_lowercase()).into(); |
34 | |
35 | if param.1.flags().contains(ParamAttributes::In) { |
36 | if param.0.is_winrt_array() { |
37 | quote! { core::slice::from_raw_parts(core::mem::transmute_copy(&#name), #abi_size_name as usize) } |
38 | } else if param.0.is_primitive() { |
39 | quote! { #name } |
40 | } else if param.0.is_const_ref() || param.0.is_interface() { |
41 | quote! { core::mem::transmute_copy(&#name) } |
42 | } else { |
43 | quote! { core::mem::transmute(&#name) } |
44 | } |
45 | } else if param.0.is_winrt_array() { |
46 | quote! { core::slice::from_raw_parts_mut(core::mem::transmute_copy(&#name), #abi_size_name as usize) } |
47 | } else if param.0.is_winrt_array_ref() { |
48 | quote! { windows_core::ArrayProxy::from_raw_parts(core::mem::transmute_copy(&#name), #abi_size_name).as_array() } |
49 | } else { |
50 | quote! { core::mem::transmute_copy(&#name) } |
51 | } |
52 | }); |
53 | |
54 | let this = if this { |
55 | quote! { this, } |
56 | } else { |
57 | quote! {} |
58 | }; |
59 | |
60 | match &self.signature.return_type.0 { |
61 | Type::Void => { |
62 | if noexcept { |
63 | quote! { |
64 | #inner(#this #(#invoke_args,)*); |
65 | windows_core::HRESULT(0) |
66 | } |
67 | } else { |
68 | quote! { |
69 | #inner(#this #(#invoke_args,)*).into() |
70 | } |
71 | } |
72 | } |
73 | _ if self.signature.return_type.0.is_winrt_array() => { |
74 | if noexcept { |
75 | quote! { |
76 | let ok__ = #inner(#this #(#invoke_args,)*); |
77 | let (ok_data__, ok_data_len__) = ok__.into_abi(); |
78 | result__.write(core::mem::transmute(ok_data__)); |
79 | result_size__.write(ok_data_len__); |
80 | windows_core::HRESULT(0) |
81 | } |
82 | } else { |
83 | quote! { |
84 | match #inner(#this #(#invoke_args,)*) { |
85 | Ok(ok__) => { |
86 | let (ok_data__, ok_data_len__) = ok__.into_abi(); |
87 | // use `ptr::write` since `result` could be uninitialized |
88 | result__.write(core::mem::transmute(ok_data__)); |
89 | result_size__.write(ok_data_len__); |
90 | windows_core::HRESULT(0) |
91 | } |
92 | Err(err) => err.into() |
93 | } |
94 | } |
95 | } |
96 | } |
97 | _ => { |
98 | let forget = if self.signature.return_type.0.is_copyable() { |
99 | quote! {} |
100 | } else { |
101 | quote! { core::mem::forget(ok__); } |
102 | }; |
103 | |
104 | if noexcept { |
105 | quote! { |
106 | let ok__ = #inner(#this #(#invoke_args,)*); |
107 | // use `ptr::write` since `result` could be uninitialized |
108 | result__.write(core::mem::transmute_copy(&ok__)); |
109 | #forget |
110 | windows_core::HRESULT(0) |
111 | } |
112 | } else { |
113 | quote! { |
114 | match #inner(#this #(#invoke_args,)*) { |
115 | Ok(ok__) => { |
116 | // use `ptr::write` since `result` could be uninitialized |
117 | result__.write(core::mem::transmute_copy(&ok__)); |
118 | #forget |
119 | windows_core::HRESULT(0) |
120 | } |
121 | Err(err) => err.into() |
122 | } |
123 | } |
124 | } |
125 | } |
126 | } |
127 | } |
128 | |
129 | pub fn write_impl_signature( |
130 | &self, |
131 | writer: &Writer, |
132 | named_params: bool, |
133 | has_this: bool, |
134 | ) -> TokenStream { |
135 | let noexcept = self.def.has_attribute("NoExceptionAttribute" ); |
136 | |
137 | let params = self.signature.params.iter().map(|p| { |
138 | let default_type = p.0.write_default(writer); |
139 | |
140 | let sig = if p.1.flags().contains(ParamAttributes::In) { |
141 | if p.0.is_winrt_array() { |
142 | quote! { &[#default_type] } |
143 | } else if p.0.is_primitive() { |
144 | quote! { #default_type } |
145 | } else if p.0.is_interface() { |
146 | let type_name = p.0.write_name(writer); |
147 | quote! { windows_core::Ref<'_, #type_name> } |
148 | } else { |
149 | quote! { &#default_type } |
150 | } |
151 | } else if p.0.is_winrt_array() { |
152 | quote! { &mut [#default_type] } |
153 | } else if p.0.is_winrt_array_ref() { |
154 | let kind = p.0.write_name(writer); |
155 | quote! { &mut windows_core::Array<#kind> } |
156 | } else if p.0.is_interface() { |
157 | let type_name = p.0.write_name(writer); |
158 | quote! { windows_core::OutRef<'_, #type_name> } |
159 | } else { |
160 | quote! { &mut #default_type } |
161 | }; |
162 | |
163 | if named_params { |
164 | let name = to_ident(p.1.name()); |
165 | quote! { #name: #sig } |
166 | } else { |
167 | sig |
168 | } |
169 | }); |
170 | |
171 | let return_type_tokens = if self.signature.return_type.0 == Type::Void { |
172 | quote! { () } |
173 | } else { |
174 | let tokens = self.signature.return_type.0.write_name(writer); |
175 | |
176 | if self.signature.return_type.0.is_winrt_array() { |
177 | quote! { windows_core::Array<#tokens> } |
178 | } else { |
179 | tokens |
180 | } |
181 | }; |
182 | |
183 | let return_type_tokens = if noexcept { |
184 | if self.signature.return_type.0.is_interface() { |
185 | quote! { -> Option<#return_type_tokens> } |
186 | } else if self.signature.return_type.0 == Type::Void { |
187 | quote! {} |
188 | } else { |
189 | quote! { -> #return_type_tokens } |
190 | } |
191 | } else { |
192 | quote! { -> windows_core::Result<#return_type_tokens> } |
193 | }; |
194 | |
195 | if has_this { |
196 | quote! { |
197 | (&self, #(#params),*) #return_type_tokens |
198 | } |
199 | } else { |
200 | quote! { |
201 | (#(#params),*) #return_type_tokens |
202 | } |
203 | } |
204 | } |
205 | |
206 | pub fn write_abi(&self, writer: &Writer, named_params: bool) -> TokenStream { |
207 | let args = self.signature.params.iter().map(|param| { |
208 | let name = to_ident(¶m.1.name().to_lowercase()); |
209 | let abi = param.0.write_abi(writer); |
210 | let abi_size_name: TokenStream = format!(" {}_array_size" , name.as_str()).into(); |
211 | |
212 | if param.1.flags().contains(ParamAttributes::In) { |
213 | if param.0.is_winrt_array() { |
214 | if named_params { |
215 | quote! { #abi_size_name: u32, #name: *const #abi } |
216 | } else { |
217 | quote! { u32, *const #abi } |
218 | } |
219 | } else if param.0.is_const_ref() { |
220 | if named_params { |
221 | quote! { #name: &#abi } |
222 | } else { |
223 | quote! { &#abi } |
224 | } |
225 | } else if named_params { |
226 | quote! { #name: #abi } |
227 | } else { |
228 | quote! { #abi } |
229 | } |
230 | } else if param.0.is_winrt_array() { |
231 | if named_params { |
232 | quote! { #abi_size_name: u32, #name: *mut #abi } |
233 | } else { |
234 | quote! { u32, *mut #abi } |
235 | } |
236 | } else if param.0.is_winrt_array_ref() { |
237 | if named_params { |
238 | quote! { #abi_size_name: *mut u32, #name: *mut *mut #abi } |
239 | } else { |
240 | quote! { *mut u32, *mut *mut #abi } |
241 | } |
242 | } else if named_params { |
243 | quote! { #name: *mut #abi } |
244 | } else { |
245 | quote! { *mut #abi } |
246 | } |
247 | }); |
248 | |
249 | let return_arg = match &self.signature.return_type.0 { |
250 | Type::Void => quote! {}, |
251 | Type::Array(ty) => { |
252 | let ty = ty.write_abi(writer); |
253 | if named_params { |
254 | quote! { result_size__: *mut u32, result__: *mut *mut #ty } |
255 | } else { |
256 | quote! { *mut u32, *mut *mut #ty } |
257 | } |
258 | } |
259 | ty => { |
260 | let ty = ty.write_abi(writer); |
261 | if named_params { |
262 | quote! { result__: *mut #ty } |
263 | } else { |
264 | quote! { *mut #ty } |
265 | } |
266 | } |
267 | }; |
268 | |
269 | if named_params { |
270 | quote! { |
271 | this: *mut core::ffi::c_void, #(#args,)* #return_arg |
272 | } |
273 | } else { |
274 | quote! { |
275 | *mut core::ffi::c_void, #(#args,)* #return_arg |
276 | } |
277 | } |
278 | } |
279 | |
280 | pub fn write( |
281 | &self, |
282 | writer: &Writer, |
283 | interface: Option<&Interface>, |
284 | kind: InterfaceKind, |
285 | method_names: &mut MethodNames, |
286 | virtual_names: &mut MethodNames, |
287 | ) -> TokenStream { |
288 | let params = if kind == InterfaceKind::Composable { |
289 | &self.signature.params[..self.signature.params.len() - 2] |
290 | } else { |
291 | &self.signature.params |
292 | }; |
293 | |
294 | let name = if kind == InterfaceKind::Composable && params.is_empty() { |
295 | quote!(new) |
296 | } else { |
297 | method_names.add(self.def) |
298 | }; |
299 | |
300 | let args = { |
301 | let args = params.iter().map(|param|{ |
302 | let name = to_ident(¶m.1.name().to_lowercase()); |
303 | |
304 | if param.1.flags().contains(ParamAttributes::In) { |
305 | if param.0.is_winrt_array() { |
306 | if param.0.is_copyable() { |
307 | quote! { #name.len().try_into().unwrap(), #name.as_ptr() } |
308 | } else { |
309 | quote! { #name.len().try_into().unwrap(), core::mem::transmute(#name.as_ptr()) } |
310 | } |
311 | } else if param.0.is_convertible() { |
312 | quote! { #name.param().abi() } |
313 | } else if param.0.is_copyable() { |
314 | if param.0.is_const_ref() { |
315 | quote! { &#name } |
316 | } else { |
317 | quote! { #name } |
318 | } |
319 | } else { |
320 | quote! { core::mem::transmute_copy(#name) } |
321 | } |
322 | } else if param.0.is_winrt_array() { |
323 | if param.0.is_copyable() { |
324 | quote! { #name.len().try_into().unwrap(), #name.as_mut_ptr() } |
325 | } else { |
326 | quote! { #name.len().try_into().unwrap(), core::mem::transmute_copy(&#name) } |
327 | } |
328 | } else if param.0.is_winrt_array_ref() { |
329 | quote! { #name.set_abi_len(), #name as *mut _ as _ } |
330 | } else if param.0.is_copyable() { |
331 | quote! { #name } |
332 | } else { |
333 | quote! { #name as *mut _ as _ } |
334 | } |
335 | }); |
336 | |
337 | let return_arg = match &self.signature.return_type.0 { |
338 | Type::Void => quote! {}, |
339 | ty => { |
340 | if ty.is_winrt_array() { |
341 | let ty = ty.write_name(writer); |
342 | quote! { windows_core::Array::<#ty>::set_abi_len(core::mem::transmute(&mut result__)), result__.as_mut_ptr() as *mut _ as _ } |
343 | } else { |
344 | quote! { &mut result__ } |
345 | } |
346 | } |
347 | }; |
348 | |
349 | if kind == InterfaceKind::Composable { |
350 | quote! { |
351 | #(#args,)* core::ptr::null_mut(), &mut core::ptr::null_mut(), #return_arg |
352 | } |
353 | } else { |
354 | quote! { |
355 | #(#args,)* #return_arg |
356 | } |
357 | } |
358 | }; |
359 | |
360 | let generics = params |
361 | .iter() |
362 | .enumerate() |
363 | .filter_map(|(position, (ty, def))| { |
364 | if is_convertible(ty, *def) { |
365 | let name: TokenStream = format!("P {position}" ).into(); |
366 | Some(name) |
367 | } else { |
368 | None |
369 | } |
370 | }); |
371 | |
372 | let where_clause = { |
373 | let constraints: Vec<_> = params |
374 | .iter() |
375 | .enumerate() |
376 | .filter_map(|(position, (ty, def))| { |
377 | if is_convertible(ty, *def) { |
378 | let name: TokenStream = format!("P {position}" ).into(); |
379 | let ty = ty.write_name(writer); |
380 | |
381 | Some(quote! { #name: windows_core::Param<#ty>, }) |
382 | } else { |
383 | None |
384 | } |
385 | }) |
386 | .collect(); |
387 | |
388 | if constraints.is_empty() { |
389 | quote! {} |
390 | } else { |
391 | quote! { where #(#constraints)* } |
392 | } |
393 | }; |
394 | |
395 | let params = params.iter().enumerate().map(|(position, param)| { |
396 | let name = to_ident(¶m.1.name().to_lowercase()); |
397 | let kind = param.0.write_name(writer); |
398 | let default_type = param.0.write_default(writer); |
399 | |
400 | if param.1.flags().contains(ParamAttributes::In) { |
401 | if param.0.is_winrt_array() { |
402 | quote! { #name: &[#default_type], } |
403 | } else if is_convertible(¶m.0, param.1) { |
404 | let kind: TokenStream = format!("P {position}" ).into(); |
405 | quote! { #name: #kind, } |
406 | } else if param.0.is_copyable() { |
407 | quote! { #name: #kind, } |
408 | } else { |
409 | quote! { #name: &#kind, } |
410 | } |
411 | } else if param.0.is_winrt_array() { |
412 | quote! { #name: &mut [#default_type], } |
413 | } else if param.0.is_winrt_array_ref() { |
414 | quote! { #name: &mut windows_core::Array<#kind>, } |
415 | } else { |
416 | quote! { #name: &mut #default_type, } |
417 | } |
418 | }); |
419 | |
420 | let return_type = match &self.signature.return_type.0 { |
421 | Type::Void => quote! { () }, |
422 | _ => { |
423 | let tokens = self.signature.return_type.0.write_name(writer); |
424 | if self.signature.return_type.0.is_winrt_array() { |
425 | quote! { windows_core::Array<#tokens> } |
426 | } else { |
427 | quote! { #tokens } |
428 | } |
429 | } |
430 | }; |
431 | |
432 | let noexcept = self.def.has_attribute("NoExceptionAttribute" ); |
433 | |
434 | let return_type = if noexcept { |
435 | if self.signature.return_type.0.is_interface() { |
436 | quote! { -> Option<#return_type> } |
437 | } else if self.signature.return_type.0 == Type::Void { |
438 | quote! {} |
439 | } else { |
440 | quote! { -> #return_type } |
441 | } |
442 | } else { |
443 | quote! { -> windows_core::Result<#return_type> } |
444 | }; |
445 | |
446 | let vname = virtual_names.add(self.def); |
447 | let vcall = quote! { (windows_core::Interface::vtable(this).#vname)(windows_core::Interface::as_raw(this), #args) }; |
448 | |
449 | let vcall = match &self.signature.return_type.0 { |
450 | Type::Void => { |
451 | if noexcept { |
452 | quote! { |
453 | let hresult__ = #vcall; |
454 | debug_assert!(hresult__.0 == 0); |
455 | } |
456 | } else { |
457 | quote! { |
458 | #vcall.ok() |
459 | } |
460 | } |
461 | } |
462 | _ if self.signature.return_type.0.is_winrt_array() => { |
463 | if noexcept { |
464 | quote! { |
465 | let mut result__ = core::mem::MaybeUninit::zeroed(); |
466 | let hresult__ = #vcall; |
467 | debug_assert!(hresult__.0 == 0); |
468 | result__.assume_init() |
469 | } |
470 | } else { |
471 | quote! { |
472 | let mut result__ = core::mem::MaybeUninit::zeroed(); |
473 | #vcall |
474 | .map(|| result__.assume_init()) |
475 | } |
476 | } |
477 | } |
478 | _ => { |
479 | if noexcept { |
480 | if self.signature.return_type.0.is_copyable() { |
481 | quote! { |
482 | let mut result__ = core::mem::zeroed(); |
483 | let hresult__ = #vcall; |
484 | debug_assert!(hresult__.0 == 0); |
485 | result__ } |
486 | } else { |
487 | quote! { |
488 | let mut result__ = core::mem::zeroed(); |
489 | let hresult__ = #vcall; |
490 | debug_assert!(hresult__.0 == 0); |
491 | core::mem::transmute(result__) } |
492 | } |
493 | } else if !self.signature.return_type.0.is_convertible() { |
494 | if self.signature.return_type.0.is_primitive() { |
495 | quote! { |
496 | let mut result__ = core::mem::zeroed(); |
497 | #vcall |
498 | .map(||result__) } |
499 | } else { |
500 | quote! { |
501 | let mut result__ = core::mem::zeroed(); |
502 | #vcall |
503 | .map(||core::mem::transmute(result__)) } |
504 | } |
505 | } else { |
506 | quote! { let mut result__ = core::mem::zeroed(); |
507 | #vcall.and_then(|| windows_core::Type::from_abi(result__)) } |
508 | } |
509 | } |
510 | }; |
511 | |
512 | match kind { |
513 | InterfaceKind::Default => quote! { |
514 | pub fn #name<#(#generics,)*>(&self, #(#params)*) #return_type #where_clause { |
515 | let this = self; |
516 | unsafe { |
517 | #vcall |
518 | } |
519 | } |
520 | }, |
521 | InterfaceKind::None | InterfaceKind::Base => { |
522 | let unwrap = if noexcept { |
523 | quote! { .unwrap() } |
524 | } else { |
525 | quote! { ? } |
526 | }; |
527 | |
528 | let interface_name = interface.unwrap().write_name(writer); |
529 | |
530 | quote! { |
531 | pub fn #name<#(#generics,)*>(&self, #(#params)*) #return_type #where_clause { |
532 | let this = &windows_core::Interface::cast::<#interface_name>(self)#unwrap; |
533 | unsafe { |
534 | #vcall |
535 | } |
536 | } |
537 | } |
538 | } |
539 | InterfaceKind::Static | InterfaceKind::Composable => { |
540 | let interface_name = to_ident(interface.unwrap().def.name()); |
541 | |
542 | quote! { |
543 | pub fn #name<#(#generics,)*>(#(#params)*) #return_type #where_clause { |
544 | Self::#interface_name(|this| unsafe { #vcall }) |
545 | } |
546 | } |
547 | } |
548 | } |
549 | } |
550 | } |
551 | |
552 | fn is_convertible(ty: &Type, param: Param) -> bool { |
553 | param.flags().contains(ParamAttributes::In) && ty.is_convertible() |
554 | } |
555 | |