1 | use super::*; |
2 | |
3 | #[derive (Clone, Debug, PartialEq, Eq, Hash)] |
4 | pub struct CppFn { |
5 | pub namespace: &'static str, |
6 | pub method: MethodDef, |
7 | } |
8 | |
9 | impl Ord for CppFn { |
10 | fn cmp(&self, other: &Self) -> Ordering { |
11 | (self.method.name(), self.method).cmp(&(other.method.name(), other.method)) |
12 | } |
13 | } |
14 | |
15 | impl PartialOrd for CppFn { |
16 | fn partial_cmp(&self, other: &Self) -> Option<Ordering> { |
17 | Some(self.cmp(other)) |
18 | } |
19 | } |
20 | |
21 | impl CppFn { |
22 | pub fn type_name(&self) -> TypeName { |
23 | TypeName(self.namespace, self.method.name()) |
24 | } |
25 | |
26 | pub fn write_name(&self, writer: &Writer) -> TokenStream { |
27 | self.type_name().write(writer, &[]) |
28 | } |
29 | |
30 | pub fn write_link(&self, writer: &Writer, underlying_types: bool) -> TokenStream { |
31 | let name = self.method.name(); |
32 | let library = self.method.module_name().to_lowercase(); |
33 | let impl_map = self.method.impl_map().unwrap(); |
34 | let mut symbol = Some(impl_map.import_name()); |
35 | |
36 | if symbol == Some(name) { |
37 | symbol = None; |
38 | } |
39 | |
40 | let name = to_ident(self.method.name()); |
41 | let impl_flags = impl_map.flags(); |
42 | |
43 | let abi = if impl_flags.contains(PInvokeAttributes::CallConvPlatformapi) { |
44 | "system" |
45 | } else if impl_flags.contains(PInvokeAttributes::CallConvCdecl) { |
46 | "cdecl" |
47 | } else { |
48 | panic!() |
49 | }; |
50 | |
51 | let signature = self.method.signature(self.namespace, &[]); |
52 | |
53 | let params = signature.params.iter().map(|(ty, param)| { |
54 | let name = to_ident(¶m.name().to_lowercase()); |
55 | let ty = if underlying_types { |
56 | ty.underlying_type().write_abi(writer) |
57 | } else { |
58 | ty.write_abi(writer) |
59 | }; |
60 | quote! { #name: #ty } |
61 | }); |
62 | |
63 | let return_sig = writer.write_return_sig(self.method, &signature, underlying_types); |
64 | |
65 | let vararg = |
66 | if writer.config.sys && signature.call_flags.contains(MethodCallAttributes::VARARG) { |
67 | quote! { , ... } |
68 | } else { |
69 | quote! {} |
70 | }; |
71 | |
72 | link_fmt(quote! { |
73 | windows_targets::link!(#library #abi #symbol fn #name(#(#params),* #vararg) #return_sig); |
74 | }) |
75 | } |
76 | |
77 | pub fn write(&self, writer: &Writer) -> TokenStream { |
78 | let name = to_ident(self.method.name()); |
79 | let signature = self.method.signature(self.namespace, &[]); |
80 | let mut dependencies = TypeMap::new(); |
81 | |
82 | if writer.config.package { |
83 | self.dependencies(&mut dependencies); |
84 | } |
85 | |
86 | let link = self.write_link(writer, false); |
87 | let cfg = writer.write_cfg(self.method, self.namespace, &dependencies, false); |
88 | let window_long = self.write_window_long(); |
89 | if writer.config.sys { |
90 | return quote! { |
91 | #cfg |
92 | #link |
93 | #window_long |
94 | }; |
95 | } |
96 | |
97 | let method = CppMethod::new(self.method, self.namespace); |
98 | let args = method.write_args(); |
99 | let params = method.write_params(writer); |
100 | let generics = method.write_generics(); |
101 | let abi_return_type = method.write_return(writer); |
102 | |
103 | let wrapper = match method.return_hint { |
104 | ReturnHint::Query(..) => { |
105 | let where_clause = method.write_where(writer, true); |
106 | |
107 | quote! { |
108 | #cfg |
109 | #[inline] |
110 | pub unsafe fn #name<#generics T>(#params) -> windows_core::Result<T> #where_clause { |
111 | #link |
112 | let mut result__ = core::ptr::null_mut(); |
113 | unsafe { #name(#args).and_then(||windows_core::Type::from_abi(result__)) } |
114 | } |
115 | } |
116 | } |
117 | ReturnHint::QueryOptional(..) => { |
118 | let where_clause = method.write_where(writer, true); |
119 | |
120 | quote! { |
121 | #cfg |
122 | #[inline] |
123 | pub unsafe fn #name<#generics T>(#params result__: *mut Option<T>) -> windows_core::Result<()> #where_clause { |
124 | #link |
125 | unsafe { #name(#args).ok() } |
126 | } |
127 | } |
128 | } |
129 | ReturnHint::ResultValue => { |
130 | let where_clause = method.write_where(writer, false); |
131 | let return_type = signature.params[signature.params.len() - 1].0.deref(); |
132 | |
133 | let map = if !return_type.is_interface() { |
134 | quote! { map(||core::mem::transmute(result__)) } |
135 | } else { |
136 | quote! { and_then(||windows_core::Type::from_abi(result__)) } |
137 | }; |
138 | |
139 | let return_type = return_type.write_name(writer); |
140 | |
141 | quote! { |
142 | #cfg |
143 | #[inline] |
144 | pub unsafe fn #name<#generics>(#params) -> windows_core::Result<#return_type> #where_clause { |
145 | #link |
146 | unsafe { |
147 | let mut result__ = core::mem::zeroed(); |
148 | #name(#args).#map |
149 | } |
150 | } |
151 | } |
152 | } |
153 | ReturnHint::ResultVoid => { |
154 | let where_clause = method.write_where(writer, false); |
155 | |
156 | quote! { |
157 | #cfg |
158 | #[inline] |
159 | pub unsafe fn #name<#generics>(#params) -> windows_core::Result<()> #where_clause { |
160 | #link |
161 | unsafe { #name(#args).ok() } |
162 | } |
163 | } |
164 | } |
165 | ReturnHint::ReturnValue => { |
166 | let where_clause = method.write_where(writer, false); |
167 | |
168 | let return_type = method.signature.params[method.signature.params.len() - 1] |
169 | .0 |
170 | .deref(); |
171 | |
172 | if return_type.is_interface() { |
173 | let return_type = return_type.write_name(writer); |
174 | |
175 | quote! { |
176 | #cfg |
177 | #[inline] |
178 | pub unsafe fn #name<#generics>(#params) -> windows_core::Result<#return_type> #where_clause { |
179 | #link |
180 | unsafe { |
181 | let mut result__ = core::mem::zeroed(); |
182 | #name(#args); |
183 | windows_core::Type::from_abi(result__) |
184 | } |
185 | } |
186 | } |
187 | } else { |
188 | let map = if return_type.is_copyable() { |
189 | quote! { result__ } |
190 | } else { |
191 | quote! { core::mem::transmute(result__) } |
192 | }; |
193 | |
194 | let where_clause = method.write_where(writer, false); |
195 | let return_type = return_type.write_name(writer); |
196 | |
197 | quote! { |
198 | #cfg |
199 | #[inline] |
200 | pub unsafe fn #name<#generics>(#params) -> #return_type #where_clause { |
201 | #link |
202 | unsafe { |
203 | let mut result__ = core::mem::zeroed(); |
204 | #name(#args); |
205 | #map |
206 | } |
207 | } |
208 | } |
209 | } |
210 | } |
211 | ReturnHint::ReturnStruct | ReturnHint::None => { |
212 | let where_clause = method.write_where(writer, false); |
213 | |
214 | if method.handle_last_error() { |
215 | let return_type = signature.return_type.0.write_name(writer); |
216 | |
217 | quote! { |
218 | #cfg |
219 | #[inline] |
220 | pub unsafe fn #name<#generics>(#params) -> windows_core::Result<#return_type> #where_clause { |
221 | #link |
222 | let result__ = unsafe { #name(#args) }; |
223 | (!result__.is_invalid()).then_some(result__).ok_or_else(windows_core::Error::from_win32) |
224 | } |
225 | } |
226 | } else { |
227 | quote! { |
228 | #cfg |
229 | #[inline] |
230 | pub unsafe fn #name<#generics>(#params) #abi_return_type #where_clause { |
231 | #link |
232 | unsafe { #name(#args) } |
233 | } |
234 | } |
235 | } |
236 | } |
237 | }; |
238 | |
239 | quote! { |
240 | #wrapper |
241 | #window_long |
242 | } |
243 | } |
244 | |
245 | fn write_window_long(&self) -> TokenStream { |
246 | match self.method.name() { |
247 | "GetWindowLongPtrA" => quote! { |
248 | #[cfg(target_pointer_width = "32" )] |
249 | pub use GetWindowLongA as GetWindowLongPtrA; |
250 | }, |
251 | "GetWindowLongPtrW" => quote! { |
252 | #[cfg(target_pointer_width = "32" )] |
253 | pub use GetWindowLongW as GetWindowLongPtrW; |
254 | }, |
255 | "SetWindowLongPtrA" => quote! { |
256 | #[cfg(target_pointer_width = "32" )] |
257 | pub use SetWindowLongA as SetWindowLongPtrA; |
258 | }, |
259 | "SetWindowLongPtrW" => quote! { |
260 | #[cfg(target_pointer_width = "32" )] |
261 | pub use SetWindowLongW as SetWindowLongPtrW; |
262 | }, |
263 | _ => quote! {}, |
264 | } |
265 | } |
266 | |
267 | pub fn dependencies(&self, dependencies: &mut TypeMap) { |
268 | self.method |
269 | .signature(self.namespace, &[]) |
270 | .dependencies(dependencies); |
271 | |
272 | let dependency = match self.method.name() { |
273 | "GetWindowLongPtrA" => Some("GetWindowLongA" ), |
274 | "GetWindowLongPtrW" => Some("GetWindowLongW" ), |
275 | "SetWindowLongPtrA" => Some("SetWindowLongA" ), |
276 | "SetWindowLongPtrW" => Some("SetWindowLongW" ), |
277 | _ => None, |
278 | }; |
279 | |
280 | if let Some(dependency) = dependency { |
281 | self.method |
282 | .reader() |
283 | .unwrap_full_name(self.namespace, dependency) |
284 | .dependencies(dependencies); |
285 | } |
286 | } |
287 | } |
288 | |
289 | impl Writer { |
290 | pub fn write_return_sig( |
291 | &self, |
292 | method: MethodDef, |
293 | signature: &Signature, |
294 | underlying_types: bool, |
295 | ) -> TokenStream { |
296 | match &signature.return_type.0 { |
297 | Type::Void => { |
298 | if method.has_attribute("DoesNotReturnAttribute" ) { |
299 | quote! { -> ! } |
300 | } else { |
301 | quote! {} |
302 | } |
303 | } |
304 | ty => { |
305 | let ty = if underlying_types { |
306 | ty.underlying_type().write_default(self) |
307 | } else { |
308 | ty.write_default(self) |
309 | }; |
310 | |
311 | quote! { -> #ty } |
312 | } |
313 | } |
314 | } |
315 | } |
316 | |
317 | fn link_fmt(tokens: TokenStream) -> TokenStream { |
318 | let mut tokens: String = tokens.0.replacen(pat:" ! ( " , to:"!(" , count:1); |
319 | tokens = tokens.replacen(pat:" ( " , to:"(" , count:1); |
320 | tokens = tokens.replace(from:" , " , to:", " ); |
321 | tokens = tokens.replace(from:" )" , to:")" ); |
322 | tokens.into() |
323 | } |
324 | |