From d3eee4a51e8c5975f2398c679525a040a61ecb35 Mon Sep 17 00:00:00 2001 From: Kenny Kerr Date: Wed, 15 Jun 2022 14:56:47 -0700 Subject: [PATCH] Basic metadata generation (#1820) --- crates/libs/bindgen/src/structs.rs | 10 +- .../libs/metadata/src/{reader => }/flags.rs | 40 ++++++- crates/libs/metadata/src/imp.rs | 45 ++++++++ crates/libs/metadata/src/lib.rs | 50 +-------- crates/libs/metadata/src/reader/mod.rs | 20 ++-- crates/libs/metadata/src/writer/codes.rs | 51 +++++++++ crates/libs/metadata/src/writer/mod.rs | 28 ++--- crates/libs/metadata/src/writer/pe.rs | 5 +- .../src/writer/tables/assembly_ref.rs | 14 +++ .../libs/metadata/src/writer/tables/field.rs | 11 +- crates/libs/metadata/src/writer/tables/mod.rs | 106 ++++++++++++------ .../libs/metadata/src/writer/tables/param.rs | 4 +- .../metadata/src/writer/tables/type_def.rs | 14 ++- .../metadata/src/writer/tables/type_ref.rs | 25 ++++- crates/libs/metadata/src/writer/type_name.rs | 23 ++++ .../tests/{test.rs => fn_call_size.rs} | 0 crates/tests/metadata/tests/writer.rs | 63 +++++++++++ 17 files changed, 379 insertions(+), 130 deletions(-) rename crates/libs/metadata/src/{reader => }/flags.rs (62%) create mode 100644 crates/libs/metadata/src/imp.rs create mode 100644 crates/libs/metadata/src/writer/codes.rs create mode 100644 crates/libs/metadata/src/writer/tables/assembly_ref.rs create mode 100644 crates/libs/metadata/src/writer/type_name.rs rename crates/tests/metadata/tests/{test.rs => fn_call_size.rs} (100%) create mode 100644 crates/tests/metadata/tests/writer.rs diff --git a/crates/libs/bindgen/src/structs.rs b/crates/libs/bindgen/src/structs.rs index d3c65532b6..f6bdaa3a8d 100644 --- a/crates/libs/bindgen/src/structs.rs +++ b/crates/libs/bindgen/src/structs.rs @@ -54,14 +54,14 @@ fn gen_struct_with_name(gen: &Gen, def: TypeDef, struct_name: &str, cfg: &Cfg) - if gen.reader.field_flags(f).literal() { quote! {} - } else if !gen.sys && flags.union() && !gen.reader.field_is_blittable(f, def) { + } else if !gen.sys && flags.explicit_layout() && !gen.reader.field_is_blittable(f, def) { quote! { pub #name: ::core::mem::ManuallyDrop<#ty>, } } else { quote! { pub #name: #ty, } } }); - let struct_or_union = if flags.union() { + let struct_or_union = if flags.explicit_layout() { quote! { union } } else { quote! { struct } @@ -153,7 +153,7 @@ fn gen_compare_traits(gen: &Gen, def: TypeDef, name: &TokenStream, cfg: &Cfg) -> if gen.sys { quote! {} - } else if gen.reader.type_def_is_blittable(def) || gen.reader.type_def_flags(def).union() || gen.reader.type_def_class_layout(def).is_some() { + } else if gen.reader.type_def_is_blittable(def) || gen.reader.type_def_flags(def).explicit_layout() || gen.reader.type_def_class_layout(def).is_some() { quote! { #features impl ::core::cmp::PartialEq for #name { @@ -197,7 +197,7 @@ fn gen_compare_traits(gen: &Gen, def: TypeDef, name: &TokenStream, cfg: &Cfg) -> } fn gen_debug(gen: &Gen, def: TypeDef, ident: &TokenStream, cfg: &Cfg) -> TokenStream { - if gen.sys || gen.reader.type_def_has_union(def) || gen.reader.type_def_has_packing(def) { + if gen.sys || gen.reader.type_def_has_explicit_layout(def) || gen.reader.type_def_has_packing(def) { quote! {} } else { let name = ident.as_str(); @@ -245,7 +245,7 @@ fn gen_copy_clone(gen: &Gen, def: TypeDef, name: &TokenStream, cfg: &Cfg) -> Tok } } } - } else if gen.reader.type_def_flags(def).union() { + } else if gen.reader.type_def_flags(def).explicit_layout() { quote! { #features impl ::core::clone::Clone for #name { diff --git a/crates/libs/metadata/src/reader/flags.rs b/crates/libs/metadata/src/flags.rs similarity index 62% rename from crates/libs/metadata/src/reader/flags.rs rename to crates/libs/metadata/src/flags.rs index 184cafe882..ca6e4a06d7 100644 --- a/crates/libs/metadata/src/reader/flags.rs +++ b/crates/libs/metadata/src/flags.rs @@ -1,8 +1,19 @@ +#[derive(Default)] pub struct FieldAttributes(pub usize); + +#[derive(Default)] pub struct MethodAttributes(pub usize); + +#[derive(Default)] pub struct MethodImplAttributes(pub usize); + +#[derive(Default)] pub struct ParamAttributes(pub usize); + +#[derive(Default)] pub struct PInvokeAttributes(pub usize); + +#[derive(Default)] pub struct TypeAttributes(pub usize); impl FieldAttributes { @@ -27,9 +38,11 @@ impl ParamAttributes { pub fn input(&self) -> bool { self.0 & 0x1 != 0 } + pub fn output(&self) -> bool { self.0 & 0x2 != 0 } + pub fn optional(&self) -> bool { self.0 & 0x10 != 0 } @@ -42,13 +55,38 @@ impl PInvokeAttributes { } impl TypeAttributes { - pub fn union(&self) -> bool { + pub fn public(&self) -> bool { + self.0 & 0x1 != 0 + } + pub fn set_public(&mut self) { + self.0 |= 0x1; + } + + pub fn explicit_layout(&self) -> bool { self.0 & 0x10 != 0 } + pub fn set_explicit_layout(&mut self) { + self.0 |= 0x10; + } + + pub fn get_abstract(&self) -> bool { + self.0 & 0x80 != 0 + } + pub fn set_abstract(&mut self) { + self.0 |= 0x80; + } + pub fn winrt(&self) -> bool { self.0 & 0x4000 != 0 } + pub fn set_winrt(&mut self) { + self.0 |= 0x4000; + } + pub fn interface(&self) -> bool { self.0 & 0x20 != 0 } + pub fn set_interface(&mut self) { + self.0 |= 0x20 + } } diff --git a/crates/libs/metadata/src/imp.rs b/crates/libs/metadata/src/imp.rs new file mode 100644 index 0000000000..465272af4d --- /dev/null +++ b/crates/libs/metadata/src/imp.rs @@ -0,0 +1,45 @@ +#[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; + +extern "C" { + pub fn strlen(cs: *const u8) -> usize; +} + +pub fn composite_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; + loop { + value >>= 1; + if value == 0 { + break; + } + 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/crates/libs/metadata/src/lib.rs b/crates/libs/metadata/src/lib.rs index 3773c94b0b..85d133a38c 100644 --- a/crates/libs/metadata/src/lib.rs +++ b/crates/libs/metadata/src/lib.rs @@ -1,55 +1,13 @@ #![allow(dead_code)] use std::collections::*; +mod flags; +mod imp; pub mod reader; pub mod writer; +pub use flags::*; +use imp::*; use std::io::*; use std::mem::*; use std::ptr::*; - -#[repr(C)] -#[derive(Default)] -struct METADATA_HEADER { - signature: u32, - major_version: u16, - minor_version: u16, - reserved: u32, - length: u32, - version: [u8; 20], - flags: u16, - streams: u16, -} - -const METADATA_SIGNATURE: u32 = 0x424A_5342; - -extern "C" { - fn strlen(cs: *const u8) -> usize; -} - -fn composite_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; - loop { - value >>= 1; - if value == 0 { - break; - } - 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/crates/libs/metadata/src/reader/mod.rs b/crates/libs/metadata/src/reader/mod.rs index a6a1942ba6..a3946198f9 100644 --- a/crates/libs/metadata/src/reader/mod.rs +++ b/crates/libs/metadata/src/reader/mod.rs @@ -1,7 +1,6 @@ mod blob; mod codes; mod file; -mod flags; mod guid; mod row; mod tree; @@ -12,7 +11,6 @@ use super::*; pub use blob::*; pub use codes::*; pub use file::*; -pub use flags::*; pub use guid::*; pub use r#type::*; pub use row::*; @@ -94,7 +92,7 @@ pub enum AsyncKind { OperationWithProgress, } -#[derive(PartialEq, Eq)] +#[derive(PartialEq, Eq, Debug)] pub enum TypeKind { Interface, Class, @@ -589,7 +587,7 @@ impl<'a> Reader<'a> { } fn type_def_size(reader: &Reader, def: TypeDef) -> usize { if reader.type_def_kind(def) == TypeKind::Struct { - if reader.type_def_flags(def).union() { + if reader.type_def_flags(def).explicit_layout() { reader.type_def_fields(def).map(|field| type_size(reader, &reader.field_type(field, Some(def)))).max().unwrap_or(1) } else { reader.type_def_fields(def).fold(0, |sum, field| sum + type_size(reader, &reader.field_type(field, Some(def)))) @@ -721,7 +719,7 @@ impl<'a> Reader<'a> { } pub fn type_def_stdcall(&self, row: TypeDef) -> usize { if self.type_def_kind(row) == TypeKind::Struct { - if self.type_def_flags(row).union() { + if self.type_def_flags(row).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)))) @@ -806,15 +804,15 @@ impl<'a> Reader<'a> { _ => false, } } - pub fn type_def_has_union(&self, row: TypeDef) -> bool { + 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).union() { + if reader.type_def_flags(row).explicit_layout() { return true; } - if reader.type_def_fields(row).any(|field| reader.type_has_union(&reader.field_type(field, Some(row)))) { + if reader.type_def_fields(row).any(|field| reader.type_has_explicit_layout(&reader.field_type(field, Some(row)))) { return true; } false @@ -1357,10 +1355,10 @@ impl<'a> Reader<'a> { _ => true, } } - pub fn type_has_union(&self, ty: &Type) -> bool { + pub fn type_has_explicit_layout(&self, ty: &Type) -> bool { match ty { - Type::TypeDef((row, _)) => self.type_def_has_union(*row), - Type::Win32Array((ty, _)) => self.type_has_union(ty), + Type::TypeDef((row, _)) => self.type_def_has_explicit_layout(*row), + Type::Win32Array((ty, _)) => self.type_has_explicit_layout(ty), _ => false, } } diff --git a/crates/libs/metadata/src/writer/codes.rs b/crates/libs/metadata/src/writer/codes.rs new file mode 100644 index 0000000000..4f9a07d270 --- /dev/null +++ b/crates/libs/metadata/src/writer/codes.rs @@ -0,0 +1,51 @@ +#[derive(Clone, Copy)] +pub enum ResolutionScope { + None, + Module(usize), + ModuleRef(usize), + AssemblyRef(usize), + TypeRef(usize), +} + +impl ResolutionScope { + pub fn encode(&self) -> usize { + 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, + _ => unimplemented!(), + } + } +} + +impl Default for ResolutionScope { + fn default() -> Self { + Self::None + } +} + +#[derive(Clone, Copy)] +pub enum TypeDefOrRef { + None, + TypeDef(usize), + TypeRef(usize), + TypeSpec(usize), +} + +impl TypeDefOrRef { + pub fn encode(&self) -> usize { + match self { + Self::TypeDef(row) => ((row + 1) << 2), + Self::TypeRef(row) => ((row + 1) << 2) + 1, + Self::TypeSpec(row) => ((row + 1) << 2) + 2, + _ => 0, + } + } +} + +impl Default for TypeDefOrRef { + fn default() -> Self { + Self::None + } +} diff --git a/crates/libs/metadata/src/writer/mod.rs b/crates/libs/metadata/src/writer/mod.rs index d6e0503e5b..5f13d6ced5 100644 --- a/crates/libs/metadata/src/writer/mod.rs +++ b/crates/libs/metadata/src/writer/mod.rs @@ -1,29 +1,17 @@ mod blobs; +mod codes; mod gen; mod helpers; -mod pe; +pub mod pe; mod strings; mod tables; -use blobs::*; +mod type_name; use super::*; +use blobs::*; +use codes::*; pub use gen::*; -use helpers::*; +pub use helpers::*; use strings::*; -use tables::*; - -pub fn test() { - let mut tables = Tables::new(); - tables.module.push(Module::new("test.winmd")); - tables.type_def.push(TypeDef::module()); - - let mut stringable = TypeDef::winrt_interface("IStringable", "Windows.Foundation"); - stringable.method_list.push(MethodDef::new("ToString")); - tables.type_def.push(stringable); - - let mut closable = TypeDef::winrt_interface("IClosable", "Windows.Foundation"); - closable.method_list.push(MethodDef::new("Close")); - tables.type_def.push(closable); - - pe::write("/git/test.winmd", tables); -} +pub use tables::*; +pub use type_name::*; diff --git a/crates/libs/metadata/src/writer/pe.rs b/crates/libs/metadata/src/writer/pe.rs index 526a739bc0..7a91ca8d36 100644 --- a/crates/libs/metadata/src/writer/pe.rs +++ b/crates/libs/metadata/src/writer/pe.rs @@ -1,7 +1,7 @@ use super::*; use windows_sys::{Win32::System::Diagnostics::Debug::*, Win32::System::SystemServices::*}; -pub(crate) fn write(filename: &str, tables: Tables) { +pub fn write(filename: &str, tables: Tables) { let mut dos: IMAGE_DOS_HEADER = unsafe { zeroed() }; dos.e_magic = IMAGE_DOS_SIGNATURE as _; dos.e_lfarlc = 64; @@ -80,7 +80,8 @@ pub(crate) fn write(filename: &str, tables: Tables) { let mut buffer = Vec::::new(); buffer.write(&dos); - buffer.write(&IMAGE_NT_SIGNATURE); + // TODO: workaround for https://github.com/microsoft/win32metadata/issues/963 + buffer.write(&(IMAGE_NT_SIGNATURE as u32)); buffer.write(&file); buffer.write(&optional); buffer.write(§ion); diff --git a/crates/libs/metadata/src/writer/tables/assembly_ref.rs b/crates/libs/metadata/src/writer/tables/assembly_ref.rs new file mode 100644 index 0000000000..fa88ae32b3 --- /dev/null +++ b/crates/libs/metadata/src/writer/tables/assembly_ref.rs @@ -0,0 +1,14 @@ +#[derive(Default, Clone)] +pub struct AssemblyRef { + pub major_version: u16, + pub minor_version: u16, + pub build_number: u16, + pub revision_number: u16, + pub name: String, +} + +impl AssemblyRef { + pub fn mscorlib() -> Self { + Self { name: "mscorlib".to_string(), major_version: 4, ..Default::default() } + } +} diff --git a/crates/libs/metadata/src/writer/tables/field.rs b/crates/libs/metadata/src/writer/tables/field.rs index 5a17501890..56ab71f384 100644 --- a/crates/libs/metadata/src/writer/tables/field.rs +++ b/crates/libs/metadata/src/writer/tables/field.rs @@ -1,2 +1,11 @@ #[derive(Default)] -pub struct Field {} +pub struct Field { + pub name: String, + pub signature: Vec, +} + +impl Field { + pub fn new(name: &str) -> Self { + Self { name: name.to_string(), ..Default::default() } + } +} diff --git a/crates/libs/metadata/src/writer/tables/mod.rs b/crates/libs/metadata/src/writer/tables/mod.rs index 26f041907f..0658dda561 100644 --- a/crates/libs/metadata/src/writer/tables/mod.rs +++ b/crates/libs/metadata/src/writer/tables/mod.rs @@ -1,5 +1,6 @@ pub use super::*; +mod assembly_ref; mod class_layout; mod constant; mod custom_attribute; @@ -17,6 +18,7 @@ mod type_def; mod type_ref; mod type_spec; +pub use assembly_ref::*; pub use class_layout::*; pub use constant::*; pub use custom_attribute::*; @@ -50,18 +52,25 @@ pub struct Tables { pub module_ref: Vec, pub type_spec: Vec, pub impl_map: Vec, + pub assembly_ref: Vec, pub nested_class: Vec, pub generic_param: Vec, } impl Tables { - pub fn new() -> Self { - Self::default() + pub fn new(module: &str) -> Self { + let mut new = Self::default(); + new.module.push(Module::new(module)); + new.type_def.push(TypeDef::module()); + new } pub(crate) fn into_stream(mut self, strings: &mut Strings, blobs: &mut Blobs) -> Vec { self.normalize(); + let resolution_scope = composite_index_size(&[self.module.len(), self.module_ref.len(), self.assembly_ref.len(), self.type_ref.len()]); + let type_def_or_ref = composite_index_size(&[self.type_def.len(), self.type_ref.len(), self.type_spec.len()]); + let mut buffer = Vec::new(); let header = Header::new(); buffer.write(&header); @@ -80,6 +89,7 @@ impl Tables { buffer.write(&(self.module_ref.len() as u32)); buffer.write(&(self.type_spec.len() as u32)); buffer.write(&(self.impl_map.len() as u32)); + buffer.write(&(self.assembly_ref.len() as u32)); buffer.write(&(self.nested_class.len() as u32)); buffer.write(&(self.generic_param.len() as u32)); @@ -91,15 +101,27 @@ impl Tables { buffer.write(&0u32); // EncBaseId (reserved) } + for type_ref in &self.type_ref { + write_coded_index(&mut buffer, type_ref.assembly_index.encode(), resolution_scope); + buffer.write(&strings.insert(&type_ref.type_name.name)); + buffer.write(&strings.insert(&type_ref.type_name.namespace)); + } + for type_def in &self.type_def { - buffer.write(&type_def.flags); - buffer.write(&strings.insert(&type_def.name)); - buffer.write(&strings.insert(&type_def.namespace)); - buffer.write(&0u16); // Extends + buffer.write(&(type_def.flags.0 as u32)); + buffer.write(&strings.insert(&type_def.type_name.name)); + buffer.write(&strings.insert(&type_def.type_name.namespace)); + write_coded_index(&mut buffer, type_def.extends_index.encode(), type_def_or_ref); write_index(&mut buffer, type_def.field_index, self.field.len()); write_index(&mut buffer, type_def.method_index, self.method_def.len()); } + for field in &self.field { + buffer.write(&0u16); // Flags + buffer.write(&strings.insert(&field.name)); + buffer.write(&blobs.insert(&field.signature)); + } + for method_def in &self.method_def { buffer.write(&0u32); // RVA buffer.write(&0u16); // ImplFlags @@ -110,29 +132,61 @@ impl Tables { } for param in &self.param { - buffer.write(¶m.flags); + buffer.write(&(param.flags.0 as u16)); buffer.write(¶m.sequence); buffer.write(&strings.insert(¶m.name)); } + for assembly_ref in &self.assembly_ref { + buffer.write(&assembly_ref.major_version); + buffer.write(&assembly_ref.minor_version); + buffer.write(&assembly_ref.build_number); + buffer.write(&assembly_ref.revision_number); + buffer.write(&0u32); // Flags + buffer.write(&0u32); // PublicKeyOrToken + buffer.write(&strings.insert(&assembly_ref.name)); + buffer.write(&0u32); // Culture + buffer.write(&0u32); // HashValue + } + buffer.resize(round(buffer.len(), 4), 0); buffer } // Once all of the type information has been added, normalization is the process of packing - // the various relational records into their respective tables and leaving only offsets behind. + // the various relational records into their respective tables and leaving only indexes behind. fn normalize(&mut self) { for type_def in &mut self.type_def { type_def.field_index = self.field.len(); type_def.method_index = self.method_def.len(); self.field.append(&mut type_def.field_list); self.method_def.append(&mut type_def.method_list); + + if let Some(extends) = type_def.extends.take() { + let index = if let Some(index) = self.type_ref.iter().position(|row| row.type_name == extends.type_name) { + index + } else { + self.type_ref.push(extends); + self.type_ref.len() - 1 + }; + type_def.extends_index = TypeDefOrRef::TypeRef(index); + } } for method_def in &mut self.method_def { method_def.param_index = self.param.len(); self.param.append(&mut method_def.param_list); } + + for type_ref in &mut self.type_ref { + let index = if let Some(index) = self.assembly_ref.iter().position(|row| row.name == type_ref.assembly_ref.name) { + index + } else { + self.assembly_ref.push(type_ref.assembly_ref.clone()); + self.assembly_ref.len() - 1 + }; + type_ref.assembly_index = ResolutionScope::AssemblyRef(index); + } } } @@ -166,8 +220,9 @@ impl Header { 1 << 0x0C | // CustomAttribute 1 << 0x0F | // ClassLayout 1 << 0x1A | // ModuleRef - 1 << 0x1B | // TypeSpec, + 1 << 0x1B | // TypeSpec 1 << 0x1C | // ImplMap + 1 << 0x23 | // AssemblyRef 1 << 0x29 | // NestedClass 1 << 0x2A, // GenericParam // TODO: mark sorted tables? @@ -184,29 +239,10 @@ fn write_index(buffer: &mut Vec, index: usize, len: usize) { } } -// fn coded_index_size(tables: &[usize]) -> u32 { -// 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; -// loop { -// value >>= 1; -// if value == 0 { -// break; -// } -// bits += 1; -// } -// bits -// } - -// let bits_needed = bits_needed(tables.len()); - -// if tables.iter().all(|len| small(*len, bits_needed)) { -// 2 -// } else { -// 4 -// } -// } +fn write_coded_index(buffer: &mut Vec, value: usize, size: usize) { + if size == 2 { + buffer.write(&(value as u16)) + } else { + buffer.write(&(value as u32)) + } +} diff --git a/crates/libs/metadata/src/writer/tables/param.rs b/crates/libs/metadata/src/writer/tables/param.rs index d9611d3155..da94cc70dd 100644 --- a/crates/libs/metadata/src/writer/tables/param.rs +++ b/crates/libs/metadata/src/writer/tables/param.rs @@ -1,6 +1,8 @@ +use super::*; + #[derive(Default)] pub struct Param { - pub flags: u16, + pub flags: ParamAttributes, pub sequence: u16, pub name: String, } diff --git a/crates/libs/metadata/src/writer/tables/type_def.rs b/crates/libs/metadata/src/writer/tables/type_def.rs index 4624e97746..e43adfc5c3 100644 --- a/crates/libs/metadata/src/writer/tables/type_def.rs +++ b/crates/libs/metadata/src/writer/tables/type_def.rs @@ -2,21 +2,23 @@ use super::*; #[derive(Default)] pub struct TypeDef { - pub flags: u32, - pub name: String, - pub namespace: String, + pub flags: TypeAttributes, + pub type_name: TypeName, + pub extends: Option, pub field_list: Vec, pub method_list: Vec, + pub(crate) field_index: usize, pub(crate) method_index: usize, + pub(crate) extends_index: TypeDefOrRef, } impl TypeDef { pub fn module() -> Self { - Self { name: "".to_string(), ..Default::default() } + Self::new(TypeName::new("", "")) } - pub fn winrt_interface(name: &str, namespace: &str) -> Self { - Self { name: name.to_string(), namespace: namespace.to_string(), ..Default::default() } + pub fn new(type_name: TypeName) -> Self { + Self { type_name, ..Default::default() } } } diff --git a/crates/libs/metadata/src/writer/tables/type_ref.rs b/crates/libs/metadata/src/writer/tables/type_ref.rs index aa019e521b..0827eac3ac 100644 --- a/crates/libs/metadata/src/writer/tables/type_ref.rs +++ b/crates/libs/metadata/src/writer/tables/type_ref.rs @@ -1,2 +1,23 @@ -#[derive(Default)] -pub struct TypeRef {} +use super::*; + +#[derive(Default, Clone)] +pub struct TypeRef { + pub type_name: TypeName, + pub assembly_ref: AssemblyRef, + + pub(crate) assembly_index: ResolutionScope, +} + +impl TypeRef { + pub fn system_value_type() -> Self { + Self { assembly_ref: AssemblyRef::mscorlib(), type_name: TypeName::system_value_type(), ..Default::default() } + } + + pub fn system_enum() -> Self { + Self { assembly_ref: AssemblyRef::mscorlib(), type_name: TypeName::system_enum(), ..Default::default() } + } + + pub fn system_delegate() -> Self { + Self { assembly_ref: AssemblyRef::mscorlib(), type_name: TypeName::system_delegate(), ..Default::default() } + } +} diff --git a/crates/libs/metadata/src/writer/type_name.rs b/crates/libs/metadata/src/writer/type_name.rs new file mode 100644 index 0000000000..e32fe9dd78 --- /dev/null +++ b/crates/libs/metadata/src/writer/type_name.rs @@ -0,0 +1,23 @@ +#[derive(Clone, Default, PartialEq, PartialOrd, Eq, Ord)] +pub struct TypeName { + pub namespace: String, + pub name: String, +} + +impl TypeName { + pub fn new(namespace: &str, name: &str) -> Self { + Self { namespace: namespace.to_string(), name: name.to_string() } + } + + pub fn system_value_type() -> Self { + Self::new("System", "ValueType") + } + + pub fn system_enum() -> Self { + Self::new("System", "Enum") + } + + pub fn system_delegate() -> Self { + Self::new("System", "MulticastDelegate") + } +} diff --git a/crates/tests/metadata/tests/test.rs b/crates/tests/metadata/tests/fn_call_size.rs similarity index 100% rename from crates/tests/metadata/tests/test.rs rename to crates/tests/metadata/tests/fn_call_size.rs diff --git a/crates/tests/metadata/tests/writer.rs b/crates/tests/metadata/tests/writer.rs new file mode 100644 index 0000000000..5cd59c04a6 --- /dev/null +++ b/crates/tests/metadata/tests/writer.rs @@ -0,0 +1,63 @@ +#[test] +fn writer() { + let temp_file = std::env::temp_dir().join("test_metadata.winmd"); + { + use metadata::writer::*; + + let mut tables = Tables::new("test.winmd"); + + let mut def = TypeDef::new(TypeName::new("TestWindows.Foundation", "IStringable")); + def.flags.set_public(); + def.flags.set_abstract(); + def.flags.set_winrt(); + def.flags.set_interface(); + + let mut method = MethodDef::new("ToString"); + method.param_list.push(Param { name: "param123".to_string(), sequence: 123, ..Default::default() }); + def.method_list.push(method); + + tables.type_def.push(def); + + let mut def = TypeDef::new(TypeName::new("TestWindows.Foundation", "Rect")); + def.flags.set_public(); + def.flags.set_winrt(); + def.extends = Some(TypeRef::system_value_type()); + def.field_list.push(Field::new("Height")); + tables.type_def.push(def); + + let mut def = TypeDef::new(TypeName::new("TestWindows.Foundation", "AsyncStatus")); + def.flags.set_public(); + def.flags.set_winrt(); + def.extends = Some(TypeRef::system_enum()); + tables.type_def.push(def); + + pe::write(temp_file.to_str().unwrap(), tables); + } + { + use metadata::reader::*; + + let files = vec![File::new(temp_file.to_str().unwrap()).unwrap()]; + let reader = &Reader::new(&files); + let def = reader.get(TypeName::new("TestWindows.Foundation", "IStringable")).next().unwrap(); + assert_eq!(reader.type_def_kind(def), TypeKind::Interface); + assert!(reader.type_def_flags(def).winrt()); + + let method = reader.type_def_methods(def).next().unwrap(); + assert_eq!(reader.method_def_name(method), "ToString"); + + let param = reader.method_def_params(method).next().unwrap(); + assert_eq!(reader.param_name(param), "param123"); + assert_eq!(reader.param_sequence(param), 123); + + let def = reader.get(TypeName::new("TestWindows.Foundation", "Rect")).next().unwrap(); + assert_eq!(reader.type_def_kind(def), TypeKind::Struct); + assert!(reader.type_def_flags(def).winrt()); + + let field = reader.type_def_fields(def).next().unwrap(); + assert_eq!(reader.field_name(field), "Height"); + + let def = reader.get(TypeName::new("TestWindows.Foundation", "AsyncStatus")).next().unwrap(); + assert_eq!(reader.type_def_kind(def), TypeKind::Enum); + assert!(reader.type_def_flags(def).winrt()); + } +}