| 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 | |