1use super::*;
2
3#[derive(Clone, Debug)]
4pub 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)]
13pub 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)]
24pub 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
37impl 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
76impl 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, &param_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(&param.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(&param.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(&param.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(&param.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(&param.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(&param.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(&quote! { #name: Option<#ty>, });
580 } else {
581 tokens.combine(&quote! { #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(&quote! { #name: Option<#ty>, });
594 } else {
595 tokens.combine(&quote! { #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(&quote! { #name: Option<#ty>, });
606 } else {
607 tokens.combine(&quote! { #name: #ty, });
608 }
609 }
610 ParamHint::ArrayRelativePtr(_) => {}
611 ParamHint::IntoParam => {
612 let kind: TokenStream = format!("P{position}").into();
613 tokens.combine(&quote! { #name: #kind, });
614 }
615 ParamHint::Optional => {
616 let kind = ty.write_default(writer);
617 tokens.combine(&quote! { #name: Option<#kind>, });
618 }
619 ParamHint::Bool => {
620 tokens.combine(&quote! { #name: bool, });
621 }
622 ParamHint::ValueType | ParamHint::Blittable => {
623 let kind = ty.write_default(writer);
624 tokens.combine(&quote! { #name: #kind, });
625 }
626 ParamHint::None => {
627 let kind = ty.write_default(writer);
628 tokens.combine(&quote! { #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(&param.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
750fn write_produce_type(writer: &Writer, ty: &Type, param: Param) -> TokenStream {
751 let name: TokenStream = to_ident(&param.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
771fn write_invoke_arg(ty: &Type, param: Param, _hint: ParamHint) -> TokenStream {
772 let name: TokenStream = to_ident(&param.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
785fn is_convertible(ty: &Type, param: Param, hint: ParamHint) -> bool {
786 !param.flags().contains(ParamAttributes::Out) && !hint.is_array() && ty.is_convertible()
787}
788
789fn 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
808fn 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
841fn 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