From 593ff2e91ff361d8decd0ed9dd2e9bf03997a09b Mon Sep 17 00:00:00 2001 From: Kenny Kerr Date: Tue, 30 May 2023 16:17:22 -0500 Subject: [PATCH] Metadata parsing and generation (#2525) --- .github/workflows/clippy.yml | 1 + .github/workflows/test.yml | 3 +- .gitignore | 1 - crates/libs/bindgen/src/com_methods.rs | 4 +- crates/libs/bindgen/src/delegates.rs | 2 +- crates/libs/bindgen/src/enums.rs | 4 +- crates/libs/bindgen/src/functions.rs | 2 +- crates/libs/bindgen/src/gen.rs | 38 +- crates/libs/bindgen/src/implements.rs | 4 +- crates/libs/bindgen/src/interfaces.rs | 4 +- crates/libs/bindgen/src/lib.rs | 2 +- crates/libs/bindgen/src/structs.rs | 20 +- crates/libs/bindgen/src/winrt_methods.rs | 6 +- crates/libs/metadata/src/attributes.rs | 66 +-- crates/libs/metadata/src/lib.rs | 2 +- crates/libs/metadata/src/reader/codes.rs | 24 +- crates/libs/metadata/src/reader/mod.rs | 59 +-- crates/libs/metadata/src/reader/type_name.rs | 1 - crates/libs/metadata/src/writer/idl/format.rs | 1 + crates/libs/metadata/src/writer/idl/mod.rs | 3 + crates/libs/metadata/src/writer/idl/read.rs | 458 ++++++++++-------- crates/libs/metadata/src/writer/idl/write.rs | 2 + crates/libs/metadata/src/writer/mod.rs | 5 +- crates/libs/metadata/src/writer/winmd/read.rs | 4 +- .../metadata/src/writer/winmd/write/codes.rs | 47 +- .../metadata/src/writer/winmd/write/file.rs | 2 +- .../metadata/src/writer/winmd/write/mod.rs | 42 +- .../metadata/src/writer/winmd/write/tables.rs | 28 ++ crates/tests/riddle/Cargo.toml | 8 + crates/tests/riddle/src/lib.rs | 35 ++ crates/tests/riddle/tests/struct.idl | 8 + crates/tests/riddle/tests/struct.rs | 29 ++ crates/tools/lib/src/lib.rs | 4 +- 33 files changed, 542 insertions(+), 377 deletions(-) create mode 100644 crates/tests/riddle/Cargo.toml create mode 100644 crates/tests/riddle/src/lib.rs create mode 100644 crates/tests/riddle/tests/struct.idl create mode 100644 crates/tests/riddle/tests/struct.rs diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index acc796c79c..76d122148e 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -109,6 +109,7 @@ jobs: cargo clippy -p test_reserved && cargo clippy -p test_resources && cargo clippy -p test_return_struct && + cargo clippy -p test_riddle && cargo clippy -p test_simple_component && cargo clippy -p test_standalone && cargo clippy -p test_string_param && diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 437ba0477d..4aade96da2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -96,8 +96,8 @@ jobs: cargo test -p test_enums && cargo test -p test_error && cargo test -p test_event && - cargo clean && cargo test -p test_extensions && + cargo clean && cargo test -p test_handles && cargo test -p test_helpers && cargo test -p test_implement && @@ -116,6 +116,7 @@ jobs: cargo test -p test_reserved && cargo test -p test_resources && cargo test -p test_return_struct && + cargo test -p test_riddle && cargo test -p test_simple_component && cargo test -p test_standalone && cargo test -p test_string_param && diff --git a/.gitignore b/.gitignore index 4b438598c7..b473848567 100644 --- a/.gitignore +++ b/.gitignore @@ -9,5 +9,4 @@ bin *.user *.filters *.bin -*.idl *.winmd diff --git a/crates/libs/bindgen/src/com_methods.rs b/crates/libs/bindgen/src/com_methods.rs index b9189acdf8..02281e37f1 100644 --- a/crates/libs/bindgen/src/com_methods.rs +++ b/crates/libs/bindgen/src/com_methods.rs @@ -220,7 +220,7 @@ fn gen_win32_invoke_arg(gen: &Gen, param: &SignatureParam) -> TokenStream { if gen .reader .param_flags(param.def) - .contains(ParamAttributes::INPUT) + .contains(ParamAttributes::In) && gen.reader.type_is_nullable(¶m.ty) { quote! { ::windows_core::from_raw_borrowed(&#name) } @@ -228,7 +228,7 @@ fn gen_win32_invoke_arg(gen: &Gen, param: &SignatureParam) -> TokenStream { || (gen .reader .param_flags(param.def) - .contains(ParamAttributes::INPUT) + .contains(ParamAttributes::In) && !gen.reader.type_is_primitive(¶m.ty)) { quote! { ::core::mem::transmute(&#name) } diff --git a/crates/libs/bindgen/src/delegates.rs b/crates/libs/bindgen/src/delegates.rs index 508b5a64d3..6d2926620b 100644 --- a/crates/libs/bindgen/src/delegates.rs +++ b/crates/libs/bindgen/src/delegates.rs @@ -4,7 +4,7 @@ pub fn gen(gen: &Gen, def: TypeDef) -> TokenStream { if gen .reader .type_def_flags(def) - .contains(TypeAttributes::WINRT) + .contains(TypeAttributes::WindowsRuntime) { gen_delegate(gen, def) } else { diff --git a/crates/libs/bindgen/src/enums.rs b/crates/libs/bindgen/src/enums.rs index e5be33769d..d241761968 100644 --- a/crates/libs/bindgen/src/enums.rs +++ b/crates/libs/bindgen/src/enums.rs @@ -17,7 +17,7 @@ pub fn gen(gen: &Gen, def: TypeDef) -> TokenStream { if gen .reader .field_flags(field) - .contains(FieldAttributes::LITERAL) + .contains(FieldAttributes::Literal) { let field_name = to_ident(gen.reader.field_name(field)); let constant = gen.reader.field_constant(field).unwrap(); @@ -185,7 +185,7 @@ pub fn gen(gen: &Gen, def: TypeDef) -> TokenStream { if gen .reader .type_def_flags(def) - .contains(TypeAttributes::WINRT) + .contains(TypeAttributes::WindowsRuntime) { let signature = Literal::byte_string(gen.reader.type_def_signature(def, &[]).as_bytes()); diff --git a/crates/libs/bindgen/src/functions.rs b/crates/libs/bindgen/src/functions.rs index eb04be555f..df1446106d 100644 --- a/crates/libs/bindgen/src/functions.rs +++ b/crates/libs/bindgen/src/functions.rs @@ -270,7 +270,7 @@ fn handle_last_error(gen: &Gen, def: MethodDef, signature: &Signature) -> bool { if gen .reader .impl_map_flags(map) - .contains(PInvokeAttributes::LAST_ERROR) + .contains(PInvokeAttributes::SupportsLastError) { if let Type::TypeDef((return_type, _)) = &signature.return_type { if gen.reader.type_def_is_handle(*return_type) { diff --git a/crates/libs/bindgen/src/gen.rs b/crates/libs/bindgen/src/gen.rs index 34e441aa02..6674857a10 100644 --- a/crates/libs/bindgen/src/gen.rs +++ b/crates/libs/bindgen/src/gen.rs @@ -203,7 +203,7 @@ impl<'a> Gen<'a> { if self .reader .type_def_flags(*def) - .contains(TypeAttributes::WINRT) + .contains(TypeAttributes::WindowsRuntime) { quote! { *mut ::core::ffi::c_void } } else { @@ -692,7 +692,7 @@ impl<'a> Gen<'a> { if self .reader .type_def_flags(def) - .contains(TypeAttributes::WINRT) + .contains(TypeAttributes::WindowsRuntime) { let type_signature = if self.reader.type_def_kind(def) == TypeKind::Class { let type_signature = @@ -756,7 +756,7 @@ impl<'a> Gen<'a> { if self .reader .type_def_flags(def) - .contains(TypeAttributes::WINRT) + .contains(TypeAttributes::WindowsRuntime) { // TODO: this needs to use a ConstBuffer-like facility to accomodate generics let runtime_name = format!("{}", self.reader.type_def_type_name(def)); @@ -918,7 +918,7 @@ impl<'a> Gen<'a> { let is_winrt = self .reader .type_def_flags(def) - .contains(TypeAttributes::WINRT); + .contains(TypeAttributes::WindowsRuntime); let crate_name = self.crate_name(); @@ -958,11 +958,7 @@ impl<'a> Gen<'a> { let abi_size_name: TokenStream = format!("{}_array_size", self.reader.param_name(p.def)).into(); - if self - .reader - .param_flags(p.def) - .contains(ParamAttributes::INPUT) - { + if self.reader.param_flags(p.def).contains(ParamAttributes::In) { if p.ty.is_winrt_array() { quote! { #abi_size_name: u32, #name: *const #abi, } } else if p.ty.is_const_ref() { @@ -1036,7 +1032,7 @@ impl<'a> Gen<'a> { SignatureParamKind::ArrayFixed(_) | SignatureParamKind::ArrayRelativeLen(_) | SignatureParamKind::ArrayRelativeByteLen(_) => { - let map = if flags.contains(ParamAttributes::OPTIONAL) { + let map = if flags.contains(ParamAttributes::Optional) { quote! { #name.as_deref().map_or(::core::ptr::null(), |slice|slice.as_ptr()) } } else { quote! { #name.as_ptr() } @@ -1046,7 +1042,7 @@ impl<'a> Gen<'a> { SignatureParamKind::ArrayRelativePtr(relative) => { let name = self.param_name(params[relative].def); let flags = self.reader.param_flags(params[relative].def); - if flags.contains(ParamAttributes::OPTIONAL) { + if flags.contains(ParamAttributes::Optional) { quote! { #name.as_deref().map_or(0, |slice|slice.len() as _), } } else { quote! { #name.len() as _, } @@ -1059,7 +1055,7 @@ impl<'a> Gen<'a> { quote! { #name.into_param().abi(), } } SignatureParamKind::OptionalPointer => { - if flags.contains(ParamAttributes::OUTPUT) { + if flags.contains(ParamAttributes::Out) { quote! { ::core::mem::transmute(#name.unwrap_or(::std::ptr::null_mut())), } } else { quote! { ::core::mem::transmute(#name.unwrap_or(::std::ptr::null())), } @@ -1111,7 +1107,7 @@ impl<'a> Gen<'a> { let ty = if self .reader .param_flags(param.def) - .contains(ParamAttributes::OUTPUT) + .contains(ParamAttributes::Out) { quote! { &mut [#ty; #len] } } else { @@ -1120,7 +1116,7 @@ impl<'a> Gen<'a> { if self .reader .param_flags(param.def) - .contains(ParamAttributes::OPTIONAL) + .contains(ParamAttributes::Optional) { tokens.combine("e! { #name: ::core::option::Option<#ty>, }); } else { @@ -1133,7 +1129,7 @@ impl<'a> Gen<'a> { let ty = if self .reader .param_flags(param.def) - .contains(ParamAttributes::OUTPUT) + .contains(ParamAttributes::Out) { quote! { &mut [#ty] } } else { @@ -1142,7 +1138,7 @@ impl<'a> Gen<'a> { if self .reader .param_flags(param.def) - .contains(ParamAttributes::OPTIONAL) + .contains(ParamAttributes::Optional) { tokens.combine("e! { #name: ::core::option::Option<#ty>, }); } else { @@ -1153,7 +1149,7 @@ impl<'a> Gen<'a> { let ty = if self .reader .param_flags(param.def) - .contains(ParamAttributes::OUTPUT) + .contains(ParamAttributes::Out) { quote! { &mut [u8] } } else { @@ -1162,7 +1158,7 @@ impl<'a> Gen<'a> { if self .reader .param_flags(param.def) - .contains(ParamAttributes::OPTIONAL) + .contains(ParamAttributes::Optional) { tokens.combine("e! { #name: ::core::option::Option<#ty>, }); } else { @@ -1197,7 +1193,7 @@ impl<'a> Gen<'a> { if self .reader .type_def_flags(def) - .contains(TypeAttributes::WINRT) + .contains(TypeAttributes::WindowsRuntime) { let is_delegate = self.reader.type_def_kind(def) == TypeKind::Delegate; let params = signature @@ -1262,7 +1258,7 @@ impl<'a> Gen<'a> { let sig = if self .reader .param_flags(param.def) - .contains(ParamAttributes::INPUT) + .contains(ParamAttributes::In) { if param.ty.is_winrt_array() { quote! { &[#default_type] } @@ -1297,7 +1293,7 @@ impl<'a> Gen<'a> { if self .reader .param_flags(param.def) - .contains(ParamAttributes::INPUT) + .contains(ParamAttributes::In) { if self.reader.type_is_primitive(¶m.ty) { quote! { #name: #kind, } diff --git a/crates/libs/bindgen/src/implements.rs b/crates/libs/bindgen/src/implements.rs index 729ef6bcf8..275576eb93 100644 --- a/crates/libs/bindgen/src/implements.rs +++ b/crates/libs/bindgen/src/implements.rs @@ -49,7 +49,7 @@ pub fn gen(gen: &Gen, def: TypeDef) -> TokenStream { if gen .reader .type_def_flags(def) - .contains(TypeAttributes::WINRT) + .contains(TypeAttributes::WindowsRuntime) { // TODO: this awkward wrapping of TypeDefs needs fixing for interface in gen @@ -82,7 +82,7 @@ pub fn gen(gen: &Gen, def: TypeDef) -> TokenStream { let signature = gen.reader.method_def_signature(method, generics); let vtbl_signature = gen.vtbl_signature(def, generics, &signature); - let invoke_upcall = if gen.reader.type_def_flags(def).contains(TypeAttributes::WINRT) { winrt_methods::gen_upcall(gen, &signature, quote! { this.#name }) } else { com_methods::gen_upcall(gen, &signature, quote! { this.#name }) }; + let invoke_upcall = if gen.reader.type_def_flags(def).contains(TypeAttributes::WindowsRuntime) { winrt_methods::gen_upcall(gen, &signature, quote! { this.#name }) } else { com_methods::gen_upcall(gen, &signature, quote! { this.#name }) }; if has_unknown_base { quote! { diff --git a/crates/libs/bindgen/src/interfaces.rs b/crates/libs/bindgen/src/interfaces.rs index 52c9cadec1..143e3bf552 100644 --- a/crates/libs/bindgen/src/interfaces.rs +++ b/crates/libs/bindgen/src/interfaces.rs @@ -66,7 +66,7 @@ fn gen_win_interface(gen: &Gen, def: TypeDef) -> TokenStream { if gen .reader .type_def_flags(def) - .contains(TypeAttributes::WINRT) + .contains(TypeAttributes::WindowsRuntime) { for method in gen.reader.type_def_methods(def) { methods.combine(&winrt_methods::gen( @@ -170,7 +170,7 @@ fn gen_win_interface(gen: &Gen, def: TypeDef) -> TokenStream { if gen .reader .type_def_flags(def) - .contains(TypeAttributes::WINRT) + .contains(TypeAttributes::WindowsRuntime) { for interface in &interfaces { let into = gen.type_name(&interface.ty); diff --git a/crates/libs/bindgen/src/lib.rs b/crates/libs/bindgen/src/lib.rs index 0957a4f648..7712a73d63 100644 --- a/crates/libs/bindgen/src/lib.rs +++ b/crates/libs/bindgen/src/lib.rs @@ -87,7 +87,7 @@ pub fn namespace(gen: &Gen, tree: &Tree) -> String { if gen .reader .type_def_flags(def) - .contains(TypeAttributes::WINRT) + .contains(TypeAttributes::WindowsRuntime) { types .entry(kind) diff --git a/crates/libs/bindgen/src/structs.rs b/crates/libs/bindgen/src/structs.rs index f90c9ae062..2ca9588619 100644 --- a/crates/libs/bindgen/src/structs.rs +++ b/crates/libs/bindgen/src/structs.rs @@ -50,16 +50,16 @@ fn gen_struct_with_name(gen: &Gen, def: TypeDef, struct_name: &str, cfg: &Cfg) - let name = to_ident(gen.reader.field_name(f)); let ty = gen.reader.field_type(f, Some(def)); - if gen.reader.field_flags(f).contains(FieldAttributes::LITERAL) { + if gen.reader.field_flags(f).contains(FieldAttributes::Literal) { quote! {} } else if !gen.sys - && flags.contains(TypeAttributes::EXPLICIT_LAYOUT) + && flags.contains(TypeAttributes::ExplicitLayout) && !gen.reader.field_is_copyable(f, def) { let ty = gen.type_default_name(&ty); quote! { pub #name: ::std::mem::ManuallyDrop<#ty>, } } else if !gen.sys - && !flags.contains(TypeAttributes::WINRT) + && !flags.contains(TypeAttributes::WindowsRuntime) && !gen.reader.field_is_blittable(f, def) { if let Type::Win32Array((ty, len)) = ty { @@ -75,7 +75,7 @@ fn gen_struct_with_name(gen: &Gen, def: TypeDef, struct_name: &str, cfg: &Cfg) - } }); - let struct_or_union = if flags.contains(TypeAttributes::EXPLICIT_LAYOUT) { + let struct_or_union = if flags.contains(TypeAttributes::ExplicitLayout) { quote! { union } } else { quote! { struct } @@ -139,7 +139,7 @@ fn gen_windows_traits(gen: &Gen, def: TypeDef, name: &TokenStream, cfg: &Cfg) -> if gen .reader .type_def_flags(def) - .contains(TypeAttributes::WINRT) + .contains(TypeAttributes::WindowsRuntime) { let signature = Literal::byte_string(gen.reader.type_def_signature(def, &[]).as_bytes()); @@ -168,7 +168,7 @@ fn gen_compare_traits(gen: &Gen, def: TypeDef, name: &TokenStream, cfg: &Cfg) -> } else { let fields = gen.reader.type_def_fields(def).filter_map(|f| { let name = to_ident(gen.reader.field_name(f)); - if gen.reader.field_flags(f).contains(FieldAttributes::LITERAL) { + if gen.reader.field_flags(f).contains(FieldAttributes::Literal) { None } else { Some(quote! { self.#name == other.#name }) @@ -199,7 +199,7 @@ fn gen_debug(gen: &Gen, def: TypeDef, ident: &TokenStream, cfg: &Cfg) -> TokenSt let features = gen.cfg_features(cfg); let fields = gen.reader.type_def_fields(def).filter_map(|f| { - if gen.reader.field_flags(f).contains(FieldAttributes::LITERAL) { + if gen.reader.field_flags(f).contains(FieldAttributes::Literal) { None } else { let name = gen.reader.field_name(f); @@ -244,7 +244,7 @@ fn gen_copy_clone(gen: &Gen, def: TypeDef, name: &TokenStream, cfg: &Cfg) -> Tok } else if !gen .reader .type_def_flags(def) - .contains(TypeAttributes::WINRT) + .contains(TypeAttributes::WindowsRuntime) { quote! { #features @@ -257,7 +257,7 @@ fn gen_copy_clone(gen: &Gen, def: TypeDef, name: &TokenStream, cfg: &Cfg) -> Tok } else { let fields = gen.reader.type_def_fields(def).map(|f| { let name = to_ident(gen.reader.field_name(f)); - if gen.reader.field_flags(f).contains(FieldAttributes::LITERAL) { + if gen.reader.field_flags(f).contains(FieldAttributes::Literal) { quote! {} } else if gen.reader.field_is_blittable(f, def) { quote! { #name: self.#name } @@ -286,7 +286,7 @@ fn gen_struct_constants( let features = gen.cfg_features(cfg); let constants = gen.reader.type_def_fields(def).filter_map(|f| { - if gen.reader.field_flags(f).contains(FieldAttributes::LITERAL) { + if gen.reader.field_flags(f).contains(FieldAttributes::Literal) { if let Some(constant) = gen.reader.field_constant(f) { let name = to_ident(gen.reader.field_name(f)); let value = gen.typed_value(&gen.reader.constant_value(constant)); diff --git a/crates/libs/bindgen/src/winrt_methods.rs b/crates/libs/bindgen/src/winrt_methods.rs index 2cc906f145..356b22098c 100644 --- a/crates/libs/bindgen/src/winrt_methods.rs +++ b/crates/libs/bindgen/src/winrt_methods.rs @@ -119,7 +119,7 @@ fn gen_winrt_params(gen: &Gen, params: &[SignatureParam]) -> TokenStream { if gen .reader .param_flags(param.def) - .contains(ParamAttributes::INPUT) + .contains(ParamAttributes::In) { if param.ty.is_winrt_array() { result.combine("e! { #name: &[#default_type], }); @@ -152,7 +152,7 @@ fn gen_winrt_abi_args(gen: &Gen, params: &[SignatureParam]) -> TokenStream { let param = if gen .reader .param_flags(param.def) - .contains(ParamAttributes::INPUT) + .contains(ParamAttributes::In) { if param.ty.is_winrt_array() { if gen.reader.type_is_blittable(¶m.ty) { @@ -245,7 +245,7 @@ fn gen_winrt_invoke_arg(gen: &Gen, param: &SignatureParam) -> TokenStream { if gen .reader .param_flags(param.def) - .contains(ParamAttributes::INPUT) + .contains(ParamAttributes::In) { if param.ty.is_winrt_array() { quote! { ::core::slice::from_raw_parts(::core::mem::transmute_copy(&#name), #abi_size_name as _) } diff --git a/crates/libs/metadata/src/attributes.rs b/crates/libs/metadata/src/attributes.rs index a7a6d1d850..d28bedc816 100644 --- a/crates/libs/metadata/src/attributes.rs +++ b/crates/libs/metadata/src/attributes.rs @@ -2,28 +2,28 @@ 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); + 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 SpecialName: Self = Self(0x200); + pub const RTSpecialName: Self = Self(0x400); + pub const HasDefault: 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); + pub const Abstract: Self = Self(0x400); + pub const HideBySig: Self = Self(0x80); + pub const NewSlot: Self = Self(0x100); + pub const Public: Self = Self(0x6); + pub const SpecialName: Self = Self(0x800); + pub const Virtual: Self = Self(0x40); } flags!(MethodImplAttributes, usize); impl MethodImplAttributes { - pub const PRESERVE_SIG: Self = Self(0x80); + pub const PreserveSig: Self = Self(0x80); } flags!(MethodCallAttributes, u8); @@ -34,28 +34,34 @@ impl MethodCallAttributes { flags!(ParamAttributes, u16); impl ParamAttributes { - pub const INPUT: Self = Self(0x1); - pub const OUTPUT: Self = Self(0x2); - pub const OPTIONAL: Self = Self(0x10); + pub const In: Self = Self(0x1); + pub const Out: 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); + pub const SupportsLastError: Self = Self(0x40); + pub const CallConvPlatformapi: Self = Self(0x100); + pub const CallConvCdecl: Self = Self(0x200); + pub const CallConvStdcall: Self = Self(0x300); + pub const CallConvThiscall: Self = Self(0x400); + pub const CallConvFastcall: 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); + pub const Public: Self = Self(0x1); + pub const ExplicitLayout: Self = Self(0x10); + pub const Abstract: Self = Self(0x80); + pub const Sealed: Self = Self(0x100); + pub const WindowsRuntime: Self = Self(0x4000); + pub const Interface: Self = Self(0x20); + pub const SequentialLayout: Self = Self(0x8); + pub const Import: Self = Self(0x1000); +} + +flags!(AssemblyFlags, u32); +impl AssemblyFlags { + pub const WindowsRuntime: Self = Self(0x200); } diff --git a/crates/libs/metadata/src/lib.rs b/crates/libs/metadata/src/lib.rs index 52717013b7..2f4e1fb6c1 100644 --- a/crates/libs/metadata/src/lib.rs +++ b/crates/libs/metadata/src/lib.rs @@ -1,4 +1,4 @@ -#![allow(dead_code)] +#![allow(dead_code, non_upper_case_globals)] use std::collections::*; mod attributes; diff --git a/crates/libs/metadata/src/reader/codes.rs b/crates/libs/metadata/src/reader/codes.rs index 5f92974965..da1b65526b 100644 --- a/crates/libs/metadata/src/reader/codes.rs +++ b/crates/libs/metadata/src/reader/codes.rs @@ -88,7 +88,6 @@ impl Decode for MemberRefParent { #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] pub enum TypeDefOrRef { - None, TypeDef(TypeDef), TypeRef(TypeRef), TypeSpec(TypeSpec), @@ -96,9 +95,6 @@ pub enum TypeDefOrRef { 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))), @@ -120,3 +116,23 @@ impl TypeOrMethodDef { }) as _ } } + +pub enum ResolutionScope { + Module(Module), + ModuleRef(ModuleRef), + AssemblyRef(AssemblyRef), + TypeRef(TypeRef), +} + +impl Decode for ResolutionScope { + fn decode(file: usize, code: usize) -> Self { + let (kind, row) = (code & ((1 << 2) - 1), (code >> 2) - 1); + match kind { + 0 => Self::Module(Module(Row::new(row, TABLE_MODULE, file))), + 1 => Self::ModuleRef(ModuleRef(Row::new(row, TABLE_MODULEREF, file))), + 2 => Self::AssemblyRef(AssemblyRef(Row::new(row, TABLE_ASSEMBLYREF, file))), + 3 => Self::TypeRef(TypeRef(Row::new(row, TABLE_TYPEREF, file))), + _ => unimplemented!(), + } + } +} diff --git a/crates/libs/metadata/src/reader/mod.rs b/crates/libs/metadata/src/reader/mod.rs index afa6666ac3..a6bff7111f 100644 --- a/crates/libs/metadata/src/reader/mod.rs +++ b/crates/libs/metadata/src/reader/mod.rs @@ -34,7 +34,9 @@ tables! { InterfaceImpl, MemberRef, MethodDef, + Module, ModuleRef, + AssemblyRef, Param, TypeDef, TypeRef, @@ -556,7 +558,7 @@ impl<'a> Reader<'a> { } 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 self.method_def_flags(row).contains(MethodAttributes::SpecialName) { if name.starts_with("get") { name[4..].to_string() } else if name.starts_with("put") { @@ -603,7 +605,7 @@ impl<'a> Reader<'a> { } pub fn method_def_last_error(&self, row: MethodDef) -> bool { if let Some(map) = self.method_def_impl_map(row) { - self.impl_map_flags(map).contains(PInvokeAttributes::LAST_ERROR) + self.impl_map_flags(map).contains(PInvokeAttributes::SupportsLastError) } else { false } @@ -623,7 +625,7 @@ impl<'a> Reader<'a> { } None } else { - let is_output = self.param_flags(param).contains(ParamAttributes::OUTPUT); + let is_output = self.param_flags(param).contains(ParamAttributes::Out); let mut ty = self.type_from_blob(&mut blob, None, generics); if self.param_is_const(param) || !is_output { ty = ty.to_const_type(); @@ -642,7 +644,7 @@ impl<'a> Reader<'a> { 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() { + if !self.param_flags(params[relative].def).contains(ParamAttributes::Out) && position != relative && !params[relative].ty.is_pointer() { params[relative].kind = SignatureParamKind::ArrayRelativePtr(position); } else { params[position].kind = SignatureParamKind::Other; @@ -699,7 +701,7 @@ impl<'a> Reader<'a> { } } else { let flags = self.param_flags(param.def); - if param.ty.is_pointer() && (flags.contains(ParamAttributes::OPTIONAL) || self.param_is_reserved(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; @@ -716,9 +718,9 @@ impl<'a> Reader<'a> { 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) { + if flags.contains(PInvokeAttributes::CallConvPlatformapi) { "system" - } else if flags.contains(PInvokeAttributes::CONV_CDECL) { + } else if flags.contains(PInvokeAttributes::CallConvCdecl) { "cdecl" } else { unimplemented!() @@ -731,7 +733,7 @@ impl<'a> Reader<'a> { 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) { + if self.type_def_flags(def).contains(TypeAttributes::ExplicitLayout) { 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; @@ -913,7 +915,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).contains(TypeAttributes::EXPLICIT_LAYOUT) { + if self.type_def_flags(row).contains(TypeAttributes::ExplicitLayout) { 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)))) @@ -925,14 +927,14 @@ impl<'a> Reader<'a> { 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) { + if self.type_def_flags(row).contains(TypeAttributes::WindowsRuntime) { 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), + TypeKind::Delegate => !self.type_def_flags(row).contains(TypeAttributes::WindowsRuntime), _ => false, } } @@ -940,12 +942,12 @@ impl<'a> Reader<'a> { 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), + TypeKind::Delegate => !self.type_def_flags(row).contains(TypeAttributes::WindowsRuntime), _ => 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 + !self.type_def_flags(row).contains(TypeAttributes::WindowsRuntime) && 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) { @@ -983,7 +985,7 @@ impl<'a> Reader<'a> { 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") + self.type_def_flags(row).contains(TypeAttributes::WindowsRuntime) || 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") @@ -1014,7 +1016,7 @@ impl<'a> Reader<'a> { 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), + TypeKind::Delegate => !self.type_def_flags(row).contains(TypeAttributes::WindowsRuntime), _ => false, } } @@ -1023,7 +1025,7 @@ impl<'a> Reader<'a> { return false; } fn check(reader: &Reader, row: TypeDef) -> bool { - if reader.type_def_flags(row).contains(TypeAttributes::EXPLICIT_LAYOUT) { + if reader.type_def_flags(row).contains(TypeAttributes::ExplicitLayout) { return true; } if reader.type_def_fields(row).any(|field| reader.type_has_explicit_layout(&reader.field_type(field, Some(row)))) { @@ -1117,7 +1119,7 @@ impl<'a> Reader<'a> { 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) + self.type_def_attributes(row).any(|attribute| self.attribute_name(attribute) == "FlagsAttribute") || (self.type_def_flags(row).contains(TypeAttributes::WindowsRuntime) && 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) { @@ -1161,7 +1163,7 @@ impl<'a> Reader<'a> { TypeKind::Interface | TypeKind::Class => true, // Win32 callbacks are defined as `Option` so we don't include them here to avoid them // from being doubly wrapped in `Option`. - TypeKind::Delegate => self.type_def_flags(row).contains(TypeAttributes::WINRT), + TypeKind::Delegate => self.type_def_flags(row).contains(TypeAttributes::WindowsRuntime), _ => false, } } @@ -1263,7 +1265,7 @@ impl<'a> Reader<'a> { } } - if self.type_def_flags(def).contains(TypeAttributes::WINRT) { + if self.type_def_flags(def).contains(TypeAttributes::WindowsRuntime) { for interface in self.type_def_interfaces(def, generics) { if let Type::TypeDef((def, generics)) = interface.ty { combine(self, def, &generics, &mut cfg); @@ -1289,7 +1291,7 @@ impl<'a> Reader<'a> { } } TypeKind::Interface => { - if !self.type_def_flags(row).contains(TypeAttributes::WINRT) { + if !self.type_def_flags(row).contains(TypeAttributes::WindowsRuntime) { for def in self.type_def_vtables(row) { if let Type::TypeDef((def, _)) = def { cfg.add_feature(self.type_def_namespace(def)); @@ -1314,7 +1316,7 @@ impl<'a> Reader<'a> { } pub fn type_def_vtables(&self, row: TypeDef) -> Vec { let mut result = Vec::new(); - if self.type_def_flags(row).contains(TypeAttributes::WINRT) { + if self.type_def_flags(row).contains(TypeAttributes::WindowsRuntime) { result.push(Type::IUnknown); if self.type_def_kind(row) != TypeKind::Delegate { result.push(Type::IInspectable); @@ -1356,6 +1358,9 @@ impl<'a> Reader<'a> { pub fn type_ref_type_name(&self, row: TypeRef) -> TypeName { TypeName::new(self.type_ref_namespace(row), self.type_ref_name(row)) } + pub fn type_ref_resolution_scope(&self, row: TypeRef) -> ResolutionScope { + self.row_decode(row.0, 0) + } // // TypeSpec table queries @@ -1389,7 +1394,7 @@ impl<'a> Reader<'a> { 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)) + !self.param_flags(param.def).contains(ParamAttributes::Out) && !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 @@ -1404,7 +1409,7 @@ impl<'a> Reader<'a> { 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() { + if flags.contains(ParamAttributes::In) || !flags.contains(ParamAttributes::Out) || flags.contains(ParamAttributes::Optional) || param.kind.is_array() { return false; } if self.param_kind(param.def).is_array() { @@ -1429,7 +1434,7 @@ impl<'a> Reader<'a> { 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) { + if self.param_flags(signature.params[object].def).contains(ParamAttributes::Optional) { return SignatureKind::QueryOptional(QueryPosition { object, guid }); } else { return SignatureKind::Query(QueryPosition { object, guid }); @@ -1454,11 +1459,11 @@ impl<'a> Reader<'a> { 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) + !flags.contains(ParamAttributes::Out) }) } fn signature_param_is_query_guid(&self, params: &[SignatureParam]) -> Option { - params.iter().rposition(|param| param.ty == Type::ConstPtr((Box::new(Type::GUID), 1)) && !self.param_flags(param.def).contains(ParamAttributes::OUTPUT)) + params.iter().rposition(|param| param.ty == Type::ConstPtr((Box::new(Type::GUID), 1)) && !self.param_flags(param.def).contains(ParamAttributes::Out)) } fn signature_param_is_query_object(&self, params: &[SignatureParam]) -> Option { params.iter().rposition(|param| param.ty == Type::MutPtr((Box::new(Type::Void), 2)) && self.param_is_com_out_ptr(param.def)) @@ -1853,7 +1858,7 @@ impl<'a> Reader<'a> { match ty { Type::TypeDef((row, _)) => { let flags = self.type_def_flags(*row); - if !flags.contains(TypeAttributes::WINRT) { + if !flags.contains(TypeAttributes::WindowsRuntime) { false } else { match self.type_def_kind(*row) { diff --git a/crates/libs/metadata/src/reader/type_name.rs b/crates/libs/metadata/src/reader/type_name.rs index c4d72e3986..9bc4b46245 100644 --- a/crates/libs/metadata/src/reader/type_name.rs +++ b/crates/libs/metadata/src/reader/type_name.rs @@ -4,7 +4,6 @@ pub struct TypeName<'a> { pub name: &'a str, } -#[allow(non_upper_case_globals)] impl<'a> TypeName<'a> { pub const Enum: Self = Self::from_const("System", "Enum"); pub const Delegate: Self = Self::from_const("System", "MulticastDelegate"); diff --git a/crates/libs/metadata/src/writer/idl/format.rs b/crates/libs/metadata/src/writer/idl/format.rs index c15acfeb23..7f84a8de7a 100644 --- a/crates/libs/metadata/src/writer/idl/format.rs +++ b/crates/libs/metadata/src/writer/idl/format.rs @@ -169,6 +169,7 @@ impl Printer { fn idl_class(&mut self, _member: &IdlClass) {} fn trait_item_fn(&mut self, method: &syn::TraitItemFn) { + self.attrs(&method.attrs); self.signature(&method.sig); } diff --git a/crates/libs/metadata/src/writer/idl/mod.rs b/crates/libs/metadata/src/writer/idl/mod.rs index 01c73cf631..b9ab56ee52 100644 --- a/crates/libs/metadata/src/writer/idl/mod.rs +++ b/crates/libs/metadata/src/writer/idl/mod.rs @@ -8,6 +8,9 @@ pub use format::*; pub use read::*; pub use write::*; +// The value of the IDL-specific memory representation is that it allows for constructs that are not modeled in the abstract Module +// tree such as the use declarations and if we get rid of it we'd always "format" IDL by stripping out any of that into a single +// canonical form which would not be very friendly to developers. pub struct IdlFile { references: Vec, modules: Vec, diff --git a/crates/libs/metadata/src/writer/idl/read.rs b/crates/libs/metadata/src/writer/idl/read.rs index bef19a68d9..1b078effd8 100644 --- a/crates/libs/metadata/src/writer/idl/read.rs +++ b/crates/libs/metadata/src/writer/idl/read.rs @@ -1,303 +1,333 @@ use super::*; use syn::spanned::Spanned; +// Phases are needed to read use declarations from IDL files before resolving those types +// in the second pass. #[derive(PartialEq, Copy, Clone)] enum ReadPhase { Index, Define, } -pub fn read_idl(tree: &mut Module, paths: &[String], filter: &Filter) -> Result<()> { - let mut files = vec![]; +impl Module { + pub fn read_idl(&mut self, paths: &[String], filter: &Filter) -> Result<()> { + let mut files = vec![]; - for path in paths { - if extension(path).1 == "idl" { - files.push((path.as_str(), IdlFile::parse_str(&read_to_string(path)?).map_err(|error| error.with_path(path))?)); + for path in paths { + if extension(path).1 == "idl" { + files.push((path.as_str(), IdlFile::parse_str(&read_to_string(path)?).map_err(|error| error.with_path(path))?)); + } } - } - for (path, file) in &files { - read_file(tree, file, filter, ReadPhase::Index).map_err(|error| error.with_path(path))?; - } - - for (path, file) in &files { - read_file(tree, file, filter, ReadPhase::Define).map_err(|error| error.with_path(path))?; - } + for (path, file) in &files { + self.read_file(file, filter, ReadPhase::Index).map_err(|error| error.with_path(path))?; + } - Ok(()) -} + for (path, file) in &files { + self.read_file(file, filter, ReadPhase::Define).map_err(|error| error.with_path(path))?; + } -fn read_file(tree: &mut Module, file: &IdlFile, filter: &Filter, phase: ReadPhase) -> Result<()> { - for module in &file.modules { - read_module(tree, file, module, &module.ident.to_string(), filter, phase)?; + Ok(()) } - Ok(()) -} - -fn read_module(tree: &mut Module, file: &IdlFile, module: &IdlModule, namespace: &str, filter: &Filter, phase: ReadPhase) -> Result<()> { - if filter.includes_namespace(namespace) { - for member in &module.members { - read_member(tree, file, member, namespace, filter, phase)?; + fn read_file(&mut self, file: &IdlFile, filter: &Filter, phase: ReadPhase) -> Result<()> { + for module in &file.modules { + self.read_module(file, module, &module.ident.to_string(), filter, phase)?; } + + Ok(()) } - Ok(()) -} + fn read_module(&mut self, file: &IdlFile, module: &IdlModule, namespace: &str, filter: &Filter, phase: ReadPhase) -> Result<()> { + if filter.includes_namespace(namespace) { + for member in &module.members { + self.read_member(file, member, namespace, filter, phase)?; + } + } -fn read_member(tree: &mut Module, file: &IdlFile, member: &IdlModuleMember, namespace: &str, filter: &Filter, phase: ReadPhase) -> Result<()> { - match member { - IdlModuleMember::Module(member) => read_module(tree, file, member, &format!("{namespace}.{}", member.ident), filter, phase), - IdlModuleMember::Interface(member) => read_interface(tree, file, member, namespace, filter, phase), - IdlModuleMember::Struct(member) => read_struct(tree, file, member, namespace, filter, phase), - IdlModuleMember::Enum(member) => read_enum(tree, file, member, namespace, filter, phase), - IdlModuleMember::Class(member) => read_class(tree, file, member, namespace, filter, phase), + Ok(()) } -} -fn read_interface(tree: &mut Module, _file: &IdlFile, ty: &IdlInterface, namespace: &str, filter: &Filter, phase: ReadPhase) -> Result<()> { - let ident = ty.ident.to_string(); + fn read_member(&mut self, file: &IdlFile, member: &IdlModuleMember, namespace: &str, filter: &Filter, phase: ReadPhase) -> Result<()> { + match member { + IdlModuleMember::Module(member) => self.read_module(file, member, &format!("{namespace}.{}", member.ident), filter, phase), + IdlModuleMember::Interface(member) => self.read_interface(file, member, namespace, filter, phase), + IdlModuleMember::Struct(member) => self.read_struct(file, member, namespace, filter, phase), + IdlModuleMember::Enum(member) => self.read_enum(file, member, namespace, filter, phase), + IdlModuleMember::Class(member) => self.read_class(file, member, namespace, filter, phase), + } + } - if filter.includes_type_name(reader::TypeName::new(namespace, &ident)) { - let vec = tree.insert(namespace, 0).types.entry(ident).or_default(); + fn read_interface(&mut self, _file: &IdlFile, ty: &IdlInterface, namespace: &str, filter: &Filter, phase: ReadPhase) -> Result<()> { + let ident = ty.ident.to_string(); - if phase == ReadPhase::Define { - let mut def = TypeDef { extends: None, ..Default::default() }; + if filter.includes_type_name(reader::TypeName::new(namespace, &ident)) { + match phase { + ReadPhase::Index => { + self.insert(namespace, 0).types.entry(ident).or_default(); + } + ReadPhase::Define => { + let flags = TypeAttributes::Public | TypeAttributes::Interface | TypeAttributes::WindowsRuntime | TypeAttributes::Abstract; + let mut def = TypeDef { flags, extends: None, ..Default::default() }; - for method in &ty.methods { - let name = method.sig.ident.to_string(); - let mut params = vec![]; + for method in &ty.methods { + let name = method.sig.ident.to_string(); + let mut params = vec![]; - for input in &method.sig.inputs { - let syn::FnArg::Typed(pat_type) = input else { + for input in &method.sig.inputs { + let syn::FnArg::Typed(pat_type) = input else { todo!(); }; - let syn::Pat::Ident(ref pat_ident) = *pat_type.pat else { + let syn::Pat::Ident(ref pat_ident) = *pat_type.pat else { todo!(); }; - let name = pat_ident.ident.to_string(); - let ty = read_ty(namespace, &pat_type.ty)?; - params.push(Param { name, ty, ..Default::default() }); - } + let name = pat_ident.ident.to_string(); + let ty = self.read_ty(namespace, &pat_type.ty)?; + params.push(Param { name, ty, ..Default::default() }); + } - let ty = if let syn::ReturnType::Type(_, ty) = &method.sig.output { read_ty(namespace, ty)? } else { Type::Void }; - let return_type = Param { ty, ..Default::default() }; + let ty = if let syn::ReturnType::Type(_, ty) = &method.sig.output { self.read_ty(namespace, ty)? } else { Type::Void }; + let return_type = Param { ty, ..Default::default() }; + let flags = MethodAttributes::Public; - def.methods.push(Method { name, params, return_type, ..Default::default() }); - } + def.methods.push(Method { flags, name, params, return_type, ..Default::default() }); + } - vec.push(def); + self.insert(namespace, 0).types.entry(ident).or_default().push(def); + } + } } - } - Ok(()) -} + Ok(()) + } -fn read_struct(tree: &mut Module, _file: &IdlFile, ty: &IdlStruct, namespace: &str, filter: &Filter, phase: ReadPhase) -> Result<()> { - let ident = ty.item.ident.to_string(); + fn read_struct(&mut self, _file: &IdlFile, ty: &IdlStruct, namespace: &str, filter: &Filter, phase: ReadPhase) -> Result<()> { + let ident = ty.item.ident.to_string(); - if filter.includes_type_name(reader::TypeName::new(namespace, &ident)) { - let vec = tree.insert(namespace, 0).types.entry(ident).or_default(); + if filter.includes_type_name(reader::TypeName::new(namespace, &ident)) { + match phase { + ReadPhase::Index => { + self.insert(namespace, 0).types.entry(ident).or_default(); + } + ReadPhase::Define => { + let flags = TypeAttributes::Public | TypeAttributes::WindowsRuntime | TypeAttributes::Sealed | TypeAttributes::Import | TypeAttributes::SequentialLayout; + let mut def = TypeDef { flags, extends: Some(TypeRef { namespace: "System".to_string(), name: "ValueType".to_string(), ..Default::default() }), ..Default::default() }; - if phase == ReadPhase::Define { - let mut def = TypeDef { extends: Some(TypeRef { namespace: "System".to_string(), name: "ValueType".to_string(), ..Default::default() }), ..Default::default() }; + let syn::Fields::Named(fields) = &ty.item.fields else { + unimplemented!(); + }; - let syn::Fields::Named(fields) = &ty.item.fields else { - unimplemented!(); - }; + for field in &fields.named { + let Some(ref ident) = field.ident else { + unimplemented!(); + }; - for field in &fields.named { - let Some(ref ident) = field.ident else { - unimplemented!(); - }; + let flags = FieldAttributes::Public; + let name = ident.to_string(); + let ty = self.read_ty(namespace, &field.ty)?; + def.fields.push(Field { flags, name, ty, ..Default::default() }); + } - let flags = FieldAttributes::PUBLIC; - let name = ident.to_string(); - let ty = read_ty(namespace, &field.ty)?; - def.fields.push(Field { flags, name, ty, ..Default::default() }); + self.insert(namespace, 0).types.entry(ident).or_default().push(def); + } } - - vec.push(def); } - } - Ok(()) -} + Ok(()) + } -fn read_enum(tree: &mut Module, _file: &IdlFile, ty: &IdlEnum, namespace: &str, filter: &Filter, phase: ReadPhase) -> Result<()> { - let ident = ty.item.ident.to_string(); + fn read_enum(&mut self, _file: &IdlFile, ty: &IdlEnum, namespace: &str, filter: &Filter, phase: ReadPhase) -> Result<()> { + let ident = ty.item.ident.to_string(); - if filter.includes_type_name(reader::TypeName::new(namespace, &ident)) { - let vec = tree.insert(namespace, 0).types.entry(ident.clone()).or_default(); + if filter.includes_type_name(reader::TypeName::new(namespace, &ident)) { + match phase { + ReadPhase::Index => { + self.insert(namespace, 0).types.entry(ident).or_default(); + } + ReadPhase::Define => { + let mut def = TypeDef { extends: Some(TypeRef { namespace: "System".to_string(), name: "Enum".to_string(), ..Default::default() }), ..Default::default() }; + let enum_type = Type::TypeRef(TypeRef { namespace: namespace.to_string(), name: ident.clone(), ..Default::default() }); - if phase == ReadPhase::Define { - let mut def = TypeDef { extends: Some(TypeRef { namespace: "System".to_string(), name: "Enum".to_string(), ..Default::default() }), ..Default::default() }; - let enum_type = Type::TypeRef(TypeRef { namespace: namespace.to_string(), name: ident, ..Default::default() }); + for variant in &ty.item.variants { + if let Some((_, expr)) = &variant.discriminant { + let flags = FieldAttributes::Public; + let name = variant.ident.to_string(); + let value = self.read_expr(expr, false)?; - for variant in &ty.item.variants { - if let Some((_, expr)) = &variant.discriminant { - let flags = FieldAttributes::PUBLIC; - let name = variant.ident.to_string(); - let value = read_expr(expr, false)?; + def.fields.push(Field { flags, name, ty: enum_type.clone(), value: Some(value) }); + } + } - def.fields.push(Field { flags, name, ty: enum_type.clone(), value: Some(value) }); + self.insert(namespace, 0).types.entry(ident).or_default().push(def); } } - - vec.push(def); } + + Ok(()) } - Ok(()) -} + fn read_class(&mut self, _file: &IdlFile, ty: &IdlClass, namespace: &str, filter: &Filter, _phase: ReadPhase) -> Result<()> { + let ident = ty.ident.to_string(); -fn read_class(tree: &mut Module, _file: &IdlFile, ty: &IdlClass, namespace: &str, filter: &Filter, _phase: ReadPhase) -> Result<()> { - let ident = ty.ident.to_string(); + if filter.includes_type_name(reader::TypeName::new(namespace, &ident)) { + self.insert(namespace, 0).types.entry(ident).or_default(); + } - if filter.includes_type_name(reader::TypeName::new(namespace, &ident)) { - tree.insert(namespace, 0).types.entry(ident).or_default(); + Ok(()) } - Ok(()) -} + fn read_expr(&mut self, expr: &syn::Expr, neg: bool) -> Result { + match expr { + syn::Expr::Lit(lit) => self.read_expr_lit(lit, neg), + syn::Expr::Unary(unary) => self.read_expr_unary(unary), + _ => todo!("{:?}", expr), + } + } -fn read_expr(expr: &syn::Expr, neg: bool) -> Result { - match expr { - syn::Expr::Lit(lit) => read_expr_lit(lit, neg), - syn::Expr::Unary(unary) => read_expr_unary(unary), - _ => todo!("{:?}", expr), + fn read_expr_unary(&mut self, unary: &syn::ExprUnary) -> Result { + self.read_expr(&unary.expr, true) } -} -fn read_expr_unary(unary: &syn::ExprUnary) -> Result { - read_expr(&unary.expr, true) -} + fn read_expr_lit(&mut self, expr: &syn::ExprLit, neg: bool) -> Result { + self.read_lit(&expr.lit, neg) + } -fn read_expr_lit(expr: &syn::ExprLit, neg: bool) -> Result { - read_lit(&expr.lit, neg) -} + fn read_lit(&mut self, lit: &syn::Lit, neg: bool) -> Result { + match lit { + syn::Lit::Int(lit) => self.read_lit_int(lit, neg), + syn::Lit::Str(lit) => self.read_lit_str(lit), + _ => todo!("{:?}", lit), + } + } -fn read_lit(lit: &syn::Lit, neg: bool) -> Result { - match lit { - syn::Lit::Int(lit) => read_lit_int(lit, neg), - syn::Lit::Str(lit) => read_lit_str(lit), - _ => todo!("{:?}", lit), + fn read_lit_str(&mut self, lit: &syn::LitStr) -> Result { + Ok(Value::String(lit.value())) } -} -fn read_lit_str(lit: &syn::LitStr) -> Result { - Ok(Value::String(lit.value())) -} + fn read_lit_int(&mut self, lit: &syn::LitInt, neg: bool) -> Result { + fn parse(lit: &syn::LitInt, neg: bool) -> Result { + let raw = if neg { format!("-{}", lit.base10_digits()) } else { lit.base10_digits().to_string() }; + raw.parse().map_err(|_| Error::new("failed to parse literal").with_span(lit.span())) + } -fn read_lit_int(lit: &syn::LitInt, neg: bool) -> Result { - fn parse(lit: &syn::LitInt, neg: bool) -> Result { - let raw = if neg { format!("-{}", lit.base10_digits()) } else { lit.base10_digits().to_string() }; - raw.parse().map_err(|_| Error::new("failed to parse literal").with_span(lit.span())) + match lit.suffix() { + "i8" => Ok(Value::I8(parse(lit, neg)?)), + "u8" => Ok(Value::U8(parse(lit, neg)?)), + "i16" => Ok(Value::I16(parse(lit, neg)?)), + "u16" => Ok(Value::U16(parse(lit, neg)?)), + "i32" => Ok(Value::I32(parse(lit, neg)?)), + "u32" => Ok(Value::U32(parse(lit, neg)?)), + "i64" => Ok(Value::I64(parse(lit, neg)?)), + "u64" => Ok(Value::U64(parse(lit, neg)?)), + suffix => todo!("suffix {:?}", suffix), + } } - match lit.suffix() { - "i8" => Ok(Value::I8(parse(lit, neg)?)), - "u8" => Ok(Value::U8(parse(lit, neg)?)), - "i16" => Ok(Value::I16(parse(lit, neg)?)), - "u16" => Ok(Value::U16(parse(lit, neg)?)), - "i32" => Ok(Value::I32(parse(lit, neg)?)), - "u32" => Ok(Value::U32(parse(lit, neg)?)), - "i64" => Ok(Value::I64(parse(lit, neg)?)), - "u64" => Ok(Value::U64(parse(lit, neg)?)), - suffix => todo!("suffix {:?}", suffix), + fn read_ty(&mut self, namespace: &str, ty: &syn::Type) -> Result { + match ty { + syn::Type::Path(ty) => self.read_type_path(namespace, ty), + syn::Type::Ptr(ptr) => self.read_type_ptr(namespace, ptr), + syn::Type::Array(array) => self.read_type_array(namespace, array), + _ => unimplemented!(), + } } -} -fn read_ty(namespace: &str, ty: &syn::Type) -> Result { - match ty { - syn::Type::Path(ty) => read_type_path(namespace, ty), - syn::Type::Ptr(ptr) => read_type_ptr(namespace, ptr), - syn::Type::Array(array) => read_type_array(namespace, array), - _ => unimplemented!(), - } -} + fn read_type_array(&mut self, namespace: &str, array: &syn::TypeArray) -> Result { + let ty = self.read_ty(namespace, &array.elem)?; -fn read_type_array(namespace: &str, array: &syn::TypeArray) -> Result { - let ty = read_ty(namespace, &array.elem)?; + if let syn::Expr::Lit(lit) = &array.len { + if let syn::Lit::Int(lit) = &lit.lit { + return Ok(ty.into_array(lit.base10_parse()?)); + } + } - if let syn::Expr::Lit(lit) = &array.len { - if let syn::Lit::Int(lit) = &lit.lit { - return Ok(ty.into_array(lit.base10_parse()?)); + todo!() + } + + fn read_type_ptr(&mut self, namespace: &str, ptr: &syn::TypePtr) -> Result { + let ty = self.read_ty(namespace, &ptr.elem)?; + if ptr.mutability.is_some() { + Ok(ty.into_mut_ptr()) + } else { + Ok(ty.into_const_ptr()) } } - todo!() -} + fn read_type_path(&mut self, namespace: &str, ty: &syn::TypePath) -> Result { + if ty.qself.is_some() { + unimplemented!(); + } -fn read_type_ptr(namespace: &str, ptr: &syn::TypePtr) -> Result { - let ty = read_ty(namespace, &ptr.elem)?; - if ptr.mutability.is_some() { - Ok(ty.into_mut_ptr()) - } else { - Ok(ty.into_const_ptr()) + self.read_path(namespace, &ty.path) } -} -fn read_type_path(namespace: &str, ty: &syn::TypePath) -> Result { - if ty.qself.is_some() { - unimplemented!(); - } + fn read_path(&mut self, current: &str, path: &syn::Path) -> Result { + if let Some(segment) = path.segments.first() { + if path.segments.len() == 1 { + let name = segment.ident.to_string(); + + return match name.as_str() { + "void" => Ok(Type::Void), + "bool" => Ok(Type::Bool), + "char" => Ok(Type::Char), + "i8" => Ok(Type::I8), + "u8" => Ok(Type::U8), + "i16" => Ok(Type::I16), + "u16" => Ok(Type::U16), + "i32" => Ok(Type::I32), + "u32" => Ok(Type::U32), + "i64" => Ok(Type::I64), + "u64" => Ok(Type::U64), + "f32" => Ok(Type::F32), + "f64" => Ok(Type::F64), + "isize" => Ok(Type::ISize), + "usize" => Ok(Type::USize), + "HSTRING" => Ok(Type::String), + "GUID" => Ok(Type::GUID), + "IUnknown" => Ok(Type::IUnknown), + "IInspectable" => Ok(Type::IInspectable), + "HRESULT" => Ok(Type::HRESULT), + "PSTR" => Ok(Type::PSTR), + "PWSTR" => Ok(Type::PWSTR), + "PCSTR" => Ok(Type::PCSTR), + "PCWSTR" => Ok(Type::PCWSTR), + "BSTR" => Ok(Type::BSTR), + _ => Ok(Type::TypeRef(TypeRef { namespace: current.to_string(), name, ..Default::default() })), + }; + } + } - read_path(namespace, &ty.path) -} + let mut current: Vec = current.split('.').map(|segment| segment.to_string()).collect(); + let mut builder = vec![]; -fn read_path(current: &str, path: &syn::Path) -> Result { - if let Some(segment) = path.segments.first() { - if path.segments.len() == 1 { - let name = segment.ident.to_string(); - - return match name.as_str() { - "void" => Ok(Type::Void), - "bool" => Ok(Type::Bool), - "char" => Ok(Type::Char), - "i8" => Ok(Type::I8), - "u8" => Ok(Type::U8), - "i16" => Ok(Type::I16), - "u16" => Ok(Type::U16), - "i32" => Ok(Type::I32), - "u32" => Ok(Type::U32), - "i64" => Ok(Type::I64), - "u64" => Ok(Type::U64), - "f32" => Ok(Type::F32), - "f64" => Ok(Type::F64), - "isize" => Ok(Type::ISize), - "usize" => Ok(Type::USize), - "HSTRING" => Ok(Type::String), - "GUID" => Ok(Type::GUID), - "IUnknown" => Ok(Type::IUnknown), - "IInspectable" => Ok(Type::IInspectable), - "HRESULT" => Ok(Type::HRESULT), - "PSTR" => Ok(Type::PSTR), - "PWSTR" => Ok(Type::PWSTR), - "PCSTR" => Ok(Type::PCSTR), - "PCWSTR" => Ok(Type::PCWSTR), - "BSTR" => Ok(Type::BSTR), - _ => Ok(Type::TypeRef(TypeRef { namespace: current.to_string(), name, ..Default::default() })), - }; + for segment in &path.segments { + let segment = segment.ident.to_string(); + if segment == "super" { + current.pop().ok_or_else(|| syn::Error::new(path.span(), "no parent module"))?; + } else { + builder.push(segment); + } } - } - let mut current: Vec = current.split('.').map(|segment| segment.to_string()).collect(); - let mut name = vec![]; + current.extend_from_slice(&builder); + + let (name, namespace) = current.split_last().ok_or_else(|| syn::Error::new(path.span(), "no type name"))?; + let namespace = namespace.join("."); - for segment in &path.segments { - let segment = segment.ident.to_string(); - if segment == "super" { - current.pop().ok_or_else(|| syn::Error::new(path.span(), "no parent module"))?; + if self.contains_type(&namespace, name) { + Ok(Type::TypeRef(TypeRef { namespace, name: name.to_string(), ..Default::default() })) } else { - name.append(&mut current); - name.push(segment); + let (name, namespace) = builder.split_last().ok_or_else(|| syn::Error::new(path.span(), "no type name"))?; + let namespace = namespace.join("."); + + // TODO: should we include winmd references to validate - that will also help with AssemblyRef info needed for winmd + //if self.contains_type(&namespace, name) { + Ok(Type::TypeRef(TypeRef { namespace, name: name.to_string(), ..Default::default() })) + // } else { + // Err(Error::new("type not found").with_span(path.span())) + // } } } - - let (last, rest) = name.split_last().ok_or_else(|| syn::Error::new(path.span(), "no type name"))?; - - Ok(Type::TypeRef(TypeRef { namespace: rest.join("."), name: last.to_string(), ..Default::default() })) } diff --git a/crates/libs/metadata/src/writer/idl/write.rs b/crates/libs/metadata/src/writer/idl/write.rs index e4c361842d..73849d004e 100644 --- a/crates/libs/metadata/src/writer/idl/write.rs +++ b/crates/libs/metadata/src/writer/idl/write.rs @@ -172,6 +172,7 @@ fn interface_to_idl(module: &Module, name: &str, ty: &TypeDef) -> TokenStream { let name = to_ident(name); let methods = ty.methods.iter().map(|method| { + let attributes = attributes_to_idl(module, &method.attributes); let name = to_ident(&method.name); let params = method.params.iter().map(|param| { let name = to_ident(¶m.name); @@ -185,6 +186,7 @@ fn interface_to_idl(module: &Module, name: &str, ty: &TypeDef) -> TokenStream { quote! {} }; quote! { + #attributes fn #name(#(#params),*) #return_type; } }); diff --git a/crates/libs/metadata/src/writer/mod.rs b/crates/libs/metadata/src/writer/mod.rs index ce4184dc89..ad2deb7192 100644 --- a/crates/libs/metadata/src/writer/mod.rs +++ b/crates/libs/metadata/src/writer/mod.rs @@ -26,7 +26,7 @@ impl Module { pub fn read(input: &[String], filter: &Filter) -> Result { let mut module = Module::default(); winmd::read_winmd(&mut module, input, filter)?; - idl::read_idl(&mut module, input, filter)?; + module.read_idl(input, filter)?; Ok(module) } @@ -88,7 +88,8 @@ pub struct TypeRef { pub namespace: String, pub name: String, pub generics: Vec, - // store an optional `assembly` for the name of the winmd/idl file where the type originates + // TODO: store an `assembly` for the name of the winmd/idl file where the type originates? + // pub assembly: String, } #[derive(Debug, Clone)] diff --git a/crates/libs/metadata/src/writer/winmd/read.rs b/crates/libs/metadata/src/writer/winmd/read.rs index c1f86800c4..ffc7787952 100644 --- a/crates/libs/metadata/src/writer/winmd/read.rs +++ b/crates/libs/metadata/src/writer/winmd/read.rs @@ -27,7 +27,7 @@ fn read_type_def(reader: &reader::Reader, ty: reader::TypeDef) -> Result Result ((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)] +#[derive(Clone)] pub enum TypeDefOrRef { - #[default] - None, TypeDef(u32), TypeRef(u32), TypeSpec(u32), @@ -37,16 +34,13 @@ impl TypeDefOrRef { 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)] +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)] pub enum HasConstant { - #[default] - None, Field(u32), Param(u32), Property(u32), @@ -58,37 +52,6 @@ impl HasConstant { 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/crates/libs/metadata/src/writer/winmd/write/file.rs b/crates/libs/metadata/src/writer/winmd/write/file.rs index 46d06ebcb6..0fc3fb6b8d 100644 --- a/crates/libs/metadata/src/writer/winmd/write/file.rs +++ b/crates/libs/metadata/src/writer/winmd/write/file.rs @@ -56,7 +56,7 @@ pub fn write(mut tables: Vec, mut strings: Vec, mut blobs: Vec) -> R major_version: 1, minor_version: 1, length: 20, - version: *b"WindowsRuntime\0\0\0\0\0\0", + version: *b"WindowsRuntime 1.4\0\0", streams: 4, ..Default::default() }; diff --git a/crates/libs/metadata/src/writer/winmd/write/mod.rs b/crates/libs/metadata/src/writer/winmd/write/mod.rs index 3546c247ad..a925c5abd8 100644 --- a/crates/libs/metadata/src/writer/winmd/write/mod.rs +++ b/crates/libs/metadata/src/writer/winmd/write/mod.rs @@ -25,9 +25,24 @@ pub fn write_winmd(module: &Module, path: &str) -> Result<()> { let mut gen = Gen::new(module); gen.tables.TypeDef.push(tables::TypeDef { TypeName: gen.strings.insert(""), ..Default::default() }); - gen.module_scope = ResolutionScope::Module(gen.tables.Module.push2(tables::Module { Mvid: 1, ..Default::default() })).encode(); - // Some winmd parsers will fail to read without an `mscorlib` reference. The `insert_module_types` funciton will typically include it + // The Assembly table needs the module file name without it's extension. + let file_name = std::path::Path::new(path).with_extension("").file_name().map_or(path.to_string(), |name| name.to_string_lossy().to_string()); + + gen.tables.Assembly.push(tables::Assembly { + Name: gen.strings.insert(&file_name), + HashAlgId: 0x00008004, + MajorVersion: 0xFF, + MinorVersion: 0xFF, + BuildNumber: 0xFF, + RevisionNumber: 0xFF, + Flags: AssemblyFlags::WindowsRuntime.0, + ..Default::default() + }); + + gen.module_scope = ResolutionScope::Module(gen.tables.Module.push2(tables::Module { Name: gen.strings.insert("name.winmd"), Mvid: 1, ..Default::default() })).encode(); + + // Some winmd parsers will fail to read without an `mscorlib` reference. The `insert_module_types` function will typically include it // automatically but a minimal `Module` tree may not add this dependency. gen.insert_scope("System"); @@ -140,9 +155,28 @@ impl<'a> Gen<'a> { fn insert_scope(&mut self, namespace: &'a str) -> u32 { if let Some(scope) = self.scopes.get(namespace) { *scope + } else if namespace == "System" { + let scope = ResolutionScope::AssemblyRef(self.tables.AssemblyRef.push2(tables::AssemblyRef { + Name: self.strings.insert("mscorlib"), + MajorVersion: 4, + PublicKeyOrToken: self.blobs.insert(&[0xB7, 0x7A, 0x5C, 0x56, 0x19, 0x34, 0xE0, 0x89]), + ..Default::default() + })) + .encode(); + self.scopes.insert(namespace, scope); + scope } else { - let name = if namespace == "System" { "mscorlib" } else { namespace }; - let scope = ResolutionScope::AssemblyRef(self.tables.AssemblyRef.push2(tables::AssemblyRef { Name: self.strings.insert(name), ..Default::default() })).encode(); + // TODO: may need to capture the original assembly info for external references. + let scope = ResolutionScope::AssemblyRef(self.tables.AssemblyRef.push2(tables::AssemblyRef { + Name: self.strings.insert(namespace), + MajorVersion: 0xFF, + MinorVersion: 0xFF, + BuildNumber: 0xFF, + RevisionNumber: 0xFF, + Flags: AssemblyFlags::WindowsRuntime.0, + ..Default::default() + })) + .encode(); self.scopes.insert(namespace, scope); scope } diff --git a/crates/libs/metadata/src/writer/winmd/write/tables.rs b/crates/libs/metadata/src/writer/winmd/write/tables.rs index c1cdcfd26f..06b5dd62f2 100644 --- a/crates/libs/metadata/src/writer/winmd/write/tables.rs +++ b/crates/libs/metadata/src/writer/winmd/write/tables.rs @@ -4,6 +4,7 @@ use super::{coded_index_size, Error, Result, Write}; #[derive(Default)] pub struct Tables { + pub Assembly: Vec, pub AssemblyRef: Vec, pub ClassLayout: Vec, pub Constant: Vec, @@ -24,6 +25,19 @@ pub struct Tables { pub TypeSpec: Vec, } +#[derive(Default)] +pub struct Assembly { + pub HashAlgId: u32, + pub MajorVersion: u16, + pub MinorVersion: u16, + pub BuildNumber: u16, + pub RevisionNumber: u16, + pub Flags: u32, + pub PublicKey: u32, + pub Name: u32, + pub Culture: u32, +} + #[derive(Default)] pub struct AssemblyRef { pub MajorVersion: u16, @@ -185,6 +199,7 @@ impl Tables { 1 << 0x1A | // ModuleRef 1 << 0x1B | // TypeSpec 1 << 0x1C | // ImplMap + 1 << 0x20 | // Assembly 1 << 0x23 | // AssemblyRef 1 << 0x29 | // NestedClass 1 << 0x2A; // GenericParam @@ -217,6 +232,7 @@ impl Tables { buffer.write_u32(self.ModuleRef.len() as _); buffer.write_u32(self.TypeSpec.len() as _); buffer.write_u32(self.ImplMap.len() as _); + buffer.write_u32(self.Assembly.len() as _); buffer.write_u32(self.AssemblyRef.len() as _); buffer.write_u32(self.NestedClass.len() as _); buffer.write_u32(self.GenericParam.len() as _); @@ -273,6 +289,18 @@ impl Tables { buffer.write_u32(x.Value); } + for x in self.Assembly { + buffer.write_u32(x.HashAlgId); + 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.PublicKey); + buffer.write_u32(x.Name); + buffer.write_u32(x.Culture); + } + for x in self.AssemblyRef { buffer.write_u16(x.MajorVersion); buffer.write_u16(x.MinorVersion); diff --git a/crates/tests/riddle/Cargo.toml b/crates/tests/riddle/Cargo.toml new file mode 100644 index 0000000000..f2bf9ccca6 --- /dev/null +++ b/crates/tests/riddle/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "test_riddle" +version = "0.0.0" +authors = ["Microsoft"] +edition = "2018" + +[dependencies.windows-metadata] +path = "../../libs/metadata" diff --git a/crates/tests/riddle/src/lib.rs b/crates/tests/riddle/src/lib.rs new file mode 100644 index 0000000000..faddf58649 --- /dev/null +++ b/crates/tests/riddle/src/lib.rs @@ -0,0 +1,35 @@ +use std::process::Command; + +pub fn run_riddle(idl: &str) -> String { + let mut command = Command::new("cargo.exe"); + + command + .arg("install") + .arg("--path") + .arg("../../tools/riddle"); + + if !command.status().unwrap().success() { + panic!("Failed to install riddle"); + } + + let winmd = std::path::Path::new(idl) + .with_extension("winmd") + .to_string_lossy() + .into_owned(); + + let mut command = Command::new("riddle.exe"); + command.arg("-in").arg(idl).arg("-out").arg(&winmd); + + if !command.status().unwrap().success() { + panic!("Failed to run riddle"); + } + + let mut command = Command::new("riddle.exe"); + command.arg("-in").arg(&winmd).arg("-out").arg(idl); + + if !command.status().unwrap().success() { + panic!("Failed to run riddle"); + } + + winmd +} diff --git a/crates/tests/riddle/tests/struct.idl b/crates/tests/riddle/tests/struct.idl new file mode 100644 index 0000000000..3b7819f91d --- /dev/null +++ b/crates/tests/riddle/tests/struct.idl @@ -0,0 +1,8 @@ +mod Test { + struct Name { + a: i32, + b: f32, + c: u64, + d: f64, + } +} \ No newline at end of file diff --git a/crates/tests/riddle/tests/struct.rs b/crates/tests/riddle/tests/struct.rs new file mode 100644 index 0000000000..d4fff6817b --- /dev/null +++ b/crates/tests/riddle/tests/struct.rs @@ -0,0 +1,29 @@ +use test_riddle::run_riddle; +use windows_metadata::reader::*; + +#[test] +fn riddle_struct() { + let output = run_riddle("tests/struct.idl"); + let files = File::with_default(&[&output]).expect("Failed to open winmd files"); + let reader = &Reader::new(&files); + + let def = reader + .get(TypeName::new("Test", "Name")) + .next() + .expect("Type missing"); + + assert_eq!(reader.type_def_kind(def), TypeKind::Struct); + + let fields: Vec = reader.type_def_fields(def).collect(); + assert_eq!(fields.len(), 4); + + assert_eq!(reader.field_name(fields[0]), "a"); + assert_eq!(reader.field_name(fields[1]), "b"); + assert_eq!(reader.field_name(fields[2]), "c"); + assert_eq!(reader.field_name(fields[3]), "d"); + + assert!(matches!(reader.field_type(fields[0], None), Type::I32)); + assert!(matches!(reader.field_type(fields[1], None), Type::F32)); + assert!(matches!(reader.field_type(fields[2], None), Type::U64)); + assert!(matches!(reader.field_type(fields[3], None), Type::F64)); +} diff --git a/crates/tools/lib/src/lib.rs b/crates/tools/lib/src/lib.rs index bc5e3cc206..266130b863 100644 --- a/crates/tools/lib/src/lib.rs +++ b/crates/tools/lib/src/lib.rs @@ -23,10 +23,10 @@ fn combine(files: &[metadata::reader::File], libraries: &mut BTreeMap