1 | use std::collections::*; |
2 | pub use windows_metadata::*; |
3 | |
4 | #[derive (Clone)] |
5 | pub struct Interface { |
6 | pub ty: Type, |
7 | pub kind: InterfaceKind, |
8 | } |
9 | |
10 | #[derive (Copy, Clone, PartialEq, Eq)] |
11 | pub enum InterfaceKind { |
12 | None, |
13 | Default, |
14 | Overridable, |
15 | Static, |
16 | Base, |
17 | } |
18 | |
19 | #[derive (Copy, Clone, PartialEq, Eq)] |
20 | pub struct QueryPosition { |
21 | pub object: usize, |
22 | pub guid: usize, |
23 | } |
24 | |
25 | #[derive (Copy, Clone, PartialEq, Eq)] |
26 | pub enum SignatureKind { |
27 | Query(QueryPosition), |
28 | QueryOptional(QueryPosition), |
29 | ResultValue, |
30 | ResultVoid, |
31 | ReturnStruct, |
32 | ReturnValue, |
33 | ReturnVoid, |
34 | PreserveSig, |
35 | } |
36 | |
37 | #[derive (Copy, Clone, Eq, PartialEq)] |
38 | pub enum SignatureParamKind { |
39 | ArrayFixed(usize), |
40 | ArrayRelativeLen(usize), |
41 | ArrayRelativeByteLen(usize), |
42 | ArrayRelativePtr(usize), |
43 | TryInto, |
44 | IntoParam, |
45 | OptionalPointer, |
46 | ValueType, |
47 | Blittable, |
48 | Other, |
49 | } |
50 | |
51 | pub struct Signature { |
52 | pub def: MethodDef, |
53 | pub params: Vec<SignatureParam>, |
54 | pub return_type: Type, |
55 | pub call_flags: MethodCallAttributes, |
56 | } |
57 | |
58 | pub struct SignatureParam { |
59 | pub def: Param, |
60 | pub ty: Type, |
61 | pub kind: SignatureParamKind, |
62 | } |
63 | |
64 | #[derive (PartialEq, Eq, Debug)] |
65 | pub enum AsyncKind { |
66 | None, |
67 | Action, |
68 | ActionWithProgress, |
69 | Operation, |
70 | OperationWithProgress, |
71 | } |
72 | |
73 | #[derive (Clone, PartialEq, Eq, Default)] |
74 | pub struct Guid(pub u32, pub u16, pub u16, pub u8, pub u8, pub u8, pub u8, pub u8, pub u8, pub u8, pub u8); |
75 | |
76 | impl Guid { |
77 | pub fn from_args(args: &[(&str, Value)]) -> Self { |
78 | fn unwrap_u32(value: &Value) -> u32 { |
79 | match value { |
80 | Value::U32(value) => *value, |
81 | rest => unimplemented!(" {rest:?}" ), |
82 | } |
83 | } |
84 | fn unwrap_u16(value: &Value) -> u16 { |
85 | match value { |
86 | Value::U16(value) => *value, |
87 | rest => unimplemented!(" {rest:?}" ), |
88 | } |
89 | } |
90 | fn unwrap_u8(value: &Value) -> u8 { |
91 | match value { |
92 | Value::U8(value) => *value, |
93 | rest => unimplemented!(" {rest:?}" ), |
94 | } |
95 | } |
96 | Self(unwrap_u32(&args[0].1), unwrap_u16(&args[1].1), unwrap_u16(&args[2].1), unwrap_u8(&args[3].1), unwrap_u8(&args[4].1), unwrap_u8(&args[5].1), unwrap_u8(&args[6].1), unwrap_u8(&args[7].1), unwrap_u8(&args[8].1), unwrap_u8(&args[9].1), unwrap_u8(&args[10].1)) |
97 | } |
98 | |
99 | pub fn from_string_args(args: &[&str]) -> Self { |
100 | Self(args[0].parse().unwrap(), args[1].parse().unwrap(), args[2].parse().unwrap(), args[3].parse().unwrap(), args[4].parse().unwrap(), args[5].parse().unwrap(), args[6].parse().unwrap(), args[7].parse().unwrap(), args[8].parse().unwrap(), args[9].parse().unwrap(), args[10].parse().unwrap()) |
101 | } |
102 | } |
103 | |
104 | impl std::fmt::Debug for Guid { |
105 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
106 | write!(f, " {:08x?}- {:04x?}- {:04x?}- {:02x?}{:02x?}- {:02x?}{:02x?}{:02x?}{:02x?}{:02x?}{:02x?}" , self.0, self.1, self.2, self.3, self.4, self.5, self.6, self.7, self.8, self.9, self.10) |
107 | } |
108 | } |
109 | |
110 | impl SignatureParamKind { |
111 | fn is_array(&self) -> bool { |
112 | matches!(self, Self::ArrayFixed(_) | Self::ArrayRelativeLen(_) | Self::ArrayRelativeByteLen(_) | Self::ArrayRelativePtr(_)) |
113 | } |
114 | } |
115 | |
116 | impl SignatureParam { |
117 | pub fn is_convertible(&self) -> bool { |
118 | !self.def.flags().contains(ParamAttributes::Out) && !self.ty.is_winrt_array() && !self.ty.is_pointer() && !self.kind.is_array() && (type_is_borrowed(&self.ty) || type_is_non_exclusive_winrt_interface(&self.ty) || type_is_trivially_convertible(&self.ty)) |
119 | } |
120 | |
121 | fn is_retval(&self) -> bool { |
122 | // The Win32 metadata uses `RetValAttribute` to call out retval methods but it is employed |
123 | // very sparingly, so this heuristic is used to apply the transformation more uniformly. |
124 | if self.def.has_attribute("RetValAttribute" ) { |
125 | return true; |
126 | } |
127 | if !self.ty.is_pointer() { |
128 | return false; |
129 | } |
130 | if self.ty.is_void() { |
131 | return false; |
132 | } |
133 | let flags = self.def.flags(); |
134 | if flags.contains(ParamAttributes::In) || !flags.contains(ParamAttributes::Out) || flags.contains(ParamAttributes::Optional) || self.kind.is_array() { |
135 | return false; |
136 | } |
137 | if param_kind(self.def).is_array() { |
138 | return false; |
139 | } |
140 | // If it's bigger than 128 bits, best to pass as a reference. |
141 | if self.ty.deref().size() > 16 { |
142 | return false; |
143 | } |
144 | // Win32 callbacks are defined as `Option<T>` so we don't include them here to avoid |
145 | // producing the `Result<Option<T>>` anti-pattern. |
146 | match self.ty.deref() { |
147 | Type::TypeDef(def, _) => !type_def_is_callback(def), |
148 | _ => true, |
149 | } |
150 | } |
151 | } |
152 | |
153 | impl Signature { |
154 | pub fn kind(&self) -> SignatureKind { |
155 | if self.def.has_attribute("CanReturnMultipleSuccessValuesAttribute" ) { |
156 | return SignatureKind::PreserveSig; |
157 | } |
158 | match &self.return_type { |
159 | Type::Void if self.is_retval() => SignatureKind::ReturnValue, |
160 | Type::Void => SignatureKind::ReturnVoid, |
161 | Type::HRESULT => { |
162 | if self.params.len() >= 2 { |
163 | if let Some((guid, object)) = signature_param_is_query(&self.params) { |
164 | if self.params[object].def.flags().contains(ParamAttributes::Optional) { |
165 | return SignatureKind::QueryOptional(QueryPosition { object, guid }); |
166 | } else { |
167 | return SignatureKind::Query(QueryPosition { object, guid }); |
168 | } |
169 | } |
170 | } |
171 | if self.is_retval() { |
172 | SignatureKind::ResultValue |
173 | } else { |
174 | SignatureKind::ResultVoid |
175 | } |
176 | } |
177 | Type::TypeDef(def, _) if def.type_name() == TypeName::WIN32_ERROR => SignatureKind::ResultVoid, |
178 | Type::TypeDef(def, _) if def.type_name() == TypeName::BOOL && method_def_last_error(self.def) => SignatureKind::ResultVoid, |
179 | _ if type_is_struct(&self.return_type) => SignatureKind::ReturnStruct, |
180 | _ => SignatureKind::PreserveSig, |
181 | } |
182 | } |
183 | |
184 | fn is_retval(&self) -> bool { |
185 | self.params.last().map_or(false, |param| param.is_retval()) |
186 | && self.params[..self.params.len() - 1].iter().all(|param| { |
187 | let flags = param.def.flags(); |
188 | !flags.contains(ParamAttributes::Out) |
189 | }) |
190 | } |
191 | } |
192 | |
193 | pub fn type_def_invoke_method(row: TypeDef) -> MethodDef { |
194 | row.methods().find(|method| method.name() == "Invoke" ).expect(msg:"`Invoke` method not found" ) |
195 | } |
196 | |
197 | pub fn type_def_generics(def: TypeDef) -> Vec<Type> { |
198 | def.generics().map(Type::GenericParam).collect() |
199 | } |
200 | |
201 | // TODO: namespace should not be required - it's a hack to accomodate Win32 metadata |
202 | // TODO: this is very Rust-specific and Win32-metadata specific with all of its translation. Replace with literal signature parser that just returns slice of types. |
203 | pub fn method_def_signature(namespace: &str, row: MethodDef, generics: &[Type]) -> Signature { |
204 | let reader = row.reader(); |
205 | let mut blob = row.blob(4); |
206 | let call_flags = MethodCallAttributes(blob.read_usize() as u8); |
207 | let _param_count = blob.read_usize(); |
208 | let mut return_type = reader.type_from_blob(&mut blob, None, generics); |
209 | |
210 | let mut params: Vec<SignatureParam> = row |
211 | .params() |
212 | .filter_map(|param| { |
213 | let param_is_const = param.has_attribute("ConstAttribute" ); |
214 | if param.sequence() == 0 { |
215 | if param_is_const { |
216 | return_type = return_type.clone().to_const_type(); |
217 | } |
218 | None |
219 | } else { |
220 | let is_output = param.flags().contains(ParamAttributes::Out); |
221 | let mut ty = reader.type_from_blob(&mut blob, None, generics); |
222 | |
223 | if let Some(name) = param_or_enum(param) { |
224 | let def = reader.get_type_def(namespace, &name).next().expect("Enum not found" ); |
225 | ty = Type::PrimitiveOrEnum(Box::new(ty), Box::new(Type::TypeDef(def, Vec::new()))); |
226 | } |
227 | |
228 | if param_is_const || !is_output { |
229 | ty = ty.to_const_type(); |
230 | } |
231 | if !is_output { |
232 | ty = ty.to_const_ptr(); |
233 | } |
234 | let kind = param_kind(param); |
235 | Some(SignatureParam { def: param, ty, kind }) |
236 | } |
237 | }) |
238 | .collect(); |
239 | |
240 | for position in 0..params.len() { |
241 | // Point len params back to the corresponding ptr params. |
242 | match params[position].kind { |
243 | SignatureParamKind::ArrayRelativeLen(relative) | SignatureParamKind::ArrayRelativeByteLen(relative) => { |
244 | // The len params must be input only. |
245 | if !params[relative].def.flags().contains(ParamAttributes::Out) && position != relative && !params[relative].ty.is_pointer() { |
246 | params[relative].kind = SignatureParamKind::ArrayRelativePtr(position); |
247 | } else { |
248 | params[position].kind = SignatureParamKind::Other; |
249 | } |
250 | } |
251 | SignatureParamKind::ArrayFixed(_) => { |
252 | if params[position].def.has_attribute("FreeWithAttribute" ) { |
253 | params[position].kind = SignatureParamKind::Other; |
254 | } |
255 | } |
256 | _ => {} |
257 | } |
258 | } |
259 | |
260 | let mut sets = BTreeMap::<usize, Vec<usize>>::new(); |
261 | |
262 | // Finds sets of ptr params pointing at the same len param. |
263 | for (position, param) in params.iter().enumerate() { |
264 | match param.kind { |
265 | SignatureParamKind::ArrayRelativeLen(relative) | SignatureParamKind::ArrayRelativeByteLen(relative) => { |
266 | sets.entry(relative).or_default().push(position); |
267 | } |
268 | _ => {} |
269 | } |
270 | } |
271 | |
272 | // Remove all sets. |
273 | for (len, ptrs) in sets { |
274 | if ptrs.len() > 1 { |
275 | params[len].kind = SignatureParamKind::Other; |
276 | for ptr in ptrs { |
277 | params[ptr].kind = SignatureParamKind::Other; |
278 | } |
279 | } |
280 | } |
281 | |
282 | // Remove any byte arrays that aren't byte-sized types. |
283 | for position in 0..params.len() { |
284 | if let SignatureParamKind::ArrayRelativeByteLen(relative) = params[position].kind { |
285 | if !params[position].ty.is_byte_size() { |
286 | params[position].kind = SignatureParamKind::Other; |
287 | params[relative].kind = SignatureParamKind::Other; |
288 | } |
289 | } |
290 | } |
291 | |
292 | for param in &mut params { |
293 | if param.kind == SignatureParamKind::Other { |
294 | if param.is_convertible() { |
295 | if type_is_non_exclusive_winrt_interface(¶m.ty) { |
296 | param.kind = SignatureParamKind::TryInto; |
297 | } else { |
298 | param.kind = SignatureParamKind::IntoParam; |
299 | } |
300 | } else { |
301 | let flags = param.def.flags(); |
302 | if param.ty.is_pointer() && (flags.contains(ParamAttributes::Optional) || param.def.has_attribute("ReservedAttribute" )) { |
303 | param.kind = SignatureParamKind::OptionalPointer; |
304 | } else if type_is_primitive(¶m.ty) && (!param.ty.is_pointer() || type_is_blittable(¶m.ty.deref())) { |
305 | param.kind = SignatureParamKind::ValueType; |
306 | } else if type_is_blittable(¶m.ty) { |
307 | param.kind = SignatureParamKind::Blittable; |
308 | } |
309 | } |
310 | } |
311 | } |
312 | |
313 | Signature { def: row, params, return_type, call_flags } |
314 | } |
315 | |
316 | fn param_kind(row: Param) -> SignatureParamKind { |
317 | for attribute: Attribute in row.attributes() { |
318 | match attribute.name() { |
319 | "NativeArrayInfoAttribute" => { |
320 | for (_, value: Value) in attribute.args() { |
321 | match value { |
322 | Value::I16(value: i16) => return SignatureParamKind::ArrayRelativeLen(value as usize), |
323 | Value::I32(value: i32) => return SignatureParamKind::ArrayFixed(value as usize), |
324 | _ => {} |
325 | } |
326 | } |
327 | } |
328 | "MemorySizeAttribute" => { |
329 | for (_, value: Value) in attribute.args() { |
330 | if let Value::I16(value: i16) = value { |
331 | return SignatureParamKind::ArrayRelativeByteLen(value as usize); |
332 | } |
333 | } |
334 | } |
335 | _ => {} |
336 | } |
337 | } |
338 | SignatureParamKind::Other |
339 | } |
340 | |
341 | // TODO: this is a terribly broken Win32 metadata attribute - need to get rid of it. |
342 | fn param_or_enum(row: Param) -> Option<String> { |
343 | row.find_attribute(name:"AssociatedEnumAttribute" ).and_then(|attribute: Attribute| { |
344 | for (_, arg: Value) in attribute.args() { |
345 | if let Value::String(name: String) = arg { |
346 | return Some(name); |
347 | } |
348 | } |
349 | None |
350 | }) |
351 | } |
352 | |
353 | fn signature_param_is_query(params: &[SignatureParam]) -> Option<(usize, usize)> { |
354 | if let Some(guid: usize) = params.iter().rposition(|param: &SignatureParam| param.ty == Type::ConstPtr(Box::new(Type::GUID), 1) && !param.def.flags().contains(ParamAttributes::Out)) { |
355 | if let Some(object: usize) = params.iter().rposition(|param: &SignatureParam| param.ty == Type::MutPtr(Box::new(Type::Void), 2) && param.def.has_attribute(name:"ComOutPtrAttribute" )) { |
356 | return Some((guid, object)); |
357 | } |
358 | } |
359 | |
360 | None |
361 | } |
362 | |
363 | fn method_def_last_error(row: MethodDef) -> bool { |
364 | if let Some(map: ImplMap) = row.impl_map() { |
365 | map.flags().contains(PInvokeAttributes::SupportsLastError) |
366 | } else { |
367 | false |
368 | } |
369 | } |
370 | |
371 | pub fn type_is_borrowed(ty: &Type) -> bool { |
372 | match ty { |
373 | Type::TypeDef(row: &TypeDef, _) => !type_def_is_blittable(*row), |
374 | Type::BSTR | Type::PCSTR | Type::PCWSTR | Type::IInspectable | Type::IUnknown | Type::GenericParam(_) => true, |
375 | _ => false, |
376 | } |
377 | } |
378 | |
379 | pub fn type_is_non_exclusive_winrt_interface(ty: &Type) -> bool { |
380 | match ty { |
381 | Type::TypeDef(row: &TypeDef, _) => { |
382 | let flags: TypeAttributes = row.flags(); |
383 | if !flags.contains(TypeAttributes::WindowsRuntime) { |
384 | false |
385 | } else { |
386 | match row.kind() { |
387 | TypeKind::Interface => !type_def_is_exclusive(*row), |
388 | TypeKind::Class => row.has_attribute(name:"ComposableAttribute" ), |
389 | _ => false, |
390 | } |
391 | } |
392 | } |
393 | _ => false, |
394 | } |
395 | } |
396 | |
397 | fn type_is_trivially_convertible(ty: &Type) -> bool { |
398 | match ty { |
399 | Type::TypeDef(row: &TypeDef, _) => match row.kind() { |
400 | TypeKind::Struct => type_def_is_handle(*row), |
401 | _ => false, |
402 | }, |
403 | _ => false, |
404 | } |
405 | } |
406 | |
407 | fn type_def_is_callback(row: TypeDef) -> bool { |
408 | !row.flags().contains(TypeAttributes::WindowsRuntime) && row.kind() == TypeKind::Delegate |
409 | } |
410 | |
411 | pub fn type_has_callback(ty: &Type) -> bool { |
412 | match ty { |
413 | Type::TypeDef(row: &TypeDef, _) => type_def_has_callback(*row), |
414 | Type::Win32Array(ty: &Box, _) => type_has_callback(ty), |
415 | _ => false, |
416 | } |
417 | } |
418 | |
419 | pub fn type_def_has_callback(row: TypeDef) -> bool { |
420 | if type_def_is_callback(row) { |
421 | return true; |
422 | } |
423 | if row.kind() != TypeKind::Struct { |
424 | return false; |
425 | } |
426 | fn check(row: TypeDef) -> bool { |
427 | if row.fields().any(|field| type_has_callback(&field.ty(Some(row)))) { |
428 | return true; |
429 | } |
430 | false |
431 | } |
432 | let type_name = row.type_name(); |
433 | if type_name.namespace.is_empty() { |
434 | check(row) |
435 | } else { |
436 | for row in row.reader().get_type_def(type_name.namespace, type_name.name) { |
437 | if check(row) { |
438 | return true; |
439 | } |
440 | } |
441 | false |
442 | } |
443 | } |
444 | |
445 | pub fn type_interfaces(ty: &Type) -> Vec<Interface> { |
446 | // TODO: collect into btree map and then return collected vec |
447 | // This will both sort the results and should make finding dupes faster |
448 | fn walk(result: &mut Vec<Interface>, parent: &Type, is_base: bool) { |
449 | if let Type::TypeDef(row, generics) = parent { |
450 | for mut child in type_def_interfaces(*row, generics) { |
451 | child.kind = if !is_base && child.kind == InterfaceKind::Default { |
452 | InterfaceKind::Default |
453 | } else if child.kind == InterfaceKind::Overridable { |
454 | continue; |
455 | } else if is_base { |
456 | InterfaceKind::Base |
457 | } else { |
458 | InterfaceKind::None |
459 | }; |
460 | let mut found = false; |
461 | for existing in result.iter_mut() { |
462 | if existing.ty == child.ty { |
463 | found = true; |
464 | if child.kind == InterfaceKind::Default { |
465 | existing.kind = child.kind |
466 | } |
467 | } |
468 | } |
469 | if !found { |
470 | walk(result, &child.ty, is_base); |
471 | result.push(child); |
472 | } |
473 | } |
474 | } |
475 | } |
476 | let mut result = Vec::new(); |
477 | walk(&mut result, ty, false); |
478 | if let Type::TypeDef(row, _) = ty { |
479 | if row.kind() == TypeKind::Class { |
480 | for base in type_def_bases(*row) { |
481 | walk(&mut result, &Type::TypeDef(base, Vec::new()), true); |
482 | } |
483 | for attribute in row.attributes() { |
484 | match attribute.name() { |
485 | "StaticAttribute" | "ActivatableAttribute" => { |
486 | for (_, arg) in attribute.args() { |
487 | if let Value::TypeName(type_name) = arg { |
488 | let def = row.reader().get_type_def(type_name.namespace, type_name.name).next().expect("Type not found" ); |
489 | result.push(Interface { ty: Type::TypeDef(def, Vec::new()), kind: InterfaceKind::Static }); |
490 | break; |
491 | } |
492 | } |
493 | } |
494 | _ => {} |
495 | } |
496 | } |
497 | } |
498 | } |
499 | result.sort_by(|a, b| type_name(&a.ty).cmp(type_name(&b.ty))); |
500 | result |
501 | } |
502 | |
503 | fn type_name(ty: &Type) -> &str { |
504 | match ty { |
505 | Type::TypeDef(row: &TypeDef, _) => row.name(), |
506 | _ => "" , |
507 | } |
508 | } |
509 | |
510 | pub fn field_is_blittable(row: Field, enclosing: TypeDef) -> bool { |
511 | type_is_blittable(&row.ty(enclosing:Some(enclosing))) |
512 | } |
513 | |
514 | pub fn field_is_copyable(row: Field, enclosing: TypeDef) -> bool { |
515 | type_is_copyable(&row.ty(enclosing:Some(enclosing))) |
516 | } |
517 | |
518 | pub fn type_is_blittable(ty: &Type) -> bool { |
519 | match ty { |
520 | Type::TypeDef(row: &TypeDef, _) => type_def_is_blittable(*row), |
521 | Type::String | Type::BSTR | Type::IInspectable | Type::IUnknown | Type::GenericParam(_) => false, |
522 | Type::Win32Array(kind: &Box, _) => type_is_blittable(ty:kind), |
523 | Type::WinrtArray(kind: &Box) => type_is_blittable(ty:kind), |
524 | _ => true, |
525 | } |
526 | } |
527 | |
528 | fn type_is_copyable(ty: &Type) -> bool { |
529 | match ty { |
530 | Type::TypeDef(row: &TypeDef, _) => type_def_is_copyable(*row), |
531 | Type::String | Type::BSTR | Type::IInspectable | Type::IUnknown | Type::GenericParam(_) => false, |
532 | Type::Win32Array(kind: &Box, _) => type_is_copyable(ty:kind), |
533 | Type::WinrtArray(kind: &Box) => type_is_copyable(ty:kind), |
534 | _ => true, |
535 | } |
536 | } |
537 | |
538 | pub fn type_def_is_blittable(row: TypeDef) -> bool { |
539 | match row.kind() { |
540 | TypeKind::Struct => { |
541 | if row.flags().contains(TypeAttributes::WindowsRuntime) { |
542 | row.fields().all(|field: Field| field_is_blittable(row:field, enclosing:row)) |
543 | } else { |
544 | true |
545 | } |
546 | } |
547 | TypeKind::Enum => true, |
548 | TypeKind::Delegate => !row.flags().contains(TypeAttributes::WindowsRuntime), |
549 | _ => false, |
550 | } |
551 | } |
552 | |
553 | pub fn type_def_is_copyable(row: TypeDef) -> bool { |
554 | match row.kind() { |
555 | TypeKind::Struct => row.fields().all(|field: Field| field_is_copyable(row:field, enclosing:row)), |
556 | TypeKind::Enum => true, |
557 | TypeKind::Delegate => !row.flags().contains(TypeAttributes::WindowsRuntime), |
558 | _ => false, |
559 | } |
560 | } |
561 | |
562 | pub fn type_def_is_exclusive(row: TypeDef) -> bool { |
563 | row.has_attribute(name:"ExclusiveToAttribute" ) |
564 | } |
565 | |
566 | pub fn type_is_struct(ty: &Type) -> bool { |
567 | // This check is used to detect virtual functions that return C-style PODs that affect how the stack is packed for x86. |
568 | // It could be defined as a struct with more than one field but that check is complicated as it would have to detect |
569 | // nested structs. Fortunately, this is rare enough that this check is sufficient. |
570 | match ty { |
571 | Type::TypeDef(row: &TypeDef, _) => row.kind() == TypeKind::Struct && !type_def_is_handle(*row), |
572 | Type::GUID => true, |
573 | _ => false, |
574 | } |
575 | } |
576 | |
577 | fn type_def_is_primitive(row: TypeDef) -> bool { |
578 | match row.kind() { |
579 | TypeKind::Enum => true, |
580 | TypeKind::Struct => type_def_is_handle(row), |
581 | TypeKind::Delegate => !row.flags().contains(TypeAttributes::WindowsRuntime), |
582 | _ => false, |
583 | } |
584 | } |
585 | |
586 | pub fn type_is_primitive(ty: &Type) -> bool { |
587 | match ty { |
588 | Type::TypeDef(row: &TypeDef, _) => type_def_is_primitive(*row), |
589 | Type::Bool | Type::Char | Type::I8 | Type::U8 | Type::I16 | Type::U16 | Type::I32 | Type::U32 | Type::I64 | Type::U64 | Type::F32 | Type::F64 | Type::ISize | Type::USize | Type::HRESULT | Type::ConstPtr(_, _) | Type::MutPtr(_, _) => true, |
590 | _ => false, |
591 | } |
592 | } |
593 | |
594 | fn type_has_explicit_layout(ty: &Type) -> bool { |
595 | match ty { |
596 | Type::TypeDef(row: &TypeDef, _) => type_def_has_explicit_layout(*row), |
597 | Type::Win32Array(ty: &Box, _) => type_has_explicit_layout(ty), |
598 | _ => false, |
599 | } |
600 | } |
601 | |
602 | pub fn type_def_has_explicit_layout(row: TypeDef) -> bool { |
603 | if row.kind() != TypeKind::Struct { |
604 | return false; |
605 | } |
606 | fn check(row: TypeDef) -> bool { |
607 | if row.flags().contains(TypeAttributes::ExplicitLayout) { |
608 | return true; |
609 | } |
610 | if row.fields().any(|field| type_has_explicit_layout(&field.ty(Some(row)))) { |
611 | return true; |
612 | } |
613 | false |
614 | } |
615 | let type_name = row.type_name(); |
616 | if type_name.namespace.is_empty() { |
617 | check(row) |
618 | } else { |
619 | for row in row.reader().get_type_def(type_name.namespace, type_name.name) { |
620 | if check(row) { |
621 | return true; |
622 | } |
623 | } |
624 | false |
625 | } |
626 | } |
627 | |
628 | fn type_has_packing(ty: &Type) -> bool { |
629 | match ty { |
630 | Type::TypeDef(row: &TypeDef, _) => type_def_has_packing(*row), |
631 | Type::Win32Array(ty: &Box, _) => type_has_packing(ty), |
632 | _ => false, |
633 | } |
634 | } |
635 | |
636 | pub fn type_def_has_packing(row: TypeDef) -> bool { |
637 | if row.kind() != TypeKind::Struct { |
638 | return false; |
639 | } |
640 | fn check(row: TypeDef) -> bool { |
641 | if row.class_layout().is_some() { |
642 | return true; |
643 | } |
644 | if row.fields().any(|field| type_has_packing(&field.ty(Some(row)))) { |
645 | return true; |
646 | } |
647 | false |
648 | } |
649 | let type_name = row.type_name(); |
650 | if type_name.namespace.is_empty() { |
651 | check(row) |
652 | } else { |
653 | for row in row.reader().get_type_def(type_name.namespace, type_name.name) { |
654 | if check(row) { |
655 | return true; |
656 | } |
657 | } |
658 | false |
659 | } |
660 | } |
661 | |
662 | pub fn type_def_interfaces(def: TypeDef, generics: &[Type]) -> impl Iterator<Item = Interface> + '_ { |
663 | def.interface_impls().map(|imp: InterfaceImpl| { |
664 | let kind: InterfaceKind = if imp.has_attribute(name:"DefaultAttribute" ) { InterfaceKind::Default } else { InterfaceKind::None }; |
665 | Interface { kind, ty: imp.ty(generics) } |
666 | }) |
667 | } |
668 | |
669 | pub fn type_def_default_interface(row: TypeDef) -> Option<Type> { |
670 | type_def_interfaces(def:row, &[]).find_map(move |interface: Interface| if interface.kind == InterfaceKind::Default { Some(interface.ty) } else { None }) |
671 | } |
672 | |
673 | fn type_signature(ty: &Type) -> String { |
674 | match ty { |
675 | Type::Bool => "b1" .to_string(), |
676 | Type::Char => "c2" .to_string(), |
677 | Type::I8 => "i1" .to_string(), |
678 | Type::U8 => "u1" .to_string(), |
679 | Type::I16 => "i2" .to_string(), |
680 | Type::U16 => "u2" .to_string(), |
681 | Type::I32 => "i4" .to_string(), |
682 | Type::U32 => "u4" .to_string(), |
683 | Type::I64 => "i8" .to_string(), |
684 | Type::U64 => "u8" .to_string(), |
685 | Type::F32 => "f4" .to_string(), |
686 | Type::F64 => "f8" .to_string(), |
687 | Type::ISize => "is" .to_string(), |
688 | Type::USize => "us" .to_string(), |
689 | Type::String => "string" .to_string(), |
690 | Type::IInspectable => "cinterface(IInspectable)" .to_string(), |
691 | Type::GUID => "g16" .to_string(), |
692 | Type::HRESULT => "struct(Windows.Foundation.HResult;i4)" .to_string(), |
693 | Type::TypeDef(row: &TypeDef, generics: &Vec) => type_def_signature(*row, generics), |
694 | rest: &Type => unimplemented!(" {rest:?}" ), |
695 | } |
696 | } |
697 | |
698 | pub fn type_def_signature(row: TypeDef, generics: &[Type]) -> String { |
699 | match row.kind() { |
700 | TypeKind::Interface => type_def_interface_signature(row, generics), |
701 | TypeKind::Class => { |
702 | if let Some(Type::TypeDef(default, generics)) = type_def_default_interface(row) { |
703 | format!("rc( {}; {})" , row.type_name(), type_def_interface_signature(default, &generics)) |
704 | } else { |
705 | unimplemented!(); |
706 | } |
707 | } |
708 | TypeKind::Enum => format!("enum( {}; {})" , row.type_name(), type_signature(&row.underlying_type())), |
709 | TypeKind::Struct => { |
710 | let mut result = format!("struct( {}" , row.type_name()); |
711 | for field in row.fields() { |
712 | result.push(';' ); |
713 | result.push_str(&type_signature(&field.ty(Some(row)))); |
714 | } |
715 | result.push(')' ); |
716 | result |
717 | } |
718 | TypeKind::Delegate => { |
719 | if generics.is_empty() { |
720 | format!("delegate( {})" , type_def_interface_signature(row, generics)) |
721 | } else { |
722 | type_def_interface_signature(row, generics) |
723 | } |
724 | } |
725 | } |
726 | } |
727 | |
728 | fn type_def_interface_signature(row: TypeDef, generics: &[Type]) -> String { |
729 | let guid: Guid = type_def_guid(row).unwrap(); |
730 | if generics.is_empty() { |
731 | format!(" {{{guid:#?}}}" ) |
732 | } else { |
733 | let mut result: String = format!("pinterface( {{{guid:#?}}}" ); |
734 | for generic: &Type in generics { |
735 | result.push(ch:';' ); |
736 | result.push_str(&type_signature(ty:generic)); |
737 | } |
738 | result.push(ch:')' ); |
739 | result |
740 | } |
741 | } |
742 | |
743 | pub fn type_def_is_handle(row: TypeDef) -> bool { |
744 | row.has_attribute(name:"NativeTypedefAttribute" ) |
745 | } |
746 | |
747 | pub fn type_def_guid(row: TypeDef) -> Option<Guid> { |
748 | row.find_attribute(name:"GuidAttribute" ).map(|attribute: Attribute| Guid::from_args(&attribute.args())) |
749 | } |
750 | |
751 | pub fn type_def_bases(mut row: TypeDef) -> Vec<TypeDef> { |
752 | let mut bases: Vec = Vec::new(); |
753 | loop { |
754 | match row.extends() { |
755 | Some(base: TypeName) if base != TypeName::Object => { |
756 | row = row.reader().get_type_def(base.namespace, base.name).next().expect(msg:"Type not found" ); |
757 | bases.push(row); |
758 | } |
759 | _ => break, |
760 | } |
761 | } |
762 | bases |
763 | } |
764 | |
765 | pub fn type_def_invalid_values(row: TypeDef) -> Vec<i64> { |
766 | let mut values: Vec = Vec::new(); |
767 | for attribute: Attribute in row.attributes() { |
768 | if attribute.name() == "InvalidHandleValueAttribute" { |
769 | if let Some((_, Value::I64(value: &i64))) = attribute.args().first() { |
770 | values.push(*value); |
771 | } |
772 | } |
773 | } |
774 | values |
775 | } |
776 | |
777 | fn type_def_is_nullable(row: TypeDef) -> bool { |
778 | match row.kind() { |
779 | TypeKind::Interface | TypeKind::Class => true, |
780 | // Win32 callbacks are defined as `Option<T>` so we don't include them here to avoid them |
781 | // from being doubly wrapped in `Option`. |
782 | TypeKind::Delegate => row.flags().contains(TypeAttributes::WindowsRuntime), |
783 | _ => false, |
784 | } |
785 | } |
786 | |
787 | pub fn type_is_nullable(ty: &Type) -> bool { |
788 | match ty { |
789 | Type::TypeDef(row: &TypeDef, _) => type_def_is_nullable(*row), |
790 | Type::IInspectable | Type::IUnknown => true, |
791 | _ => false, |
792 | } |
793 | } |
794 | |
795 | pub fn type_def_vtables(row: TypeDef) -> Vec<Type> { |
796 | let mut result = Vec::new(); |
797 | if row.flags().contains(TypeAttributes::WindowsRuntime) { |
798 | result.push(Type::IUnknown); |
799 | if row.kind() != TypeKind::Delegate { |
800 | result.push(Type::IInspectable); |
801 | } |
802 | } else { |
803 | let mut next = row; |
804 | while let Some(base) = next.interface_impls().map(move |imp| imp.ty(&[])).next() { |
805 | match base { |
806 | Type::TypeDef(row, _) => { |
807 | next = row; |
808 | result.insert(0, base); |
809 | } |
810 | Type::IInspectable => { |
811 | result.insert(0, Type::IUnknown); |
812 | result.insert(1, Type::IInspectable); |
813 | break; |
814 | } |
815 | Type::IUnknown => { |
816 | result.insert(0, Type::IUnknown); |
817 | break; |
818 | } |
819 | rest => unimplemented!(" {rest:?}" ), |
820 | } |
821 | } |
822 | } |
823 | result |
824 | } |
825 | |