diff options
Diffstat (limited to 'vendor/windows-metadata/src')
23 files changed, 4646 insertions, 0 deletions
diff --git a/vendor/windows-metadata/src/attributes.rs b/vendor/windows-metadata/src/attributes.rs new file mode 100644 index 000000000..40b6326fe --- /dev/null +++ b/vendor/windows-metadata/src/attributes.rs @@ -0,0 +1,55 @@ +use super::*; + +flags!(FieldAttributes, u16); +impl FieldAttributes { + pub const PRIVATE: Self = Self(0x1); + pub const PUBLIC: Self = Self(0x6); + pub const LITERAL: Self = Self(0x40); + pub const STATIC: Self = Self(0x10); + pub const SPECIAL: Self = Self(0x200); + pub const RUNTIME_SPECIAL: Self = Self(0x400); + pub const HAS_DEFAULT: Self = Self(0x8000); +} + +flags!(MethodAttributes, u16); +impl MethodAttributes { + pub const ABSTRACT: Self = Self(0x400); + pub const HIDE_BY_SIG: Self = Self(0x80); + pub const NEW_SLOT: Self = Self(0x100); + pub const PUBLIC: Self = Self(0x6); + pub const SPECIAL: Self = Self(0x800); + pub const VIRTUAL: Self = Self(0x40); +} + +flags!(MethodImplAttributes, usize); +impl MethodImplAttributes { + pub const PRESERVE_SIG: Self = Self(0x80); +} + +flags!(ParamAttributes, u16); +impl ParamAttributes { + pub const INPUT: Self = Self(0x1); + pub const OUTPUT: Self = Self(0x2); + pub const OPTIONAL: Self = Self(0x10); +} + +flags!(PInvokeAttributes, usize); +impl PInvokeAttributes { + pub const LAST_ERROR: Self = Self(0x40); + pub const CONV_PLATFORM: Self = Self(0x100); + pub const CONV_CDECL: Self = Self(0x200); + pub const CONV_STDCALL: Self = Self(0x300); + pub const CONV_THISCALL: Self = Self(0x400); + pub const CONV_FASTCALL: Self = Self(0x500); +} + +flags!(TypeAttributes, u32); +impl TypeAttributes { + pub const PUBLIC: Self = Self(0x1); + pub const EXPLICIT_LAYOUT: Self = Self(0x10); + pub const ABSTRACT: Self = Self(0x80); + pub const SEALED: Self = Self(0x100); + pub const WINRT: Self = Self(0x4000); + pub const INTERFACE: Self = Self(0x20); + pub const SEQUENTIAL_LAYOUT: Self = Self(0x8); +} diff --git a/vendor/windows-metadata/src/bindings.rs b/vendor/windows-metadata/src/bindings.rs new file mode 100644 index 000000000..9e25db928 --- /dev/null +++ b/vendor/windows-metadata/src/bindings.rs @@ -0,0 +1,175 @@ +// Note: these definitions are taken from `windows-sys` to avoid a circular dependency. +#![allow(non_snake_case, non_camel_case_types)] + +pub type IMAGE_DIRECTORY_ENTRY = u32; +pub type IMAGE_DLL_CHARACTERISTICS = u16; +pub type IMAGE_FILE_CHARACTERISTICS = u16; +pub type IMAGE_FILE_MACHINE = u16; +pub type IMAGE_OPTIONAL_HEADER_MAGIC = u16; +pub type IMAGE_SECTION_CHARACTERISTICS = u32; +pub type IMAGE_SUBSYSTEM = u16; + +pub const IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR: IMAGE_DIRECTORY_ENTRY = 14u32; +pub const IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE: IMAGE_DLL_CHARACTERISTICS = 64u16; +pub const IMAGE_DLLCHARACTERISTICS_NO_SEH: IMAGE_DLL_CHARACTERISTICS = 1024u16; +pub const IMAGE_DLLCHARACTERISTICS_NX_COMPAT: IMAGE_DLL_CHARACTERISTICS = 256u16; +pub const IMAGE_DOS_SIGNATURE: u16 = 23117u16; +pub const IMAGE_FILE_32BIT_MACHINE: IMAGE_FILE_CHARACTERISTICS = 256u16; +pub const IMAGE_FILE_DLL: IMAGE_FILE_CHARACTERISTICS = 8192u16; +pub const IMAGE_FILE_EXECUTABLE_IMAGE: IMAGE_FILE_CHARACTERISTICS = 2u16; +pub const IMAGE_FILE_MACHINE_I386: IMAGE_FILE_MACHINE = 332u16; +pub const IMAGE_NT_OPTIONAL_HDR32_MAGIC: IMAGE_OPTIONAL_HEADER_MAGIC = 267u16; +pub const IMAGE_NT_OPTIONAL_HDR64_MAGIC: IMAGE_OPTIONAL_HEADER_MAGIC = 523u16; +pub const IMAGE_NT_SIGNATURE: u32 = 17744u32; +pub const IMAGE_SUBSYSTEM_WINDOWS_CUI: IMAGE_SUBSYSTEM = 3u16; + +#[repr(C, packed(2))] +pub struct IMAGE_DOS_HEADER { + pub e_magic: u16, + pub e_cblp: u16, + pub e_cp: u16, + pub e_crlc: u16, + pub e_cparhdr: u16, + pub e_minalloc: u16, + pub e_maxalloc: u16, + pub e_ss: u16, + pub e_sp: u16, + pub e_csum: u16, + pub e_ip: u16, + pub e_cs: u16, + pub e_lfarlc: u16, + pub e_ovno: u16, + pub e_res: [u16; 4], + pub e_oemid: u16, + pub e_oeminfo: u16, + pub e_res2: [u16; 10], + pub e_lfanew: i32, +} + +#[repr(C)] +pub struct IMAGE_FILE_HEADER { + pub Machine: IMAGE_FILE_MACHINE, + pub NumberOfSections: u16, + pub TimeDateStamp: u32, + pub PointerToSymbolTable: u32, + pub NumberOfSymbols: u32, + pub SizeOfOptionalHeader: u16, + pub Characteristics: IMAGE_FILE_CHARACTERISTICS, +} + +#[repr(C)] +pub struct IMAGE_OPTIONAL_HEADER32 { + pub Magic: IMAGE_OPTIONAL_HEADER_MAGIC, + pub MajorLinkerVersion: u8, + pub MinorLinkerVersion: u8, + pub SizeOfCode: u32, + pub SizeOfInitializedData: u32, + pub SizeOfUninitializedData: u32, + pub AddressOfEntryPoint: u32, + pub BaseOfCode: u32, + pub BaseOfData: u32, + pub ImageBase: u32, + pub SectionAlignment: u32, + pub FileAlignment: u32, + pub MajorOperatingSystemVersion: u16, + pub MinorOperatingSystemVersion: u16, + pub MajorImageVersion: u16, + pub MinorImageVersion: u16, + pub MajorSubsystemVersion: u16, + pub MinorSubsystemVersion: u16, + pub Win32VersionValue: u32, + pub SizeOfImage: u32, + pub SizeOfHeaders: u32, + pub CheckSum: u32, + pub Subsystem: IMAGE_SUBSYSTEM, + pub DllCharacteristics: IMAGE_DLL_CHARACTERISTICS, + pub SizeOfStackReserve: u32, + pub SizeOfStackCommit: u32, + pub SizeOfHeapReserve: u32, + pub SizeOfHeapCommit: u32, + pub LoaderFlags: u32, + pub NumberOfRvaAndSizes: u32, + pub DataDirectory: [IMAGE_DATA_DIRECTORY; 16], +} + +#[repr(C, packed(4))] +pub struct IMAGE_OPTIONAL_HEADER64 { + pub Magic: IMAGE_OPTIONAL_HEADER_MAGIC, + pub MajorLinkerVersion: u8, + pub MinorLinkerVersion: u8, + pub SizeOfCode: u32, + pub SizeOfInitializedData: u32, + pub SizeOfUninitializedData: u32, + pub AddressOfEntryPoint: u32, + pub BaseOfCode: u32, + pub ImageBase: u64, + pub SectionAlignment: u32, + pub FileAlignment: u32, + pub MajorOperatingSystemVersion: u16, + pub MinorOperatingSystemVersion: u16, + pub MajorImageVersion: u16, + pub MinorImageVersion: u16, + pub MajorSubsystemVersion: u16, + pub MinorSubsystemVersion: u16, + pub Win32VersionValue: u32, + pub SizeOfImage: u32, + pub SizeOfHeaders: u32, + pub CheckSum: u32, + pub Subsystem: IMAGE_SUBSYSTEM, + pub DllCharacteristics: IMAGE_DLL_CHARACTERISTICS, + pub SizeOfStackReserve: u64, + pub SizeOfStackCommit: u64, + pub SizeOfHeapReserve: u64, + pub SizeOfHeapCommit: u64, + pub LoaderFlags: u32, + pub NumberOfRvaAndSizes: u32, + pub DataDirectory: [IMAGE_DATA_DIRECTORY; 16], +} + +#[repr(C)] +pub struct IMAGE_SECTION_HEADER { + pub Name: [u8; 8], + pub Misc: IMAGE_SECTION_HEADER_0, + pub VirtualAddress: u32, + pub SizeOfRawData: u32, + pub PointerToRawData: u32, + pub PointerToRelocations: u32, + pub PointerToLinenumbers: u32, + pub NumberOfRelocations: u16, + pub NumberOfLinenumbers: u16, + pub Characteristics: IMAGE_SECTION_CHARACTERISTICS, +} + +#[repr(C)] +pub union IMAGE_SECTION_HEADER_0 { + pub PhysicalAddress: u32, + pub VirtualSize: u32, +} + +#[repr(C)] +pub struct IMAGE_DATA_DIRECTORY { + pub VirtualAddress: u32, + pub Size: u32, +} + +#[repr(C)] +pub struct IMAGE_COR20_HEADER { + pub cb: u32, + pub MajorRuntimeVersion: u16, + pub MinorRuntimeVersion: u16, + pub MetaData: IMAGE_DATA_DIRECTORY, + pub Flags: u32, + pub Anonymous: IMAGE_COR20_HEADER_0, + pub Resources: IMAGE_DATA_DIRECTORY, + pub StrongNameSignature: IMAGE_DATA_DIRECTORY, + pub CodeManagerTable: IMAGE_DATA_DIRECTORY, + pub VTableFixups: IMAGE_DATA_DIRECTORY, + pub ExportAddressTableJumps: IMAGE_DATA_DIRECTORY, + pub ManagedNativeHeader: IMAGE_DATA_DIRECTORY, +} + +#[repr(C)] +pub union IMAGE_COR20_HEADER_0 { + pub EntryPointToken: u32, + pub EntryPointRVA: u32, +} diff --git a/vendor/windows-metadata/src/imp.rs b/vendor/windows-metadata/src/imp.rs new file mode 100644 index 000000000..27a735a45 --- /dev/null +++ b/vendor/windows-metadata/src/imp.rs @@ -0,0 +1,43 @@ +#[repr(C)] +#[derive(Default)] +pub struct METADATA_HEADER { + pub signature: u32, + pub major_version: u16, + pub minor_version: u16, + pub reserved: u32, + pub length: u32, + pub version: [u8; 20], + pub flags: u16, + pub streams: u16, +} + +pub const METADATA_SIGNATURE: u32 = 0x424A_5342; + +/// A coded index (see codes.rs) is a table index that may refer to different tables. The size of the column in memory +/// must therefore be large enough to hold an index for a row in the largest possible table. This function determines +/// this size for the given winmd file. +pub fn coded_index_size(tables: &[usize]) -> usize { + fn small(row_count: usize, bits: u8) -> bool { + (row_count as u64) < (1u64 << (16 - bits)) + } + + fn bits_needed(value: usize) -> u8 { + let mut value = value - 1; + let mut bits: u8 = 1; + while { + value >>= 1; + value != 0 + } { + bits += 1; + } + bits + } + + let bits_needed = bits_needed(tables.len()); + + if tables.iter().all(|table| small(*table, bits_needed)) { + 2 + } else { + 4 + } +} diff --git a/vendor/windows-metadata/src/lib.rs b/vendor/windows-metadata/src/lib.rs new file mode 100644 index 000000000..0e832603c --- /dev/null +++ b/vendor/windows-metadata/src/lib.rs @@ -0,0 +1,57 @@ +#![allow(dead_code)] + +use std::collections::*; +mod attributes; +mod bindings; +mod imp; +pub mod reader; +pub mod writer; + +pub use attributes::*; +use bindings::*; +use imp::*; +use std::io::*; +use std::mem::*; +use std::ptr::*; + +macro_rules! flags { + ($name:ident, $size:ty) => { + #[derive(Default, Copy, Clone, PartialEq, Eq)] + pub struct $name(pub $size); + impl $name { + pub fn contains(&self, contains: Self) -> bool { + *self & contains == contains + } + } + impl std::ops::BitOr for $name { + type Output = Self; + fn bitor(self, other: Self) -> Self { + Self(self.0 | other.0) + } + } + impl std::ops::BitAnd for $name { + type Output = Self; + fn bitand(self, other: Self) -> Self { + Self(self.0 & other.0) + } + } + impl std::ops::BitOrAssign for $name { + fn bitor_assign(&mut self, other: Self) { + self.0.bitor_assign(other.0) + } + } + impl std::ops::BitAndAssign for $name { + fn bitand_assign(&mut self, other: Self) { + self.0.bitand_assign(other.0) + } + } + impl std::ops::Not for $name { + type Output = Self; + fn not(self) -> Self { + Self(self.0.not()) + } + } + }; +} + +pub(crate) use flags; diff --git a/vendor/windows-metadata/src/reader/blob.rs b/vendor/windows-metadata/src/reader/blob.rs new file mode 100644 index 000000000..b7d4880d0 --- /dev/null +++ b/vendor/windows-metadata/src/reader/blob.rs @@ -0,0 +1,147 @@ +use super::*; +use std::convert::*; + +pub struct Blob<'a> { + pub file: usize, + pub slice: &'a [u8], +} + +impl<'a> std::ops::Deref for Blob<'a> { + type Target = [u8]; + + fn deref(&self) -> &[u8] { + self.slice + } +} + +impl<'a> Blob<'a> { + pub fn new(file: usize, slice: &'a [u8]) -> Self { + Self { file, slice } + } + pub fn peek_usize(&self) -> (usize, usize) { + if self[0] & 0x80 == 0 { + (self[0] as usize, 1) + } else if self[0] & 0xC0 == 0x80 { + ((((self[0] & 0x3F) as usize) << 8) | self[1] as usize, 2) + } else { + ((((self[0] & 0x1F) as usize) << 24) | (self[1] as usize) << 16 | (self[2] as usize) << 8 | self[3] as usize, 4) + } + } + pub fn read_usize(&mut self) -> usize { + let (value, offset) = self.peek_usize(); + self.offset(offset); + value + } + pub fn read_expected(&mut self, expected: usize) -> bool { + let (value, offset) = self.peek_usize(); + if value == expected { + self.offset(offset); + true + } else { + false + } + } + pub fn read_modifiers(&mut self) -> Vec<TypeDefOrRef> { + let mut mods = vec![]; + loop { + let (value, offset) = self.peek_usize(); + if value != 32 && value != 31 { + break; + } else { + self.offset(offset); + mods.push(TypeDefOrRef::decode(self.file, self.read_usize())) + } + } + mods + } + pub fn read_str(&mut self) -> &str { + let len = self.read_usize(); + let value = unsafe { std::str::from_utf8_unchecked(&self.slice[..len]) }; + self.offset(len); + value + } + pub fn read_string(self) -> String { + let slice = self.slice; + if slice.as_ptr().align_offset(std::mem::align_of::<u16>()) > 0 { + let slice = slice.chunks_exact(2).take(slice.len() / 2).map(|chunk| u16::from_le_bytes(chunk.try_into().unwrap())).collect::<Vec<u16>>(); + String::from_utf16_lossy(&slice) + } else { + let slice = unsafe { std::slice::from_raw_parts(slice.as_ptr() as *const u16, slice.len() / 2) }; + String::from_utf16_lossy(slice) + } + } + pub fn read_bool(&mut self) -> bool { + // A bool is specified as "a single byte with value 0 (false) or 1 (true)". + match self.read_u8() { + 0 => false, + 1 => true, + _ => panic!("Illegal bool value"), + } + } + pub fn read_i8(&mut self) -> i8 { + let value = i8::from_le_bytes(self[..1].try_into().unwrap()); + self.offset(1); + value + } + pub fn read_u8(&mut self) -> u8 { + let value = u8::from_le_bytes(self[..1].try_into().unwrap()); + self.offset(1); + value + } + pub fn read_i16(&mut self) -> i16 { + let value = i16::from_le_bytes(self[..2].try_into().unwrap()); + self.offset(2); + value + } + pub fn read_u16(&mut self) -> u16 { + let value = u16::from_le_bytes(self[..2].try_into().unwrap()); + self.offset(2); + value + } + pub fn read_i32(&mut self) -> i32 { + let value = i32::from_le_bytes(self[..4].try_into().unwrap()); + self.offset(4); + value + } + pub fn read_u32(&mut self) -> u32 { + let value = u32::from_le_bytes(self[..4].try_into().unwrap()); + self.offset(4); + value + } + pub fn read_i64(&mut self) -> i64 { + let value = i64::from_le_bytes(self[..8].try_into().unwrap()); + self.offset(8); + value + } + pub fn read_u64(&mut self) -> u64 { + let value = u64::from_le_bytes(self[..8].try_into().unwrap()); + self.offset(8); + value + } + pub fn read_f32(&mut self) -> f32 { + let value = f32::from_le_bytes(self[..4].try_into().unwrap()); + self.offset(4); + value + } + pub fn read_f64(&mut self) -> f64 { + let value = f64::from_le_bytes(self[..8].try_into().unwrap()); + self.offset(8); + value + } + pub fn read_integer(&mut self, ty: Type) -> Integer { + match ty { + Type::I8 => Integer::I8(self.read_i8()), + Type::U8 => Integer::U8(self.read_u8()), + Type::I16 => Integer::I16(self.read_i16()), + Type::U16 => Integer::U16(self.read_u16()), + Type::I32 => Integer::I32(self.read_i32()), + Type::U32 => Integer::U32(self.read_u32()), + Type::I64 => Integer::I64(self.read_i64()), + Type::U64 => Integer::U64(self.read_u64()), + _ => panic!("Type is not an integer"), + } + } + fn offset(&mut self, offset: usize) { + self.slice = &self.slice[offset..]; + } +} diff --git a/vendor/windows-metadata/src/reader/codes.rs b/vendor/windows-metadata/src/reader/codes.rs new file mode 100644 index 000000000..a6cfa4885 --- /dev/null +++ b/vendor/windows-metadata/src/reader/codes.rs @@ -0,0 +1,122 @@ +use super::*; + +pub trait Decode { + fn decode(file: usize, code: usize) -> Self; +} + +pub enum AttributeType { + MemberRef(MemberRef), +} + +impl Decode for AttributeType { + fn decode(file: usize, code: usize) -> Self { + let (kind, row) = (code & ((1 << 3) - 1), (code >> 3) - 1); + match kind { + 3 => Self::MemberRef(MemberRef(Row::new(row, TABLE_MEMBERREF, file))), + _ => unimplemented!(), + } + } +} + +pub enum HasAttribute { + MethodDef(MethodDef), + Field(Field), + TypeRef(TypeRef), + TypeDef(TypeDef), + Param(Param), + InterfaceImpl(InterfaceImpl), + MemberRef(MemberRef), + TypeSpec(TypeSpec), + GenericParam(GenericParam), +} + +impl HasAttribute { + pub fn encode(&self) -> usize { + (match self { + Self::MethodDef(row) => (row.0.row + 1) << 5, + Self::Field(row) => ((row.0.row + 1) << 5) | 1, + Self::TypeRef(row) => ((row.0.row + 1) << 5) | 2, + Self::TypeDef(row) => ((row.0.row + 1) << 5) | 3, + Self::Param(row) => ((row.0.row + 1) << 5) | 4, + Self::InterfaceImpl(row) => ((row.0.row + 1) << 5) | 5, + Self::MemberRef(row) => ((row.0.row + 1) << 5) | 6, + Self::TypeSpec(row) => ((row.0.row + 1) << 5) | 13, + Self::GenericParam(row) => ((row.0.row + 1) << 5) | 19, + }) as _ + } +} + +#[derive(Clone)] +pub enum HasConstant { + Field(Field), +} + +impl HasConstant { + pub fn encode(&self) -> usize { + (match self { + Self::Field(row) => (row.0.row + 1) << 2, + }) as _ + } +} + +#[derive(Clone)] +pub enum MemberForwarded { + MethodDef(MethodDef), +} + +impl MemberForwarded { + pub fn encode(&self) -> usize { + (match self { + Self::MethodDef(value) => ((value.0.row + 1) << 1) | 1, + }) as _ + } +} + +pub enum MemberRefParent { + TypeRef(TypeRef), +} + +impl Decode for MemberRefParent { + fn decode(file: usize, code: usize) -> Self { + let (kind, row) = (code & ((1 << 3) - 1), (code >> 3) - 1); + match kind { + 1 => Self::TypeRef(TypeRef(Row::new(row, TABLE_TYPEREF, file))), + _ => unimplemented!(), + } + } +} + +#[derive(Copy, Clone)] +pub enum TypeDefOrRef { + None, + TypeDef(TypeDef), + TypeRef(TypeRef), + TypeSpec(TypeSpec), +} + +impl Decode for TypeDefOrRef { + fn decode(file: usize, code: usize) -> Self { + if code == 0 { + return Self::None; + } + let (kind, row) = (code & ((1 << 2) - 1), (code >> 2) - 1); + match kind { + 0 => Self::TypeDef(TypeDef(Row::new(row, TABLE_TYPEDEF, file))), + 1 => Self::TypeRef(TypeRef(Row::new(row, TABLE_TYPEREF, file))), + 2 => Self::TypeSpec(TypeSpec(Row::new(row, TABLE_TYPESPEC, file))), + _ => unimplemented!(), + } + } +} + +pub enum TypeOrMethodDef { + TypeDef(TypeDef), +} + +impl TypeOrMethodDef { + pub fn encode(&self) -> usize { + (match self { + Self::TypeDef(value) => (value.0.row + 1) << 1, + }) as _ + } +} diff --git a/vendor/windows-metadata/src/reader/file.rs b/vendor/windows-metadata/src/reader/file.rs new file mode 100644 index 000000000..b00b4fc52 --- /dev/null +++ b/vendor/windows-metadata/src/reader/file.rs @@ -0,0 +1,537 @@ +use super::*; +use std::cmp::*; + +#[derive(Default)] +pub struct File { + bytes: Vec<u8>, + strings: usize, + blobs: usize, + pub(crate) tables: [Table; TABLE_LEN], +} + +#[derive(Default)] +pub struct Table { + pub offset: usize, + pub len: usize, + pub width: usize, + pub columns: [Column; 6], +} + +#[derive(Default)] +pub struct Column { + pub offset: usize, + pub width: usize, +} + +pub const TABLE_CONSTANT: usize = 0; +pub const TABLE_CUSTOMATTRIBUTE: usize = 1; +pub const TABLE_FIELD: usize = 2; +pub const TABLE_GENERICPARAM: usize = 3; +pub const TABLE_INTERFACEIMPL: usize = 4; +pub const TABLE_MEMBERREF: usize = 5; +pub const TABLE_METHODDEF: usize = 6; +pub const TABLE_PARAM: usize = 7; +pub const TABLE_TYPEDEF: usize = 8; +pub const TABLE_TYPEREF: usize = 9; +pub const TABLE_TYPESPEC: usize = 10; +pub const TABLE_IMPLMAP: usize = 11; +pub const TABLE_MODULEREF: usize = 12; +pub const TABLE_NESTEDCLASS: usize = 13; +pub const TABLE_MODULE: usize = 14; +pub const TABLE_ASSEMBLYREF: usize = 15; +pub const TABLE_CLASSLAYOUT: usize = 16; +pub const TABLE_LEN: usize = 17; + +fn error(message: &str) -> Error { + Error::new(ErrorKind::Other, message) +} + +fn error_invalid_winmd() -> Error { + error("File is not a valid `winmd` file") +} + +impl File { + pub fn with_default(paths: &[&str]) -> Result<Vec<Self>> { + let mut files = vec![Self::from_buffer(std::include_bytes!("../../default/Windows.winmd").to_vec())?, Self::from_buffer(std::include_bytes!("../../default/Windows.Wdk.winmd").to_vec())?, Self::from_buffer(std::include_bytes!("../../default/Windows.Win32.winmd").to_vec())?]; + + for path in paths { + files.push(Self::new(std::path::Path::new(path))?); + } + + Ok(files) + } + + pub fn new<P: AsRef<std::path::Path>>(path: P) -> Result<Self> { + Self::from_buffer(std::fs::read(&path)?) + } + + pub fn from_buffer(bytes: Vec<u8>) -> Result<Self> { + let mut result = File { bytes, ..Default::default() }; + + let dos = result.bytes.view_as::<IMAGE_DOS_HEADER>(0); + + if dos.e_magic != IMAGE_DOS_SIGNATURE as _ || result.bytes.copy_as::<u32>(dos.e_lfanew as _) != IMAGE_NT_SIGNATURE { + return Err(error_invalid_winmd()); + } + + let file_offset = dos.e_lfanew as usize + size_of::<u32>(); + let file = result.bytes.view_as::<IMAGE_FILE_HEADER>(file_offset); + + let optional_offset = file_offset + size_of::<IMAGE_FILE_HEADER>(); + + let (com_virtual_address, sections) = match result.bytes.copy_as::<u16>(optional_offset) { + IMAGE_NT_OPTIONAL_HDR32_MAGIC => { + let optional = result.bytes.view_as::<IMAGE_OPTIONAL_HEADER32>(optional_offset); + (optional.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR as usize].VirtualAddress, result.bytes.view_as_slice_of::<IMAGE_SECTION_HEADER>(optional_offset + size_of::<IMAGE_OPTIONAL_HEADER32>(), file.NumberOfSections as usize)) + } + IMAGE_NT_OPTIONAL_HDR64_MAGIC => { + let optional = result.bytes.view_as::<IMAGE_OPTIONAL_HEADER64>(optional_offset); + (optional.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR as usize].VirtualAddress, result.bytes.view_as_slice_of::<IMAGE_SECTION_HEADER>(optional_offset + size_of::<IMAGE_OPTIONAL_HEADER64>(), file.NumberOfSections as usize)) + } + _ => return Err(error_invalid_winmd()), + }; + + let clr = result.bytes.view_as::<IMAGE_COR20_HEADER>(offset_from_rva(section_from_rva(sections, com_virtual_address)?, com_virtual_address) as _); + + if clr.cb != size_of::<IMAGE_COR20_HEADER>() as _ { + return Err(error_invalid_winmd()); + } + + let metadata_offset = offset_from_rva(section_from_rva(sections, clr.MetaData.VirtualAddress)?, clr.MetaData.VirtualAddress); + let metadata = result.bytes.view_as::<METADATA_HEADER>(metadata_offset as _); + + if metadata.signature != METADATA_SIGNATURE { + return Err(error_invalid_winmd()); + } + + // The METADATA_HEADER struct is not a fixed size so have to offset a little more carefully. + let mut view = metadata_offset + metadata.length as usize + 20; + let mut tables_data: (usize, usize) = (0, 0); + + for _ in 0..result.bytes.copy_as::<u16>(metadata_offset + metadata.length as usize + 18) { + let stream_offset = result.bytes.copy_as::<u32>(view) as usize; + let stream_len = result.bytes.copy_as::<u32>(view + 4) as usize; + let stream_name = result.bytes.view_as_str(view + 8); + match stream_name { + b"#Strings" => result.strings = metadata_offset + stream_offset, + b"#Blob" => result.blobs = metadata_offset + stream_offset, + b"#~" => tables_data = (metadata_offset + stream_offset, stream_len), + b"#GUID" => {} + b"#US" => {} + _ => unimplemented!(), + } + let mut padding = 4 - stream_name.len() % 4; + if padding == 0 { + padding = 4; + } + view += 8 + stream_name.len() + padding; + } + + let heap_sizes = result.bytes.copy_as::<u8>(tables_data.0 + 6); + let string_index_size = if (heap_sizes & 1) == 1 { 4 } else { 2 }; + let guid_index_size = if (heap_sizes >> 1 & 1) == 1 { 4 } else { 2 }; + let blob_index_size = if (heap_sizes >> 2 & 1) == 1 { 4 } else { 2 }; + let valid_bits = result.bytes.copy_as::<u64>(tables_data.0 + 8); + view = tables_data.0 + 24; + + // These tables are unused by the reader, but needed temporarily to calculate sizes and offsets for subsequent tables. + let unused_empty = Table::default(); + let mut unused_assembly = Table::default(); + let mut unused_assembly_os = Table::default(); + let mut unused_assembly_processor = Table::default(); + let mut unused_assembly_ref_os = Table::default(); + let mut unused_assembly_ref_processor = Table::default(); + let mut unused_decl_security = Table::default(); + let mut unused_event = Table::default(); + let mut unused_event_map = Table::default(); + let mut unused_exported_type = Table::default(); + let mut unused_field_layout = Table::default(); + let mut unused_field_marshal = Table::default(); + let mut unused_field_rva = Table::default(); + let mut unused_file = Table::default(); + let mut unused_generic_param_constraint = Table::default(); + let mut unused_manifest_resource = Table::default(); + let mut unused_method_impl = Table::default(); + let mut unused_method_semantics = Table::default(); + let mut unused_method_spec = Table::default(); + let mut unused_property = Table::default(); + let mut unused_property_map = Table::default(); + let mut unused_standalone_sig = Table::default(); + + for i in 0..64 { + if (valid_bits >> i & 1) == 0 { + continue; + } + + let len = result.bytes.copy_as::<u32>(view) as _; + view += 4; + + match i { + 0x00 => result.tables[TABLE_MODULE].len = len, + 0x01 => result.tables[TABLE_TYPEREF].len = len, + 0x02 => result.tables[TABLE_TYPEDEF].len = len, + 0x04 => result.tables[TABLE_FIELD].len = len, + 0x06 => result.tables[TABLE_METHODDEF].len = len, + 0x08 => result.tables[TABLE_PARAM].len = len, + 0x09 => result.tables[TABLE_INTERFACEIMPL].len = len, + 0x0a => result.tables[TABLE_MEMBERREF].len = len, + 0x0b => result.tables[TABLE_CONSTANT].len = len, + 0x0c => result.tables[TABLE_CUSTOMATTRIBUTE].len = len, + 0x0d => unused_field_marshal.len = len, + 0x0e => unused_decl_security.len = len, + 0x0f => result.tables[TABLE_CLASSLAYOUT].len = len, + 0x10 => unused_field_layout.len = len, + 0x11 => unused_standalone_sig.len = len, + 0x12 => unused_event_map.len = len, + 0x14 => unused_event.len = len, + 0x15 => unused_property_map.len = len, + 0x17 => unused_property.len = len, + 0x18 => unused_method_semantics.len = len, + 0x19 => unused_method_impl.len = len, + 0x1a => result.tables[TABLE_MODULEREF].len = len, + 0x1b => result.tables[TABLE_TYPESPEC].len = len, + 0x1c => result.tables[TABLE_IMPLMAP].len = len, + 0x1d => unused_field_rva.len = len, + 0x20 => unused_assembly.len = len, + 0x21 => unused_assembly_processor.len = len, + 0x22 => unused_assembly_os.len = len, + 0x23 => result.tables[TABLE_ASSEMBLYREF].len = len, + 0x24 => unused_assembly_ref_processor.len = len, + 0x25 => unused_assembly_ref_os.len = len, + 0x26 => unused_file.len = len, + 0x27 => unused_exported_type.len = len, + 0x28 => unused_manifest_resource.len = len, + 0x29 => result.tables[TABLE_NESTEDCLASS].len = len, + 0x2a => result.tables[TABLE_GENERICPARAM].len = len, + 0x2b => unused_method_spec.len = len, + 0x2c => unused_generic_param_constraint.len = len, + _ => unreachable!(), + }; + } + + let tables = &result.tables; + let type_def_or_ref = coded_index_size(&[tables[TABLE_TYPEDEF].len, tables[TABLE_TYPEREF].len, tables[TABLE_TYPESPEC].len]); + let has_constant = coded_index_size(&[tables[TABLE_FIELD].len, tables[TABLE_PARAM].len, unused_property.len]); + let has_field_marshal = coded_index_size(&[tables[TABLE_FIELD].len, tables[TABLE_PARAM].len]); + let has_decl_security = coded_index_size(&[tables[TABLE_TYPEDEF].len, tables[TABLE_METHODDEF].len, unused_assembly.len]); + let member_ref_parent = coded_index_size(&[tables[TABLE_TYPEDEF].len, tables[TABLE_TYPEREF].len, tables[TABLE_MODULEREF].len, tables[TABLE_METHODDEF].len, tables[TABLE_TYPESPEC].len]); + let has_semantics = coded_index_size(&[unused_event.len, unused_property.len]); + let method_def_or_ref = coded_index_size(&[tables[TABLE_METHODDEF].len, tables[TABLE_MEMBERREF].len]); + let member_forwarded = coded_index_size(&[tables[TABLE_FIELD].len, tables[TABLE_METHODDEF].len]); + let implementation = coded_index_size(&[unused_file.len, tables[TABLE_ASSEMBLYREF].len, unused_exported_type.len]); + let custom_attribute_type = coded_index_size(&[tables[TABLE_METHODDEF].len, tables[TABLE_MEMBERREF].len, unused_empty.len, unused_empty.len, unused_empty.len]); + let resolution_scope = coded_index_size(&[tables[TABLE_MODULE].len, tables[TABLE_MODULEREF].len, tables[TABLE_ASSEMBLYREF].len, tables[TABLE_TYPEREF].len]); + let type_or_method_def = coded_index_size(&[tables[TABLE_TYPEDEF].len, tables[TABLE_METHODDEF].len]); + + let has_custom_attribute = coded_index_size(&[ + tables[TABLE_METHODDEF].len, + tables[TABLE_FIELD].len, + tables[TABLE_TYPEREF].len, + tables[TABLE_TYPEDEF].len, + tables[TABLE_PARAM].len, + tables[TABLE_INTERFACEIMPL].len, + tables[TABLE_MEMBERREF].len, + tables[TABLE_MODULE].len, + unused_property.len, + unused_event.len, + unused_standalone_sig.len, + tables[TABLE_MODULEREF].len, + tables[TABLE_TYPESPEC].len, + unused_assembly.len, + tables[TABLE_ASSEMBLYREF].len, + unused_file.len, + unused_exported_type.len, + unused_manifest_resource.len, + tables[TABLE_GENERICPARAM].len, + unused_generic_param_constraint.len, + unused_method_spec.len, + ]); + + unused_assembly.set_columns(4, 8, 4, blob_index_size, string_index_size, string_index_size); + unused_assembly_os.set_columns(4, 4, 4, 0, 0, 0); + unused_assembly_processor.set_columns(4, 0, 0, 0, 0, 0); + result.tables[TABLE_ASSEMBLYREF].set_columns(8, 4, blob_index_size, string_index_size, string_index_size, blob_index_size); + unused_assembly_ref_os.set_columns(4, 4, 4, result.tables[TABLE_ASSEMBLYREF].index_width(), 0, 0); + unused_assembly_ref_processor.set_columns(4, result.tables[TABLE_ASSEMBLYREF].index_width(), 0, 0, 0, 0); + result.tables[TABLE_CLASSLAYOUT].set_columns(2, 4, result.tables[TABLE_TYPEDEF].index_width(), 0, 0, 0); + result.tables[TABLE_CONSTANT].set_columns(2, has_constant, blob_index_size, 0, 0, 0); + result.tables[TABLE_CUSTOMATTRIBUTE].set_columns(has_custom_attribute, custom_attribute_type, blob_index_size, 0, 0, 0); + unused_decl_security.set_columns(2, has_decl_security, blob_index_size, 0, 0, 0); + unused_event_map.set_columns(result.tables[TABLE_TYPEDEF].index_width(), unused_event.index_width(), 0, 0, 0, 0); + unused_event.set_columns(2, string_index_size, type_def_or_ref, 0, 0, 0); + unused_exported_type.set_columns(4, 4, string_index_size, string_index_size, implementation, 0); + result.tables[TABLE_FIELD].set_columns(2, string_index_size, blob_index_size, 0, 0, 0); + unused_field_layout.set_columns(4, result.tables[TABLE_FIELD].index_width(), 0, 0, 0, 0); + unused_field_marshal.set_columns(has_field_marshal, blob_index_size, 0, 0, 0, 0); + unused_field_rva.set_columns(4, result.tables[TABLE_FIELD].index_width(), 0, 0, 0, 0); + unused_file.set_columns(4, string_index_size, blob_index_size, 0, 0, 0); + result.tables[TABLE_GENERICPARAM].set_columns(2, 2, type_or_method_def, string_index_size, 0, 0); + unused_generic_param_constraint.set_columns(result.tables[TABLE_GENERICPARAM].index_width(), type_def_or_ref, 0, 0, 0, 0); + result.tables[TABLE_IMPLMAP].set_columns(2, member_forwarded, string_index_size, result.tables[TABLE_MODULEREF].index_width(), 0, 0); + result.tables[TABLE_INTERFACEIMPL].set_columns(result.tables[TABLE_TYPEDEF].index_width(), type_def_or_ref, 0, 0, 0, 0); + unused_manifest_resource.set_columns(4, 4, string_index_size, implementation, 0, 0); + result.tables[TABLE_MEMBERREF].set_columns(member_ref_parent, string_index_size, blob_index_size, 0, 0, 0); + result.tables[TABLE_METHODDEF].set_columns(4, 2, 2, string_index_size, blob_index_size, result.tables[TABLE_PARAM].index_width()); + unused_method_impl.set_columns(result.tables[TABLE_TYPEDEF].index_width(), method_def_or_ref, method_def_or_ref, 0, 0, 0); + unused_method_semantics.set_columns(2, result.tables[TABLE_METHODDEF].index_width(), has_semantics, 0, 0, 0); + unused_method_spec.set_columns(method_def_or_ref, blob_index_size, 0, 0, 0, 0); + result.tables[TABLE_MODULE].set_columns(2, string_index_size, guid_index_size, guid_index_size, guid_index_size, 0); + result.tables[TABLE_MODULEREF].set_columns(string_index_size, 0, 0, 0, 0, 0); + result.tables[TABLE_NESTEDCLASS].set_columns(result.tables[TABLE_TYPEDEF].index_width(), result.tables[TABLE_TYPEDEF].index_width(), 0, 0, 0, 0); + result.tables[TABLE_PARAM].set_columns(2, 2, string_index_size, 0, 0, 0); + unused_property.set_columns(2, string_index_size, blob_index_size, 0, 0, 0); + unused_property_map.set_columns(result.tables[TABLE_TYPEDEF].index_width(), unused_property.index_width(), 0, 0, 0, 0); + unused_standalone_sig.set_columns(blob_index_size, 0, 0, 0, 0, 0); + result.tables[TABLE_TYPEDEF].set_columns(4, string_index_size, string_index_size, type_def_or_ref, result.tables[TABLE_FIELD].index_width(), result.tables[TABLE_METHODDEF].index_width()); + result.tables[TABLE_TYPEREF].set_columns(resolution_scope, string_index_size, string_index_size, 0, 0, 0); + result.tables[TABLE_TYPESPEC].set_columns(blob_index_size, 0, 0, 0, 0, 0); + + result.tables[TABLE_MODULE].set_data(&mut view); + result.tables[TABLE_TYPEREF].set_data(&mut view); + result.tables[TABLE_TYPEDEF].set_data(&mut view); + result.tables[TABLE_FIELD].set_data(&mut view); + result.tables[TABLE_METHODDEF].set_data(&mut view); + result.tables[TABLE_PARAM].set_data(&mut view); + result.tables[TABLE_INTERFACEIMPL].set_data(&mut view); + result.tables[TABLE_MEMBERREF].set_data(&mut view); + result.tables[TABLE_CONSTANT].set_data(&mut view); + result.tables[TABLE_CUSTOMATTRIBUTE].set_data(&mut view); + unused_field_marshal.set_data(&mut view); + unused_decl_security.set_data(&mut view); + result.tables[TABLE_CLASSLAYOUT].set_data(&mut view); + unused_field_layout.set_data(&mut view); + unused_standalone_sig.set_data(&mut view); + unused_event_map.set_data(&mut view); + unused_event.set_data(&mut view); + unused_property_map.set_data(&mut view); + unused_property.set_data(&mut view); + unused_method_semantics.set_data(&mut view); + unused_method_impl.set_data(&mut view); + result.tables[TABLE_MODULEREF].set_data(&mut view); + result.tables[TABLE_TYPESPEC].set_data(&mut view); + result.tables[TABLE_IMPLMAP].set_data(&mut view); + unused_field_rva.set_data(&mut view); + unused_assembly.set_data(&mut view); + unused_assembly_processor.set_data(&mut view); + unused_assembly_os.set_data(&mut view); + result.tables[TABLE_ASSEMBLYREF].set_data(&mut view); + unused_assembly_ref_processor.set_data(&mut view); + unused_assembly_ref_os.set_data(&mut view); + unused_file.set_data(&mut view); + unused_exported_type.set_data(&mut view); + unused_manifest_resource.set_data(&mut view); + result.tables[TABLE_NESTEDCLASS].set_data(&mut view); + result.tables[TABLE_GENERICPARAM].set_data(&mut view); + + Ok(result) + } + + pub fn usize(&self, row: usize, table: usize, column: usize) -> usize { + let table = &self.tables[table]; + let column = &table.columns[column]; + let offset = table.offset + row * table.width + column.offset; + match column.width { + 1 => self.bytes.copy_as::<u8>(offset) as _, + 2 => self.bytes.copy_as::<u16>(offset) as _, + 4 => self.bytes.copy_as::<u32>(offset) as _, + _ => self.bytes.copy_as::<u64>(offset) as _, + } + } + + /// Returns the string from the `#Strings` stream as referenced by + /// the (table, row, column) triple. + /// + /// # Panics + /// + /// * When any element of the (table, row, column) triple is invalid. + /// * When the offset in the string table is out of bounds. + /// * When no null terminator can be found in the string table. + /// * When the null-terminated string is not valid utf-8. + pub fn str(&self, row: usize, table: usize, column: usize) -> &str { + let offset = self.strings + self.usize(row, table, column); + + let bytes = &self.bytes[offset..]; + let nul_pos = bytes.iter().position(|&c| c == 0).expect("expected null-terminated C-string"); + std::str::from_utf8(&bytes[..nul_pos]).expect("expected valid utf-8 C-string") + } + + pub fn blob(&self, row: usize, table: usize, column: usize) -> &[u8] { + let offset = self.blobs + self.usize(row, table, column); + let initial_byte = self.bytes[offset]; + let (blob_size, blob_size_bytes) = match initial_byte >> 5 { + 0..=3 => (initial_byte & 0x7f, 1), + 4..=5 => (initial_byte & 0x3f, 2), + 6 => (initial_byte & 0x1f, 4), + _ => unimplemented!(), + }; + let mut blob_size = blob_size as usize; + for byte in &self.bytes[offset + 1..offset + blob_size_bytes] { + blob_size = blob_size.checked_shl(8).unwrap_or(0) + (*byte as usize); + } + let offset = offset + blob_size_bytes; + &self.bytes[offset..offset + blob_size] + } + + pub fn equal_range(&self, table: usize, column: usize, value: usize) -> (usize, usize) { + let mut first = 0; + let mut last = self.tables[table].len; + let mut count = last; + loop { + if count == 0 { + last = first; + break; + } + let count2 = count / 2; + let middle = first + count2; + let middle_value = self.usize(middle, table, column); + match middle_value.cmp(&value) { + Ordering::Less => { + first = middle + 1; + count -= count2 + 1; + } + Ordering::Greater => count = count2, + Ordering::Equal => { + let first2 = self.lower_bound_of(table, first, middle, column, value); + first += count; + last = self.upper_bound_of(table, middle + 1, first, column, value); + first = first2; + break; + } + } + } + (first, last) + } + + fn lower_bound_of(&self, table: usize, mut first: usize, last: usize, column: usize, value: usize) -> usize { + let mut count = last - first; + while count > 0 { + let count2 = count / 2; + let middle = first + count2; + if self.usize(middle, table, column) < value { + first = middle + 1; + count -= count2 + 1; + } else { + count = count2; + } + } + first + } + + pub fn upper_bound_of(&self, table: usize, mut first: usize, last: usize, column: usize, value: usize) -> usize { + let mut count = last - first; + while count > 0 { + let count2 = count / 2; + let middle = first + count2; + if value < self.usize(middle, table, column) { + count = count2 + } else { + first = middle + 1; + count -= count2 + 1; + } + } + first + } +} + +impl Table { + fn index_width(&self) -> usize { + if self.len < (1 << 16) { + 2 + } else { + 4 + } + } + + fn set_columns(&mut self, a: usize, b: usize, c: usize, d: usize, e: usize, f: usize) { + self.width = a + b + c + d + e + f; + self.columns[0] = Column::new(0, a); + if b != 0 { + self.columns[1] = Column::new(a, b); + } + if c != 0 { + self.columns[2] = Column::new(a + b, c); + } + if d != 0 { + self.columns[3] = Column::new(a + b + c, d); + } + if e != 0 { + self.columns[4] = Column::new(a + b + c + d, e); + } + if f != 0 { + self.columns[5] = Column::new(a + b + c + d + e, f); + } + } + + fn set_data(&mut self, offset: &mut usize) { + if self.len != 0 { + let next = *offset + self.len * self.width; + self.offset = *offset; + *offset = next; + } + } +} + +impl Column { + fn new(offset: usize, width: usize) -> Self { + Self { offset, width } + } +} + +macro_rules! assert_proper_length { + ($self:expr, $t:ty, $offset:expr, $size:expr) => { + let enough_room = $offset + $size <= $self.len(); + assert!(enough_room, "Invalid file: not enough bytes at offset {} to represent T", $offset); + }; +} + +macro_rules! assert_proper_length_and_alignment { + ($self:expr, $t:ty, $offset:expr, $size:expr) => {{ + assert_proper_length!($self, $t, $offset, $size); + let ptr = &$self[$offset] as *const u8 as *const $t; + let properly_aligned = ptr.align_offset(align_of::<$t>()) == 0; + assert!(properly_aligned, "Invalid file: offset {} is not properly aligned to T", $offset); + ptr + }}; +} + +trait View { + fn view_as<T>(&self, offset: usize) -> &T; + fn view_as_slice_of<T>(&self, offset: usize, len: usize) -> &[T]; + fn copy_as<T: Copy>(&self, offset: usize) -> T; + fn view_as_str(&self, offset: usize) -> &[u8]; +} + +impl View for [u8] { + fn view_as<T>(&self, offset: usize) -> &T { + let ptr = assert_proper_length_and_alignment!(self, T, offset, size_of::<T>()); + unsafe { &*ptr } + } + + fn view_as_slice_of<T>(&self, offset: usize, len: usize) -> &[T] { + let ptr = assert_proper_length_and_alignment!(self, T, offset, size_of::<T>() * len); + unsafe { std::slice::from_raw_parts(ptr, len) } + } + + fn copy_as<T>(&self, offset: usize) -> T { + assert_proper_length!(self, T, offset, size_of::<T>()); + unsafe { + let mut data = MaybeUninit::zeroed().assume_init(); + copy_nonoverlapping(self[offset..].as_ptr(), &mut data as *mut T as *mut u8, size_of::<T>()); + data + } + } + + fn view_as_str(&self, offset: usize) -> &[u8] { + let buffer = &self[offset..]; + let index = buffer.iter().position(|c| *c == b'\0').expect("Invalid file"); + &self[offset..offset + index] + } +} + +fn section_from_rva(sections: &[IMAGE_SECTION_HEADER], rva: u32) -> Result<&IMAGE_SECTION_HEADER> { + sections.iter().find(|&s| rva >= s.VirtualAddress && rva < s.VirtualAddress + unsafe { s.Misc.VirtualSize }).ok_or_else(error_invalid_winmd) +} + +fn offset_from_rva(section: &IMAGE_SECTION_HEADER, rva: u32) -> usize { + (rva - section.VirtualAddress + section.PointerToRawData) as usize +} diff --git a/vendor/windows-metadata/src/reader/filter.rs b/vendor/windows-metadata/src/reader/filter.rs new file mode 100644 index 000000000..4b6651fb6 --- /dev/null +++ b/vendor/windows-metadata/src/reader/filter.rs @@ -0,0 +1,157 @@ +use super::*; + +#[derive(Default)] +pub struct Filter<'a>(Vec<(&'a str, bool)>); + +impl<'a> Filter<'a> { + pub fn new(include: &[&'a str], exclude: &[&'a str]) -> Self { + let mut rules = vec![]; + + for include in include { + rules.push((*include, true)); + } + + for exclude in exclude { + rules.push((*exclude, false)); + } + + rules.sort_unstable_by(|left, right| { + let left = (left.0.len(), !left.1); + let right = (right.0.len(), !right.1); + left.cmp(&right).reverse() + }); + + Self(rules) + } + + pub fn includes_namespace(&self, namespace: &str) -> bool { + if self.is_empty() { + return true; + } + + for rule in &self.0 { + if rule.1 { + // include + if rule.0.starts_with(namespace) { + return true; + } + if namespace.starts_with(rule.0) { + return true; + } + } else { + // exclude + if namespace.starts_with(rule.0) { + return false; + } + } + } + + false + } + + pub fn includes_type(&self, reader: &Reader, ty: TypeDef) -> bool { + self.includes_type_name(reader.type_def_type_name(ty)) + } + + fn includes_type_name(&self, type_name: TypeName) -> bool { + if self.is_empty() { + return true; + } + + for rule in &self.0 { + if match_type_name(rule.0, type_name.namespace, type_name.name) { + return rule.1; + } + } + + false + } + + fn is_empty(&self) -> bool { + self.0.is_empty() + } +} + +fn match_type_name(rule: &str, namespace: &str, name: &str) -> bool { + if rule.len() <= namespace.len() { + return namespace.starts_with(rule); + } + + if !rule.starts_with(namespace) { + return false; + } + + if rule.as_bytes()[namespace.len()] != b'.' { + return false; + } + + name.starts_with(&rule[namespace.len() + 1..]) +} + +#[cfg(test)] +mod tests { + use super::*; + + fn includes_type_name(filter: &Filter, full_name: &str) -> bool { + filter.includes_type_name(TypeName::parse(full_name)) + } + + #[test] + fn test_namespace() { + let include = ["N1.N2"]; + let exclude = ["N1.N2.N3"]; + let f = Filter::new(&include, &exclude); + + assert!(f.includes_namespace("N1")); + assert!(f.includes_namespace("N1.N2")); + assert!(f.includes_namespace("N1.N2.N4")); + + assert!(!f.includes_namespace("N1.N2.N3")); + assert!(!f.includes_namespace("N1.N2.N3.N4")); + } + + #[test] + fn test_simple() { + let include = ["N1", "N3", "N3.N4.N5"]; + let exclude = ["N2", "N3.N4"]; + let f = Filter::new(&include, &exclude); + + assert!(!f.is_empty()); + + assert!(!includes_type_name(&f, "NN.T")); + + assert!(includes_type_name(&f, "N1.T")); + assert!(includes_type_name(&f, "N3.T")); + + assert!(!includes_type_name(&f, "N2.T")); + assert!(!includes_type_name(&f, "N3.N4.T")); + + assert!(includes_type_name(&f, "N3.N4.N5.T")); + } + + #[test] + fn filter_excludes_same_length() { + let include = ["N.N1", "N.N2"]; + let exclude = ["N.N3", "N.N4"]; + let f = Filter::new(&include, &exclude); + + assert!(!f.is_empty()); + + assert!(includes_type_name(&f, "N.N1.T")); + assert!(includes_type_name(&f, "N.N2.T")); + + assert!(!includes_type_name(&f, "N.N3.T")); + assert!(!includes_type_name(&f, "N.N4.T")); + } + + #[test] + fn filter_exclude_include_precedence() { + let include = ["N.T"]; + let exclude = ["N.T"]; + let f = Filter::new(&include, &exclude); + + assert!(!f.is_empty()); + + assert!(!includes_type_name(&f, "N.T")); + } +} diff --git a/vendor/windows-metadata/src/reader/guid.rs b/vendor/windows-metadata/src/reader/guid.rs new file mode 100644 index 000000000..96444b8b2 --- /dev/null +++ b/vendor/windows-metadata/src/reader/guid.rs @@ -0,0 +1,40 @@ +#![allow(clippy::many_single_char_names)] + +use super::*; + +#[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: &[(String, Value)]) -> Self { + fn unwrap_u32(value: &Value) -> u32 { + match value { + Value::U32(value) => *value, + _ => unimplemented!(), + } + } + fn unwrap_u16(value: &Value) -> u16 { + match value { + Value::U16(value) => *value, + _ => unimplemented!(), + } + } + fn unwrap_u8(value: &Value) -> u8 { + match value { + Value::U8(value) => *value, + _ => unimplemented!(), + } + } + 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) + } +} diff --git a/vendor/windows-metadata/src/reader/mod.rs b/vendor/windows-metadata/src/reader/mod.rs new file mode 100644 index 000000000..7e551cc8f --- /dev/null +++ b/vendor/windows-metadata/src/reader/mod.rs @@ -0,0 +1,1833 @@ +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)]; diff --git a/vendor/windows-metadata/src/reader/row.rs b/vendor/windows-metadata/src/reader/row.rs new file mode 100644 index 000000000..727edb201 --- /dev/null +++ b/vendor/windows-metadata/src/reader/row.rs @@ -0,0 +1,15 @@ +#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] +pub struct Row { + pub row: u32, + pub table: u16, + pub file: u16, +} + +impl Row { + pub fn new(row: usize, table: usize, file: usize) -> Self { + Self { row: row as _, table: table as _, file: file as _ } + } + pub fn next(&self) -> Self { + Self { row: self.row + 1, table: self.table, file: self.file } + } +} diff --git a/vendor/windows-metadata/src/reader/tree.rs b/vendor/windows-metadata/src/reader/tree.rs new file mode 100644 index 000000000..3d576ea41 --- /dev/null +++ b/vendor/windows-metadata/src/reader/tree.rs @@ -0,0 +1,31 @@ +use super::*; + +#[derive(Debug)] +pub struct Tree<'a> { + pub namespace: &'a str, + pub nested: BTreeMap<&'a str, Tree<'a>>, +} + +impl<'a> Tree<'a> { + pub(crate) fn from_namespace(namespace: &'a str) -> Self { + Self { namespace, nested: BTreeMap::new() } + } + pub(crate) fn insert_namespace(&mut self, namespace: &'a str, pos: usize) -> &mut Self { + if let Some(next) = namespace[pos..].find('.') { + let next = pos + next; + self.nested.entry(&namespace[pos..next]).or_insert_with(|| Self::from_namespace(&namespace[..next])).insert_namespace(namespace, next + 1) + } else { + self.nested.entry(&namespace[pos..]).or_insert_with(|| Self::from_namespace(namespace)) + } + } + pub fn flatten(&self) -> Vec<&Self> { + std::iter::once(self).chain(self.nested.values().flat_map(|tree| tree.flatten())).collect() + } + pub fn seek(mut self, namespace: &'a str) -> Option<Self> { + if let Some(next) = namespace.find('.') { + self.nested.remove(&namespace[..next]).and_then(|tree| tree.seek(&namespace[next + 1..])) + } else { + self.nested.remove(namespace) + } + } +} diff --git a/vendor/windows-metadata/src/reader/type.rs b/vendor/windows-metadata/src/reader/type.rs new file mode 100644 index 000000000..222f90150 --- /dev/null +++ b/vendor/windows-metadata/src/reader/type.rs @@ -0,0 +1,152 @@ +use super::*; + +#[derive(Clone, PartialEq, PartialOrd, Eq, Ord)] +pub enum Type { + Void, + Bool, + Char, + I8, + U8, + I16, + U16, + I32, + U32, + I64, + U64, + F32, + F64, + ISize, + USize, + String, + GUID, + IUnknown, + IInspectable, + HRESULT, + PSTR, + PWSTR, + PCSTR, + PCWSTR, + BSTR, + TypeName, + GenericParam(GenericParam), + TypeDef((TypeDef, Vec<Self>)), + MutPtr((Box<Self>, usize)), + ConstPtr((Box<Self>, usize)), + Win32Array((Box<Self>, usize)), + WinrtArray(Box<Self>), + WinrtArrayRef(Box<Self>), + WinrtConstRef(Box<Self>), +} + +impl Type { + /// Creates a `Type` object from an `ELEMENT_TYPE` (see ECMA-335) type constant, typically + /// used to indicate the type of a constant or primitive type signature. + pub fn from_code(code: usize) -> Option<Self> { + match code { + 0x01 => Some(Self::Void), + 0x02 => Some(Self::Bool), + 0x03 => Some(Self::Char), + 0x04 => Some(Self::I8), + 0x05 => Some(Self::U8), + 0x06 => Some(Self::I16), + 0x07 => Some(Self::U16), + 0x08 => Some(Self::I32), + 0x09 => Some(Self::U32), + 0x0a => Some(Self::I64), + 0x0b => Some(Self::U64), + 0x0c => Some(Self::F32), + 0x0d => Some(Self::F64), + 0x18 => Some(Self::ISize), + 0x19 => Some(Self::USize), + 0x0e => Some(Self::String), + 0x1c => Some(Self::IInspectable), + _ => None, + } + } + + /// Converts the `Type` to an equivalent `const` variant if appropriate. + pub fn to_const_type(self) -> Self { + match self { + Self::MutPtr((kind, pointers)) => Self::MutPtr((Box::new(kind.to_const_type()), pointers)), + Self::ConstPtr((kind, pointers)) => Self::ConstPtr((Box::new(kind.to_const_type()), pointers)), + Self::PSTR => Self::PCSTR, + Self::PWSTR => Self::PCWSTR, + _ => self, + } + } + + /// Converts a mutable pointer type, if appropriate, to a const pointer type. + pub fn to_const_ptr(self) -> Self { + match self { + Self::MutPtr((kind, pointers)) => Self::ConstPtr((kind, pointers)), + _ => self, + } + } + + /// Removes one level of indirection, typically used when transforming a logical return or array parameter + /// from its underlying type signature. + pub fn deref(&self) -> Self { + match self { + Self::ConstPtr((kind, 1)) | Self::MutPtr((kind, 1)) => { + if **kind == Self::Void { + Self::U8 + } else { + *kind.clone() + } + } + Self::ConstPtr((kind, pointers)) => Self::ConstPtr((kind.clone(), pointers - 1)), + Self::MutPtr((kind, pointers)) => Self::MutPtr((kind.clone(), pointers - 1)), + Self::PSTR | Self::PCSTR => Self::U8, + Self::PWSTR | Self::PCWSTR => Self::U16, + _ => panic!("`deref` can only be called on pointer types"), + } + } + + /// Returns `true` if the `Type` represents a WinRT array. + pub fn is_winrt_array(&self) -> bool { + matches!(self, Type::WinrtArray(_)) + } + + /// Returns `true` if the `Type` represents a mutable WinRT array reference. + pub fn is_winrt_array_ref(&self) -> bool { + matches!(self, Type::WinrtArrayRef(_)) + } + + /// Returns `true` if the `Type` represents an immutable WinRT array reference. + pub fn is_winrt_const_ref(&self) -> bool { + matches!(self, Type::WinrtConstRef(_)) + } + + /// Returns `true` if the `Type` is a generic parameter. + pub fn is_generic(&self) -> bool { + matches!(self, Type::GenericParam(_)) + } + + /// Returns `true` if the `Type` is a pointer. + pub fn is_pointer(&self) -> bool { + matches!(self, Type::ConstPtr(_) | Type::MutPtr(_)) + } + + /// Returns `true` if the `Type` is unsigned. + pub fn is_unsigned(&self) -> bool { + matches!(self, Type::U8 | Type::U16 | Type::U32 | Type::U64 | Type::USize) + } + + /// Returns `true` if the `Type` is incomplete. + pub fn is_void(&self) -> bool { + match self { + Type::ConstPtr((kind, _)) | Type::MutPtr((kind, _)) => kind.is_void(), + Type::Void => true, + _ => false, + } + } + + /// Returns `true` if the `Type` has a byte-sized address. + pub fn is_byte_size(&self) -> bool { + match self { + Type::ConstPtr((kind, _)) | Type::MutPtr((kind, _)) => kind.is_byte_size(), + Type::I8 | Type::U8 | Type::PSTR | Type::PCSTR => true, + _ => false, + } + } +} diff --git a/vendor/windows-metadata/src/reader/type_name.rs b/vendor/windows-metadata/src/reader/type_name.rs new file mode 100644 index 000000000..2e7b40808 --- /dev/null +++ b/vendor/windows-metadata/src/reader/type_name.rs @@ -0,0 +1,75 @@ +#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord)] +pub struct TypeName<'a> { + pub namespace: &'a str, + pub name: &'a str, +} + +#[allow(non_upper_case_globals)] +impl<'a> TypeName<'a> { + pub const None: Self = Self::from_const("", ""); + pub const Enum: Self = Self::from_const("System", "Enum"); + pub const Delegate: Self = Self::from_const("System", "MulticastDelegate"); + pub const Struct: Self = Self::from_const("System", "ValueType"); + pub const Object: Self = Self::from_const("System", "Object"); + pub const GUID: Self = Self::from_const("System", "Guid"); + pub const Type: Self = Self::from_const("System", "Type"); + pub const Attribute: Self = Self::from_const("System", "Attribute"); + pub const IsConst: Self = Self::from_const("System.Runtime.CompilerServices", "IsConst"); + + pub const HResult: Self = Self::from_const("Windows.Foundation", "HResult"); + pub const IAsyncAction: Self = Self::from_const("Windows.Foundation", "IAsyncAction"); + pub const IAsyncActionWithProgress: Self = Self::from_const("Windows.Foundation", "IAsyncActionWithProgress"); + pub const IAsyncOperation: Self = Self::from_const("Windows.Foundation", "IAsyncOperation"); + pub const IAsyncOperationWithProgress: Self = Self::from_const("Windows.Foundation", "IAsyncOperationWithProgress"); + + pub const Matrix3x2: Self = Self::from_const("Windows.Foundation.Numerics", "Matrix3x2"); + pub const Matrix4x4: Self = Self::from_const("Windows.Foundation.Numerics", "Matrix4x4"); + + pub const IIterable: Self = Self::from_const("Windows.Foundation.Collections", "IIterable"); + pub const IIterator: Self = Self::from_const("Windows.Foundation.Collections", "IIterator"); + pub const IVectorView: Self = Self::from_const("Windows.Foundation.Collections", "IVectorView"); + pub const IVector: Self = Self::from_const("Windows.Foundation.Collections", "IVector"); + + pub const NTSTATUS: Self = Self::from_const("Windows.Win32.Foundation", "NTSTATUS"); + pub const PWSTR: Self = Self::from_const("Windows.Win32.Foundation", "PWSTR"); + pub const PSTR: Self = Self::from_const("Windows.Win32.Foundation", "PSTR"); + pub const BSTR: Self = Self::from_const("Windows.Win32.Foundation", "BSTR"); + pub const HANDLE: Self = Self::from_const("Windows.Win32.Foundation", "HANDLE"); + pub const HRESULT: Self = Self::from_const("Windows.Win32.Foundation", "HRESULT"); + pub const CHAR: Self = Self::from_const("Windows.Win32.Foundation", "CHAR"); + + pub const D2D_MATRIX_3X2_F: Self = Self::from_const("Windows.Win32.Graphics.Direct2D.Common", "D2D_MATRIX_3X2_F"); + pub const D3DMATRIX: Self = Self::from_const("Windows.Win32.Graphics.Direct3D", "D3DMATRIX"); + pub const IUnknown: Self = Self::from_const("Windows.Win32.System.Com", "IUnknown"); + pub const HSTRING: Self = Self::from_const("Windows.Win32.System.WinRT", "HSTRING"); + pub const IInspectable: Self = Self::from_const("Windows.Win32.System.WinRT", "IInspectable"); + pub const IRestrictedErrorInfo: Self = Self::from_const("Windows.Win32.System.WinRT", "IRestrictedErrorInfo"); + pub const IDispatch: Self = Self::from_const("Windows.Win32.System.Com", "IDispatch"); + + const fn from_const(namespace: &'static str, name: &'static str) -> Self { + Self { namespace, name } + } + + pub fn new(namespace: &'a str, name: &'a str) -> Self { + Self { namespace, name: trim_tick(name) } + } + + pub fn parse(full_name: &'a str) -> Self { + let index = full_name.rfind('.').expect("Expected full name separated with `.`"); + Self { namespace: &full_name[0..index], name: &full_name[index + 1..] } + } +} + +impl<'a> std::fmt::Display for TypeName<'a> { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(fmt, "{}.{}", self.namespace, self.name) + } +} + +pub fn trim_tick(name: &str) -> &str { + if name.as_bytes().iter().rev().nth(1) == Some(&b'`') { + &name[..name.len() - 2] + } else { + name + } +} diff --git a/vendor/windows-metadata/src/writer/imp/blobs.rs b/vendor/windows-metadata/src/writer/imp/blobs.rs new file mode 100644 index 000000000..08bad113d --- /dev/null +++ b/vendor/windows-metadata/src/writer/imp/blobs.rs @@ -0,0 +1,60 @@ +use super::*; + +#[derive(Default)] +pub struct Blobs { + // Blobs don't need to be sorted. A map is used to collapse duplicates. A `BTreeMap` in particular is used to help with reproducible builds. + map: BTreeMap<Vec<u8>, u32>, + stream: Vec<u8>, +} + +pub struct StagedBlobs(Blobs); + +impl Blobs { + pub fn insert(&mut self, value: Vec<u8>) { + if !value.is_empty() { + self.map.entry(value).or_default(); + } + } + + pub fn stage(mut self) -> StagedBlobs { + self.stream = vec![0]; + + for (value, index) in self.map.iter_mut() { + *index = self.stream.len() as _; + + match value.len() { + 0..=0x7F => self.stream.push(value.len() as _), + 0x80..=0x3FFF => { + self.stream.push((0x80 | value.len() >> 8) as _); + self.stream.push((0xFF & value.len()) as _); + } + _ => { + self.stream.push((0xC0 | value.len() >> 24) as _); + self.stream.push((0xFF & value.len() >> 16) as _); + self.stream.push((0xFF & value.len() >> 8) as _); + self.stream.push((0xFF & value.len()) as _); + } + } + + self.stream.extend_from_slice(value); + } + + self.stream.resize(round(self.stream.len(), 4), 0); + StagedBlobs(self) + } +} + +impl StagedBlobs { + pub fn stream(self) -> Vec<u8> { + self.0.stream + } + + #[track_caller] + pub fn index(&self, value: &[u8]) -> u32 { + if value.is_empty() { + 0 + } else { + self.0.map[value] + } + } +} diff --git a/vendor/windows-metadata/src/writer/imp/codes.rs b/vendor/windows-metadata/src/writer/imp/codes.rs new file mode 100644 index 000000000..3c1b60048 --- /dev/null +++ b/vendor/windows-metadata/src/writer/imp/codes.rs @@ -0,0 +1,94 @@ +/// A `ResolutionScope` is an index into a certain table indicating the scope in which a TypeRef can be resolved. +#[derive(Default, Clone)] +pub enum ResolutionScope { + #[default] + None, + Module(u32), + ModuleRef(u32), + AssemblyRef(u32), + TypeRef(u32), +} + +impl ResolutionScope { + pub fn encode(&self) -> u32 { + match self { + Self::Module(row) => (row + 1) << 2, + Self::ModuleRef(row) => ((row + 1) << 2) + 1, + Self::AssemblyRef(row) => ((row + 1) << 2) + 2, + Self::TypeRef(row) => ((row + 1) << 2) + 3, + _ => 0, + } + } +} + +/// A `TypeDefOrRef` is an index into a certain table used to locate a type definition. +#[derive(Default, Clone)] +pub enum TypeDefOrRef { + #[default] + None, + TypeDef(u32), + TypeRef(u32), + TypeSpec(u32), +} + +impl TypeDefOrRef { + pub fn encode(&self) -> u32 { + match self { + Self::TypeDef(row) => (row + 1) << 2, + Self::TypeRef(row) => ((row + 1) << 2) + 1, + Self::TypeSpec(row) => ((row + 1) << 2) + 2, + _ => 0, + } + } +} + +/// A `HasConstant` is an index into a certain table used to identify the parent of a row in the `Constant` table. +#[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub enum HasConstant { + #[default] + None, + Field(u32), + Param(u32), + Property(u32), +} + +impl HasConstant { + pub fn encode(&self) -> u32 { + match self { + Self::Field(row) => (row + 1) << 2, + Self::Param(row) => ((row + 1) << 2) + 1, + Self::Property(row) => ((row + 1) << 2) + 2, + _ => 0, + } + } +} + +#[derive(Default, Clone)] +pub enum HasCustomAttribute { + #[default] + None, +} + +#[derive(Default, Clone)] +pub enum CustomAttributeType { + #[default] + None, +} + +#[derive(Default, Clone)] +pub enum TypeOrMethodDef { + #[default] + None, +} + +#[derive(Default, Clone)] +pub enum MemberForwarded { + #[default] + None, +} + +#[derive(Default, Clone)] +pub enum MemberRefParent { + #[default] + None, +} diff --git a/vendor/windows-metadata/src/writer/imp/definitions.rs b/vendor/windows-metadata/src/writer/imp/definitions.rs new file mode 100644 index 000000000..476e3ed52 --- /dev/null +++ b/vendor/windows-metadata/src/writer/imp/definitions.rs @@ -0,0 +1,43 @@ +use super::*; + +#[derive(Default)] +pub struct Definitions<'a> { + map: BTreeMap<(&'a str, &'a str), Definition<'a>>, +} + +pub struct Definition<'a> { + pub item: &'a Item, + pub index: u32, + pub value_type: bool, +} + +pub struct StagedDefinitions<'a>(Definitions<'a>); + +impl<'a> Definitions<'a> { + pub fn insert(&mut self, item: &'a Item) { + if self.map.insert(item_type_name(item), Definition { item, index: 0, value_type: item_value_type(item) }).is_some() { + panic!("Duplicate type found"); + } + } + + pub fn stage(mut self) -> StagedDefinitions<'a> { + for (index, value) in self.map.values_mut().enumerate() { + value.index = index as _; + } + StagedDefinitions(self) + } +} + +impl<'a> StagedDefinitions<'a> { + pub fn get(&self, namespace: &'a str, name: &'a str) -> Option<&'a Definition> { + self.0.map.get(&(namespace, name)) + } + + pub fn items(&self) -> impl Iterator<Item = &Item> { + self.0.map.values().map(|value| value.item) + } + + pub fn iter(&self) -> impl Iterator<Item = (u32, &Item)> { + self.0.map.values().map(|value| (value.index, value.item)) + } +} diff --git a/vendor/windows-metadata/src/writer/imp/file.rs b/vendor/windows-metadata/src/writer/imp/file.rs new file mode 100644 index 000000000..2f25f942b --- /dev/null +++ b/vendor/windows-metadata/src/writer/imp/file.rs @@ -0,0 +1,138 @@ +use super::*; +use std::mem::*; + +pub fn write(tables: Tables, strings: StagedStrings, blobs: StagedBlobs) -> Vec<u8> { + unsafe { + let mut tables = tables.stream(); + let mut strings = strings.stream(); + let mut blobs = blobs.stream(); + let mut guids = vec![0; 16]; // zero guid + let size_of_streams = tables.len() + guids.len() + strings.len() + blobs.len(); + + let mut dos: IMAGE_DOS_HEADER = zeroed(); + dos.e_magic = IMAGE_DOS_SIGNATURE as _; + dos.e_lfarlc = 64; + dos.e_lfanew = size_of::<IMAGE_DOS_HEADER>() as _; + + let mut file: IMAGE_FILE_HEADER = zeroed(); + file.Machine = IMAGE_FILE_MACHINE_I386; + file.NumberOfSections = 1; + file.SizeOfOptionalHeader = size_of::<IMAGE_OPTIONAL_HEADER32>() as _; + file.Characteristics = IMAGE_FILE_DLL | IMAGE_FILE_32BIT_MACHINE | IMAGE_FILE_EXECUTABLE_IMAGE; + + let mut optional: IMAGE_OPTIONAL_HEADER32 = zeroed(); + optional.Magic = IMAGE_NT_OPTIONAL_HDR32_MAGIC; + optional.MajorLinkerVersion = 11; + optional.SizeOfInitializedData = 1024; + optional.ImageBase = 0x400000; + optional.SectionAlignment = SECTION_ALIGNMENT; + optional.FileAlignment = 512; + optional.MajorOperatingSystemVersion = 6; + optional.MinorOperatingSystemVersion = 2; + optional.MajorSubsystemVersion = 6; + optional.MinorSubsystemVersion = 2; + optional.SizeOfHeaders = 512; + optional.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_CUI; + optional.DllCharacteristics = IMAGE_DLLCHARACTERISTICS_NX_COMPAT | IMAGE_DLLCHARACTERISTICS_NO_SEH | IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE; + optional.SizeOfStackReserve = 0x100000; + optional.SizeOfHeapReserve = 4096; + optional.LoaderFlags = 0x100000; + optional.NumberOfRvaAndSizes = 16; + + let mut section: IMAGE_SECTION_HEADER = zeroed(); + section.Name = *b".text\0\0\0"; + section.Characteristics = 0x4000_0020; + section.VirtualAddress = SECTION_ALIGNMENT; + + let mut clr: IMAGE_COR20_HEADER = zeroed(); + clr.cb = size_of::<IMAGE_COR20_HEADER>() as _; + clr.MajorRuntimeVersion = 2; + clr.MinorRuntimeVersion = 5; + clr.Flags = 1; + + let metadata = METADATA_HEADER { + signature: METADATA_SIGNATURE, + major_version: 1, + minor_version: 1, + length: 20, + version: *b"WindowsRuntime\0\0\0\0\0\0", + streams: 4, + ..Default::default() + }; + + type TablesHeader = StreamHeader<4>; + type StringsHeader = StreamHeader<12>; + type GuidsHeader = StreamHeader<8>; + type BlobsHeader = StreamHeader<8>; + + let size_of_stream_headers = size_of::<TablesHeader>() + size_of::<StringsHeader>() + size_of::<GuidsHeader>() + size_of::<BlobsHeader>(); + let size_of_image = optional.FileAlignment as usize + size_of::<IMAGE_COR20_HEADER>() + size_of::<METADATA_HEADER>() + size_of_stream_headers + size_of_streams; + + optional.SizeOfImage = round(size_of_image, optional.SectionAlignment as _) as _; + section.Misc.VirtualSize = size_of_image as u32 - optional.FileAlignment; + section.SizeOfRawData = round(section.Misc.VirtualSize as _, optional.FileAlignment as _) as _; + + optional.DataDirectory[14] = IMAGE_DATA_DIRECTORY { VirtualAddress: SECTION_ALIGNMENT, Size: size_of::<IMAGE_COR20_HEADER>() as _ }; + section.PointerToRawData = optional.FileAlignment; + clr.MetaData.VirtualAddress = SECTION_ALIGNMENT + size_of::<IMAGE_COR20_HEADER>() as u32; + clr.MetaData.Size = section.Misc.VirtualSize - size_of::<IMAGE_COR20_HEADER>() as u32; + + let mut buffer = Vec::<u8>::new(); + + buffer.write_header(&dos); + buffer.write_u32(IMAGE_NT_SIGNATURE); + buffer.write_header(&file); + buffer.write_header(&optional); + buffer.write_header(§ion); + debug_assert!(buffer.len() < optional.FileAlignment as _); + buffer.resize(optional.FileAlignment as _, 0); + buffer.write_header(&clr); + let metadata_offset = buffer.len(); + buffer.write_header(&metadata); + + let stream_offset = buffer.len() - metadata_offset + size_of_stream_headers; + let tables_header = TablesHeader::new(stream_offset as _, tables.len() as _, b"#~\0\0"); + let strings_header = StringsHeader::new(tables_header.next_offset(), strings.len() as _, b"#Strings\0\0\0\0"); + let guids_header = GuidsHeader::new(strings_header.next_offset(), guids.len() as _, b"#GUID\0\0\0"); + let blobs_header = BlobsHeader::new(guids_header.next_offset(), blobs.len() as _, b"#Blob\0\0\0"); + + buffer.write_header(&tables_header); + buffer.write_header(&strings_header); + buffer.write_header(&guids_header); + buffer.write_header(&blobs_header); + + buffer.append(&mut tables); + buffer.append(&mut strings); + buffer.append(&mut guids); + buffer.append(&mut blobs); + + assert_eq!(clr.MetaData.Size as usize, buffer.len() - metadata_offset); + assert_eq!(size_of_image, buffer.len()); + + buffer + } +} + +const SECTION_ALIGNMENT: u32 = 4096; + +#[repr(C)] +struct StreamHeader<const LEN: usize> { + offset: u32, + size: u32, + name: [u8; LEN], +} + +impl<const LEN: usize> StreamHeader<LEN> { + fn new(offset: u32, size: u32, name: &[u8; LEN]) -> Self { + Self { offset, size, name: *name } + } + fn next_offset(&self) -> u32 { + self.offset + self.size + } +} + +fn guid_stream() -> Vec<u8> { + let mut buffer = Vec::new(); + buffer.resize(16, 0); // zero guid + buffer +} diff --git a/vendor/windows-metadata/src/writer/imp/mod.rs b/vendor/windows-metadata/src/writer/imp/mod.rs new file mode 100644 index 000000000..adecde371 --- /dev/null +++ b/vendor/windows-metadata/src/writer/imp/mod.rs @@ -0,0 +1,408 @@ +mod blobs; +mod codes; +mod definitions; +mod file; +mod references; +mod strings; +mod tables; + +use super::*; +use blobs::*; +use codes::*; +use definitions::*; +use references::*; +use strings::*; +use tables::Tables; + +use std::collections::BTreeMap; + +pub fn round(size: usize, round: usize) -> usize { + let round = round - 1; + (size + round) & !round +} + +pub fn write(name: &str, winrt: bool, definitions: &[Item], assemblies: &[&str]) -> Vec<u8> { + // Index assemblies used to resolve references to existing winmd files. + let assemblies: Vec<reader::File> = assemblies.iter().map(|file| reader::File::new(file).expect("Assemblies could not be loaded")).collect(); + let assemblies = &reader::Reader::new(&assemblies); + + // Build sorted list of definitions. + let definitions = &{ + let mut index = Definitions::default(); + definitions.iter().for_each(|item| index.insert(item)); + index.stage() + }; + + // Build sorted list of references. + let references = &{ + let mut index = References::default(); + for item in definitions.items() { + match item { + Item::Struct(ty) => ty.fields.iter().for_each(|field| type_reference(&field.ty, definitions, assemblies, &mut index)), + Item::Interface(_ty) => {} + _ => {} + } + } + index.stage() + }; + + // Now that we have stable type indexes, build blobs and index strings. + let (blobs, strings) = { + let mut blobs = Blobs::default(); + let mut strings = Strings::default(); + strings.insert(name); + strings.insert("<Module>"); + strings.insert("mscorlib"); + strings.insert("System"); + strings.insert("ValueType"); + strings.insert("Enum"); + strings.insert("value__"); + + for item in definitions.items() { + match item { + Item::Struct(ty) => { + strings.insert(&ty.namespace); + strings.insert(&ty.name); + ty.fields.iter().for_each(|field| { + strings.insert(&field.name); + blobs.insert(field_blob(&field.ty, definitions, references)); + }); + } + Item::Enum(ty) => { + strings.insert(&ty.namespace); + strings.insert(&ty.name); + let enum_type = Type::Named((ty.namespace.clone(), ty.name.clone())); + blobs.insert(field_blob(&enum_type, definitions, references)); + blobs.insert(field_blob(&value_to_type(&ty.constants[0].value), definitions, references)); + ty.constants.iter().for_each(|constant| { + strings.insert(&constant.name); + blobs.insert(value_blob(&constant.value)); + }); + } + Item::Interface(ty) => { + strings.insert(&ty.namespace); + strings.insert(&ty.name); + ty.methods.iter().for_each(|method| { + strings.insert(&method.name); + blobs.insert(method_blob(method, definitions, references)); + method.params.iter().for_each(|param| { + strings.insert(¶m.name); + }); + }); + } + } + } + + (blobs.stage(), strings.stage()) + }; + + // Now that everything is indexed in various heaps, write out the table records. + let tables = { + let mut tables = Tables::default(); + tables.Module.push(tables::Module { Name: strings.index(name), Mvid: 1, ..Default::default() }); + tables.TypeDef.push(tables::TypeDef { TypeName: strings.index("<Module>"), ..Default::default() }); + let mscorlib = tables.AssemblyRef.push2(tables::AssemblyRef { MajorVersion: 4, Name: strings.index("mscorlib"), ..Default::default() }); + let value_type = tables.TypeRef.push2(tables::TypeRef { TypeName: strings.index("ValueType"), TypeNamespace: strings.index("System"), ResolutionScope: ResolutionScope::AssemblyRef(mscorlib).encode() }); + let enum_type = tables.TypeRef.push2(tables::TypeRef { TypeName: strings.index("Enum"), TypeNamespace: strings.index("System"), ResolutionScope: ResolutionScope::AssemblyRef(mscorlib).encode() }); + + for (_index, item) in definitions.iter() { + match item { + Item::Struct(ty) => { + let mut flags = TypeAttributes::PUBLIC | TypeAttributes::SEQUENTIAL_LAYOUT | TypeAttributes::SEALED; + if winrt { + flags |= TypeAttributes::WINRT; + } + tables.TypeDef.push(tables::TypeDef { + Flags: flags.0, + TypeName: strings.index(&ty.name), + TypeNamespace: strings.index(&ty.namespace), + Extends: TypeDefOrRef::TypeRef(value_type).encode(), + FieldList: tables.Field.len() as _, + MethodList: tables.MethodDef.len() as _, + }); + for field in &ty.fields { + let flags = FieldAttributes::PUBLIC; + tables.Field.push(tables::Field { Flags: flags.0, Name: strings.index(&field.name), Signature: blobs.index(&field_blob(&field.ty, definitions, references)) }); + } + } + Item::Enum(ty) => { + let mut flags = TypeAttributes::PUBLIC | TypeAttributes::SEALED; + if winrt { + flags |= TypeAttributes::WINRT; + } + tables.TypeDef.push(tables::TypeDef { + Flags: flags.0, + TypeName: strings.index(&ty.name), + TypeNamespace: strings.index(&ty.namespace), + Extends: TypeDefOrRef::TypeRef(enum_type).encode(), + FieldList: tables.Field.len() as _, + MethodList: tables.MethodDef.len() as _, + }); + let enum_type = Type::Named((ty.namespace.clone(), ty.name.clone())); + let flags = FieldAttributes::PRIVATE | FieldAttributes::SPECIAL | FieldAttributes::RUNTIME_SPECIAL; + tables.Field.push2(tables::Field { + Flags: flags.0, + Name: strings.index("value__"), + Signature: blobs.index(&field_blob(&value_to_type(&ty.constants[0].value), definitions, references)), + }); + for constant in &ty.constants { + let flags = FieldAttributes::PUBLIC | FieldAttributes::STATIC | FieldAttributes::LITERAL | FieldAttributes::HAS_DEFAULT; + let field = tables.Field.push2(tables::Field { Flags: flags.0, Name: strings.index(&constant.name), Signature: blobs.index(&field_blob(&enum_type, definitions, references)) }); + tables.Constant.push(tables::Constant { Type: value_type_code(&constant.value), Parent: HasConstant::Field(field).encode(), Value: blobs.index(&value_blob(&constant.value)) }); + } + } + Item::Interface(ty) => { + let mut flags = TypeAttributes::PUBLIC | TypeAttributes::INTERFACE | TypeAttributes::ABSTRACT; + if winrt { + flags |= TypeAttributes::WINRT; + } + tables.TypeDef.push(tables::TypeDef { + Flags: flags.0, + TypeName: strings.index(&ty.name), + TypeNamespace: strings.index(&ty.namespace), + Extends: 0, + FieldList: tables.Field.len() as _, + MethodList: tables.MethodDef.len() as _, + }); + for method in &ty.methods { + let flags = MethodAttributes::ABSTRACT | MethodAttributes::HIDE_BY_SIG | MethodAttributes::NEW_SLOT | MethodAttributes::PUBLIC | MethodAttributes::VIRTUAL; + tables.MethodDef.push(tables::MethodDef { + RVA: 0, + ImplFlags: 0, + Flags: flags.0, + Name: strings.index(&method.name), + Signature: blobs.index(&method_blob(method, definitions, references)), + ParamList: tables.Param.len() as _, + }); + for (sequence, param) in method.params.iter().enumerate() { + tables.Param.push(tables::Param { Flags: param_flags_to_attributes(param.flags).0, Sequence: (sequence + 1) as _, Name: strings.index(¶m.name) }); + } + } + } + } + } + + tables + }; + + // With all of the streams prepared, write out ECMA-335 file format. + file::write(tables, strings, blobs) +} + +fn type_reference<'a>(ty: &'a Type, definitions: &StagedDefinitions, assemblies: &reader::Reader, references: &mut References<'a>) { + // TODO: More matches to come... + #[allow(clippy::single_match)] + match ty { + Type::Named((namespace, name)) => { + if definitions.get(namespace, name).is_none() { + references.insert(namespace, name, assemblies); + } + } + _ => {} + } +} + +fn param_flags_to_attributes(flags: ParamFlags) -> ParamAttributes { + let mut attributes = ParamAttributes(0); + if flags.contains(ParamFlags::INPUT) { + attributes |= ParamAttributes::INPUT; + } + if flags.contains(ParamFlags::OUTPUT) { + attributes |= ParamAttributes::OUTPUT; + } + if flags.contains(ParamFlags::OPTIONAL) { + attributes |= ParamAttributes::OPTIONAL; + } + attributes +} + +fn item_type_name(item: &Item) -> (&str, &str) { + match item { + Item::Struct(ty) => (ty.namespace.as_str(), ty.name.as_str()), + Item::Enum(ty) => (ty.namespace.as_str(), ty.name.as_str()), + Item::Interface(ty) => (ty.namespace.as_str(), ty.name.as_str()), + } +} + +fn item_value_type(item: &Item) -> bool { + match item { + Item::Struct(_) | Item::Enum(_) => true, + Item::Interface(_) => false, + } +} + +fn method_blob(method: &Method, definitions: &StagedDefinitions, references: &StagedReferences) -> Vec<u8> { + let mut blob = vec![0x20]; // HASTHIS + u32_blob(method.params.len() as _, &mut blob); + for param in &method.params { + type_blob(¶m.ty, &mut blob, definitions, references); + } + type_blob(&method.return_type, &mut blob, definitions, references); + blob +} + +fn field_blob(ty: &Type, definitions: &StagedDefinitions, references: &StagedReferences) -> Vec<u8> { + let mut blob = vec![0x6]; + type_blob(ty, &mut blob, definitions, references); + blob +} + +fn value_blob(value: &Value) -> Vec<u8> { + match value { + Value::I8(value) => value.to_le_bytes().to_vec(), + Value::U8(value) => value.to_le_bytes().to_vec(), + Value::I16(value) => value.to_le_bytes().to_vec(), + Value::U16(value) => value.to_le_bytes().to_vec(), + Value::I32(value) => value.to_le_bytes().to_vec(), + Value::U32(value) => value.to_le_bytes().to_vec(), + Value::I64(value) => value.to_le_bytes().to_vec(), + Value::U64(value) => value.to_le_bytes().to_vec(), + _ => panic!("Unsupported value type"), + } +} + +fn value_to_type(value: &Value) -> Type { + match value { + Value::I8(_) => Type::I8, + Value::U8(_) => Type::U8, + Value::I16(_) => Type::I16, + Value::U16(_) => Type::U16, + Value::I32(_) => Type::I32, + Value::U32(_) => Type::U32, + Value::I64(_) => Type::I64, + Value::U64(_) => Type::U64, + _ => panic!("Unsupported value type"), + } +} + +fn value_type_code(value: &Value) -> u16 { + match value { + Value::I8(_) => 0x04, + Value::U8(_) => 0x05, + Value::I16(_) => 0x06, + Value::U16(_) => 0x07, + Value::I32(_) => 0x08, + Value::U32(_) => 0x09, + Value::I64(_) => 0x0a, + Value::U64(_) => 0x0b, + _ => panic!("Unsupported value type"), + } +} + +fn type_blob(ty: &Type, blob: &mut Vec<u8>, definitions: &StagedDefinitions, references: &StagedReferences) { + match ty { + Type::Void => blob.push(0x01), + Type::Bool => blob.push(0x02), + Type::Char => blob.push(0x03), + Type::I8 => blob.push(0x04), + Type::U8 => blob.push(0x05), + Type::I16 => blob.push(0x06), + Type::U16 => blob.push(0x07), + Type::I32 => blob.push(0x08), + Type::U32 => blob.push(0x09), + Type::I64 => blob.push(0x0a), + Type::U64 => blob.push(0x0b), + Type::F32 => blob.push(0x0c), + Type::F64 => blob.push(0x0d), + Type::ISize => blob.push(0x18), + Type::USize => blob.push(0x19), + Type::String => blob.push(0x0e), + //Type::IInspectable => blob.push(0x1c), + Type::Named((namespace, name)) => { + let (value_type, code) = type_name_encode(namespace, name, definitions, references); + value_type_blob(value_type, blob); + u32_blob(code, blob); + } + } +} + +fn value_type_blob(value_type: bool, blob: &mut Vec<u8>) { + if value_type { + blob.push(0x11); + } else { + blob.push(0x12); + } +} + +fn u32_blob(value: u32, blob: &mut Vec<u8>) { + if value < 0x80 { + blob.push(value as _); + } else if value < 0x4000 { + blob.push((0x40 | (value & 0xFF00)) as _); + blob.push((value | 0xFF) as _); + } else { + blob.push((0x60 | (value & 0xFF000000)) as _); + blob.push((value | 0xFF0000) as _); + blob.push((value | 0xFF00) as _); + blob.push((value | 0xFF) as _); + } +} + +/// Returns the TypeDefOrRef-encoded value for the type name as well as whether the type is a value type, needed +/// in some cases like when a TypeDefOrRef appears in a signature. +fn type_name_encode(namespace: &str, name: &str, definitions: &StagedDefinitions, references: &StagedReferences) -> (bool, u32) { + if let Some(definition) = definitions.get(namespace, name) { + return (definition.value_type, TypeDefOrRef::TypeDef(definition.index + 1).encode()); + } + let reference = references.get(namespace, name).expect("Type not found"); + (reference.value_type, TypeDefOrRef::TypeRef(reference.index + 1).encode()) +} + +pub trait Write { + unsafe fn write_header<T: Sized>(&mut self, value: &T); + fn write_u8(&mut self, value: u8); + fn write_u16(&mut self, value: u16); + fn write_u32(&mut self, value: u32); + fn write_u64(&mut self, value: u64); + fn write_code(&mut self, value: u32, size: usize); + fn write_index(&mut self, index: u32, len: usize); +} + +impl Write for Vec<u8> { + unsafe fn write_header<T: Sized>(&mut self, value: &T) { + self.extend_from_slice(std::slice::from_raw_parts(value as *const _ as _, std::mem::size_of::<T>())); + } + + fn write_u8(&mut self, value: u8) { + self.extend_from_slice(&value.to_le_bytes()); + } + + fn write_u16(&mut self, value: u16) { + self.extend_from_slice(&value.to_le_bytes()); + } + + fn write_u32(&mut self, value: u32) { + self.extend_from_slice(&value.to_le_bytes()); + } + + fn write_u64(&mut self, value: u64) { + self.extend_from_slice(&value.to_le_bytes()); + } + + fn write_code(&mut self, value: u32, size: usize) { + if size == 2 { + self.write_u16(value as _); + } else { + self.write_u32(value); + } + } + + fn write_index(&mut self, index: u32, len: usize) { + if len < (1 << 16) { + self.write_u16(index as u16 + 1); + } else { + self.write_u32(index + 1); + } + } +} + +trait Push2<T> { + fn push2(&mut self, value: T) -> u32; +} + +impl<T> Push2<T> for Vec<T> { + fn push2(&mut self, value: T) -> u32 { + self.push(value); + (self.len() - 1) as _ + } +} diff --git a/vendor/windows-metadata/src/writer/imp/references.rs b/vendor/windows-metadata/src/writer/imp/references.rs new file mode 100644 index 000000000..b35a8a275 --- /dev/null +++ b/vendor/windows-metadata/src/writer/imp/references.rs @@ -0,0 +1,36 @@ +use super::*; + +#[derive(Default)] +pub struct References<'a> { + map: BTreeMap<(&'a str, &'a str), Reference>, +} + +pub struct Reference { + pub index: u32, + pub value_type: bool, +} + +pub struct StagedReferences<'a>(References<'a>); + +impl<'a> References<'a> { + pub fn insert(&mut self, namespace: &'a str, name: &'a str, assemblies: &reader::Reader) { + self.map.entry((namespace, name)).or_insert_with(|| { + let type_def = assemblies.get(reader::TypeName::new(namespace, name)).next().expect("Type not found"); + let value_type = matches!(assemblies.type_def_kind(type_def), reader::TypeKind::Struct | reader::TypeKind::Enum); + Reference { value_type, index: 0 } + }); + } + + pub fn stage(mut self) -> StagedReferences<'a> { + for (index, value) in self.map.values_mut().enumerate() { + value.index = index as _; + } + StagedReferences(self) + } +} + +impl<'a> StagedReferences<'a> { + pub fn get(&'a self, namespace: &'a str, name: &'a str) -> Option<&'a Reference> { + self.0.map.get(&(namespace, name)) + } +} diff --git a/vendor/windows-metadata/src/writer/imp/strings.rs b/vendor/windows-metadata/src/writer/imp/strings.rs new file mode 100644 index 000000000..da20e8cde --- /dev/null +++ b/vendor/windows-metadata/src/writer/imp/strings.rs @@ -0,0 +1,46 @@ +use super::*; + +#[derive(Default)] +pub struct Strings<'a> { + // Strings don't need to be sorted. A map is used to collapse duplicates. A `BTreeMap` in particular is used to help with reproducible builds. + map: BTreeMap<&'a str, u32>, + stream: Vec<u8>, +} + +pub struct StagedStrings<'a>(Strings<'a>); + +impl<'a> Strings<'a> { + pub fn insert(&mut self, value: &'a str) { + if !value.is_empty() { + self.map.entry(value).or_default(); + } + } + + pub fn stage(mut self) -> StagedStrings<'a> { + self.stream = vec![0]; + + for (value, index) in self.map.iter_mut() { + *index = self.stream.len() as _; + + self.stream.extend_from_slice(value.as_bytes()); + self.stream.push(0); // terminator + } + + self.stream.resize(round(self.stream.len(), 4), 0); + StagedStrings(self) + } +} + +impl<'a> StagedStrings<'a> { + pub fn stream(self) -> Vec<u8> { + self.0.stream + } + + pub fn index(&self, value: &str) -> u32 { + if value.is_empty() { + 0 + } else { + self.0.map[value] + } + } +} diff --git a/vendor/windows-metadata/src/writer/imp/tables.rs b/vendor/windows-metadata/src/writer/imp/tables.rs new file mode 100644 index 000000000..7b3450738 --- /dev/null +++ b/vendor/windows-metadata/src/writer/imp/tables.rs @@ -0,0 +1,287 @@ +#![allow(non_snake_case)] + +use super::*; + +#[derive(Default)] +pub struct Tables { + pub AssemblyRef: Vec<AssemblyRef>, + pub ClassLayout: Vec<ClassLayout>, + pub Constant: Vec<Constant>, + pub CustomAttribute: Vec<CustomAttribute>, + pub Field: Vec<Field>, + pub GenericParam: Vec<GenericParam>, + pub ImplMap: Vec<ImplMap>, + pub InterfaceImpl: Vec<InterfaceImpl>, + pub MemberRef: Vec<MemberRef>, + pub MethodDef: Vec<MethodDef>, + pub Module: Vec<Module>, + pub ModuleRef: Vec<ModuleRef>, + pub NestedClass: Vec<NestedClass>, + pub Param: Vec<Param>, + pub Property: Vec<Property>, + pub TypeDef: Vec<TypeDef>, + pub TypeRef: Vec<TypeRef>, + pub TypeSpec: Vec<TypeSpec>, +} + +#[derive(Default)] +pub struct AssemblyRef { + pub MajorVersion: u16, + pub MinorVersion: u16, + pub BuildNumber: u16, + pub RevisionNumber: u16, + pub Flags: u32, + pub PublicKeyOrToken: u32, + pub Name: u32, + pub Culture: u32, + pub HashValue: u32, +} + +#[derive(Default)] +pub struct ClassLayout { + pub PackingSize: u16, + pub ClassSize: u32, + pub Parent: u32, +} + +#[derive(Default)] +pub struct Constant { + pub Type: u16, + pub Parent: u32, + pub Value: u32, +} + +#[derive(Default)] +pub struct CustomAttribute { + pub Parent: u32, + pub Type: u32, + pub Value: u32, +} + +#[derive(Default)] +pub struct Field { + pub Flags: u16, + pub Name: u32, + pub Signature: u32, +} + +#[derive(Default)] +pub struct GenericParam { + pub Number: u16, + pub Flags: u16, + pub Owner: u32, + pub Name: u32, +} + +#[derive(Default)] +pub struct ImplMap { + pub MappingFlags: u16, + pub MemberForwarded: u32, + pub ImportName: u32, + pub ImportScope: u32, +} + +#[derive(Default)] +pub struct InterfaceImpl { + pub Class: u32, + pub Interface: u32, +} + +#[derive(Default)] +pub struct MemberRef { + pub Class: u32, + pub Name: u32, + pub Signature: u32, +} + +#[derive(Default)] +pub struct MethodDef { + pub RVA: u32, + pub ImplFlags: u16, + pub Flags: u16, + pub Name: u32, + pub Signature: u32, + pub ParamList: u32, +} + +#[derive(Default)] +pub struct Module { + pub Generation: u16, + pub Name: u32, + pub Mvid: u32, + pub EncId: u32, + pub EncBaseId: u32, +} + +#[derive(Default)] +pub struct ModuleRef { + pub Name: u32, +} + +#[derive(Default)] +pub struct NestedClass { + pub NestedClass: u32, + pub EnclosingClass: u32, +} + +#[derive(Default)] +pub struct Param { + pub Flags: u16, + pub Sequence: u16, + pub Name: u32, +} + +#[derive(Default)] +pub struct Property { + pub Flags: u16, + pub Name: u32, + pub Type: u32, +} + +#[derive(Default)] +pub struct TypeDef { + pub Flags: u32, + pub TypeName: u32, + pub TypeNamespace: u32, + pub Extends: u32, + pub FieldList: u32, + pub MethodList: u32, +} + +#[derive(Default)] +pub struct TypeRef { + pub ResolutionScope: u32, + pub TypeName: u32, + pub TypeNamespace: u32, +} + +#[derive(Default)] +pub struct TypeSpec { + pub Signature: u32, +} + +impl Tables { + pub fn stream(self) -> Vec<u8> { + let resolution_scope = coded_index_size(&[self.Module.len(), self.ModuleRef.len(), self.AssemblyRef.len(), self.TypeRef.len()]); + let type_def_or_ref = coded_index_size(&[self.TypeDef.len(), self.TypeRef.len(), self.TypeSpec.len()]); + let has_constant = coded_index_size(&[self.Field.len(), self.Param.len(), self.Property.len()]); + + let valid_tables: u64 = 1 << 0 | // Module + 1 << 0x01 | // TypeRef + 1 << 0x02 | // TypeDef + 1 << 0x04 | // Field + 1 << 0x06 | // MethodDef + 1 << 0x08 | // Param + 1 << 0x09 | // InterfaceImpl + 1 << 0x0A | // MemberRef + 1 << 0x0B | // Constant + 1 << 0x0C | // CustomAttribute + 1 << 0x0F | // ClassLayout + 1 << 0x17 | // Property + 1 << 0x1A | // ModuleRef + 1 << 0x1B | // TypeSpec + 1 << 0x1C | // ImplMap + 1 << 0x23 | // AssemblyRef + 1 << 0x29 | // NestedClass + 1 << 0x2A; // GenericParam + + // The table stream header... + + let mut buffer = Vec::new(); + buffer.write_u32(0); // Reserved + buffer.write_u8(2); // MajorVersion + buffer.write_u8(0); // MinorVersion + buffer.write_u8(0b111); // HeapSizes + buffer.write_u8(0); // Reserved + buffer.write_u64(valid_tables); + buffer.write_u64(0); // Sorted + + // Followed by the length of each of the valid tables... + + buffer.write_u32(self.Module.len() as u32); + buffer.write_u32(self.TypeRef.len() as u32); + buffer.write_u32(self.TypeDef.len() as u32); + buffer.write_u32(self.Field.len() as u32); + buffer.write_u32(self.MethodDef.len() as u32); + buffer.write_u32(self.Param.len() as u32); + buffer.write_u32(self.InterfaceImpl.len() as u32); + buffer.write_u32(self.MemberRef.len() as u32); + buffer.write_u32(self.Constant.len() as u32); + buffer.write_u32(self.CustomAttribute.len() as u32); + buffer.write_u32(self.ClassLayout.len() as u32); + buffer.write_u32(self.Property.len() as u32); + buffer.write_u32(self.ModuleRef.len() as u32); + buffer.write_u32(self.TypeSpec.len() as u32); + buffer.write_u32(self.ImplMap.len() as u32); + buffer.write_u32(self.AssemblyRef.len() as u32); + buffer.write_u32(self.NestedClass.len() as u32); + buffer.write_u32(self.GenericParam.len() as u32); + + // Followed by each table's rows... + + for x in self.Module { + buffer.write_u16(x.Generation); + buffer.write_u32(x.Name); + buffer.write_u32(x.Mvid); + buffer.write_u32(x.EncId); + buffer.write_u32(x.EncBaseId); + } + + for x in self.TypeRef { + buffer.write_code(x.ResolutionScope, resolution_scope); + buffer.write_u32(x.TypeName); + buffer.write_u32(x.TypeNamespace); + } + + for x in self.TypeDef { + buffer.write_u32(x.Flags); + buffer.write_u32(x.TypeName); + buffer.write_u32(x.TypeNamespace); + buffer.write_code(x.Extends, type_def_or_ref); + buffer.write_index(x.FieldList, self.Field.len()); + buffer.write_index(x.MethodList, self.MethodDef.len()); + } + + for x in self.Field { + buffer.write_u16(x.Flags); + buffer.write_u32(x.Name); + buffer.write_u32(x.Signature); + } + + for x in self.MethodDef { + buffer.write_u32(x.RVA); + buffer.write_u16(x.ImplFlags); + buffer.write_u16(x.Flags); + buffer.write_u32(x.Name); + buffer.write_u32(x.Signature); + buffer.write_index(x.ParamList, self.Param.len()); + } + + for x in self.Param { + buffer.write_u16(x.Flags); + buffer.write_u16(x.Sequence); + buffer.write_u32(x.Name); + } + + for x in self.Constant { + buffer.write_u16(x.Type); + buffer.write_code(x.Parent, has_constant); + buffer.write_u32(x.Value); + } + + for x in self.AssemblyRef { + buffer.write_u16(x.MajorVersion); + buffer.write_u16(x.MinorVersion); + buffer.write_u16(x.BuildNumber); + buffer.write_u16(x.RevisionNumber); + buffer.write_u32(x.Flags); + buffer.write_u32(x.PublicKeyOrToken); + buffer.write_u32(x.Name); + buffer.write_u32(x.Culture); + buffer.write_u32(x.HashValue); + } + + buffer.resize(round(buffer.len(), 4), 0); + buffer + } +} diff --git a/vendor/windows-metadata/src/writer/mod.rs b/vendor/windows-metadata/src/writer/mod.rs new file mode 100644 index 000000000..8dcf332b8 --- /dev/null +++ b/vendor/windows-metadata/src/writer/mod.rs @@ -0,0 +1,95 @@ +mod imp; +use super::*; + +// Generates an in-memory .winmd file. +pub fn write(name: &str, winrt: bool, items: &[Item], assemblies: &[&str]) -> Vec<u8> { + imp::write(name, winrt, items, assemblies) +} + +pub enum Item { + Struct(Struct), + Enum(Enum), + Interface(Interface), +} + +pub struct Struct { + pub namespace: String, + pub name: String, + pub fields: Vec<Field>, +} + +pub struct Enum { + pub namespace: String, + pub name: String, + pub constants: Vec<Constant>, +} + +pub struct Interface { + pub namespace: String, + pub name: String, + pub methods: Vec<Method>, +} + +pub struct Field { + pub name: String, + pub ty: Type, +} + +pub struct Constant { + pub name: String, + pub value: Value, +} + +pub struct Method { + pub name: String, + pub return_type: Type, + pub params: Vec<Param>, +} + +pub struct Param { + pub name: String, + pub ty: Type, + pub flags: ParamFlags, +} + +flags!(ParamFlags, u32); +impl ParamFlags { + pub const INPUT: Self = Self(0x1); + pub const OUTPUT: Self = Self(0x2); + pub const OPTIONAL: Self = Self(0x10); +} + +pub enum Type { + Void, + Bool, + Char, + I8, + U8, + I16, + U16, + I32, + U32, + I64, + U64, + F32, + F64, + ISize, + USize, + String, + Named((String, String)), +} + +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), +} |