1 | use super::*; |
2 | |
3 | #[derive (Clone, Debug)] |
4 | pub struct CppMethod { |
5 | pub def: MethodDef, |
6 | pub signature: Signature, |
7 | pub dependencies: TypeMap, |
8 | pub return_hint: ReturnHint, |
9 | pub param_hints: Vec<ParamHint>, |
10 | } |
11 | |
12 | #[derive (Clone, Debug, PartialEq)] |
13 | pub enum ReturnHint { |
14 | None, |
15 | Query(usize, usize), |
16 | QueryOptional(usize, usize), |
17 | ResultValue, |
18 | ResultVoid, |
19 | ReturnStruct, |
20 | ReturnValue, |
21 | } |
22 | |
23 | #[derive (Copy, Clone, PartialEq, Debug)] |
24 | pub enum ParamHint { |
25 | None, |
26 | ArrayFixed(usize), |
27 | ArrayRelativeLen(usize), |
28 | ArrayRelativeByteLen(usize), |
29 | ArrayRelativePtr(usize), |
30 | IntoParam, |
31 | Optional, |
32 | ValueType, |
33 | Blittable, |
34 | Bool, |
35 | } |
36 | |
37 | impl ParamHint { |
38 | fn from_param(param: Param) -> Self { |
39 | for attribute in param.attributes() { |
40 | match attribute.name() { |
41 | "NativeArrayInfoAttribute" => { |
42 | for (_, value) in attribute.args() { |
43 | match value { |
44 | Value::I16(value) => { |
45 | return ParamHint::ArrayRelativeLen(value as usize) |
46 | } |
47 | Value::I32(value) => return ParamHint::ArrayFixed(value as usize), |
48 | _ => {} |
49 | } |
50 | } |
51 | } |
52 | "MemorySizeAttribute" => { |
53 | for (_, value) in attribute.args() { |
54 | if let Value::I16(value) = value { |
55 | return ParamHint::ArrayRelativeByteLen(value as usize); |
56 | } |
57 | } |
58 | } |
59 | _ => {} |
60 | } |
61 | } |
62 | ParamHint::None |
63 | } |
64 | |
65 | fn is_array(&self) -> bool { |
66 | matches!( |
67 | self, |
68 | Self::ArrayFixed(_) |
69 | | Self::ArrayRelativeLen(_) |
70 | | Self::ArrayRelativeByteLen(_) |
71 | | Self::ArrayRelativePtr(_) |
72 | ) |
73 | } |
74 | } |
75 | |
76 | impl CppMethod { |
77 | pub fn new(def: MethodDef, namespace: &'static str) -> Self { |
78 | let signature = def.signature(namespace, &[]); |
79 | |
80 | let mut param_hints = vec![ParamHint::None; signature.params.len()]; |
81 | |
82 | for (position, (_, param)) in signature.params.iter().enumerate() { |
83 | param_hints[position] = ParamHint::from_param(*param); |
84 | } |
85 | |
86 | let mut dependencies = TypeMap::new(); |
87 | signature.dependencies(&mut dependencies); |
88 | |
89 | for position in 0..signature.params.len() { |
90 | // Point len params back to the corresponding ptr params. |
91 | match param_hints[position] { |
92 | ParamHint::ArrayRelativeLen(relative) |
93 | | ParamHint::ArrayRelativeByteLen(relative) => { |
94 | // The len params must be input only. |
95 | if !signature.params[relative] |
96 | .1 |
97 | .flags() |
98 | .contains(ParamAttributes::Out) |
99 | && position != relative |
100 | && !signature.params[relative].0.is_pointer() |
101 | { |
102 | param_hints[relative] = ParamHint::ArrayRelativePtr(position); |
103 | } else { |
104 | param_hints[position] = ParamHint::None; |
105 | } |
106 | } |
107 | ParamHint::ArrayFixed(_) => { |
108 | if signature.params[position] |
109 | .1 |
110 | .has_attribute("FreeWithAttribute" ) |
111 | { |
112 | param_hints[position] = ParamHint::None; |
113 | } |
114 | } |
115 | _ => {} |
116 | } |
117 | } |
118 | |
119 | let mut sets = BTreeMap::<usize, Vec<usize>>::new(); |
120 | |
121 | // Finds sets of ptr params pointing at the same len param. |
122 | for (position, hint) in param_hints.iter().enumerate() { |
123 | match hint { |
124 | ParamHint::ArrayRelativeLen(relative) |
125 | | ParamHint::ArrayRelativeByteLen(relative) => { |
126 | sets.entry(*relative).or_default().push(position); |
127 | } |
128 | _ => {} |
129 | } |
130 | } |
131 | |
132 | // Remove all sets. |
133 | for (len, ptrs) in sets { |
134 | if ptrs.len() > 1 { |
135 | param_hints[len] = ParamHint::None; |
136 | for ptr in ptrs { |
137 | param_hints[ptr] = ParamHint::None; |
138 | } |
139 | } |
140 | } |
141 | |
142 | // Remove any byte arrays that aren't byte-sized types. |
143 | for position in 0..param_hints.len() { |
144 | if let ParamHint::ArrayRelativeByteLen(relative) = param_hints[position] { |
145 | if !signature.params[position].0.is_byte_size() { |
146 | param_hints[position] = ParamHint::None; |
147 | param_hints[relative] = ParamHint::None; |
148 | } |
149 | } |
150 | } |
151 | |
152 | for (position, hint) in param_hints.iter_mut().enumerate() { |
153 | if *hint == ParamHint::None { |
154 | let ty = &signature.params[position].0; |
155 | let param = signature.params[position].1; |
156 | let flags = param.flags(); |
157 | |
158 | if is_convertible(ty, param, *hint) { |
159 | *hint = ParamHint::IntoParam; |
160 | } else if ty.is_copyable() |
161 | && (flags.contains(ParamAttributes::Optional) |
162 | || param.has_attribute("ReservedAttribute" )) |
163 | { |
164 | *hint = ParamHint::Optional; |
165 | } else if !flags.contains(ParamAttributes::Out) && ty.type_name() == TypeName::BOOL |
166 | { |
167 | *hint = ParamHint::Bool; |
168 | } else if ty.is_primitive() && (!ty.is_pointer() || ty.deref().is_copyable()) { |
169 | *hint = ParamHint::ValueType; |
170 | } else if ty.is_copyable() { |
171 | *hint = ParamHint::Blittable; |
172 | } |
173 | } |
174 | } |
175 | |
176 | let is_retval = is_retval(&signature, ¶m_hints); |
177 | let mut return_hint = ReturnHint::None; |
178 | |
179 | let last_error = if let Some(map) = def.impl_map() { |
180 | map.flags().contains(PInvokeAttributes::SupportsLastError) |
181 | } else { |
182 | false |
183 | }; |
184 | |
185 | if !def.has_attribute("CanReturnMultipleSuccessValuesAttribute" ) { |
186 | match &signature.return_type.0 { |
187 | Type::Void if is_retval => return_hint = ReturnHint::ReturnValue, |
188 | Type::HRESULT => { |
189 | if is_retval { |
190 | return_hint = ReturnHint::ResultValue |
191 | } else { |
192 | return_hint = ReturnHint::ResultVoid |
193 | }; |
194 | |
195 | if signature.params.len() >= 2 { |
196 | if let Some((guid, object)) = signature_param_is_query(&signature.params) { |
197 | if signature.params[object] |
198 | .1 |
199 | .flags() |
200 | .contains(ParamAttributes::Optional) |
201 | { |
202 | return_hint = ReturnHint::QueryOptional(object, guid); |
203 | } else { |
204 | return_hint = ReturnHint::Query(object, guid); |
205 | } |
206 | } |
207 | } |
208 | } |
209 | Type::CppStruct(ty) |
210 | if TypeName(ty.def.namespace(), ty.def.name()) == TypeName::BOOL |
211 | && last_error => |
212 | { |
213 | // TODO: maybe use ResultBool here to make the code gen less ambiguous |
214 | return_hint = ReturnHint::ResultVoid |
215 | } |
216 | Type::GUID => return_hint = ReturnHint::ReturnStruct, |
217 | Type::CppStruct(ty) if !ty.is_handle() => return_hint = ReturnHint::ReturnStruct, |
218 | _ => {} |
219 | }; |
220 | } |
221 | |
222 | Self { |
223 | def, |
224 | signature, |
225 | dependencies, |
226 | param_hints, |
227 | return_hint, |
228 | } |
229 | } |
230 | |
231 | pub fn write_generics(&self) -> TokenStream { |
232 | let mut tokens = quote! {}; |
233 | |
234 | for (position, hint) in self.param_hints.iter().enumerate() { |
235 | if *hint == ParamHint::IntoParam { |
236 | let name: TokenStream = format!("P {position}," ).into(); |
237 | tokens.combine(name); |
238 | } |
239 | } |
240 | |
241 | tokens |
242 | } |
243 | |
244 | pub fn write_where(&self, writer: &Writer, query: bool) -> TokenStream { |
245 | let mut tokens = quote! {}; |
246 | |
247 | for (position, (ty, _)) in self.signature.params.iter().enumerate() { |
248 | if self.param_hints[position] == ParamHint::IntoParam { |
249 | let name: TokenStream = format!("P {position}" ).into(); |
250 | let into = ty.write_name(writer); |
251 | tokens.combine(quote! { #name: windows_core::Param<#into>, }) |
252 | } |
253 | } |
254 | |
255 | if query { |
256 | tokens.combine(quote! { T: windows_core::Interface }) |
257 | } |
258 | |
259 | if tokens.is_empty() { |
260 | tokens |
261 | } else { |
262 | quote! { where #tokens } |
263 | } |
264 | } |
265 | |
266 | pub fn write( |
267 | &self, |
268 | writer: &Writer, |
269 | method_names: &mut MethodNames, |
270 | virtual_names: &mut MethodNames, |
271 | ) -> TokenStream { |
272 | let name = method_names.add(self.def); |
273 | let vname = virtual_names.add(self.def); |
274 | |
275 | let args = self.write_args(); |
276 | let params = self.write_params(writer); |
277 | let generics = self.write_generics(); |
278 | let abi_return_type = self.write_return(writer); |
279 | |
280 | match self.return_hint { |
281 | ReturnHint::Query(..) => { |
282 | let where_clause = self.write_where(writer, true); |
283 | |
284 | quote! { |
285 | pub unsafe fn #name<#generics T>(&self, #params) -> windows_core::Result<T> #where_clause { |
286 | let mut result__ = core::ptr::null_mut(); |
287 | unsafe { (windows_core::Interface::vtable(self).#vname)(windows_core::Interface::as_raw(self),#args).and_then(||windows_core::Type::from_abi(result__)) } |
288 | } |
289 | } |
290 | } |
291 | ReturnHint::QueryOptional(..) => { |
292 | let where_clause = self.write_where(writer, true); |
293 | |
294 | quote! { |
295 | pub unsafe fn #name<#generics T>(&self, #params result__: *mut Option<T>) -> windows_core::Result<()> #where_clause { |
296 | unsafe { (windows_core::Interface::vtable(self).#vname)(windows_core::Interface::as_raw(self),#args).ok() } |
297 | } |
298 | } |
299 | } |
300 | ReturnHint::ResultValue => { |
301 | let where_clause = self.write_where(writer, false); |
302 | |
303 | let return_type = self.signature.params[self.signature.params.len() - 1] |
304 | .0 |
305 | .deref(); |
306 | |
307 | let map = if return_type.is_copyable() { |
308 | quote! { map(||result__) } |
309 | } else if return_type.is_interface() { |
310 | quote! { and_then(||windows_core::Type::from_abi(result__)) } |
311 | } else { |
312 | quote! { map(||core::mem::transmute(result__)) } |
313 | }; |
314 | |
315 | let return_type = return_type.write_name(writer); |
316 | |
317 | quote! { |
318 | pub unsafe fn #name<#generics>(&self, #params) -> windows_core::Result<#return_type> #where_clause { |
319 | unsafe { |
320 | let mut result__ = core::mem::zeroed(); |
321 | (windows_core::Interface::vtable(self).#vname)(windows_core::Interface::as_raw(self),#args).#map |
322 | } |
323 | } |
324 | } |
325 | } |
326 | ReturnHint::ResultVoid => { |
327 | let where_clause = self.write_where(writer, false); |
328 | |
329 | quote! { |
330 | pub unsafe fn #name<#generics>(&self, #params) -> windows_core::Result<()> #where_clause { |
331 | unsafe { (windows_core::Interface::vtable(self).#vname)(windows_core::Interface::as_raw(self),#args).ok() } |
332 | } |
333 | } |
334 | } |
335 | ReturnHint::ReturnValue => { |
336 | let where_clause = self.write_where(writer, false); |
337 | |
338 | let return_type = self.signature.params[self.signature.params.len() - 1] |
339 | .0 |
340 | .deref(); |
341 | |
342 | if return_type.is_interface() { |
343 | let return_type = return_type.write_name(writer); |
344 | |
345 | quote! { |
346 | pub unsafe fn #name<#generics>(&self, #params) -> windows_core::Result<#return_type> #where_clause { |
347 | unsafe { |
348 | let mut result__ = core::mem::zeroed(); |
349 | (windows_core::Interface::vtable(self).#vname)(windows_core::Interface::as_raw(self), #args); |
350 | windows_core::Type::from_abi(result__) |
351 | } |
352 | } |
353 | } |
354 | } else { |
355 | let map = if return_type.is_copyable() { |
356 | quote! { result__ } |
357 | } else { |
358 | quote! { core::mem::transmute(result__) } |
359 | }; |
360 | |
361 | let return_type = return_type.write_name(writer); |
362 | let where_clause = self.write_where(writer, false); |
363 | |
364 | quote! { |
365 | pub unsafe fn #name<#generics>(&self, #params) -> #return_type #where_clause { |
366 | unsafe { |
367 | let mut result__ = core::mem::zeroed(); |
368 | (windows_core::Interface::vtable(self).#vname)(windows_core::Interface::as_raw(self), #args); |
369 | #map |
370 | } |
371 | } |
372 | } |
373 | } |
374 | } |
375 | ReturnHint::ReturnStruct => { |
376 | let return_type = self.signature.return_type.0.write_name(writer); |
377 | let where_clause = self.write_where(writer, false); |
378 | |
379 | quote! { |
380 | pub unsafe fn #name<#generics>(&self, #params) -> #return_type #where_clause { |
381 | unsafe { |
382 | let mut result__ = core::mem::zeroed(); |
383 | (windows_core::Interface::vtable(self).#vname)(windows_core::Interface::as_raw(self), &mut result__, #args); |
384 | result__ |
385 | } |
386 | } |
387 | } |
388 | } |
389 | ReturnHint::None => { |
390 | let where_clause = self.write_where(writer, false); |
391 | |
392 | quote! { |
393 | pub unsafe fn #name<#generics>(&self, #params) #abi_return_type #where_clause { |
394 | unsafe { (windows_core::Interface::vtable(self).#vname)(windows_core::Interface::as_raw(self), #args) } |
395 | } |
396 | } |
397 | } |
398 | } |
399 | } |
400 | |
401 | pub fn write_upcall(&self, parent_impl: &TokenStream, name: &TokenStream) -> TokenStream { |
402 | match self.return_hint { |
403 | ReturnHint::ResultValue => { |
404 | let invoke_args = self.signature.params[..self.signature.params.len() - 1] |
405 | .iter() |
406 | .enumerate() |
407 | .map(|(position, param)| { |
408 | write_invoke_arg(¶m.0, param.1, self.param_hints[position]) |
409 | }); |
410 | |
411 | let result = to_ident( |
412 | &self.signature.params[self.signature.params.len() - 1] |
413 | .1 |
414 | .name() |
415 | .to_lowercase(), |
416 | ); |
417 | |
418 | quote! { |
419 | match #parent_impl::#name(this, #(#invoke_args,)*) { |
420 | Ok(ok__) => { |
421 | // use `ptr::write` since the result could be uninitialized |
422 | #result.write(core::mem::transmute(ok__)); |
423 | windows_core::HRESULT(0) |
424 | } |
425 | Err(err) => err.into() |
426 | } |
427 | } |
428 | } |
429 | ReturnHint::Query(..) | ReturnHint::QueryOptional(..) | ReturnHint::ResultVoid => { |
430 | let invoke_args = |
431 | self.signature |
432 | .params |
433 | .iter() |
434 | .enumerate() |
435 | .map(|(position, param)| { |
436 | write_invoke_arg(¶m.0, param.1, self.param_hints[position]) |
437 | }); |
438 | |
439 | quote! { |
440 | #parent_impl::#name(this, #(#invoke_args,)*).into() |
441 | } |
442 | } |
443 | ReturnHint::ReturnStruct => { |
444 | let invoke_args = |
445 | self.signature |
446 | .params |
447 | .iter() |
448 | .enumerate() |
449 | .map(|(position, param)| { |
450 | write_invoke_arg(¶m.0, param.1, self.param_hints[position]) |
451 | }); |
452 | |
453 | quote! { |
454 | *result__ = #parent_impl::#name(this, #(#invoke_args,)*) |
455 | } |
456 | } |
457 | _ => { |
458 | let invoke_args = |
459 | self.signature |
460 | .params |
461 | .iter() |
462 | .enumerate() |
463 | .map(|(position, param)| { |
464 | write_invoke_arg(¶m.0, param.1, self.param_hints[position]) |
465 | }); |
466 | |
467 | quote! { |
468 | #parent_impl::#name(this, #(#invoke_args,)*) |
469 | } |
470 | } |
471 | } |
472 | } |
473 | |
474 | pub fn write_impl_signature(&self, writer: &Writer, _named_params: bool) -> TokenStream { |
475 | let mut params = quote! {}; |
476 | |
477 | if self.return_hint == ReturnHint::ResultValue { |
478 | for (ty, param) in &self.signature.params[..self.signature.params.len() - 1] { |
479 | params.combine(write_produce_type(writer, ty, *param)); |
480 | } |
481 | } else { |
482 | for (ty, param) in &self.signature.params { |
483 | params.combine(write_produce_type(writer, ty, *param)); |
484 | } |
485 | } |
486 | |
487 | let return_type = match self.return_hint { |
488 | ReturnHint::Query(..) | ReturnHint::QueryOptional(..) | ReturnHint::ResultVoid => { |
489 | quote! { -> windows_core::Result<()> } |
490 | } |
491 | ReturnHint::ResultValue => { |
492 | let return_type = self.signature.params[self.signature.params.len() - 1] |
493 | .0 |
494 | .deref(); |
495 | let return_type = return_type.write_name(writer); |
496 | |
497 | quote! { -> windows_core::Result<#return_type> } |
498 | } |
499 | _ => self.write_return(writer), |
500 | }; |
501 | |
502 | quote! { (&self, #params) #return_type } |
503 | } |
504 | |
505 | pub fn write_abi(&self, writer: &Writer, named_params: bool) -> TokenStream { |
506 | let mut params: Vec<_> = self |
507 | .signature |
508 | .params |
509 | .iter() |
510 | .map(|(ty, param)| { |
511 | let ty = ty.write_abi(writer); |
512 | |
513 | if named_params { |
514 | let name = to_ident(¶m.name().to_lowercase()); |
515 | quote! { #name: #ty } |
516 | } else { |
517 | ty |
518 | } |
519 | }) |
520 | .collect(); |
521 | |
522 | let mut return_sig = quote! {}; |
523 | |
524 | match self.return_hint { |
525 | ReturnHint::ReturnStruct => { |
526 | let return_type = self.signature.return_type.0.write_abi(writer); |
527 | |
528 | if named_params { |
529 | params.insert(0, quote! { result__: *mut #return_type }); |
530 | } else { |
531 | params.insert(0, quote! { *mut #return_type }); |
532 | } |
533 | } |
534 | _ => { |
535 | return_sig = writer.write_return_sig(self.def, &self.signature, false); |
536 | } |
537 | } |
538 | |
539 | let this = if named_params { |
540 | quote! { this: *mut core::ffi::c_void } |
541 | } else { |
542 | quote! { *mut core::ffi::c_void } |
543 | }; |
544 | |
545 | quote! { (#this, #(#params),*) #return_sig } |
546 | } |
547 | |
548 | pub fn write_params(&self, writer: &Writer) -> TokenStream { |
549 | let mut tokens = quote! {}; |
550 | |
551 | for (position, (ty, param)) in self.signature.params.iter().enumerate() { |
552 | match self.return_hint { |
553 | ReturnHint::Query(object, guid) | ReturnHint::QueryOptional(object, guid) => { |
554 | if object == position || guid == position { |
555 | continue; |
556 | } |
557 | } |
558 | ReturnHint::ReturnValue | ReturnHint::ResultValue |
559 | if self.signature.params.len() - 1 == position => |
560 | { |
561 | continue; |
562 | } |
563 | _ => {} |
564 | } |
565 | |
566 | let name = to_ident(¶m.name().to_lowercase()); |
567 | |
568 | match self.param_hints[position] { |
569 | ParamHint::ArrayFixed(fixed) => { |
570 | let ty = ty.deref(); |
571 | let ty = ty.write_default(writer); |
572 | let len = Literal::u32_unsuffixed(fixed as u32); |
573 | let ty = if param.flags().contains(ParamAttributes::Out) { |
574 | quote! { &mut [#ty; #len] } |
575 | } else { |
576 | quote! { &[#ty; #len] } |
577 | }; |
578 | if param.flags().contains(ParamAttributes::Optional) { |
579 | tokens.combine("e! { #name: Option<#ty>, }); |
580 | } else { |
581 | tokens.combine("e! { #name: #ty, }); |
582 | } |
583 | } |
584 | ParamHint::ArrayRelativeLen(_) => { |
585 | let ty = ty.deref(); |
586 | let ty = ty.write_default(writer); |
587 | let ty = if param.flags().contains(ParamAttributes::Out) { |
588 | quote! { &mut [#ty] } |
589 | } else { |
590 | quote! { &[#ty] } |
591 | }; |
592 | if param.flags().contains(ParamAttributes::Optional) { |
593 | tokens.combine("e! { #name: Option<#ty>, }); |
594 | } else { |
595 | tokens.combine("e! { #name: #ty, }); |
596 | } |
597 | } |
598 | ParamHint::ArrayRelativeByteLen(_) => { |
599 | let ty = if param.flags().contains(ParamAttributes::Out) { |
600 | quote! { &mut [u8] } |
601 | } else { |
602 | quote! { &[u8] } |
603 | }; |
604 | if param.flags().contains(ParamAttributes::Optional) { |
605 | tokens.combine("e! { #name: Option<#ty>, }); |
606 | } else { |
607 | tokens.combine("e! { #name: #ty, }); |
608 | } |
609 | } |
610 | ParamHint::ArrayRelativePtr(_) => {} |
611 | ParamHint::IntoParam => { |
612 | let kind: TokenStream = format!("P {position}" ).into(); |
613 | tokens.combine("e! { #name: #kind, }); |
614 | } |
615 | ParamHint::Optional => { |
616 | let kind = ty.write_default(writer); |
617 | tokens.combine("e! { #name: Option<#kind>, }); |
618 | } |
619 | ParamHint::Bool => { |
620 | tokens.combine("e! { #name: bool, }); |
621 | } |
622 | ParamHint::ValueType | ParamHint::Blittable => { |
623 | let kind = ty.write_default(writer); |
624 | tokens.combine("e! { #name: #kind, }); |
625 | } |
626 | ParamHint::None => { |
627 | let kind = ty.write_default(writer); |
628 | tokens.combine("e! { #name: &#kind, }); |
629 | } |
630 | } |
631 | } |
632 | |
633 | tokens |
634 | } |
635 | |
636 | pub fn write_args(&self) -> TokenStream { |
637 | let mut tokens = quote! {}; |
638 | |
639 | for (position, (ty, param)) in self.signature.params.iter().enumerate() { |
640 | let new = match self.return_hint { |
641 | ReturnHint::Query(object, _) if object == position => { |
642 | quote! { &mut result__, } |
643 | } |
644 | ReturnHint::ReturnValue | ReturnHint::ResultValue |
645 | if self.signature.params.len() - 1 == position => |
646 | { |
647 | quote! { &mut result__, } |
648 | } |
649 | ReturnHint::QueryOptional(object, _) if object == position => { |
650 | quote! { result__ as *mut _ as *mut _, } |
651 | } |
652 | ReturnHint::Query(_, guid) | ReturnHint::QueryOptional(_, guid) |
653 | if guid == position => |
654 | { |
655 | quote! { &T::IID, } |
656 | } |
657 | _ => { |
658 | let name = to_ident(¶m.name().to_lowercase()); |
659 | let flags = param.flags(); |
660 | match self.param_hints[position] { |
661 | ParamHint::ArrayFixed(_) |
662 | | ParamHint::ArrayRelativeLen(_) |
663 | | ParamHint::ArrayRelativeByteLen(_) => { |
664 | let map = if flags.contains(ParamAttributes::Optional) { |
665 | quote! { #name.as_deref().map_or(core::ptr::null(), |slice|slice.as_ptr()) } |
666 | } else { |
667 | quote! { #name.as_ptr() } |
668 | }; |
669 | quote! { core::mem::transmute(#map), } |
670 | } |
671 | ParamHint::ArrayRelativePtr(relative) => { |
672 | let name = |
673 | to_ident(&self.signature.params[relative].1.name().to_lowercase()); |
674 | let flags = self.signature.params[relative].1.flags(); |
675 | if flags.contains(ParamAttributes::Optional) { |
676 | quote! { #name.as_deref().map_or(0, |slice|slice.len().try_into().unwrap()), } |
677 | } else { |
678 | quote! { #name.len().try_into().unwrap(), } |
679 | } |
680 | } |
681 | ParamHint::IntoParam => { |
682 | quote! { #name.param().abi(), } |
683 | } |
684 | ParamHint::Optional => { |
685 | quote! { #name.unwrap_or(core::mem::zeroed()) as _, } |
686 | } |
687 | ParamHint::Bool => { |
688 | quote! { #name.into(), } |
689 | } |
690 | ParamHint::ValueType => { |
691 | if flags.contains(ParamAttributes::Out) { |
692 | quote! { #name as _, } |
693 | } else { |
694 | quote! { #name, } |
695 | } |
696 | } |
697 | ParamHint::Blittable => { |
698 | if matches!(ty, Type::PrimitiveOrEnum(_, _)) { |
699 | quote! { #name.0 as _, } |
700 | } else { |
701 | quote! { core::mem::transmute(#name), } |
702 | } |
703 | } |
704 | ParamHint::None => { |
705 | quote! { core::mem::transmute_copy(#name), } |
706 | } |
707 | } |
708 | } |
709 | }; |
710 | tokens.combine(&new) |
711 | } |
712 | |
713 | tokens |
714 | } |
715 | |
716 | pub fn write_return(&self, writer: &Writer) -> TokenStream { |
717 | match &self.signature.return_type.0 { |
718 | Type::Void if self.def.has_attribute("DoesNotReturnAttribute" ) => quote! { -> ! }, |
719 | Type::Void => quote! {}, |
720 | ty => { |
721 | let ty = ty.write_default(writer); |
722 | quote! { -> #ty } |
723 | } |
724 | } |
725 | } |
726 | |
727 | pub fn handle_last_error(&self) -> bool { |
728 | if let Some(map) = self.def.impl_map() { |
729 | if map.flags().contains(PInvokeAttributes::SupportsLastError) { |
730 | if let Type::CppStruct(ty) = &self.signature.return_type.0 { |
731 | if ty.is_handle() { |
732 | // https://github.com/microsoft/windows-rs/issues/2392#issuecomment-1477765781 |
733 | if self.def.name() == "LocalFree" { |
734 | return false; |
735 | } |
736 | if ty.def.underlying_type().is_pointer() { |
737 | return true; |
738 | } |
739 | if !ty.def.invalid_values().is_empty() { |
740 | return true; |
741 | } |
742 | } |
743 | } |
744 | } |
745 | } |
746 | false |
747 | } |
748 | } |
749 | |
750 | fn write_produce_type(writer: &Writer, ty: &Type, param: Param) -> TokenStream { |
751 | let name: TokenStream = to_ident(¶m.name().to_lowercase()); |
752 | let kind: TokenStream = ty.write_default(writer); |
753 | |
754 | if !param.flags().contains(ParamAttributes::Out) && ty.is_interface() { |
755 | let type_name: TokenStream = ty.write_name(writer); |
756 | quote! { #name: windows_core::Ref<'_, #type_name>, } |
757 | } else if param.flags().contains(ParamAttributes::Out) && ty.deref().is_interface() { |
758 | let type_name: TokenStream = ty.deref().write_name(writer); |
759 | quote! { #name: windows_core::OutRef<'_, #type_name>, } |
760 | } else if !param.flags().contains(ParamAttributes::Out) { |
761 | if ty.is_primitive() { |
762 | quote! { #name: #kind, } |
763 | } else { |
764 | quote! { #name: &#kind, } |
765 | } |
766 | } else { |
767 | quote! { #name: #kind, } |
768 | } |
769 | } |
770 | |
771 | fn write_invoke_arg(ty: &Type, param: Param, _hint: ParamHint) -> TokenStream { |
772 | let name: TokenStream = to_ident(¶m.name().to_lowercase()); |
773 | |
774 | if !param.flags().contains(ParamAttributes::Out) && ty.is_interface() { |
775 | quote! { core::mem::transmute_copy(&#name) } |
776 | } else if (!ty.is_pointer() && ty.is_interface()) |
777 | || (!param.flags().contains(ParamAttributes::Out) && !ty.is_primitive()) |
778 | { |
779 | quote! { core::mem::transmute(&#name) } |
780 | } else { |
781 | quote! { core::mem::transmute_copy(&#name) } |
782 | } |
783 | } |
784 | |
785 | fn is_convertible(ty: &Type, param: Param, hint: ParamHint) -> bool { |
786 | !param.flags().contains(ParamAttributes::Out) && !hint.is_array() && ty.is_convertible() |
787 | } |
788 | |
789 | fn is_retval(signature: &Signature, param_hints: &[ParamHint]) -> bool { |
790 | // First we check whether there's an actual retval parameter. |
791 | if let Some(param: &(Type, Param)) = signature.params.last() { |
792 | if param.1.has_attribute(name:"RetValAttribute" ) { |
793 | return true; |
794 | } |
795 | } |
796 | |
797 | if let Some((ty: &Type, param: &Param)) = signature.params.last() { |
798 | if is_param_retval(ty, *param, hint:param_hints[param_hints.len() - 1]) { |
799 | return signatureIter<'_, (Type, Param)>.params[..signature.params.len() - 1] |
800 | .iter() |
801 | .all(|(_, param: &Param)| !param.flags().contains(ParamAttributes::Out)); |
802 | } |
803 | } |
804 | |
805 | false |
806 | } |
807 | |
808 | fn is_param_retval(ty: &Type, param: Param, hint: ParamHint) -> bool { |
809 | // The Win32 metadata uses `RetValAttribute` to call out retval methods but it is employed |
810 | // very sparingly, so this heuristic is used to apply the transformation more uniformly. |
811 | if param.has_attribute("RetValAttribute" ) { |
812 | return true; |
813 | } |
814 | if !ty.is_pointer() { |
815 | return false; |
816 | } |
817 | if ty.is_void() { |
818 | return false; |
819 | } |
820 | let flags = param.flags(); |
821 | if flags.contains(ParamAttributes::In) |
822 | || !flags.contains(ParamAttributes::Out) |
823 | || flags.contains(ParamAttributes::Optional) |
824 | || hint.is_array() |
825 | { |
826 | return false; |
827 | } |
828 | // This is reevaluated to detect unsupported array parameters. |
829 | // https://github.com/microsoft/windows-rs/issues/3384 |
830 | if ParamHint::from_param(param).is_array() { |
831 | return false; |
832 | } |
833 | |
834 | // If it's bigger than 128 bits, best to pass as a reference. |
835 | if ty.deref().size() > 16 { |
836 | return false; |
837 | } |
838 | true |
839 | } |
840 | |
841 | fn signature_param_is_query(params: &[(Type, Param)]) -> Option<(usize, usize)> { |
842 | if let Some(guid: usize) = params.iter().rposition(|(ty: &Type, param: &Param)| { |
843 | *ty == Type::PtrConst(Box::new(Type::GUID), 1) |
844 | && !param.flags().contains(ParamAttributes::Out) |
845 | }) { |
846 | if let Some(object: usize) = params.iter().rposition(|(ty: &Type, param: &Param)| { |
847 | *ty == Type::PtrMut(Box::new(Type::Void), 2) |
848 | && param.has_attribute(name:"ComOutPtrAttribute" ) |
849 | }) { |
850 | return Some((guid, object)); |
851 | } |
852 | } |
853 | |
854 | None |
855 | } |
856 | |