| mod blob; |
| mod codes; |
| mod file; |
| mod filter; |
| mod guid; |
| mod row; |
| mod tree; |
| mod r#type; |
| mod type_name; |
| |
| pub use super::*; |
| pub use blob::*; |
| pub use codes::*; |
| pub use file::*; |
| pub use filter::*; |
| pub use guid::*; |
| pub use r#type::*; |
| pub use row::*; |
| pub use tree::*; |
| pub use type_name::*; |
| |
| macro_rules! tables { |
| ($($name:ident,)*) => ($( |
| #[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] |
| pub struct $name(pub Row); |
| )*) |
| } |
| |
| tables! { |
| Attribute, |
| ClassLayout, |
| Constant, |
| Field, |
| GenericParam, |
| ImplMap, |
| InterfaceImpl, |
| MemberRef, |
| MethodDef, |
| ModuleRef, |
| Param, |
| TypeDef, |
| TypeRef, |
| TypeSpec, |
| } |
| |
| #[derive(Clone, PartialEq, PartialOrd, Eq, Ord)] |
| pub struct Interface { |
| pub ty: Type, |
| pub kind: InterfaceKind, |
| } |
| |
| #[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord)] |
| 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, |
| } |
| |
| impl SignatureParamKind { |
| fn is_array(&self) -> bool { |
| matches!(self, Self::ArrayFixed(_) | Self::ArrayRelativeLen(_) | Self::ArrayRelativeByteLen(_) | Self::ArrayRelativePtr(_)) |
| } |
| } |
| |
| #[derive(PartialEq, Eq)] |
| pub enum AsyncKind { |
| None, |
| Action, |
| ActionWithProgress, |
| Operation, |
| OperationWithProgress, |
| } |
| |
| #[derive(Copy, Clone, PartialEq, Eq, Debug, PartialOrd, Ord)] |
| pub enum TypeKind { |
| Interface, |
| Class, |
| Enum, |
| Struct, |
| Delegate, |
| } |
| |
| pub enum Value { |
| Bool(bool), |
| U8(u8), |
| I8(i8), |
| U16(u16), |
| I16(i16), |
| U32(u32), |
| I32(i32), |
| U64(u64), |
| I64(i64), |
| F32(f32), |
| F64(f64), |
| String(String), |
| TypeDef(TypeDef), |
| Enum(TypeDef, Integer), |
| } |
| |
| pub enum Integer { |
| U8(u8), |
| I8(i8), |
| U16(u16), |
| I16(i16), |
| U32(u32), |
| I32(i32), |
| U64(u64), |
| I64(i64), |
| } |
| |
| pub struct Signature { |
| pub def: MethodDef, |
| pub params: Vec<SignatureParam>, |
| pub return_type: Option<Type>, |
| } |
| |
| pub struct SignatureParam { |
| pub def: Param, |
| pub ty: Type, |
| pub kind: SignatureParamKind, |
| } |
| |
| #[derive(Default, Clone)] |
| pub struct Cfg<'a> { |
| pub types: BTreeMap<&'a str, BTreeSet<TypeDef>>, |
| pub core_types: BTreeSet<Type>, |
| pub arches: BTreeSet<&'static str>, |
| pub implement: bool, |
| } |
| |
| impl<'a> Cfg<'a> { |
| pub fn add_feature(&mut self, feature: &'a str) { |
| self.types.entry(feature).or_default(); |
| } |
| pub fn union(&self, other: &Self) -> Self { |
| let mut union = Self::default(); |
| self.types.keys().for_each(|feature| { |
| union.types.entry(feature).or_default(); |
| }); |
| other.types.keys().for_each(|feature| { |
| union.types.entry(feature).or_default(); |
| }); |
| self.arches.iter().for_each(|arch| { |
| union.arches.insert(arch); |
| }); |
| other.arches.iter().for_each(|arch| { |
| union.arches.insert(arch); |
| }); |
| union |
| } |
| } |
| |
| pub struct Reader<'a> { |
| files: &'a [File], |
| types: HashMap<&'a str, BTreeMap<&'a str, Vec<TypeDef>>>, |
| nested: HashMap<TypeDef, BTreeMap<&'a str, TypeDef>>, |
| } |
| |
| impl<'a> Reader<'a> { |
| pub fn new(files: &'a [File]) -> Self { |
| let mut types = HashMap::<&'a str, BTreeMap<&'a str, Vec<TypeDef>>>::new(); |
| let mut nested = HashMap::<TypeDef, BTreeMap<&'a str, TypeDef>>::new(); |
| for (file_index, file) in files.iter().enumerate() { |
| for row in 0..file.tables[TABLE_TYPEDEF].len { |
| let key = Row::new(row, TABLE_TYPEDEF, file_index); |
| let namespace = file.str(key.row as _, key.table as _, 2); |
| if namespace.is_empty() { |
| continue; |
| } |
| let name = trim_tick(file.str(key.row as _, key.table as _, 1)); |
| types.entry(namespace).or_default().entry(name).or_default().push(TypeDef(key)); |
| } |
| for row in 0..file.tables[TABLE_NESTEDCLASS].len { |
| let key = Row::new(row, TABLE_NESTEDCLASS, file_index); |
| let inner = Row::new(file.usize(key.row as _, key.table as _, 0) - 1, TABLE_TYPEDEF, file_index); |
| let outer = Row::new(file.usize(key.row as _, key.table as _, 1) - 1, TABLE_TYPEDEF, file_index); |
| let name = file.str(inner.row as _, inner.table as _, 1); |
| nested.entry(TypeDef(outer)).or_default().insert(name, TypeDef(inner)); |
| } |
| } |
| Self { files, types, nested } |
| } |
| pub fn tree(&'a self, root: &'a str, filter: &Filter) -> Tree { |
| let mut tree = Tree::from_namespace(""); |
| for ns in self.types.keys() { |
| if filter.includes_namespace(ns) { |
| tree.insert_namespace(ns, 0); |
| } |
| } |
| if root.is_empty() { |
| tree |
| } else { |
| tree.seek(root).expect("Namespace not found") |
| } |
| } |
| |
| // |
| // Hash functions for fast type lookup |
| // |
| |
| pub fn namespaces(&self) -> impl Iterator<Item = &str> + '_ { |
| self.types.keys().copied() |
| } |
| pub fn namespace_types(&'a self, namespace: &str, filter: &'a Filter) -> impl Iterator<Item = TypeDef> + '_ { |
| self.types.get(namespace).map(move |types| types.values().flatten().copied().filter(move |ty| filter.includes_type(self, *ty))).into_iter().flatten() |
| } |
| pub fn nested_types(&self, type_def: TypeDef) -> impl Iterator<Item = TypeDef> + '_ { |
| self.nested.get(&type_def).map(|map| map.values().copied()).into_iter().flatten() |
| } |
| pub fn get(&self, type_name: TypeName) -> impl Iterator<Item = TypeDef> + '_ { |
| if let Some(types) = self.types.get(type_name.namespace) { |
| if let Some(definitions) = types.get(type_name.name) { |
| return Some(definitions.iter().copied()).into_iter().flatten(); |
| } |
| } |
| None.into_iter().flatten() |
| } |
| pub fn namespace_functions(&self, namespace: &str) -> impl Iterator<Item = MethodDef> + '_ { |
| self.get(TypeName::new(namespace, "Apis")).flat_map(move |apis| self.type_def_methods(apis)).filter(move |method| { |
| // The ImplMap table contains import information, without which the function cannot be linked. |
| let Some(impl_map) = self.method_def_impl_map(*method) else { |
| return false; |
| }; |
| |
| // Skip functions exported by ordinal. |
| if self.impl_map_import_name(impl_map).starts_with('#') { |
| return false; |
| } |
| |
| // If the module name lacks a `.` then it's likely either an inline function, which windows-rs |
| // doesn't currently support, or an invalid import library since the extension must be known |
| // in order to generate an import table entry unambiguously. |
| return self.module_ref_name(self.impl_map_scope(impl_map)).contains('.'); |
| }) |
| } |
| pub fn namespace_constants(&self, namespace: &str) -> impl Iterator<Item = Field> + '_ { |
| self.get(TypeName::new(namespace, "Apis")).flat_map(move |apis| self.type_def_fields(apis)) |
| } |
| |
| // |
| // Row functions providing low-level file access |
| // |
| |
| fn row_usize(&self, key: Row, column: usize) -> usize { |
| self.files[key.file as usize].usize(key.row as _, key.table as _, column) |
| } |
| fn row_str(&self, key: Row, column: usize) -> &str { |
| self.files[key.file as usize].str(key.row as _, key.table as _, column) |
| } |
| pub fn row_blob(&self, key: Row, column: usize) -> Blob { |
| let file = key.file as usize; |
| Blob::new(file, self.files[file].blob(key.row as _, key.table as _, column)) |
| } |
| fn row_equal_range(&self, key: Row, table: usize, column: usize, value: usize) -> impl Iterator<Item = Row> { |
| let (first, last) = self.files[key.file as usize].equal_range(table, column, value); |
| (first..last).map(move |row| Row::new(row, table, key.file as _)) |
| } |
| fn row_attributes(&self, key: Row, source: HasAttribute) -> impl Iterator<Item = Attribute> { |
| self.row_equal_range(key, TABLE_CUSTOMATTRIBUTE, 0, source.encode()).map(Attribute) |
| } |
| fn row_list(&self, key: Row, table: usize, column: usize) -> impl Iterator<Item = Row> { |
| let file = key.file as usize; |
| let first = self.row_usize(key, column) - 1; |
| let last = if key.row + 1 < self.files[file].tables[key.table as usize].len as _ { self.row_usize(key.next(), column) - 1 } else { self.files[file].tables[table].len }; |
| (first..last).map(move |row| Row::new(row, table, file)) |
| } |
| fn row_decode<T: Decode>(&self, key: Row, column: usize) -> T { |
| T::decode(key.file as _, self.row_usize(key, column)) |
| } |
| |
| // |
| // Attribute table queries |
| // |
| |
| pub fn attribute_name(&self, row: Attribute) -> &str { |
| let AttributeType::MemberRef(row) = self.row_decode(row.0, 1); |
| let MemberRefParent::TypeRef(row) = self.row_decode(row.0, 0); |
| self.type_ref_name(row) |
| } |
| pub fn attribute_args(&self, row: Attribute) -> Vec<(String, Value)> { |
| let AttributeType::MemberRef(member) = self.row_decode(row.0, 1); |
| let mut sig = self.member_ref_signature(member); |
| let mut values = self.row_blob(row.0, 2); |
| let _prolog = values.read_u16(); |
| let _this_and_gen_param_count = sig.read_usize(); |
| let fixed_arg_count = sig.read_usize(); |
| let _ret_type = sig.read_usize(); |
| let mut args: Vec<(String, Value)> = Vec::with_capacity(fixed_arg_count); |
| |
| for _ in 0..fixed_arg_count { |
| let arg = match self.type_from_blob(&mut sig, None, &[]).expect("Type not found") { |
| Type::Bool => Value::Bool(values.read_bool()), |
| Type::I8 => Value::I8(values.read_i8()), |
| Type::U8 => Value::U8(values.read_u8()), |
| Type::I16 => Value::I16(values.read_i16()), |
| Type::U16 => Value::U16(values.read_u16()), |
| Type::I32 => Value::I32(values.read_i32()), |
| Type::U32 => Value::U32(values.read_u32()), |
| Type::I64 => Value::I64(values.read_i64()), |
| Type::U64 => Value::U64(values.read_u64()), |
| Type::String => Value::String(values.read_str().to_string()), |
| Type::TypeName => Value::TypeDef(self.get(TypeName::parse(values.read_str())).next().expect("Type not found")), |
| Type::TypeDef((def, _)) => Value::Enum(def, values.read_integer(self.type_def_underlying_type(def))), |
| _ => unimplemented!(), |
| }; |
| |
| args.push((String::new(), arg)); |
| } |
| |
| let named_arg_count = values.read_u16(); |
| args.reserve(named_arg_count as usize); |
| |
| for _ in 0..named_arg_count { |
| let _id = values.read_u8(); |
| let arg_type = values.read_u8(); |
| let mut name = values.read_str().to_string(); |
| let arg = match arg_type { |
| 0x02 => Value::Bool(values.read_bool()), |
| 0x06 => Value::I16(values.read_i16()), |
| 0x08 => Value::I32(values.read_i32()), |
| 0x09 => Value::U32(values.read_u32()), |
| 0x0E => Value::String(values.read_str().to_string()), |
| 0x50 => Value::TypeDef(self.get(TypeName::parse(values.read_str())).next().expect("Type not found")), |
| 0x55 => { |
| let def = self.get(TypeName::parse(&name)).next().expect("Type not found"); |
| name = values.read_str().into(); |
| Value::Enum(def, values.read_integer(self.type_def_underlying_type(def))) |
| } |
| _ => unimplemented!(), |
| }; |
| args.push((name, arg)); |
| } |
| |
| args |
| } |
| |
| // |
| // ClassLayout table queries |
| // |
| |
| pub fn class_layout_packing_size(&self, row: ClassLayout) -> usize { |
| self.row_usize(row.0, 0) |
| } |
| |
| // |
| // Constant table queries |
| // |
| |
| pub fn constant_type(&self, row: Constant) -> Type { |
| let code = self.row_usize(row.0, 0); |
| Type::from_code(code).expect("Type not found") |
| } |
| pub fn constant_value(&self, row: Constant) -> Value { |
| let mut blob = self.row_blob(row.0, 2); |
| match self.constant_type(row) { |
| Type::I8 => Value::I8(blob.read_i8()), |
| Type::U8 => Value::U8(blob.read_u8()), |
| Type::I16 => Value::I16(blob.read_i16()), |
| Type::U16 => Value::U16(blob.read_u16()), |
| Type::I32 => Value::I32(blob.read_i32()), |
| Type::U32 => Value::U32(blob.read_u32()), |
| Type::I64 => Value::I64(blob.read_i64()), |
| Type::U64 => Value::U64(blob.read_u64()), |
| Type::F32 => Value::F32(blob.read_f32()), |
| Type::F64 => Value::F64(blob.read_f64()), |
| Type::String => Value::String(blob.read_string()), |
| _ => unimplemented!(), |
| } |
| } |
| |
| // |
| // Field table queries |
| // |
| |
| pub fn field_flags(&self, row: Field) -> FieldAttributes { |
| FieldAttributes(self.row_usize(row.0, 0) as _) |
| } |
| pub fn field_name(&self, row: Field) -> &str { |
| self.row_str(row.0, 1) |
| } |
| pub fn field_constant(&self, row: Field) -> Option<Constant> { |
| self.row_equal_range(row.0, TABLE_CONSTANT, 1, HasConstant::Field(row).encode()).map(Constant).next() |
| } |
| pub fn field_attributes(&self, row: Field) -> impl Iterator<Item = Attribute> { |
| self.row_attributes(row.0, HasAttribute::Field(row)) |
| } |
| pub fn field_is_const(&self, row: Field) -> bool { |
| self.field_attributes(row).any(|attribute| self.attribute_name(attribute) == "ConstAttribute") |
| } |
| pub fn field_type(&self, row: Field, enclosing: Option<TypeDef>) -> Type { |
| let mut blob = self.row_blob(row.0, 2); |
| blob.read_usize(); |
| blob.read_modifiers(); |
| let def = self.type_from_blob(&mut blob, enclosing, &[]).expect("Type not found"); |
| |
| if self.field_is_const(row) { |
| def.to_const_type().to_const_ptr() |
| } else { |
| def |
| } |
| } |
| pub fn field_is_blittable(&self, row: Field, enclosing: TypeDef) -> bool { |
| self.type_is_blittable(&self.field_type(row, Some(enclosing))) |
| } |
| pub fn field_is_copyable(&self, row: Field, enclosing: TypeDef) -> bool { |
| self.type_is_copyable(&self.field_type(row, Some(enclosing))) |
| } |
| pub fn field_guid(&self, row: Field) -> Option<GUID> { |
| for attribute in self.field_attributes(row) { |
| if self.attribute_name(attribute) == "GuidAttribute" { |
| return Some(GUID::from_args(&self.attribute_args(attribute))); |
| } |
| } |
| None |
| } |
| pub fn field_cfg(&self, row: Field) -> Cfg { |
| let mut cfg = Cfg::default(); |
| self.field_cfg_combine(row, None, &mut cfg); |
| cfg |
| } |
| fn field_cfg_combine(&'a self, row: Field, enclosing: Option<TypeDef>, cfg: &mut Cfg<'a>) { |
| self.type_cfg_combine(&self.field_type(row, enclosing), cfg) |
| } |
| pub fn field_is_ansi(&self, row: Field) -> bool { |
| for attribute in self.field_attributes(row) { |
| if self.attribute_name(attribute) == "NativeEncodingAttribute" { |
| if let Some((_, Value::String(encoding))) = self.attribute_args(attribute).get(0) { |
| if encoding == "ansi" { |
| return true; |
| } |
| } |
| } |
| } |
| false |
| } |
| |
| // |
| // GenericParam table queries |
| // |
| |
| pub fn generic_param_name(&self, row: GenericParam) -> &str { |
| self.row_str(row.0, 3) |
| } |
| |
| // |
| // ImplMap table queries |
| // |
| |
| pub fn impl_map_flags(&self, row: ImplMap) -> PInvokeAttributes { |
| PInvokeAttributes(self.row_usize(row.0, 0)) |
| } |
| pub fn impl_map_scope(&self, row: ImplMap) -> ModuleRef { |
| ModuleRef(Row::new(self.row_usize(row.0, 3) - 1, TABLE_MODULEREF, row.0.file as _)) |
| } |
| pub fn impl_map_import_name(&self, row: ImplMap) -> &str { |
| self.row_str(row.0, 2) |
| } |
| |
| // |
| // InterfaceImpl table queries |
| // |
| |
| pub fn interface_impl_attributes(&self, row: InterfaceImpl) -> impl Iterator<Item = Attribute> { |
| self.row_attributes(row.0, HasAttribute::InterfaceImpl(row)) |
| } |
| pub fn interface_impl_is_default(&self, row: InterfaceImpl) -> bool { |
| self.interface_impl_attributes(row).any(|attribute| self.attribute_name(attribute) == "DefaultAttribute") |
| } |
| pub fn interface_impl_is_overridable(&self, row: InterfaceImpl) -> bool { |
| self.interface_impl_attributes(row).any(|attribute| self.attribute_name(attribute) == "OverridableAttribute") |
| } |
| pub fn interface_impl_type(&self, row: InterfaceImpl, generics: &[Type]) -> Interface { |
| let mut kind = InterfaceKind::None; |
| for attribute in self.interface_impl_attributes(row) { |
| match self.attribute_name(attribute) { |
| "DefaultAttribute" => kind = InterfaceKind::Default, |
| "OverridableAttribute" => kind = InterfaceKind::Overridable, |
| _ => {} |
| } |
| } |
| Interface { ty: self.type_from_ref(self.row_decode(row.0, 1), None, generics), kind } |
| } |
| |
| // |
| // MemberRef table queries |
| // |
| |
| pub fn member_ref_parent(&self, row: MemberRef) -> MemberRefParent { |
| self.row_decode(row.0, 0) |
| } |
| pub fn member_ref_signature(&self, row: MemberRef) -> Blob { |
| self.row_blob(row.0, 2) |
| } |
| |
| // |
| // MethodDef table queries |
| // |
| |
| pub fn method_def_impl_flags(&self, row: MethodDef) -> MethodImplAttributes { |
| MethodImplAttributes(self.row_usize(row.0, 1)) |
| } |
| pub fn method_def_flags(&self, row: MethodDef) -> MethodAttributes { |
| MethodAttributes(self.row_usize(row.0, 2) as _) |
| } |
| pub fn method_def_name(&self, row: MethodDef) -> &str { |
| self.row_str(row.0, 3) |
| } |
| pub fn method_def_params(&self, row: MethodDef) -> impl Iterator<Item = Param> { |
| self.row_list(row.0, TABLE_PARAM, 5).map(Param) |
| } |
| pub fn method_def_attributes(&self, row: MethodDef) -> impl Iterator<Item = Attribute> { |
| self.row_attributes(row.0, HasAttribute::MethodDef(row)) |
| } |
| pub fn method_def_is_deprecated(&self, row: MethodDef) -> bool { |
| self.method_def_attributes(row).any(|attribute| self.attribute_name(attribute) == "DeprecatedAttribute") |
| } |
| pub fn method_def_does_not_return(&self, row: MethodDef) -> bool { |
| self.method_def_attributes(row).any(|attribute| self.attribute_name(attribute) == "DoesNotReturnAttribute") |
| } |
| pub fn method_def_can_return_multiple_success_values(&self, row: MethodDef) -> bool { |
| self.method_def_attributes(row).any(|attribute| self.attribute_name(attribute) == "CanReturnMultipleSuccessValuesAttribute") |
| } |
| pub fn method_def_special_name(&self, row: MethodDef) -> String { |
| let name = self.method_def_name(row); |
| if self.method_def_flags(row).contains(MethodAttributes::SPECIAL) { |
| if name.starts_with("get") { |
| name[4..].to_string() |
| } else if name.starts_with("put") { |
| format!("Set{}", &name[4..]) |
| } else if name.starts_with("add") { |
| name[4..].to_string() |
| } else if name.starts_with("remove") { |
| format!("Remove{}", &name[7..]) |
| } else { |
| name.to_string() |
| } |
| } else { |
| for attribute in self.method_def_attributes(row) { |
| if self.attribute_name(attribute) == "OverloadAttribute" { |
| for (_, arg) in self.attribute_args(attribute) { |
| if let Value::String(name) = arg { |
| return name; |
| } |
| } |
| } |
| } |
| name.to_string() |
| } |
| } |
| pub fn method_def_static_lib(&self, row: MethodDef) -> Option<String> { |
| for attribute in self.method_def_attributes(row) { |
| if self.attribute_name(attribute) == "StaticLibraryAttribute" { |
| let args = self.attribute_args(attribute); |
| if let Value::String(value) = &args[0].1 { |
| return Some(value.clone()); |
| } |
| } |
| } |
| None |
| } |
| pub fn method_def_impl_map(&self, row: MethodDef) -> Option<ImplMap> { |
| self.row_equal_range(row.0, TABLE_IMPLMAP, 1, MemberForwarded::MethodDef(row).encode()).map(ImplMap).next() |
| } |
| pub fn method_def_module_name(&self, row: MethodDef) -> String { |
| let Some(impl_map) = self.method_def_impl_map(row) else { |
| return String::new(); |
| }; |
| self.module_ref_name(self.impl_map_scope(impl_map)).to_lowercase() |
| } |
| pub fn method_def_signature(&self, row: MethodDef, generics: &[Type]) -> Signature { |
| let mut blob = self.row_blob(row.0, 4); |
| blob.read_usize(); |
| blob.read_usize(); |
| |
| let mut return_type = self.type_from_blob(&mut blob, None, generics); |
| |
| let mut params: Vec<SignatureParam> = self |
| .method_def_params(row) |
| .filter_map(|param| { |
| if self.param_sequence(param) == 0 { |
| if self.param_is_const(param) { |
| return_type = return_type.clone().map(|ty| ty.to_const_type()); |
| } |
| None |
| } else { |
| let is_output = self.param_flags(param).contains(ParamAttributes::OUTPUT); |
| let mut ty = self.type_from_blob(&mut blob, None, generics).expect("Parameter type not found"); |
| if self.param_is_const(param) || !is_output { |
| ty = ty.to_const_type(); |
| } |
| if !is_output { |
| ty = ty.to_const_ptr(); |
| } |
| let kind = self.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 !self.param_flags(params[relative].def).contains(ParamAttributes::OUTPUT) && position != relative && !params[relative].ty.is_pointer() { |
| params[relative].kind = SignatureParamKind::ArrayRelativePtr(position); |
| } else { |
| params[position].kind = SignatureParamKind::Other; |
| } |
| } |
| SignatureParamKind::ArrayFixed(_) => { |
| if self.param_free_with(params[position].def).is_some() { |
| params[position].kind = SignatureParamKind::Other; |
| } |
| } |
| _ => {} |
| } |
| } |
| |
| let mut sets = BTreeMap::<usize, Vec<usize>>::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 self.signature_param_is_convertible(param) { |
| if self.signature_param_is_failible_param(param) { |
| param.kind = SignatureParamKind::TryInto; |
| } else { |
| param.kind = SignatureParamKind::IntoParam; |
| } |
| } else { |
| let flags = self.param_flags(param.def); |
| if param.ty.is_pointer() && (flags.contains(ParamAttributes::OPTIONAL) || self.param_is_reserved(param.def)) { |
| param.kind = SignatureParamKind::OptionalPointer; |
| } else if self.type_is_primitive(¶m.ty) && (!param.ty.is_pointer() || self.type_is_blittable(¶m.ty.deref())) { |
| param.kind = SignatureParamKind::ValueType; |
| } else if self.type_is_blittable(¶m.ty) { |
| param.kind = SignatureParamKind::Blittable; |
| } |
| } |
| } |
| } |
| |
| Signature { def: row, params, return_type } |
| } |
| pub fn method_def_extern_abi(&self, def: MethodDef) -> &'static str { |
| let impl_map = self.method_def_impl_map(def).expect("ImplMap not found"); |
| let flags = self.impl_map_flags(impl_map); |
| |
| if flags.contains(PInvokeAttributes::CONV_PLATFORM) { |
| "system" |
| } else if flags.contains(PInvokeAttributes::CONV_CDECL) { |
| "cdecl" |
| } else { |
| unimplemented!() |
| } |
| } |
| pub fn method_def_size(&self, method: MethodDef) -> usize { |
| let signature = self.method_def_signature(method, &[]); |
| signature.params.iter().fold(0, |sum, param| sum + std::cmp::max(4, self.type_size(¶m.ty))) |
| } |
| pub fn type_def_size(&self, def: TypeDef) -> usize { |
| match self.type_def_kind(def) { |
| TypeKind::Struct => { |
| if self.type_def_flags(def).contains(TypeAttributes::EXPLICIT_LAYOUT) { |
| self.type_def_fields(def).map(|field| self.type_size(&self.field_type(field, Some(def)))).max().unwrap_or(1) |
| } else { |
| let mut sum = 0; |
| for field in self.type_def_fields(def) { |
| let size = self.type_size(&self.field_type(field, Some(def))); |
| let align = self.type_align(&self.field_type(field, Some(def))); |
| sum = (sum + (align - 1)) & !(align - 1); |
| sum += size; |
| } |
| sum |
| } |
| } |
| TypeKind::Enum => self.type_size(&self.type_def_underlying_type(def)), |
| _ => 4, |
| } |
| } |
| fn type_size(&self, ty: &Type) -> usize { |
| match ty { |
| Type::I8 | Type::U8 => 1, |
| Type::I16 | Type::U16 => 2, |
| Type::I64 | Type::U64 | Type::F64 => 8, |
| Type::GUID => 16, |
| Type::TypeDef((def, _)) => self.type_def_size(*def), |
| Type::Win32Array((ty, len)) => self.type_size(ty) * len, |
| _ => 4, |
| } |
| } |
| fn type_def_align(&self, def: TypeDef) -> usize { |
| match self.type_def_kind(def) { |
| TypeKind::Struct => self.type_def_fields(def).map(|field| self.type_align(&self.field_type(field, Some(def)))).max().unwrap_or(1), |
| TypeKind::Enum => self.type_align(&self.type_def_underlying_type(def)), |
| _ => 4, |
| } |
| } |
| fn type_align(&self, ty: &Type) -> usize { |
| match ty { |
| Type::I8 | Type::U8 => 1, |
| Type::I16 | Type::U16 => 2, |
| Type::I64 | Type::U64 | Type::F64 => 8, |
| Type::GUID => 4, |
| Type::TypeDef((def, _)) => self.type_def_align(*def), |
| Type::Win32Array((ty, len)) => self.type_align(ty) * len, |
| _ => 4, |
| } |
| } |
| |
| // |
| // ModuleRef table queries |
| // |
| |
| fn module_ref_name(&self, row: ModuleRef) -> &str { |
| self.row_str(row.0, 0) |
| } |
| |
| // |
| // Param table queries |
| // |
| |
| pub fn param_flags(&self, row: Param) -> ParamAttributes { |
| ParamAttributes(self.row_usize(row.0, 0) as _) |
| } |
| pub fn param_sequence(&self, row: Param) -> usize { |
| self.row_usize(row.0, 1) |
| } |
| pub fn param_name(&self, row: Param) -> &str { |
| self.row_str(row.0, 2) |
| } |
| pub fn param_attributes(&self, row: Param) -> impl Iterator<Item = Attribute> { |
| self.row_attributes(row.0, HasAttribute::Param(row)) |
| } |
| pub fn param_is_com_out_ptr(&self, row: Param) -> bool { |
| self.param_attributes(row).any(|attribute| self.attribute_name(attribute) == "ComOutPtrAttribute") |
| } |
| fn param_kind(&self, row: Param) -> SignatureParamKind { |
| for attribute in self.param_attributes(row) { |
| match self.attribute_name(attribute) { |
| "NativeArrayInfoAttribute" => { |
| for (_, value) in self.attribute_args(attribute) { |
| match value { |
| Value::I16(value) => return SignatureParamKind::ArrayRelativeLen(value as _), |
| Value::I32(value) => return SignatureParamKind::ArrayFixed(value as _), |
| _ => {} |
| } |
| } |
| } |
| "MemorySizeAttribute" => { |
| for (_, value) in self.attribute_args(attribute) { |
| if let Value::I16(value) = value { |
| return SignatureParamKind::ArrayRelativeByteLen(value as _); |
| } |
| } |
| } |
| _ => {} |
| } |
| } |
| SignatureParamKind::Other |
| } |
| pub fn param_is_retval(&self, row: Param) -> bool { |
| self.param_attributes(row).any(|attribute| self.attribute_name(attribute) == "RetValAttribute") |
| } |
| pub fn param_is_reserved(&self, row: Param) -> bool { |
| self.param_attributes(row).any(|attribute| self.attribute_name(attribute) == "ReservedAttribute") |
| } |
| pub fn param_free_with(&self, row: Param) -> Option<String> { |
| for attribute in self.param_attributes(row) { |
| if self.attribute_name(attribute) == "FreeWithAttribute" { |
| for (_, arg) in self.attribute_args(attribute) { |
| if let Value::String(name) = arg { |
| return Some(name); |
| } |
| } |
| } |
| } |
| None |
| } |
| pub fn param_is_const(&self, row: Param) -> bool { |
| self.param_attributes(row).any(|attribute| self.attribute_name(attribute) == "ConstAttribute") |
| } |
| |
| // |
| // TypeDef table queries |
| // |
| |
| pub fn type_def_flags(&self, row: TypeDef) -> TypeAttributes { |
| TypeAttributes(self.row_usize(row.0, 0) as _) |
| } |
| pub fn type_def_name(&self, row: TypeDef) -> &str { |
| self.row_str(row.0, 1) |
| } |
| pub fn type_def_namespace(&self, row: TypeDef) -> &str { |
| self.row_str(row.0, 2) |
| } |
| pub fn type_def_type_name(&self, row: TypeDef) -> TypeName { |
| TypeName::new(self.type_def_namespace(row), self.type_def_name(row)) |
| } |
| pub fn type_def_extends(&self, row: TypeDef) -> TypeName { |
| self.type_def_or_ref(self.row_decode(row.0, 3)) |
| } |
| pub fn type_def_fields(&self, row: TypeDef) -> impl Iterator<Item = Field> { |
| self.row_list(row.0, TABLE_FIELD, 4).map(Field) |
| } |
| pub fn type_def_methods(&self, row: TypeDef) -> impl Iterator<Item = MethodDef> { |
| self.row_list(row.0, TABLE_METHODDEF, 5).map(MethodDef) |
| } |
| pub fn type_def_attributes(&self, row: TypeDef) -> impl Iterator<Item = Attribute> { |
| self.row_attributes(row.0, HasAttribute::TypeDef(row)) |
| } |
| pub fn type_def_generics(&self, row: TypeDef) -> impl Iterator<Item = Type> { |
| self.row_equal_range(row.0, TABLE_GENERICPARAM, 2, TypeOrMethodDef::TypeDef(row).encode()).map(|row| Type::GenericParam(GenericParam(row))) |
| } |
| pub fn type_def_interface_impls(&self, row: TypeDef) -> impl Iterator<Item = InterfaceImpl> { |
| self.row_equal_range(row.0, TABLE_INTERFACEIMPL, 0, (row.0.row + 1) as _).map(InterfaceImpl) |
| } |
| pub fn type_def_enclosing_type(&self, row: TypeDef) -> Option<TypeDef> { |
| self.row_equal_range(row.0, TABLE_NESTEDCLASS, 0, (row.0.row + 1) as _).next().map(|row| TypeDef(Row::new(self.files[row.file as usize].usize(row.row as _, row.table as _, 1) - 1, TABLE_TYPEDEF, row.file as _))) |
| } |
| pub fn type_def_class_layout(&self, row: TypeDef) -> Option<ClassLayout> { |
| self.row_equal_range(row.0, TABLE_CLASSLAYOUT, 2, (row.0.row + 1) as _).map(ClassLayout).next() |
| } |
| pub fn type_def_underlying_type(&self, row: TypeDef) -> Type { |
| let field = self.type_def_fields(row).next().expect("Field not found"); |
| if let Some(constant) = self.field_constant(field) { |
| self.constant_type(constant) |
| } else { |
| self.field_type(field, Some(row)) |
| } |
| } |
| pub fn type_def_kind(&self, row: TypeDef) -> TypeKind { |
| if self.type_def_flags(row).contains(TypeAttributes::INTERFACE) { |
| TypeKind::Interface |
| } else { |
| match self.type_def_extends(row) { |
| TypeName::Enum => TypeKind::Enum, |
| TypeName::Delegate => TypeKind::Delegate, |
| TypeName::Struct => TypeKind::Struct, |
| _ => TypeKind::Class, |
| } |
| } |
| } |
| pub fn type_def_stdcall(&self, row: TypeDef) -> usize { |
| if self.type_def_kind(row) == TypeKind::Struct { |
| if self.type_def_flags(row).contains(TypeAttributes::EXPLICIT_LAYOUT) { |
| self.type_def_fields(row).map(|field| self.type_stdcall(&self.field_type(field, Some(row)))).max().unwrap_or(1) |
| } else { |
| self.type_def_fields(row).fold(0, |sum, field| sum + self.type_stdcall(&self.field_type(field, Some(row)))) |
| } |
| } else { |
| 4 |
| } |
| } |
| pub fn type_def_is_blittable(&self, row: TypeDef) -> bool { |
| match self.type_def_kind(row) { |
| TypeKind::Struct => { |
| if self.type_def_flags(row).contains(TypeAttributes::WINRT) { |
| self.type_def_fields(row).all(|field| self.field_is_blittable(field, row)) |
| } else { |
| true |
| } |
| } |
| TypeKind::Enum => true, |
| TypeKind::Delegate => !self.type_def_flags(row).contains(TypeAttributes::WINRT), |
| _ => false, |
| } |
| } |
| pub fn type_def_is_copyable(&self, row: TypeDef) -> bool { |
| match self.type_def_kind(row) { |
| TypeKind::Struct => self.type_def_fields(row).all(|field| self.field_is_copyable(field, row)), |
| TypeKind::Enum => true, |
| TypeKind::Delegate => !self.type_def_flags(row).contains(TypeAttributes::WINRT), |
| _ => false, |
| } |
| } |
| pub fn type_def_is_callback(&self, row: TypeDef) -> bool { |
| !self.type_def_flags(row).contains(TypeAttributes::WINRT) && self.type_def_kind(row) == TypeKind::Delegate |
| } |
| pub fn type_def_has_default_constructor(&self, row: TypeDef) -> bool { |
| for attribute in self.type_def_attributes(row) { |
| if self.attribute_name(attribute) == "ActivatableAttribute" { |
| if self.attribute_args(attribute).iter().any(|arg| matches!(arg.1, Value::TypeDef(_))) { |
| continue; |
| } else { |
| return true; |
| } |
| } |
| } |
| false |
| } |
| // TODO: consider removing all the expects and just return Option<T> and let the bindgen crate expect it |
| // that way the metadata reader is a little more schema-agnostic... |
| pub fn type_def_invoke_method(&self, row: TypeDef) -> MethodDef { |
| self.type_def_methods(row).find(|method| self.method_def_name(*method) == "Invoke").expect("`Invoke` method not found") |
| } |
| pub fn type_def_interfaces(&'a self, row: TypeDef, generics: &'a [Type]) -> impl Iterator<Item = Interface> + '_ { |
| self.type_def_interface_impls(row).map(move |row| self.interface_impl_type(row, generics)) |
| } |
| pub fn type_def_default_interface(&self, row: TypeDef) -> Option<Type> { |
| self.type_def_interfaces(row, &[]).find(|interface| interface.kind == InterfaceKind::Default).map(|interface| interface.ty) |
| } |
| pub fn type_def_has_default_interface(&self, row: TypeDef) -> bool { |
| self.type_def_interface_impls(row).any(|imp| self.interface_impl_is_default(imp)) |
| } |
| pub fn type_def_is_deprecated(&self, row: TypeDef) -> bool { |
| self.type_def_attributes(row).any(|attribute| self.attribute_name(attribute) == "DeprecatedAttribute") |
| } |
| pub fn type_def_is_handle(&self, row: TypeDef) -> bool { |
| self.type_def_attributes(row).any(|attribute| self.attribute_name(attribute) == "NativeTypedefAttribute") |
| } |
| pub fn type_def_is_exclusive(&self, row: TypeDef) -> bool { |
| self.type_def_attributes(row).any(|attribute| self.attribute_name(attribute) == "ExclusiveToAttribute") |
| } |
| pub fn type_def_is_scoped(&self, row: TypeDef) -> bool { |
| self.type_def_flags(row).contains(TypeAttributes::WINRT) || self.type_def_attributes(row).any(|attribute| self.attribute_name(attribute) == "ScopedEnumAttribute") |
| } |
| pub fn type_def_is_contract(&self, row: TypeDef) -> bool { |
| self.type_def_attributes(row).any(|attribute| self.attribute_name(attribute) == "ApiContractAttribute") |
| } |
| fn type_def_is_composable(&self, row: TypeDef) -> bool { |
| self.type_def_attributes(row).any(|attribute| self.attribute_name(attribute) == "ComposableAttribute") |
| } |
| fn type_def_is_struct(&self, row: TypeDef) -> 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. |
| self.type_def_kind(row) == TypeKind::Struct && !self.type_def_is_handle(row) |
| } |
| fn type_def_is_borrowed(&self, row: TypeDef) -> bool { |
| match self.type_def_kind(row) { |
| TypeKind::Class => self.type_def_is_composable(row), |
| TypeKind::Delegate => false, |
| _ => !self.type_def_is_blittable(row), |
| } |
| } |
| pub fn type_def_is_trivially_convertible(&self, row: TypeDef) -> bool { |
| match self.type_def_kind(row) { |
| TypeKind::Struct => self.type_def_is_handle(row), |
| _ => false, |
| } |
| } |
| pub fn type_def_is_primitive(&self, row: TypeDef) -> bool { |
| match self.type_def_kind(row) { |
| TypeKind::Enum => true, |
| TypeKind::Struct => self.type_def_is_handle(row), |
| TypeKind::Delegate => !self.type_def_flags(row).contains(TypeAttributes::WINRT), |
| _ => false, |
| } |
| } |
| pub fn type_def_has_explicit_layout(&self, row: TypeDef) -> bool { |
| if self.type_def_kind(row) != TypeKind::Struct { |
| return false; |
| } |
| fn check(reader: &Reader, row: TypeDef) -> bool { |
| if reader.type_def_flags(row).contains(TypeAttributes::EXPLICIT_LAYOUT) { |
| return true; |
| } |
| if reader.type_def_fields(row).any(|field| reader.type_has_explicit_layout(&reader.field_type(field, Some(row)))) { |
| return true; |
| } |
| false |
| } |
| let type_name = self.type_def_type_name(row); |
| if type_name.namespace.is_empty() { |
| check(self, row) |
| } else { |
| for row in self.get(type_name) { |
| if check(self, row) { |
| return true; |
| } |
| } |
| false |
| } |
| } |
| pub fn type_def_has_packing(&self, row: TypeDef) -> bool { |
| if self.type_def_kind(row) != TypeKind::Struct { |
| return false; |
| } |
| fn check(reader: &Reader, row: TypeDef) -> bool { |
| if reader.type_def_class_layout(row).is_some() { |
| return true; |
| } |
| if reader.type_def_fields(row).any(|field| reader.type_has_packing(&reader.field_type(field, Some(row)))) { |
| return true; |
| } |
| false |
| } |
| let type_name = self.type_def_type_name(row); |
| if type_name.namespace.is_empty() { |
| check(self, row) |
| } else { |
| for row in self.get(type_name) { |
| if check(self, row) { |
| return true; |
| } |
| } |
| false |
| } |
| } |
| pub fn type_def_has_callback(&self, row: TypeDef) -> bool { |
| if self.type_def_is_callback(row) { |
| return true; |
| } |
| if self.type_def_kind(row) != TypeKind::Struct { |
| return false; |
| } |
| fn check(reader: &Reader, row: TypeDef) -> bool { |
| if reader.type_def_fields(row).any(|field| reader.type_has_callback(&reader.field_type(field, Some(row)))) { |
| return true; |
| } |
| false |
| } |
| let type_name = self.type_def_type_name(row); |
| if type_name.namespace.is_empty() { |
| check(self, row) |
| } else { |
| for row in self.get(type_name) { |
| if check(self, row) { |
| return true; |
| } |
| } |
| false |
| } |
| } |
| pub fn type_def_guid(&self, row: TypeDef) -> Option<GUID> { |
| for attribute in self.type_def_attributes(row) { |
| if self.attribute_name(attribute) == "GuidAttribute" { |
| return Some(GUID::from_args(&self.attribute_args(attribute))); |
| } |
| } |
| None |
| } |
| pub fn type_def_bases(&self, mut row: TypeDef) -> Vec<TypeDef> { |
| // TODO: maybe return Vec<Type> |
| let mut bases = Vec::new(); |
| loop { |
| let extends = self.type_def_extends(row); |
| if extends == TypeName::Object { |
| break; |
| } else { |
| row = self.get(extends).next().expect("Type not found"); |
| bases.push(row); |
| } |
| } |
| bases |
| } |
| pub fn type_def_is_flags(&self, row: TypeDef) -> bool { |
| // Win32 enums use the Flags attribute. WinRT enums don't have the Flags attribute but are paritioned merely based |
| // on whether they are signed. |
| self.type_def_attributes(row).any(|attribute| self.attribute_name(attribute) == "FlagsAttribute") || (self.type_def_flags(row).contains(TypeAttributes::WINRT) && self.type_def_underlying_type(row) == Type::U32) |
| } |
| pub fn type_def_is_agile(&self, row: TypeDef) -> bool { |
| for attribute in self.type_def_attributes(row) { |
| match self.attribute_name(attribute) { |
| "AgileAttribute" => return true, |
| "MarshalingBehaviorAttribute" => { |
| if let Some((_, Value::Enum(_, Integer::I32(2)))) = self.attribute_args(attribute).get(0) { |
| return true; |
| } |
| } |
| _ => {} |
| } |
| } |
| matches!(self.type_def_type_name(row), TypeName::IAsyncAction | TypeName::IAsyncActionWithProgress | TypeName::IAsyncOperation | TypeName::IAsyncOperationWithProgress) |
| } |
| pub fn type_def_invalid_values(&self, row: TypeDef) -> Vec<i64> { |
| let mut values = Vec::new(); |
| for attribute in self.type_def_attributes(row) { |
| if self.attribute_name(attribute) == "InvalidHandleValueAttribute" { |
| if let Some((_, Value::I64(value))) = self.attribute_args(attribute).get(0) { |
| values.push(*value); |
| } |
| } |
| } |
| values |
| } |
| pub fn type_def_usable_for(&self, row: TypeDef) -> Option<TypeDef> { |
| for attribute in self.type_def_attributes(row) { |
| if self.attribute_name(attribute) == "AlsoUsableForAttribute" { |
| if let Some((_, Value::String(name))) = self.attribute_args(attribute).get(0) { |
| return self.get(TypeName::new(self.type_def_namespace(row), name.as_str())).next(); |
| } |
| } |
| } |
| None |
| } |
| pub fn type_def_is_nullable(&self, row: TypeDef) -> bool { |
| match self.type_def_kind(row) { |
| TypeKind::Interface | TypeKind::Class => true, |
| // TODO: win32 callbacks should be nullable... |
| TypeKind::Delegate => self.type_def_flags(row).contains(TypeAttributes::WINRT), |
| _ => false, |
| } |
| } |
| pub fn type_def_can_implement(&self, row: TypeDef) -> bool { |
| for attribute in self.type_def_attributes(row) { |
| if self.attribute_name(attribute) == "ExclusiveToAttribute" { |
| for (_, arg) in self.attribute_args(attribute) { |
| if let Value::TypeDef(def) = arg { |
| for child in self.type_def_interfaces(def, &[]) { |
| if child.kind == InterfaceKind::Overridable { |
| if let Type::TypeDef((def, _)) = child.ty { |
| if self.type_def_type_name(def) == self.type_def_type_name(row) { |
| return true; |
| } |
| } |
| } |
| } |
| } |
| } |
| return false; |
| } |
| } |
| true |
| } |
| pub fn type_def_async_kind(&self, row: TypeDef) -> AsyncKind { |
| match self.type_def_type_name(row) { |
| TypeName::IAsyncAction => AsyncKind::Action, |
| TypeName::IAsyncActionWithProgress => AsyncKind::ActionWithProgress, |
| TypeName::IAsyncOperation => AsyncKind::Operation, |
| TypeName::IAsyncOperationWithProgress => AsyncKind::OperationWithProgress, |
| _ => AsyncKind::None, |
| } |
| } |
| pub fn type_def_signature(&self, row: TypeDef, generics: &[Type]) -> String { |
| match self.type_def_kind(row) { |
| TypeKind::Interface => self.type_def_interface_signature(row, generics), |
| TypeKind::Class => { |
| if let Type::TypeDef((default, generics)) = self.type_def_interfaces(row, generics).find(|row| row.kind == InterfaceKind::Default).expect("Default interface not found").ty { |
| format!("rc({};{})", self.type_def_type_name(row), self.type_def_interface_signature(default, &generics)) |
| } else { |
| unimplemented!(); |
| } |
| } |
| TypeKind::Enum => format!("enum({};{})", self.type_def_type_name(row), self.type_signature(&self.type_def_underlying_type(row))), |
| TypeKind::Struct => { |
| let mut result = format!("struct({}", self.type_def_type_name(row)); |
| for field in self.type_def_fields(row) { |
| result.push(';'); |
| result.push_str(&self.type_signature(&self.field_type(field, Some(row)))); |
| } |
| result.push(')'); |
| result |
| } |
| TypeKind::Delegate => { |
| if generics.is_empty() { |
| format!("delegate({})", self.type_def_interface_signature(row, generics)) |
| } else { |
| self.type_def_interface_signature(row, generics) |
| } |
| } |
| } |
| } |
| fn type_def_interface_signature(&self, row: TypeDef, generics: &[Type]) -> String { |
| let guid = self.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(&self.type_signature(generic)); |
| } |
| result.push(')'); |
| result |
| } |
| } |
| pub fn type_def_cfg(&self, row: TypeDef, generics: &[Type]) -> Cfg { |
| let mut cfg = Cfg::default(); |
| self.type_def_cfg_combine(row, generics, &mut cfg); |
| self.cfg_add_attributes(&mut cfg, self.type_def_attributes(row)); |
| cfg |
| } |
| pub fn type_def_cfg_impl(&self, def: TypeDef, generics: &[Type]) -> Cfg { |
| let mut cfg = Cfg { implement: true, ..Default::default() }; |
| |
| fn combine<'a>(reader: &'a Reader, def: TypeDef, generics: &[Type], cfg: &mut Cfg<'a>) { |
| reader.type_def_cfg_combine(def, generics, cfg); |
| |
| for method in reader.type_def_methods(def) { |
| reader.signature_cfg_combine(&reader.method_def_signature(method, generics), cfg); |
| } |
| } |
| |
| combine(self, def, generics, &mut cfg); |
| |
| for def in self.type_def_vtables(def) { |
| if let Type::TypeDef((def, generics)) = def { |
| combine(self, def, &generics, &mut cfg); |
| } |
| } |
| |
| if self.type_def_flags(def).contains(TypeAttributes::WINRT) { |
| for interface in self.type_def_interfaces(def, generics) { |
| if let Type::TypeDef((def, generics)) = interface.ty { |
| combine(self, def, &generics, &mut cfg); |
| } |
| } |
| } |
| |
| self.cfg_add_attributes(&mut cfg, self.type_def_attributes(def)); |
| cfg |
| } |
| pub fn type_def_cfg_combine(&'a self, row: TypeDef, generics: &[Type], cfg: &mut Cfg<'a>) { |
| for generic in generics { |
| self.type_cfg_combine(generic, cfg); |
| } |
| |
| if cfg.types.entry(self.type_def_namespace(row)).or_default().insert(row) { |
| match self.type_def_kind(row) { |
| TypeKind::Class => { |
| if let Some(default_interface) = self.type_def_default_interface(row) { |
| self.type_cfg_combine(&default_interface, cfg); |
| } |
| } |
| TypeKind::Interface => { |
| if !self.type_def_flags(row).contains(TypeAttributes::WINRT) { |
| for def in self.type_def_vtables(row) { |
| if let Type::TypeDef((def, _)) = def { |
| cfg.add_feature(self.type_def_namespace(def)); |
| } |
| } |
| } |
| } |
| TypeKind::Struct => { |
| self.type_def_fields(row).for_each(|field| self.field_cfg_combine(field, Some(row), cfg)); |
| let type_name = self.type_def_type_name(row); |
| if !type_name.namespace.is_empty() { |
| for def in self.get(type_name) { |
| if def != row { |
| self.type_def_cfg_combine(def, &[], cfg); |
| } |
| } |
| } |
| } |
| TypeKind::Delegate => self.signature_cfg_combine(&self.method_def_signature(self.type_def_invoke_method(row), generics), cfg), |
| _ => {} |
| } |
| } |
| } |
| pub fn type_def_vtables(&self, row: TypeDef) -> Vec<Type> { |
| let mut result = Vec::new(); |
| if self.type_def_flags(row).contains(TypeAttributes::WINRT) { |
| result.push(Type::IUnknown); |
| if self.type_def_kind(row) != TypeKind::Delegate { |
| result.push(Type::IInspectable); |
| } |
| } else { |
| let mut next = row; |
| while let Some(base) = self.type_def_interfaces(next, &[]).next() { |
| match base.ty { |
| Type::TypeDef((row, _)) => { |
| next = row; |
| result.insert(0, base.ty); |
| } |
| Type::IInspectable => { |
| result.insert(0, Type::IUnknown); |
| result.insert(1, Type::IInspectable); |
| break; |
| } |
| Type::IUnknown => { |
| result.insert(0, Type::IUnknown); |
| break; |
| } |
| _ => unimplemented!(), |
| } |
| } |
| } |
| result |
| } |
| |
| // |
| // TypeRef table queries |
| // |
| |
| pub fn type_ref_name(&self, row: TypeRef) -> &str { |
| self.row_str(row.0, 1) |
| } |
| pub fn type_ref_namespace(&self, row: TypeRef) -> &str { |
| self.row_str(row.0, 2) |
| } |
| pub fn type_ref_type_name(&self, row: TypeRef) -> TypeName { |
| TypeName::new(self.type_ref_name(row), self.type_ref_namespace(row)) |
| } |
| |
| // |
| // TypeSpec table queries |
| // |
| |
| pub fn type_spec_signature(&self, row: TypeSpec) -> Blob { |
| self.row_blob(row.0, 0) |
| } |
| |
| // |
| // Signature queries |
| // |
| |
| pub fn signature_cfg(&self, signature: &Signature) -> Cfg { |
| let mut cfg = Cfg::default(); |
| self.signature_cfg_combine(signature, &mut cfg); |
| self.cfg_add_attributes(&mut cfg, self.method_def_attributes(signature.def)); |
| cfg |
| } |
| fn signature_cfg_combine(&'a self, signature: &Signature, cfg: &mut Cfg<'a>) { |
| signature.return_type.iter().for_each(|ty| self.type_cfg_combine(ty, cfg)); |
| signature.params.iter().for_each(|param| self.type_cfg_combine(¶m.ty, cfg)); |
| } |
| pub fn signature_param_is_borrowed(&self, param: &SignatureParam) -> bool { |
| self.type_is_borrowed(¶m.ty) |
| } |
| pub fn signature_param_is_failible_param(&self, param: &SignatureParam) -> bool { |
| self.type_is_non_exclusive_winrt_interface(¶m.ty) |
| } |
| pub fn signature_param_is_trivially_convertible(&self, param: &SignatureParam) -> bool { |
| self.type_is_trivially_convertible(¶m.ty) |
| } |
| pub fn signature_param_is_convertible(&self, param: &SignatureParam) -> bool { |
| !self.param_flags(param.def).contains(ParamAttributes::OUTPUT) && !param.ty.is_winrt_array() && !param.ty.is_pointer() && !param.kind.is_array() && (self.type_is_borrowed(¶m.ty) || self.type_is_non_exclusive_winrt_interface(¶m.ty) || self.type_is_trivially_convertible(¶m.ty)) |
| } |
| pub fn signature_param_is_retval(&self, param: &SignatureParam) -> 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.param_is_retval(param.def) { |
| return true; |
| } |
| if !param.ty.is_pointer() { |
| return false; |
| } |
| if param.ty.is_void() { |
| return false; |
| } |
| let flags = self.param_flags(param.def); |
| if flags.contains(ParamAttributes::INPUT) || !flags.contains(ParamAttributes::OUTPUT) || flags.contains(ParamAttributes::OPTIONAL) || param.kind.is_array() { |
| return false; |
| } |
| if self.param_kind(param.def).is_array() { |
| return false; |
| } |
| // If it's bigger than 128 bits, best to pass as a reference. |
| if self.type_size(¶m.ty.deref()) > 16 { |
| return false; |
| } |
| // TODO: find a way to treat this like COM interface result values. |
| !self.type_is_callback(¶m.ty.deref()) |
| } |
| pub fn signature_kind(&self, signature: &Signature) -> SignatureKind { |
| if self.method_def_can_return_multiple_success_values(signature.def) { |
| return SignatureKind::PreserveSig; |
| } |
| if let Some(return_type) = &signature.return_type { |
| match return_type { |
| Type::HRESULT => { |
| if signature.params.len() >= 2 { |
| if let Some(guid) = self.signature_param_is_query_guid(&signature.params) { |
| if let Some(object) = self.signature_param_is_query_object(&signature.params) { |
| if self.param_flags(signature.params[object].def).contains(ParamAttributes::OPTIONAL) { |
| return SignatureKind::QueryOptional(QueryPosition { object, guid }); |
| } else { |
| return SignatureKind::Query(QueryPosition { object, guid }); |
| } |
| } |
| } |
| } |
| |
| if self.signature_is_retval(signature) { |
| return SignatureKind::ResultValue; |
| } |
| |
| return SignatureKind::ResultVoid; |
| } |
| Type::TypeDef((def, _)) if self.type_def_type_name(*def) == TypeName::NTSTATUS => { |
| return SignatureKind::ResultVoid; |
| } |
| _ if self.type_is_struct(return_type) => { |
| return SignatureKind::ReturnStruct; |
| } |
| _ => return SignatureKind::PreserveSig, |
| } |
| } |
| |
| if self.signature_is_retval(signature) { |
| return SignatureKind::ReturnValue; |
| } |
| |
| SignatureKind::ReturnVoid |
| } |
| fn signature_is_retval(&self, signature: &Signature) -> bool { |
| signature.params.last().map_or(false, |param| self.signature_param_is_retval(param)) |
| && signature.params[..signature.params.len() - 1].iter().all(|param| { |
| let flags = self.param_flags(param.def); |
| !flags.contains(ParamAttributes::OUTPUT) |
| }) |
| } |
| fn signature_param_is_query_guid(&self, params: &[SignatureParam]) -> Option<usize> { |
| params.iter().rposition(|param| param.ty == Type::ConstPtr((Box::new(Type::GUID), 1)) && !self.param_flags(param.def).contains(ParamAttributes::OUTPUT)) |
| } |
| fn signature_param_is_query_object(&self, params: &[SignatureParam]) -> Option<usize> { |
| params.iter().rposition(|param| param.ty == Type::MutPtr((Box::new(Type::Void), 2)) && self.param_is_com_out_ptr(param.def)) |
| } |
| |
| // |
| // Other type queries |
| // |
| |
| fn cfg_add_attributes(&self, cfg: &mut Cfg, attributes: impl Iterator<Item = Attribute>) { |
| for attribute in attributes { |
| match self.attribute_name(attribute) { |
| "SupportedArchitectureAttribute" => { |
| if let Some((_, Value::Enum(_, Integer::I32(value)))) = self.attribute_args(attribute).get(0) { |
| if value & 1 == 1 { |
| cfg.arches.insert("x86"); |
| } |
| if value & 2 == 2 { |
| cfg.arches.insert("x86_64"); |
| } |
| if value & 4 == 4 { |
| cfg.arches.insert("aarch64"); |
| } |
| } |
| } |
| "DeprecatedAttribute" => { |
| cfg.add_feature("deprecated"); |
| } |
| _ => {} |
| } |
| } |
| } |
| pub fn type_cfg(&self, ty: &Type) -> Cfg { |
| let mut cfg = Cfg::default(); |
| self.type_cfg_combine(ty, &mut cfg); |
| cfg |
| } |
| pub fn type_cfg_combine(&'a self, ty: &Type, cfg: &mut Cfg<'a>) { |
| match ty { |
| Type::TypeDef((row, generics)) => self.type_def_cfg_combine(*row, generics, cfg), |
| Type::Win32Array((ty, _)) => self.type_cfg_combine(ty, cfg), |
| Type::ConstPtr((ty, _)) => self.type_cfg_combine(ty, cfg), |
| Type::MutPtr((ty, _)) => self.type_cfg_combine(ty, cfg), |
| Type::WinrtArray(ty) => self.type_cfg_combine(ty, cfg), |
| Type::WinrtArrayRef(ty) => self.type_cfg_combine(ty, cfg), |
| ty => _ = cfg.core_types.insert(ty.clone()), |
| } |
| } |
| pub fn type_interfaces(&self, ty: &Type) -> Vec<Interface> { |
| // TODO: collect into btree map and then return collected vec |
| // This will both sort the results and should make finding dupes faster |
| fn walk(reader: &Reader, result: &mut Vec<Interface>, parent: &Type, is_base: bool) { |
| if let Type::TypeDef((row, generics)) = parent { |
| for mut child in reader.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(reader, result, &child.ty, is_base); |
| result.push(child); |
| } |
| } |
| } |
| } |
| let mut result = Vec::new(); |
| walk(self, &mut result, ty, false); |
| if let Type::TypeDef((row, _)) = ty { |
| if self.type_def_kind(*row) == TypeKind::Class { |
| for base in self.type_def_bases(*row) { |
| walk(self, &mut result, &Type::TypeDef((base, Vec::new())), true); |
| } |
| for attribute in self.type_def_attributes(*row) { |
| match self.attribute_name(attribute) { |
| "StaticAttribute" | "ActivatableAttribute" => { |
| for (_, arg) in self.attribute_args(attribute) { |
| if let Value::TypeDef(row) = arg { |
| result.push(Interface { ty: Type::TypeDef((row, Vec::new())), kind: InterfaceKind::Static }); |
| break; |
| } |
| } |
| } |
| _ => {} |
| } |
| } |
| } |
| } |
| result.sort_by(|a, b| self.type_name(&a.ty).cmp(self.type_name(&b.ty))); |
| result |
| } |
| fn type_def_or_ref(&self, code: TypeDefOrRef) -> TypeName { |
| match code { |
| TypeDefOrRef::TypeDef(row) => TypeName::new(self.type_def_namespace(row), self.type_def_name(row)), |
| TypeDefOrRef::TypeRef(row) => TypeName::new(self.type_ref_namespace(row), self.type_ref_name(row)), |
| _ => unimplemented!(), |
| } |
| } |
| fn type_stdcall(&self, ty: &Type) -> usize { |
| match ty { |
| Type::I8 | Type::U8 => 1, |
| Type::I16 | Type::U16 => 2, |
| Type::I64 | Type::U64 | Type::F64 => 8, |
| Type::GUID => 16, |
| Type::TypeDef((row, _)) => self.type_def_stdcall(*row), |
| _ => 4, |
| } |
| } |
| pub fn type_is_exclusive(&self, ty: &Type) -> bool { |
| match ty { |
| Type::TypeDef((row, _)) => self.type_def_is_exclusive(*row), |
| _ => false, |
| } |
| } |
| pub fn type_is_blittable(&self, ty: &Type) -> bool { |
| match ty { |
| Type::TypeDef((row, _)) => self.type_def_is_blittable(*row), |
| Type::String | Type::BSTR | Type::IInspectable | Type::IUnknown | Type::GenericParam(_) => false, |
| Type::Win32Array((kind, _)) => self.type_is_blittable(kind), |
| Type::WinrtArray(kind) => self.type_is_blittable(kind), |
| _ => true, |
| } |
| } |
| pub fn type_is_copyable(&self, ty: &Type) -> bool { |
| match ty { |
| Type::TypeDef((row, _)) => self.type_def_is_copyable(*row), |
| Type::String | Type::BSTR | Type::IInspectable | Type::IUnknown | Type::GenericParam(_) => false, |
| Type::Win32Array((kind, _)) => self.type_is_copyable(kind), |
| Type::WinrtArray(kind) => self.type_is_copyable(kind), |
| _ => true, |
| } |
| } |
| pub fn type_has_explicit_layout(&self, ty: &Type) -> bool { |
| match ty { |
| Type::TypeDef((row, _)) => self.type_def_has_explicit_layout(*row), |
| Type::Win32Array((ty, _)) => self.type_has_explicit_layout(ty), |
| _ => false, |
| } |
| } |
| pub fn type_has_packing(&self, ty: &Type) -> bool { |
| match ty { |
| Type::TypeDef((row, _)) => self.type_def_has_packing(*row), |
| Type::Win32Array((ty, _)) => self.type_has_packing(ty), |
| _ => false, |
| } |
| } |
| pub fn type_has_callback(&self, ty: &Type) -> bool { |
| match ty { |
| Type::TypeDef((row, _)) => self.type_def_has_callback(*row), |
| Type::Win32Array((ty, _)) => self.type_has_callback(ty), |
| _ => false, |
| } |
| } |
| fn type_from_ref(&self, code: TypeDefOrRef, enclosing: Option<TypeDef>, generics: &[Type]) -> Type { |
| if let TypeDefOrRef::TypeSpec(def) = code { |
| let mut blob = self.type_spec_signature(def); |
| return self.type_from_blob_impl(&mut blob, None, generics); |
| } |
| |
| let mut full_name = self.type_def_or_ref(code); |
| |
| for (known_name, kind) in CORE_TYPES { |
| if full_name == known_name { |
| return kind; |
| } |
| } |
| |
| for (from, to) in REMAP_TYPES { |
| if full_name == from { |
| full_name = to; |
| break; |
| } |
| } |
| |
| if let Some(outer) = enclosing { |
| if full_name.namespace.is_empty() { |
| let nested = &self.nested[&outer]; |
| let Some(inner) = nested.get(full_name.name) else { |
| panic!("Nested type not found: {}.{}", self.type_def_type_name(outer), full_name.name); |
| }; |
| return Type::TypeDef((*inner, Vec::new())); |
| } |
| } |
| |
| if let Some(ty) = self.get(full_name).next() { |
| Type::TypeDef((ty, Vec::new())) |
| } else { |
| panic!("Type not found: {}", full_name); |
| } |
| } |
| fn type_from_blob(&self, blob: &mut Blob, enclosing: Option<TypeDef>, generics: &[Type]) -> Option<Type> { |
| let is_winrt_const_ref = blob.read_modifiers().iter().any(|def| self.type_def_or_ref(*def) == TypeName::IsConst); |
| let is_winrt_array_ref = blob.read_expected(0x10); |
| if blob.read_expected(0x01) { |
| return None; |
| } |
| |
| let is_winrt_array = blob.read_expected(0x1D); |
| |
| let mut pointers = 0; |
| |
| while blob.read_expected(0x0f) { |
| pointers += 1; |
| } |
| |
| let mut kind = self.type_from_blob_impl(blob, enclosing, generics); |
| |
| if pointers > 0 { |
| kind = Type::MutPtr((Box::new(kind), pointers)); |
| } |
| |
| Some(if is_winrt_array { |
| if is_winrt_array_ref { |
| Type::WinrtArrayRef(Box::new(kind)) |
| } else { |
| Type::WinrtArray(Box::new(kind)) |
| } |
| } else if is_winrt_const_ref { |
| Type::WinrtConstRef(Box::new(kind)) |
| } else { |
| kind |
| }) |
| } |
| fn type_from_blob_impl(&self, blob: &mut Blob, enclosing: Option<TypeDef>, generics: &[Type]) -> Type { |
| let code = blob.read_usize(); |
| |
| if let Some(code) = Type::from_code(code) { |
| return code; |
| } |
| |
| match code { |
| 0x11 | 0x12 => self.type_from_ref(TypeDefOrRef::decode(blob.file, blob.read_usize()), enclosing, generics), |
| 0x13 => generics.get(blob.read_usize()).unwrap_or(&Type::Void).clone(), |
| 0x14 => { |
| let kind = self.type_from_blob(blob, enclosing, generics).unwrap(); |
| let _rank = blob.read_usize(); |
| let _bounds_count = blob.read_usize(); |
| let bounds = blob.read_usize(); |
| Type::Win32Array((Box::new(kind), bounds)) |
| } |
| 0x15 => { |
| blob.read_usize(); |
| |
| let def = self.get(self.type_def_or_ref(TypeDefOrRef::decode(blob.file, blob.read_usize()))).next().expect("Type not found"); |
| let mut args = Vec::with_capacity(blob.read_usize()); |
| |
| for _ in 0..args.capacity() { |
| args.push(self.type_from_blob_impl(blob, enclosing, generics)); |
| } |
| |
| Type::TypeDef((def, args)) |
| } |
| _ => unimplemented!(), |
| } |
| } |
| pub fn type_name(&self, ty: &Type) -> &str { |
| match ty { |
| Type::TypeDef((row, _)) => self.type_def_name(*row), |
| _ => "", |
| } |
| } |
| pub fn type_signature(&self, 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::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)) => self.type_def_signature(*row, generics), |
| _ => unimplemented!(), |
| } |
| } |
| pub fn type_is_nullable(&self, ty: &Type) -> bool { |
| match ty { |
| Type::TypeDef((row, _)) => self.type_def_is_nullable(*row), |
| Type::IInspectable | Type::IUnknown => true, |
| _ => false, |
| } |
| } |
| fn type_is_borrowed(&self, ty: &Type) -> bool { |
| match ty { |
| Type::TypeDef((row, _)) => self.type_def_is_borrowed(*row), |
| Type::BSTR | Type::PCSTR | Type::PCWSTR | Type::IInspectable | Type::IUnknown | Type::GenericParam(_) => true, |
| _ => false, |
| } |
| } |
| pub fn type_is_non_exclusive_winrt_interface(&self, ty: &Type) -> bool { |
| match ty { |
| Type::TypeDef((row, _)) => { |
| let flags = self.type_def_flags(*row); |
| if !flags.contains(TypeAttributes::WINRT) { |
| false |
| } else { |
| match self.type_def_kind(*row) { |
| TypeKind::Interface => !self.type_def_is_exclusive(*row), |
| TypeKind::Class => self.type_def_is_composable(*row), |
| _ => false, |
| } |
| } |
| } |
| _ => false, |
| } |
| } |
| pub fn type_is_trivially_convertible(&self, ty: &Type) -> bool { |
| match ty { |
| Type::TypeDef((row, _)) => self.type_def_is_trivially_convertible(*row), |
| Type::PCSTR | Type::PCWSTR => true, |
| _ => false, |
| } |
| } |
| pub fn type_is_callback(&self, ty: &Type) -> bool { |
| match ty { |
| Type::TypeDef((row, _)) => self.type_def_is_callback(*row), |
| _ => false, |
| } |
| } |
| pub fn type_is_primitive(&self, ty: &Type) -> bool { |
| match ty { |
| Type::TypeDef((row, _)) => self.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, |
| } |
| } |
| pub fn type_is_struct(&self, ty: &Type) -> bool { |
| match ty { |
| Type::TypeDef((row, _)) => self.type_def_is_struct(*row), |
| Type::GUID => true, |
| _ => false, |
| } |
| } |
| pub fn type_underlying_type(&self, ty: &Type) -> Type { |
| match ty { |
| Type::TypeDef((row, _)) => self.type_def_underlying_type(*row), |
| Type::HRESULT => Type::I32, |
| _ => ty.clone(), |
| } |
| } |
| pub fn type_has_replacement(&self, ty: &Type) -> bool { |
| match ty { |
| Type::HRESULT | Type::PCSTR | Type::PCWSTR => true, |
| Type::TypeDef((row, _)) => self.type_def_is_handle(*row), |
| _ => false, |
| } |
| } |
| } |
| |
| pub const REMAP_TYPES: [(TypeName, TypeName); 2] = [(TypeName::D2D_MATRIX_3X2_F, TypeName::Matrix3x2), (TypeName::D3DMATRIX, TypeName::Matrix4x4)]; |
| |
| pub const CORE_TYPES: [(TypeName, Type); 11] = [(TypeName::GUID, Type::GUID), (TypeName::IUnknown, Type::IUnknown), (TypeName::HResult, Type::HRESULT), (TypeName::HRESULT, Type::HRESULT), (TypeName::HSTRING, Type::String), (TypeName::BSTR, Type::BSTR), (TypeName::IInspectable, Type::IInspectable), (TypeName::PSTR, Type::PSTR), (TypeName::PWSTR, Type::PWSTR), (TypeName::Type, Type::TypeName), (TypeName::CHAR, Type::U8)]; |