From 9918693037dce8aa4bb6f08741b6812923486c18 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 19 Jun 2024 11:26:03 +0200 Subject: Merging upstream version 1.76.0+dfsg1. Signed-off-by: Daniel Baumann --- vendor/windows-bindgen/src/metadata.rs | 824 +++++++++++++++++++++++++++++++++ 1 file changed, 824 insertions(+) create mode 100644 vendor/windows-bindgen/src/metadata.rs (limited to 'vendor/windows-bindgen/src/metadata.rs') diff --git a/vendor/windows-bindgen/src/metadata.rs b/vendor/windows-bindgen/src/metadata.rs new file mode 100644 index 000000000..b678c4b32 --- /dev/null +++ b/vendor/windows-bindgen/src/metadata.rs @@ -0,0 +1,824 @@ +use std::collections::*; +pub use windows_metadata::*; + +#[derive(Clone)] +pub struct Interface { + pub ty: Type, + pub kind: InterfaceKind, +} + +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum InterfaceKind { + None, + Default, + Overridable, + Static, + Base, +} + +#[derive(Copy, Clone, PartialEq, Eq)] +pub struct QueryPosition { + pub object: usize, + pub guid: usize, +} + +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum SignatureKind { + Query(QueryPosition), + QueryOptional(QueryPosition), + ResultValue, + ResultVoid, + ReturnStruct, + ReturnValue, + ReturnVoid, + PreserveSig, +} + +#[derive(Copy, Clone, Eq, PartialEq)] +pub enum SignatureParamKind { + ArrayFixed(usize), + ArrayRelativeLen(usize), + ArrayRelativeByteLen(usize), + ArrayRelativePtr(usize), + TryInto, + IntoParam, + OptionalPointer, + ValueType, + Blittable, + Other, +} + +pub struct Signature { + pub def: MethodDef, + pub params: Vec, + pub return_type: Type, + pub call_flags: MethodCallAttributes, +} + +pub struct SignatureParam { + pub def: Param, + pub ty: Type, + pub kind: SignatureParamKind, +} + +#[derive(PartialEq, Eq, Debug)] +pub enum AsyncKind { + None, + Action, + ActionWithProgress, + Operation, + OperationWithProgress, +} + +#[derive(Clone, PartialEq, Eq, Default)] +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); + +impl Guid { + pub fn from_args(args: &[(&str, Value)]) -> Self { + fn unwrap_u32(value: &Value) -> u32 { + match value { + Value::U32(value) => *value, + rest => unimplemented!("{rest:?}"), + } + } + fn unwrap_u16(value: &Value) -> u16 { + match value { + Value::U16(value) => *value, + rest => unimplemented!("{rest:?}"), + } + } + fn unwrap_u8(value: &Value) -> u8 { + match value { + Value::U8(value) => *value, + rest => unimplemented!("{rest:?}"), + } + } + 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)) + } + + pub fn from_string_args(args: &[&str]) -> Self { + 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()) + } +} + +impl std::fmt::Debug for Guid { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + 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) + } +} + +impl SignatureParamKind { + fn is_array(&self) -> bool { + matches!(self, Self::ArrayFixed(_) | Self::ArrayRelativeLen(_) | Self::ArrayRelativeByteLen(_) | Self::ArrayRelativePtr(_)) + } +} + +impl SignatureParam { + pub fn is_convertible(&self) -> bool { + !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)) + } + + fn is_retval(&self) -> bool { + // The Win32 metadata uses `RetValAttribute` to call out retval methods but it is employed + // very sparingly, so this heuristic is used to apply the transformation more uniformly. + if self.def.has_attribute("RetValAttribute") { + return true; + } + if !self.ty.is_pointer() { + return false; + } + if self.ty.is_void() { + return false; + } + let flags = self.def.flags(); + if flags.contains(ParamAttributes::In) || !flags.contains(ParamAttributes::Out) || flags.contains(ParamAttributes::Optional) || self.kind.is_array() { + return false; + } + if param_kind(self.def).is_array() { + return false; + } + // If it's bigger than 128 bits, best to pass as a reference. + if self.ty.deref().size() > 16 { + return false; + } + // Win32 callbacks are defined as `Option` so we don't include them here to avoid + // producing the `Result>` anti-pattern. + match self.ty.deref() { + Type::TypeDef(def, _) => !type_def_is_callback(def), + _ => true, + } + } +} + +impl Signature { + pub fn kind(&self) -> SignatureKind { + if self.def.has_attribute("CanReturnMultipleSuccessValuesAttribute") { + return SignatureKind::PreserveSig; + } + match &self.return_type { + Type::Void if self.is_retval() => SignatureKind::ReturnValue, + Type::Void => SignatureKind::ReturnVoid, + Type::HRESULT => { + if self.params.len() >= 2 { + if let Some((guid, object)) = signature_param_is_query(&self.params) { + if self.params[object].def.flags().contains(ParamAttributes::Optional) { + return SignatureKind::QueryOptional(QueryPosition { object, guid }); + } else { + return SignatureKind::Query(QueryPosition { object, guid }); + } + } + } + if self.is_retval() { + SignatureKind::ResultValue + } else { + SignatureKind::ResultVoid + } + } + Type::TypeDef(def, _) if def.type_name() == TypeName::WIN32_ERROR => SignatureKind::ResultVoid, + Type::TypeDef(def, _) if def.type_name() == TypeName::BOOL && method_def_last_error(self.def) => SignatureKind::ResultVoid, + _ if type_is_struct(&self.return_type) => SignatureKind::ReturnStruct, + _ => SignatureKind::PreserveSig, + } + } + + fn is_retval(&self) -> bool { + self.params.last().map_or(false, |param| param.is_retval()) + && self.params[..self.params.len() - 1].iter().all(|param| { + let flags = param.def.flags(); + !flags.contains(ParamAttributes::Out) + }) + } +} + +pub fn type_def_invoke_method(row: TypeDef) -> MethodDef { + row.methods().find(|method| method.name() == "Invoke").expect("`Invoke` method not found") +} + +pub fn type_def_generics(def: TypeDef) -> Vec { + def.generics().map(Type::GenericParam).collect() +} + +// TODO: namespace should not be required - it's a hack to accomodate Win32 metadata +// 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. +pub fn method_def_signature(namespace: &str, row: MethodDef, generics: &[Type]) -> Signature { + let reader = row.reader(); + let mut blob = row.blob(4); + let call_flags = MethodCallAttributes(blob.read_usize() as u8); + let _param_count = blob.read_usize(); + let mut return_type = reader.type_from_blob(&mut blob, None, generics); + + let mut params: Vec = row + .params() + .filter_map(|param| { + let param_is_const = param.has_attribute("ConstAttribute"); + if param.sequence() == 0 { + if param_is_const { + return_type = return_type.clone().to_const_type(); + } + None + } else { + let is_output = param.flags().contains(ParamAttributes::Out); + let mut ty = reader.type_from_blob(&mut blob, None, generics); + + if let Some(name) = param_or_enum(param) { + let def = reader.get_type_def(namespace, &name).next().expect("Enum not found"); + ty = Type::PrimitiveOrEnum(Box::new(ty), Box::new(Type::TypeDef(def, Vec::new()))); + } + + if param_is_const || !is_output { + ty = ty.to_const_type(); + } + if !is_output { + ty = ty.to_const_ptr(); + } + let kind = param_kind(param); + Some(SignatureParam { def: param, ty, kind }) + } + }) + .collect(); + + for position in 0..params.len() { + // Point len params back to the corresponding ptr params. + match params[position].kind { + SignatureParamKind::ArrayRelativeLen(relative) | SignatureParamKind::ArrayRelativeByteLen(relative) => { + // The len params must be input only. + if !params[relative].def.flags().contains(ParamAttributes::Out) && position != relative && !params[relative].ty.is_pointer() { + params[relative].kind = SignatureParamKind::ArrayRelativePtr(position); + } else { + params[position].kind = SignatureParamKind::Other; + } + } + SignatureParamKind::ArrayFixed(_) => { + if params[position].def.has_attribute("FreeWithAttribute") { + params[position].kind = SignatureParamKind::Other; + } + } + _ => {} + } + } + + let mut sets = BTreeMap::>::new(); + + // Finds sets of ptr params pointing at the same len param. + for (position, param) in params.iter().enumerate() { + match param.kind { + SignatureParamKind::ArrayRelativeLen(relative) | SignatureParamKind::ArrayRelativeByteLen(relative) => { + sets.entry(relative).or_default().push(position); + } + _ => {} + } + } + + // Remove all sets. + for (len, ptrs) in sets { + if ptrs.len() > 1 { + params[len].kind = SignatureParamKind::Other; + for ptr in ptrs { + params[ptr].kind = SignatureParamKind::Other; + } + } + } + + // Remove any byte arrays that aren't byte-sized types. + for position in 0..params.len() { + if let SignatureParamKind::ArrayRelativeByteLen(relative) = params[position].kind { + if !params[position].ty.is_byte_size() { + params[position].kind = SignatureParamKind::Other; + params[relative].kind = SignatureParamKind::Other; + } + } + } + + for param in &mut params { + if param.kind == SignatureParamKind::Other { + if param.is_convertible() { + if type_is_non_exclusive_winrt_interface(¶m.ty) { + param.kind = SignatureParamKind::TryInto; + } else { + param.kind = SignatureParamKind::IntoParam; + } + } else { + let flags = param.def.flags(); + if param.ty.is_pointer() && (flags.contains(ParamAttributes::Optional) || param.def.has_attribute("ReservedAttribute")) { + param.kind = SignatureParamKind::OptionalPointer; + } else if type_is_primitive(¶m.ty) && (!param.ty.is_pointer() || type_is_blittable(¶m.ty.deref())) { + param.kind = SignatureParamKind::ValueType; + } else if type_is_blittable(¶m.ty) { + param.kind = SignatureParamKind::Blittable; + } + } + } + } + + Signature { def: row, params, return_type, call_flags } +} + +fn param_kind(row: Param) -> SignatureParamKind { + for attribute in row.attributes() { + match attribute.name() { + "NativeArrayInfoAttribute" => { + for (_, value) in attribute.args() { + match value { + Value::I16(value) => return SignatureParamKind::ArrayRelativeLen(value as usize), + Value::I32(value) => return SignatureParamKind::ArrayFixed(value as usize), + _ => {} + } + } + } + "MemorySizeAttribute" => { + for (_, value) in attribute.args() { + if let Value::I16(value) = value { + return SignatureParamKind::ArrayRelativeByteLen(value as usize); + } + } + } + _ => {} + } + } + SignatureParamKind::Other +} + +// TODO: this is a terribly broken Win32 metadata attribute - need to get rid of it. +fn param_or_enum(row: Param) -> Option { + row.find_attribute("AssociatedEnumAttribute").and_then(|attribute| { + for (_, arg) in attribute.args() { + if let Value::String(name) = arg { + return Some(name); + } + } + None + }) +} + +fn signature_param_is_query(params: &[SignatureParam]) -> Option<(usize, usize)> { + if let Some(guid) = params.iter().rposition(|param| param.ty == Type::ConstPtr(Box::new(Type::GUID), 1) && !param.def.flags().contains(ParamAttributes::Out)) { + if let Some(object) = params.iter().rposition(|param| param.ty == Type::MutPtr(Box::new(Type::Void), 2) && param.def.has_attribute("ComOutPtrAttribute")) { + return Some((guid, object)); + } + } + + None +} + +fn method_def_last_error(row: MethodDef) -> bool { + if let Some(map) = row.impl_map() { + map.flags().contains(PInvokeAttributes::SupportsLastError) + } else { + false + } +} + +pub fn type_is_borrowed(ty: &Type) -> bool { + match ty { + Type::TypeDef(row, _) => !type_def_is_blittable(*row), + Type::BSTR | Type::PCSTR | Type::PCWSTR | Type::IInspectable | Type::IUnknown | Type::GenericParam(_) => true, + _ => false, + } +} + +pub fn type_is_non_exclusive_winrt_interface(ty: &Type) -> bool { + match ty { + Type::TypeDef(row, _) => { + let flags = row.flags(); + if !flags.contains(TypeAttributes::WindowsRuntime) { + false + } else { + match row.kind() { + TypeKind::Interface => !type_def_is_exclusive(*row), + TypeKind::Class => row.has_attribute("ComposableAttribute"), + _ => false, + } + } + } + _ => false, + } +} + +fn type_is_trivially_convertible(ty: &Type) -> bool { + match ty { + Type::TypeDef(row, _) => match row.kind() { + TypeKind::Struct => type_def_is_handle(*row), + _ => false, + }, + _ => false, + } +} + +fn type_def_is_callback(row: TypeDef) -> bool { + !row.flags().contains(TypeAttributes::WindowsRuntime) && row.kind() == TypeKind::Delegate +} + +pub fn type_has_callback(ty: &Type) -> bool { + match ty { + Type::TypeDef(row, _) => type_def_has_callback(*row), + Type::Win32Array(ty, _) => type_has_callback(ty), + _ => false, + } +} + +pub fn type_def_has_callback(row: TypeDef) -> bool { + if type_def_is_callback(row) { + return true; + } + if row.kind() != TypeKind::Struct { + return false; + } + fn check(row: TypeDef) -> bool { + if row.fields().any(|field| type_has_callback(&field.ty(Some(row)))) { + return true; + } + false + } + let type_name = row.type_name(); + if type_name.namespace.is_empty() { + check(row) + } else { + for row in row.reader().get_type_def(type_name.namespace, type_name.name) { + if check(row) { + return true; + } + } + false + } +} + +pub fn type_interfaces(ty: &Type) -> Vec { + // TODO: collect into btree map and then return collected vec + // This will both sort the results and should make finding dupes faster + fn walk(result: &mut Vec, parent: &Type, is_base: bool) { + if let Type::TypeDef(row, generics) = parent { + for mut child in type_def_interfaces(*row, generics) { + child.kind = if !is_base && child.kind == InterfaceKind::Default { + InterfaceKind::Default + } else if child.kind == InterfaceKind::Overridable { + continue; + } else if is_base { + InterfaceKind::Base + } else { + InterfaceKind::None + }; + let mut found = false; + for existing in result.iter_mut() { + if existing.ty == child.ty { + found = true; + if child.kind == InterfaceKind::Default { + existing.kind = child.kind + } + } + } + if !found { + walk(result, &child.ty, is_base); + result.push(child); + } + } + } + } + let mut result = Vec::new(); + walk(&mut result, ty, false); + if let Type::TypeDef(row, _) = ty { + if row.kind() == TypeKind::Class { + for base in type_def_bases(*row) { + walk(&mut result, &Type::TypeDef(base, Vec::new()), true); + } + for attribute in row.attributes() { + match attribute.name() { + "StaticAttribute" | "ActivatableAttribute" => { + for (_, arg) in attribute.args() { + if let Value::TypeName(type_name) = arg { + let def = row.reader().get_type_def(type_name.namespace, type_name.name).next().expect("Type not found"); + result.push(Interface { ty: Type::TypeDef(def, Vec::new()), kind: InterfaceKind::Static }); + break; + } + } + } + _ => {} + } + } + } + } + result.sort_by(|a, b| type_name(&a.ty).cmp(type_name(&b.ty))); + result +} + +fn type_name(ty: &Type) -> &str { + match ty { + Type::TypeDef(row, _) => row.name(), + _ => "", + } +} + +pub fn field_is_blittable(row: Field, enclosing: TypeDef) -> bool { + type_is_blittable(&row.ty(Some(enclosing))) +} + +pub fn field_is_copyable(row: Field, enclosing: TypeDef) -> bool { + type_is_copyable(&row.ty(Some(enclosing))) +} + +pub fn type_is_blittable(ty: &Type) -> bool { + match ty { + Type::TypeDef(row, _) => type_def_is_blittable(*row), + Type::String | Type::BSTR | Type::IInspectable | Type::IUnknown | Type::GenericParam(_) => false, + Type::Win32Array(kind, _) => type_is_blittable(kind), + Type::WinrtArray(kind) => type_is_blittable(kind), + _ => true, + } +} + +fn type_is_copyable(ty: &Type) -> bool { + match ty { + Type::TypeDef(row, _) => type_def_is_copyable(*row), + Type::String | Type::BSTR | Type::IInspectable | Type::IUnknown | Type::GenericParam(_) => false, + Type::Win32Array(kind, _) => type_is_copyable(kind), + Type::WinrtArray(kind) => type_is_copyable(kind), + _ => true, + } +} + +pub fn type_def_is_blittable(row: TypeDef) -> bool { + match row.kind() { + TypeKind::Struct => { + if row.flags().contains(TypeAttributes::WindowsRuntime) { + row.fields().all(|field| field_is_blittable(field, row)) + } else { + true + } + } + TypeKind::Enum => true, + TypeKind::Delegate => !row.flags().contains(TypeAttributes::WindowsRuntime), + _ => false, + } +} + +pub fn type_def_is_copyable(row: TypeDef) -> bool { + match row.kind() { + TypeKind::Struct => row.fields().all(|field| field_is_copyable(field, row)), + TypeKind::Enum => true, + TypeKind::Delegate => !row.flags().contains(TypeAttributes::WindowsRuntime), + _ => false, + } +} + +pub fn type_def_is_exclusive(row: TypeDef) -> bool { + row.has_attribute("ExclusiveToAttribute") +} + +pub fn type_is_struct(ty: &Type) -> bool { + // This check is used to detect virtual functions that return C-style PODs that affect how the stack is packed for x86. + // It could be defined as a struct with more than one field but that check is complicated as it would have to detect + // nested structs. Fortunately, this is rare enough that this check is sufficient. + match ty { + Type::TypeDef(row, _) => row.kind() == TypeKind::Struct && !type_def_is_handle(*row), + Type::GUID => true, + _ => false, + } +} + +fn type_def_is_primitive(row: TypeDef) -> bool { + match row.kind() { + TypeKind::Enum => true, + TypeKind::Struct => type_def_is_handle(row), + TypeKind::Delegate => !row.flags().contains(TypeAttributes::WindowsRuntime), + _ => false, + } +} + +pub fn type_is_primitive(ty: &Type) -> bool { + match ty { + Type::TypeDef(row, _) => type_def_is_primitive(*row), + 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, + _ => false, + } +} + +fn type_has_explicit_layout(ty: &Type) -> bool { + match ty { + Type::TypeDef(row, _) => type_def_has_explicit_layout(*row), + Type::Win32Array(ty, _) => type_has_explicit_layout(ty), + _ => false, + } +} + +pub fn type_def_has_explicit_layout(row: TypeDef) -> bool { + if row.kind() != TypeKind::Struct { + return false; + } + fn check(row: TypeDef) -> bool { + if row.flags().contains(TypeAttributes::ExplicitLayout) { + return true; + } + if row.fields().any(|field| type_has_explicit_layout(&field.ty(Some(row)))) { + return true; + } + false + } + let type_name = row.type_name(); + if type_name.namespace.is_empty() { + check(row) + } else { + for row in row.reader().get_type_def(type_name.namespace, type_name.name) { + if check(row) { + return true; + } + } + false + } +} + +fn type_has_packing(ty: &Type) -> bool { + match ty { + Type::TypeDef(row, _) => type_def_has_packing(*row), + Type::Win32Array(ty, _) => type_has_packing(ty), + _ => false, + } +} + +pub fn type_def_has_packing(row: TypeDef) -> bool { + if row.kind() != TypeKind::Struct { + return false; + } + fn check(row: TypeDef) -> bool { + if row.class_layout().is_some() { + return true; + } + if row.fields().any(|field| type_has_packing(&field.ty(Some(row)))) { + return true; + } + false + } + let type_name = row.type_name(); + if type_name.namespace.is_empty() { + check(row) + } else { + for row in row.reader().get_type_def(type_name.namespace, type_name.name) { + if check(row) { + return true; + } + } + false + } +} + +pub fn type_def_interfaces(def: TypeDef, generics: &[Type]) -> impl Iterator + '_ { + def.interface_impls().map(|imp| { + let kind = if imp.has_attribute("DefaultAttribute") { InterfaceKind::Default } else { InterfaceKind::None }; + Interface { kind, ty: imp.ty(generics) } + }) +} + +pub fn type_def_default_interface(row: TypeDef) -> Option { + type_def_interfaces(row, &[]).find_map(move |interface| if interface.kind == InterfaceKind::Default { Some(interface.ty) } else { None }) +} + +fn type_signature(ty: &Type) -> String { + match ty { + Type::Bool => "b1".to_string(), + Type::Char => "c2".to_string(), + Type::I8 => "i1".to_string(), + Type::U8 => "u1".to_string(), + Type::I16 => "i2".to_string(), + Type::U16 => "u2".to_string(), + Type::I32 => "i4".to_string(), + Type::U32 => "u4".to_string(), + Type::I64 => "i8".to_string(), + Type::U64 => "u8".to_string(), + Type::F32 => "f4".to_string(), + Type::F64 => "f8".to_string(), + Type::ISize => "is".to_string(), + Type::USize => "us".to_string(), + Type::String => "string".to_string(), + Type::IInspectable => "cinterface(IInspectable)".to_string(), + Type::GUID => "g16".to_string(), + Type::HRESULT => "struct(Windows.Foundation.HResult;i4)".to_string(), + Type::TypeDef(row, generics) => type_def_signature(*row, generics), + rest => unimplemented!("{rest:?}"), + } +} + +pub fn type_def_signature(row: TypeDef, generics: &[Type]) -> String { + match row.kind() { + TypeKind::Interface => type_def_interface_signature(row, generics), + TypeKind::Class => { + if let Some(Type::TypeDef(default, generics)) = type_def_default_interface(row) { + format!("rc({};{})", row.type_name(), type_def_interface_signature(default, &generics)) + } else { + unimplemented!(); + } + } + TypeKind::Enum => format!("enum({};{})", row.type_name(), type_signature(&row.underlying_type())), + TypeKind::Struct => { + let mut result = format!("struct({}", row.type_name()); + for field in row.fields() { + result.push(';'); + result.push_str(&type_signature(&field.ty(Some(row)))); + } + result.push(')'); + result + } + TypeKind::Delegate => { + if generics.is_empty() { + format!("delegate({})", type_def_interface_signature(row, generics)) + } else { + type_def_interface_signature(row, generics) + } + } + } +} + +fn type_def_interface_signature(row: TypeDef, generics: &[Type]) -> String { + let guid = type_def_guid(row).unwrap(); + if generics.is_empty() { + format!("{{{guid:#?}}}") + } else { + let mut result = format!("pinterface({{{guid:#?}}}"); + for generic in generics { + result.push(';'); + result.push_str(&type_signature(generic)); + } + result.push(')'); + result + } +} + +pub fn type_def_is_handle(row: TypeDef) -> bool { + row.has_attribute("NativeTypedefAttribute") +} + +pub fn type_def_guid(row: TypeDef) -> Option { + row.find_attribute("GuidAttribute").map(|attribute| Guid::from_args(&attribute.args())) +} + +pub fn type_def_bases(mut row: TypeDef) -> Vec { + let mut bases = Vec::new(); + loop { + match row.extends() { + Some(base) if base != TypeName::Object => { + row = row.reader().get_type_def(base.namespace, base.name).next().expect("Type not found"); + bases.push(row); + } + _ => break, + } + } + bases +} + +pub fn type_def_invalid_values(row: TypeDef) -> Vec { + let mut values = Vec::new(); + for attribute in row.attributes() { + if attribute.name() == "InvalidHandleValueAttribute" { + if let Some((_, Value::I64(value))) = attribute.args().first() { + values.push(*value); + } + } + } + values +} + +fn type_def_is_nullable(row: TypeDef) -> bool { + match row.kind() { + TypeKind::Interface | TypeKind::Class => true, + // Win32 callbacks are defined as `Option` so we don't include them here to avoid them + // from being doubly wrapped in `Option`. + TypeKind::Delegate => row.flags().contains(TypeAttributes::WindowsRuntime), + _ => false, + } +} + +pub fn type_is_nullable(ty: &Type) -> bool { + match ty { + Type::TypeDef(row, _) => type_def_is_nullable(*row), + Type::IInspectable | Type::IUnknown => true, + _ => false, + } +} + +pub fn type_def_vtables(row: TypeDef) -> Vec { + let mut result = Vec::new(); + if row.flags().contains(TypeAttributes::WindowsRuntime) { + result.push(Type::IUnknown); + if row.kind() != TypeKind::Delegate { + result.push(Type::IInspectable); + } + } else { + let mut next = row; + while let Some(base) = next.interface_impls().map(move |imp| imp.ty(&[])).next() { + match base { + Type::TypeDef(row, _) => { + next = row; + result.insert(0, base); + } + Type::IInspectable => { + result.insert(0, Type::IUnknown); + result.insert(1, Type::IInspectable); + break; + } + Type::IUnknown => { + result.insert(0, Type::IUnknown); + break; + } + rest => unimplemented!("{rest:?}"), + } + } + } + result +} -- cgit v1.2.3