diff --git a/crates/libs/metadata/src/attributes.rs b/crates/libs/metadata/src/attributes.rs index af33397bd0..7892147398 100644 --- a/crates/libs/metadata/src/attributes.rs +++ b/crates/libs/metadata/src/attributes.rs @@ -61,11 +61,12 @@ impl MethodAttributes { pub const Virtual: Self = Self(0x40); } -flags!(MethodImplAttributes, usize); +flags!(MethodImplAttributes, u16); impl MethodImplAttributes { pub const PreserveSig: Self = Self(0x80); } +// These are not really ECMA-335 attributes but instead the flags found in the method signature. flags!(MethodCallAttributes, u8); impl MethodCallAttributes { pub const HASTHIS: Self = Self(0x20); diff --git a/crates/libs/metadata/src/file/reader.rs b/crates/libs/metadata/src/file/reader.rs index a435d06ef4..571a06d5e3 100644 --- a/crates/libs/metadata/src/file/reader.rs +++ b/crates/libs/metadata/src/file/reader.rs @@ -175,6 +175,10 @@ pub trait RowReader<'a> { // GenericParam // + fn generic_param_number(&self, row: GenericParam) -> u16 { + self.row_usize(row, 0) as u16 + } + fn generic_param_name(&self, row: GenericParam) -> &'a str { self.row_str(row, 3) } @@ -212,7 +216,7 @@ pub trait RowReader<'a> { // fn method_def_impl_flags(&self, row: MethodDef) -> MethodImplAttributes { - MethodImplAttributes(self.row_usize(row, 1)) + MethodImplAttributes(self.row_usize(row, 1) as u16) } fn method_def_flags(&self, row: MethodDef) -> MethodAttributes { @@ -268,8 +272,8 @@ pub trait RowReader<'a> { ParamAttributes(self.row_usize(row, 0) as u16) } - fn param_sequence(&self, row: Param) -> usize { - self.row_usize(row, 1) + fn param_sequence(&self, row: Param) -> u16 { + self.row_usize(row, 1) as u16 } fn param_name(&self, row: Param) -> &'a str { @@ -307,8 +311,8 @@ pub trait RowReader<'a> { self.row_list(row, 4) } - fn type_def_generics(&self, row: TypeDef) -> Vec { - self.row_equal_range(row, 2, TypeOrMethodDef::TypeDef(row).encode()).map(Type::GenericParam).collect() + fn type_def_generics(&self, row: TypeDef) -> RowIterator { + self.row_equal_range(row, 2, TypeOrMethodDef::TypeDef(row).encode()) } fn type_def_interface_impls(&self, row: TypeDef) -> RowIterator { diff --git a/crates/libs/metadata/src/lib.rs b/crates/libs/metadata/src/lib.rs index c9c9af2211..7ded7df4a5 100644 --- a/crates/libs/metadata/src/lib.rs +++ b/crates/libs/metadata/src/lib.rs @@ -23,68 +23,7 @@ pub use row::*; use std::collections::*; pub use type_name::TypeName; -#[derive(Clone, PartialEq, PartialOrd, Eq, Ord)] -pub struct Interface { - pub ty: Type, - pub kind: InterfaceKind, -} - -#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord)] -pub enum InterfaceKind { - None, - Default, - Overridable, - Static, - Base, -} - -#[derive(Copy, Clone, PartialEq, Eq)] -pub struct QueryPosition { - pub object: usize, - pub guid: usize, -} - -#[derive(Copy, Clone, PartialEq, Eq)] -pub enum SignatureKind { - Query(QueryPosition), - QueryOptional(QueryPosition), - ResultValue, - ResultVoid, - ReturnStruct, - ReturnValue, - ReturnVoid, - PreserveSig, -} - -#[derive(Copy, Clone, Eq, PartialEq)] -pub enum SignatureParamKind { - ArrayFixed(usize), - ArrayRelativeLen(usize), - ArrayRelativeByteLen(usize), - ArrayRelativePtr(usize), - TryInto, - IntoParam, - OptionalPointer, - ValueType, - Blittable, - Other, -} - -impl SignatureParamKind { - fn is_array(&self) -> bool { - matches!(self, Self::ArrayFixed(_) | Self::ArrayRelativeLen(_) | Self::ArrayRelativeByteLen(_) | Self::ArrayRelativePtr(_)) - } -} - -#[derive(PartialEq, Eq, Debug)] -pub enum AsyncKind { - None, - Action, - ActionWithProgress, - Operation, - OperationWithProgress, -} - +// TODO: move to riddle #[derive(Copy, Clone, PartialEq, Eq, Debug, PartialOrd, Ord)] pub enum TypeKind { Interface, @@ -111,20 +50,12 @@ pub enum Value { TypeName(String), TypeRef(TypeDefOrRef), EnumDef(TypeDef, Box), - EnumRef(TypeDefOrRef, Box), } -pub struct Signature { - pub def: MethodDef, - pub params: Vec, - pub return_type: Type, +pub struct MethodDefSig { pub call_flags: MethodCallAttributes, -} - -pub struct SignatureParam { - pub def: Param, - pub ty: Type, - pub kind: SignatureParamKind, + pub return_type: Type, + pub params: Vec, } #[derive(Clone, Debug)] @@ -247,9 +178,6 @@ impl<'a> Reader<'a> { Type::String => Value::String(values.read_str().to_string()), Type::TypeName => Value::TypeName(values.read_str().to_string()), Type::TypeDef(def, _) => Value::EnumDef(def, Box::new(values.read_integer(self.type_def_underlying_type(def)))), - // It's impossible to know the type of a TypeRef so we just assume 32-bit integer which covers System.* attribute args - // reasonably well but the solution is to follow the WinRT metadata and define replacements for those attribute types. - Type::TypeRef(code) => Value::EnumRef(code, Box::new(values.read_integer(Type::I32))), rest => unimplemented!("{rest:?}"), }; @@ -320,16 +248,8 @@ impl<'a> Reader<'a> { // InterfaceImpl table queries // - fn interface_impl_type(&self, row: InterfaceImpl, generics: &[Type]) -> Interface { - let mut kind = InterfaceKind::None; - for attribute in self.attributes(row) { - match self.attribute_name(attribute) { - "DefaultAttribute" => kind = InterfaceKind::Default, - "OverridableAttribute" => kind = InterfaceKind::Overridable, - _ => {} - } - } - Interface { ty: self.type_from_ref(self.row_decode(row, 1), None, generics), kind } + pub fn interface_impl_type(&self, row: InterfaceImpl, generics: &[Type]) -> Type { + self.type_from_ref(self.row_decode(row, 1), None, generics) } // @@ -370,124 +290,16 @@ impl<'a> Reader<'a> { None }) } - 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::SupportsLastError) - } else { - false - } - } - pub fn method_def_signature(&self, namespace: &str, row: MethodDef, generics: &[Type]) -> Signature { - let mut blob = self.row_blob(row, 4); - let call_flags = MethodCallAttributes(blob.read_usize() as u8); - let _param_count = blob.read_usize(); - let mut return_type = self.type_from_blob(&mut blob, None, generics); - - let mut params: Vec = self - .method_def_params(row) - .filter_map(|param| { - let param_is_const = self.has_attribute(param, "ConstAttribute"); - if self.param_sequence(param) == 0 { - if param_is_const { - return_type = return_type.clone().to_const_type(); - } - None - } else { - let is_output = self.param_flags(param).contains(ParamAttributes::Out); - let mut ty = self.type_from_blob(&mut blob, None, generics); - - if let Some(name) = self.param_or_enum(param) { - let def = self.get_type_def(TypeName::new(namespace, &name)).next().expect("Enum not found"); - ty = Type::PrimitiveOrEnum(Box::new(ty), Box::new(Type::TypeDef(def, Vec::new()))); - } - - if param_is_const || !is_output { - ty = ty.to_const_type(); - } - if !is_output { - ty = ty.to_const_ptr(); - } - let kind = self.param_kind(param); - Some(SignatureParam { def: param, ty, kind }) - } - }) - .collect(); - - for position in 0..params.len() { - // Point len params back to the corresponding ptr params. - match params[position].kind { - SignatureParamKind::ArrayRelativeLen(relative) | SignatureParamKind::ArrayRelativeByteLen(relative) => { - // The len params must be input only. - if !self.param_flags(params[relative].def).contains(ParamAttributes::Out) && position != relative && !params[relative].ty.is_pointer() { - params[relative].kind = SignatureParamKind::ArrayRelativePtr(position); - } else { - params[position].kind = SignatureParamKind::Other; - } - } - SignatureParamKind::ArrayFixed(_) => { - if self.has_attribute(params[position].def, "FreeWithAttribute") { - params[position].kind = SignatureParamKind::Other; - } - } - _ => {} - } - } - - let mut sets = BTreeMap::>::new(); - - // Finds sets of ptr params pointing at the same len param. - for (position, param) in params.iter().enumerate() { - match param.kind { - SignatureParamKind::ArrayRelativeLen(relative) | SignatureParamKind::ArrayRelativeByteLen(relative) => { - sets.entry(relative).or_default().push(position); - } - _ => {} - } - } - // Remove all sets. - for (len, ptrs) in sets { - if ptrs.len() > 1 { - params[len].kind = SignatureParamKind::Other; - for ptr in ptrs { - params[ptr].kind = SignatureParamKind::Other; - } - } - } - - // Remove any byte arrays that aren't byte-sized types. - for position in 0..params.len() { - if let SignatureParamKind::ArrayRelativeByteLen(relative) = params[position].kind { - if !params[position].ty.is_byte_size() { - params[position].kind = SignatureParamKind::Other; - params[relative].kind = SignatureParamKind::Other; - } - } - } - - for param in &mut params { - if param.kind == SignatureParamKind::Other { - if self.signature_param_is_convertible(param) { - if self.signature_param_is_failible_param(param) { - param.kind = SignatureParamKind::TryInto; - } else { - param.kind = SignatureParamKind::IntoParam; - } - } else { - let flags = self.param_flags(param.def); - if param.ty.is_pointer() && (flags.contains(ParamAttributes::Optional) || self.has_attribute(param.def, "ReservedAttribute")) { - param.kind = SignatureParamKind::OptionalPointer; - } else if self.type_is_primitive(¶m.ty) && (!param.ty.is_pointer() || self.type_is_blittable(¶m.ty.deref())) { - param.kind = SignatureParamKind::ValueType; - } else if self.type_is_blittable(¶m.ty) { - param.kind = SignatureParamKind::Blittable; - } - } - } - } + pub fn method_def_signature(&self, method: MethodDef, generics: &[Type]) -> MethodDefSig { + let mut blob = self.row_blob(method, 4); + let call_flags = MethodCallAttributes(blob.read_usize() as u8); + let params = blob.read_usize(); + let return_type = self.type_from_blob(&mut blob, None, generics); - Signature { def: row, params, return_type, call_flags } + MethodDefSig { call_flags, return_type, params: (0..params).map(|_| self.type_from_blob(&mut blob, None, generics)).collect() } } + pub fn method_def_extern_abi(&self, def: MethodDef) -> &'static str { let impl_map = self.method_def_impl_map(def).expect("ImplMap not found"); let flags = self.impl_map_flags(impl_map); @@ -500,9 +312,9 @@ impl<'a> Reader<'a> { unimplemented!() } } - pub fn method_def_size(&self, namespace: &str, method: MethodDef) -> usize { - let signature = self.method_def_signature(namespace, method, &[]); - signature.params.iter().fold(0, |sum, param| sum + std::cmp::max(4, self.type_size(¶m.ty))) + pub fn method_def_size(&self, method: MethodDef) -> usize { + let sig = self.method_def_signature(method, &[]); + sig.params.iter().fold(0, |sum, param| sum + std::cmp::max(4, self.type_size(param))) } pub fn type_def_size(&self, def: TypeDef) -> usize { match self.type_def_kind(def) { @@ -524,7 +336,8 @@ impl<'a> Reader<'a> { _ => 4, } } - fn type_size(&self, ty: &Type) -> usize { + // TODO: this shouldn't be public - needed to work around Win32 metadata hackery. + pub fn type_size(&self, ty: &Type) -> usize { match ty { Type::I8 | Type::U8 => 1, Type::I16 | Type::U16 => 2, @@ -555,46 +368,6 @@ impl<'a> Reader<'a> { } } - // - // Param table queries - // - - fn param_kind(&self, row: Param) -> SignatureParamKind { - for attribute in self.attributes(row) { - match self.attribute_name(attribute) { - "NativeArrayInfoAttribute" => { - for (_, value) in self.attribute_args(attribute) { - match value { - Value::I16(value) => return SignatureParamKind::ArrayRelativeLen(value as usize), - Value::I32(value) => return SignatureParamKind::ArrayFixed(value as usize), - _ => {} - } - } - } - "MemorySizeAttribute" => { - for (_, value) in self.attribute_args(attribute) { - if let Value::I16(value) = value { - return SignatureParamKind::ArrayRelativeByteLen(value as usize); - } - } - } - _ => {} - } - } - SignatureParamKind::Other - } - // TODO: this is a terribly broken Win32 metadata attribute - need to get rid of it. - fn param_or_enum(&self, row: Param) -> Option { - self.find_attribute(row, "AssociatedEnumAttribute").and_then(|attribute| { - for (_, arg) in self.attribute_args(attribute) { - if let Value::String(name) = arg { - return Some(name); - } - } - None - }) - } - // // TypeDef table queries // @@ -641,9 +414,7 @@ impl<'a> Reader<'a> { _ => false, } } - fn type_def_is_callback(&self, row: TypeDef) -> bool { - !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.attributes(row) { if self.attribute_name(attribute) == "ActivatableAttribute" { @@ -656,14 +427,13 @@ impl<'a> Reader<'a> { } false } - // TODO: consider removing all the expects and just return Option and let the caller expect it - // that way the metadata reader is a little more schema-agnostic... - pub fn type_def_interfaces(&'a self, row: TypeDef, generics: &'a [Type]) -> impl Iterator + '_ { + pub fn type_def_interfaces(&'a self, row: TypeDef, generics: &'a [Type]) -> impl Iterator + '_ { self.type_def_interface_impls(row).map(move |row| self.interface_impl_type(row, generics)) } + pub fn type_def_default_interface(&self, row: TypeDef) -> Option { - self.type_def_interfaces(row, &[]).find(|interface| interface.kind == InterfaceKind::Default).map(|interface| interface.ty) + self.type_def_interface_impls(row).find_map(move |row| if self.has_attribute(row, "DefaultAttribute") { Some(self.interface_impl_type(row, &[])) } else { None }) } pub fn type_def_has_default_interface(&self, row: TypeDef) -> bool { self.type_def_interface_impls(row).any(|imp| self.has_attribute(imp, "DefaultAttribute")) @@ -680,12 +450,7 @@ impl<'a> Reader<'a> { // nested structs. Fortunately, this is rare enough that this check is sufficient. self.type_def_kind(row) == TypeKind::Struct && !self.type_def_is_handle(row) } - fn type_def_is_trivially_convertible(&self, row: TypeDef) -> bool { - match self.type_def_kind(row) { - TypeKind::Struct => self.type_def_is_handle(row), - _ => false, - } - } + fn type_def_is_primitive(&self, row: TypeDef) -> bool { match self.type_def_kind(row) { TypeKind::Enum => true, @@ -744,31 +509,7 @@ impl<'a> Reader<'a> { false } } - pub fn type_def_has_callback(&self, row: TypeDef) -> bool { - if self.type_def_is_callback(row) { - return true; - } - if self.type_def_kind(row) != TypeKind::Struct { - return false; - } - fn check(reader: &Reader, row: TypeDef) -> bool { - if reader.type_def_fields(row).any(|field| reader.type_has_callback(&reader.field_type(field, Some(row)))) { - return true; - } - false - } - let type_name = self.type_def_type_name(row); - if type_name.namespace.is_empty() { - check(self, row) - } else { - for row in self.get_type_def(type_name) { - if check(self, row) { - return true; - } - } - false - } - } + pub fn type_def_guid(&self, row: TypeDef) -> Option { self.find_attribute(row, "GuidAttribute").map(|attribute| GUID::from_args(&self.attribute_args(attribute))) } @@ -829,39 +570,12 @@ impl<'a> Reader<'a> { _ => false, } } - pub fn type_def_can_implement(&self, row: TypeDef) -> bool { - if let Some(attribute) = self.find_attribute(row, "ExclusiveToAttribute") { - for (_, arg) in self.attribute_args(attribute) { - if let Value::TypeName(type_name) = arg { - for child in self.get_type_def(TypeName::parse(&type_name)).flat_map(|def| self.type_def_interfaces(def, &[])) { - if child.kind == InterfaceKind::Overridable { - if let Type::TypeDef(def, _) = child.ty { - if self.type_def_type_name(def) == self.type_def_type_name(row) { - return true; - } - } - } - } - } - } - return false; - } - true - } - pub fn type_def_async_kind(&self, row: TypeDef) -> AsyncKind { - match self.type_def_type_name(row) { - TypeName::IAsyncAction => AsyncKind::Action, - TypeName::IAsyncActionWithProgress => AsyncKind::ActionWithProgress, - TypeName::IAsyncOperation => AsyncKind::Operation, - TypeName::IAsyncOperationWithProgress => AsyncKind::OperationWithProgress, - _ => AsyncKind::None, - } - } + pub fn type_def_signature(&self, row: TypeDef, generics: &[Type]) -> String { match self.type_def_kind(row) { TypeKind::Interface => self.type_def_interface_signature(row, generics), TypeKind::Class => { - if let Type::TypeDef(default, generics) = self.type_def_interfaces(row, generics).find(|row| row.kind == InterfaceKind::Default).expect("Default interface not found").ty { + if let Some(Type::TypeDef(default, generics)) = self.type_def_default_interface(row) { format!("rc({};{})", self.type_def_type_name(row), self.type_def_interface_signature(default, &generics)) } else { unimplemented!(); @@ -911,10 +625,10 @@ impl<'a> Reader<'a> { } else { let mut next = row; while let Some(base) = self.type_def_interfaces(next, &[]).next() { - match base.ty { + match base { Type::TypeDef(row, _) => { next = row; - result.insert(0, base.ty); + result.insert(0, base); } Type::IInspectable => { result.insert(0, Type::IUnknown); @@ -936,149 +650,10 @@ impl<'a> Reader<'a> { // Signature queries // - pub fn signature_param_is_borrowed(&self, param: &SignatureParam) -> bool { - self.type_is_borrowed(¶m.ty) - } - pub fn signature_param_is_failible_param(&self, param: &SignatureParam) -> bool { - self.type_is_non_exclusive_winrt_interface(¶m.ty) - } - pub fn signature_param_is_convertible(&self, param: &SignatureParam) -> bool { - !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)) - } - fn signature_param_is_retval(&self, param: &SignatureParam) -> bool { - // The Win32 metadata uses `RetValAttribute` to call out retval methods but it is employed - // very sparingly, so this heuristic is used to apply the transformation more uniformly. - if self.has_attribute(param.def, "RetValAttribute") { - return true; - } - if !param.ty.is_pointer() { - return false; - } - if param.ty.is_void() { - return false; - } - let flags = self.param_flags(param.def); - if flags.contains(ParamAttributes::In) || !flags.contains(ParamAttributes::Out) || flags.contains(ParamAttributes::Optional) || param.kind.is_array() { - return false; - } - if self.param_kind(param.def).is_array() { - return false; - } - // If it's bigger than 128 bits, best to pass as a reference. - if self.type_size(¶m.ty.deref()) > 16 { - return false; - } - // Win32 callbacks are defined as `Option` so we don't include them here to avoid - // producing the `Result>` anti-pattern. - !self.type_is_callback(¶m.ty.deref()) - } - pub fn signature_kind(&self, signature: &Signature) -> SignatureKind { - if self.has_attribute(signature.def, "CanReturnMultipleSuccessValuesAttribute") { - return SignatureKind::PreserveSig; - } - match &signature.return_type { - Type::Void if self.signature_is_retval(signature) => SignatureKind::ReturnValue, - Type::Void => SignatureKind::ReturnVoid, - Type::HRESULT => { - if signature.params.len() >= 2 { - if let Some(guid) = self.signature_param_is_query_guid(&signature.params) { - if let Some(object) = self.signature_param_is_query_object(&signature.params) { - if self.param_flags(signature.params[object].def).contains(ParamAttributes::Optional) { - return SignatureKind::QueryOptional(QueryPosition { object, guid }); - } else { - return SignatureKind::Query(QueryPosition { object, guid }); - } - } - } - } - if self.signature_is_retval(signature) { - SignatureKind::ResultValue - } else { - SignatureKind::ResultVoid - } - } - Type::TypeDef(def, _) if self.type_def_type_name(*def) == TypeName::NTSTATUS => SignatureKind::ResultVoid, - Type::TypeDef(def, _) if self.type_def_type_name(*def) == TypeName::WIN32_ERROR => SignatureKind::ResultVoid, - Type::TypeDef(def, _) if self.type_def_type_name(*def) == TypeName::BOOL && self.method_def_last_error(signature.def) => SignatureKind::ResultVoid, - _ if self.type_is_struct(&signature.return_type) => SignatureKind::ReturnStruct, - _ => SignatureKind::PreserveSig, - } - } - fn signature_is_retval(&self, signature: &Signature) -> bool { - signature.params.last().map_or(false, |param| self.signature_param_is_retval(param)) - && signature.params[..signature.params.len() - 1].iter().all(|param| { - let flags = self.param_flags(param.def); - !flags.contains(ParamAttributes::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::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.has_attribute(param.def, "ComOutPtrAttribute")) - } - // // Other type queries // - pub fn type_interfaces(&self, ty: &Type) -> Vec { - // TODO: collect into btree map and then return collected vec - // This will both sort the results and should make finding dupes faster - fn walk(reader: &Reader, result: &mut Vec, parent: &Type, is_base: bool) { - if let Type::TypeDef(row, generics) = parent { - for mut child in reader.type_def_interfaces(*row, generics) { - child.kind = if !is_base && child.kind == InterfaceKind::Default { - InterfaceKind::Default - } else if child.kind == InterfaceKind::Overridable { - continue; - } else if is_base { - InterfaceKind::Base - } else { - InterfaceKind::None - }; - let mut found = false; - for existing in result.iter_mut() { - if existing.ty == child.ty { - found = true; - if child.kind == InterfaceKind::Default { - existing.kind = child.kind - } - } - } - if !found { - walk(reader, result, &child.ty, is_base); - result.push(child); - } - } - } - } - let mut result = Vec::new(); - walk(self, &mut result, ty, false); - if let Type::TypeDef(row, _) = ty { - if self.type_def_kind(*row) == TypeKind::Class { - for base in self.type_def_bases(*row) { - walk(self, &mut result, &Type::TypeDef(base, Vec::new()), true); - } - for attribute in self.attributes(*row) { - match self.attribute_name(attribute) { - "StaticAttribute" | "ActivatableAttribute" => { - for (_, arg) in self.attribute_args(attribute) { - if let Value::TypeName(type_name) = arg { - let def = self.get_type_def(TypeName::parse(&type_name)).next().expect("Type not found"); - result.push(Interface { ty: Type::TypeDef(def, Vec::new()), kind: InterfaceKind::Static }); - break; - } - } - } - _ => {} - } - } - } - } - result.sort_by(|a, b| self.type_name(&a.ty).cmp(self.type_name(&b.ty))); - result - } pub fn type_def_or_ref(&self, code: TypeDefOrRef) -> TypeName { match code { TypeDefOrRef::TypeDef(row) => TypeName::new(self.type_def_namespace(row), self.type_def_name(row)), @@ -1124,13 +699,7 @@ impl<'a> Reader<'a> { _ => false, } } - pub fn type_has_callback(&self, ty: &Type) -> bool { - match ty { - Type::TypeDef(row, _) => self.type_def_has_callback(*row), - Type::Win32Array(ty, _) => self.type_has_callback(ty), - _ => false, - } - } + fn type_from_ref(&self, code: TypeDefOrRef, enclosing: Option, generics: &[Type]) -> Type { if let TypeDefOrRef::TypeSpec(def) = code { let mut blob = self.type_spec_signature(def); @@ -1170,7 +739,8 @@ impl<'a> Reader<'a> { Type::TypeRef(code) } } - fn type_from_blob(&self, blob: &mut Blob, enclosing: Option, generics: &[Type]) -> Type { + // TODO: this shouldn't be public + pub fn type_from_blob(&self, blob: &mut Blob, enclosing: Option, generics: &[Type]) -> Type { // Used by WinRT to indicate that a struct input parameter is passed by reference rather than by value on the ABI. let is_const = blob.read_modifiers().iter().any(|def| self.type_def_or_ref(*def) == TypeName::IsConst); @@ -1224,9 +794,10 @@ impl<'a> Reader<'a> { Type::Win32Array(Box::new(kind), bounds) } ELEMENT_TYPE_GENERICINST => { - blob.read_usize(); + blob.read_usize(); // ELEMENT_TYPE_VALUETYPE or ELEMENT_TYPE_CLASS - let def = self.get_type_def(self.type_def_or_ref(TypeDefOrRef::decode(blob.file, blob.read_usize()))).next().expect("Type not found"); + let type_name = self.type_def_or_ref(TypeDefOrRef::decode(blob.file, blob.read_usize())); + let def = self.get_type_def(type_name).next().unwrap_or_else(|| panic!("Type not found: {}", type_name)); let mut args = Vec::with_capacity(blob.read_usize()); for _ in 0..args.capacity() { @@ -1238,12 +809,7 @@ impl<'a> Reader<'a> { rest => unimplemented!("{rest:?}"), } } - fn type_name(&self, ty: &Type) -> &str { - match ty { - Type::TypeDef(row, _) => self.type_def_name(*row), - _ => "", - } - } + fn type_signature(&self, ty: &Type) -> String { match ty { Type::Bool => "b1".to_string(), @@ -1275,43 +841,7 @@ impl<'a> Reader<'a> { _ => false, } } - fn type_is_borrowed(&self, ty: &Type) -> bool { - match ty { - Type::TypeDef(row, _) => !self.type_def_is_blittable(*row), - Type::BSTR | Type::PCSTR | Type::PCWSTR | Type::IInspectable | Type::IUnknown | Type::GenericParam(_) => true, - _ => false, - } - } - fn type_is_non_exclusive_winrt_interface(&self, ty: &Type) -> bool { - match ty { - Type::TypeDef(row, _) => { - let flags = self.type_def_flags(*row); - if !flags.contains(TypeAttributes::WindowsRuntime) { - false - } else { - match self.type_def_kind(*row) { - TypeKind::Interface => !self.type_def_is_exclusive(*row), - TypeKind::Class => self.has_attribute(*row, "ComposableAttribute"), - _ => false, - } - } - } - _ => false, - } - } - fn type_is_trivially_convertible(&self, ty: &Type) -> bool { - match ty { - Type::TypeDef(row, _) => self.type_def_is_trivially_convertible(*row), - Type::PCSTR | Type::PCWSTR => true, - _ => false, - } - } - fn type_is_callback(&self, ty: &Type) -> bool { - match ty { - Type::TypeDef(row, _) => self.type_def_is_callback(*row), - _ => false, - } - } + pub fn type_is_primitive(&self, ty: &Type) -> bool { match ty { Type::TypeDef(row, _) => self.type_def_is_primitive(*row), @@ -1356,7 +886,7 @@ fn trim_tick(name: &str) -> &str { } } -// TODO: this should be in riddle's Rust generator if at all. +// TODO: this should be in riddle's Rust generator if at all - perhaps as convertible types rather than remapped types since there's already some precedent for that. pub const REMAP_TYPES: [(TypeName, TypeName); 2] = [(TypeName::D2D_MATRIX_3X2_F, TypeName::Matrix3x2), (TypeName::D3DMATRIX, TypeName::Matrix4x4)]; // TODO: get rid of at least the second tuple if not the whole thing. diff --git a/crates/libs/metadata/src/type.rs b/crates/libs/metadata/src/type.rs index b88f7e9f05..7974bf0329 100644 --- a/crates/libs/metadata/src/type.rs +++ b/crates/libs/metadata/src/type.rs @@ -30,7 +30,7 @@ pub enum Type { TypeName, // Regular ECMA-335 types that map to metadata - TypeRef(TypeDefOrRef), + TypeRef(TypeDefOrRef), // Note: this ought to be a TypeName but that would require Type to have a lifetime reference. GenericParam(GenericParam), TypeDef(TypeDef, Vec), diff --git a/crates/tests/metadata/tests/fn_call_size.rs b/crates/tests/metadata/tests/fn_call_size.rs index 901aa99766..b77654ac8d 100644 --- a/crates/tests/metadata/tests/fn_call_size.rs +++ b/crates/tests/metadata/tests/fn_call_size.rs @@ -316,7 +316,7 @@ fn function_size(reader: &Reader, namespace: &str, name: &str) -> usize { .get_method_def(TypeName::new(namespace, name)) .next() .expect("Function not found"); - return reader.method_def_size(namespace, method); + return reader.method_def_size(method); } fn struct_size(reader: &Reader, namespace: &str, name: &str) -> usize { diff --git a/crates/tests/riddle/src/generic_interfaces.rs b/crates/tests/riddle/src/generic_interfaces.rs new file mode 100644 index 0000000000..f21cd08892 --- /dev/null +++ b/crates/tests/riddle/src/generic_interfaces.rs @@ -0,0 +1,463 @@ +// Bindings generated by `riddle` 0.0.1 + +#![allow( + non_snake_case, + non_upper_case_globals, + non_camel_case_types, + dead_code, + clippy::all +)] +#[repr(transparent)] +pub struct IIterable(::windows_core::IUnknown, ::core::marker::PhantomData) +where + T: ::windows_core::RuntimeType + 'static; +impl IIterable { + pub fn First(&self) -> ::windows_core::Result> { + let this = self; + unsafe { + let mut result__ = ::std::mem::zeroed(); + (::windows_core::Interface::vtable(this).First)( + ::windows_core::Interface::as_raw(this), + &mut result__, + ) + .from_abi(result__) + } + } +} +impl ::windows_core::CanInto<::windows_core::IUnknown> + for IIterable +{ +} +impl ::windows_core::CanInto<::windows_core::IInspectable> + for IIterable +{ +} +impl ::core::cmp::PartialEq for IIterable { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} +impl ::core::cmp::Eq for IIterable {} +impl ::core::fmt::Debug for IIterable { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + f.debug_tuple("IIterable").field(&self.0).finish() + } +} +impl ::windows_core::RuntimeType for IIterable { + const SIGNATURE: ::windows_core::imp::ConstBuffer = { + ::windows_core::imp::ConstBuffer::new() + .push_slice(b"pinterface(") + .push_slice(b"TODO") + .push_slice(b";") + .push_other(::SIGNATURE) + .push_slice(b")") + }; +} +unsafe impl ::windows_core::Interface for IIterable { + type Vtable = IIterable_Vtbl; +} +impl ::core::clone::Clone for IIterable { + fn clone(&self) -> Self { + Self(self.0.clone(), ::core::marker::PhantomData::) + } +} +unsafe impl ::windows_core::ComInterface + for IIterable +{ + const IID: ::windows_core::GUID = + ::windows_core::GUID::from_signature(::SIGNATURE); +} +#[repr(C)] +#[doc(hidden)] +pub struct IIterable_Vtbl +where + T: ::windows_core::RuntimeType + 'static, +{ + pub base__: ::windows_core::IInspectable_Vtbl, + pub First: unsafe extern "system" fn( + this: *mut ::core::ffi::c_void, + result__: *mut *mut ::core::ffi::c_void, + ) -> ::windows_core::HRESULT, + pub T: ::core::marker::PhantomData, +} +#[repr(transparent)] +pub struct IIterator(::windows_core::IUnknown, ::core::marker::PhantomData) +where + T: ::windows_core::RuntimeType + 'static; +impl IIterator { + pub fn get_Current(&self) -> ::windows_core::Result { + let this = self; + unsafe { + let mut result__ = ::std::mem::zeroed(); + (::windows_core::Interface::vtable(this).get_Current)( + ::windows_core::Interface::as_raw(this), + &mut result__, + ) + .from_abi(result__) + } + } + pub fn get_HasCurrent(&self) -> ::windows_core::Result { + let this = self; + unsafe { + let mut result__ = ::std::mem::zeroed(); + (::windows_core::Interface::vtable(this).get_HasCurrent)( + ::windows_core::Interface::as_raw(this), + &mut result__, + ) + .from_abi(result__) + } + } + pub fn MoveNext(&self) -> ::windows_core::Result { + let this = self; + unsafe { + let mut result__ = ::std::mem::zeroed(); + (::windows_core::Interface::vtable(this).MoveNext)( + ::windows_core::Interface::as_raw(this), + &mut result__, + ) + .from_abi(result__) + } + } +} +impl ::windows_core::CanInto<::windows_core::IUnknown> + for IIterator +{ +} +impl ::windows_core::CanInto<::windows_core::IInspectable> + for IIterator +{ +} +impl ::core::cmp::PartialEq for IIterator { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} +impl ::core::cmp::Eq for IIterator {} +impl ::core::fmt::Debug for IIterator { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + f.debug_tuple("IIterator").field(&self.0).finish() + } +} +impl ::windows_core::RuntimeType for IIterator { + const SIGNATURE: ::windows_core::imp::ConstBuffer = { + ::windows_core::imp::ConstBuffer::new() + .push_slice(b"pinterface(") + .push_slice(b"TODO") + .push_slice(b";") + .push_other(::SIGNATURE) + .push_slice(b")") + }; +} +unsafe impl ::windows_core::Interface for IIterator { + type Vtable = IIterator_Vtbl; +} +impl ::core::clone::Clone for IIterator { + fn clone(&self) -> Self { + Self(self.0.clone(), ::core::marker::PhantomData::) + } +} +unsafe impl ::windows_core::ComInterface + for IIterator +{ + const IID: ::windows_core::GUID = + ::windows_core::GUID::from_signature(::SIGNATURE); +} +#[repr(C)] +#[doc(hidden)] +pub struct IIterator_Vtbl +where + T: ::windows_core::RuntimeType + 'static, +{ + pub base__: ::windows_core::IInspectable_Vtbl, + pub get_Current: unsafe extern "system" fn( + this: *mut ::core::ffi::c_void, + result__: *mut ::windows_core::AbiType, + ) -> ::windows_core::HRESULT, + pub get_HasCurrent: unsafe extern "system" fn( + this: *mut ::core::ffi::c_void, + result__: *mut bool, + ) -> ::windows_core::HRESULT, + pub MoveNext: unsafe extern "system" fn( + this: *mut ::core::ffi::c_void, + result__: *mut bool, + ) -> ::windows_core::HRESULT, + pub T: ::core::marker::PhantomData, +} +#[repr(transparent)] +pub struct IKeyValuePair( + ::windows_core::IUnknown, + ::core::marker::PhantomData, + ::core::marker::PhantomData, +) +where + K: ::windows_core::RuntimeType + 'static, + V: ::windows_core::RuntimeType + 'static; +impl + IKeyValuePair +{ + pub fn get_Key(&self) -> ::windows_core::Result { + let this = self; + unsafe { + let mut result__ = ::std::mem::zeroed(); + (::windows_core::Interface::vtable(this).get_Key)( + ::windows_core::Interface::as_raw(this), + &mut result__, + ) + .from_abi(result__) + } + } + pub fn get_Value(&self) -> ::windows_core::Result { + let this = self; + unsafe { + let mut result__ = ::std::mem::zeroed(); + (::windows_core::Interface::vtable(this).get_Value)( + ::windows_core::Interface::as_raw(this), + &mut result__, + ) + .from_abi(result__) + } + } +} +impl + ::windows_core::CanInto<::windows_core::IUnknown> for IKeyValuePair +{ +} +impl + ::windows_core::CanInto<::windows_core::IInspectable> for IKeyValuePair +{ +} +impl + ::core::cmp::PartialEq for IKeyValuePair +{ + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} +impl + ::core::cmp::Eq for IKeyValuePair +{ +} +impl + ::core::fmt::Debug for IKeyValuePair +{ + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + f.debug_tuple("IKeyValuePair").field(&self.0).finish() + } +} +impl + ::windows_core::RuntimeType for IKeyValuePair +{ + const SIGNATURE: ::windows_core::imp::ConstBuffer = { + ::windows_core::imp::ConstBuffer::new() + .push_slice(b"pinterface(") + .push_slice(b"TODO") + .push_slice(b";") + .push_other(::SIGNATURE) + .push_slice(b";") + .push_other(::SIGNATURE) + .push_slice(b")") + }; +} +unsafe impl + ::windows_core::Interface for IKeyValuePair +{ + type Vtable = IKeyValuePair_Vtbl; +} +impl + ::core::clone::Clone for IKeyValuePair +{ + fn clone(&self) -> Self { + Self( + self.0.clone(), + ::core::marker::PhantomData::, + ::core::marker::PhantomData::, + ) + } +} +unsafe impl + ::windows_core::ComInterface for IKeyValuePair +{ + const IID: ::windows_core::GUID = + ::windows_core::GUID::from_signature(::SIGNATURE); +} +#[repr(C)] +#[doc(hidden)] +pub struct IKeyValuePair_Vtbl +where + K: ::windows_core::RuntimeType + 'static, + V: ::windows_core::RuntimeType + 'static, +{ + pub base__: ::windows_core::IInspectable_Vtbl, + pub get_Key: unsafe extern "system" fn( + this: *mut ::core::ffi::c_void, + result__: *mut ::windows_core::AbiType, + ) -> ::windows_core::HRESULT, + pub get_Value: unsafe extern "system" fn( + this: *mut ::core::ffi::c_void, + result__: *mut ::windows_core::AbiType, + ) -> ::windows_core::HRESULT, + pub K: ::core::marker::PhantomData, + pub V: ::core::marker::PhantomData, +} +#[repr(transparent)] +pub struct IMapView( + ::windows_core::IUnknown, + ::core::marker::PhantomData, + ::core::marker::PhantomData, +) +where + K: ::windows_core::RuntimeType + 'static, + V: ::windows_core::RuntimeType + 'static; +impl + IMapView +{ + pub fn Lookup( + &self, + key: &mut >::Default, + ) -> ::windows_core::Result + where + P0: ::windows_core::IntoParam, + { + let this = self; + unsafe { + let mut result__ = ::std::mem::zeroed(); + (::windows_core::Interface::vtable(this).Lookup)( + ::windows_core::Interface::as_raw(this), + key as *mut _ as _, + &mut result__, + ) + .from_abi(result__) + } + } + pub fn get_Size(&self) -> ::windows_core::Result { + let this = self; + unsafe { + let mut result__ = ::std::mem::zeroed(); + (::windows_core::Interface::vtable(this).get_Size)( + ::windows_core::Interface::as_raw(this), + &mut result__, + ) + .from_abi(result__) + } + } + pub fn HasKey( + &self, + key: &mut >::Default, + ) -> ::windows_core::Result + where + P0: ::windows_core::IntoParam, + { + let this = self; + unsafe { + let mut result__ = ::std::mem::zeroed(); + (::windows_core::Interface::vtable(this).HasKey)( + ::windows_core::Interface::as_raw(this), + key as *mut _ as _, + &mut result__, + ) + .from_abi(result__) + } + } + pub fn First(&self) -> ::windows_core::Result>> { + let this = &::windows_core::ComInterface::cast::>>(self)?; + unsafe { + let mut result__ = ::std::mem::zeroed(); + (::windows_core::Interface::vtable(this).First)( + ::windows_core::Interface::as_raw(this), + &mut result__, + ) + .from_abi(result__) + } + } +} +impl + ::windows_core::CanInto<::windows_core::IUnknown> for IMapView +{ +} +impl + ::windows_core::CanInto<::windows_core::IInspectable> for IMapView +{ +} +impl + ::windows_core::CanTryInto>> for IMapView +{ +} +impl + ::core::cmp::PartialEq for IMapView +{ + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} +impl + ::core::cmp::Eq for IMapView +{ +} +impl + ::core::fmt::Debug for IMapView +{ + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + f.debug_tuple("IMapView").field(&self.0).finish() + } +} +impl + ::windows_core::RuntimeType for IMapView +{ + const SIGNATURE: ::windows_core::imp::ConstBuffer = { + ::windows_core::imp::ConstBuffer::new() + .push_slice(b"pinterface(") + .push_slice(b"TODO") + .push_slice(b";") + .push_other(::SIGNATURE) + .push_slice(b";") + .push_other(::SIGNATURE) + .push_slice(b")") + }; +} +unsafe impl + ::windows_core::Interface for IMapView +{ + type Vtable = IMapView_Vtbl; +} +impl + ::core::clone::Clone for IMapView +{ + fn clone(&self) -> Self { + Self( + self.0.clone(), + ::core::marker::PhantomData::, + ::core::marker::PhantomData::, + ) + } +} +unsafe impl + ::windows_core::ComInterface for IMapView +{ + const IID: ::windows_core::GUID = + ::windows_core::GUID::from_signature(::SIGNATURE); +} +#[repr(C)] +#[doc(hidden)] +pub struct IMapView_Vtbl +where + K: ::windows_core::RuntimeType + 'static, + V: ::windows_core::RuntimeType + 'static, +{ + pub base__: ::windows_core::IInspectable_Vtbl, + pub Lookup: unsafe extern "system" fn( + this: *mut ::core::ffi::c_void, + key: *mut ::windows_core::AbiType, + result__: *mut ::windows_core::AbiType, + ) -> ::windows_core::HRESULT, + pub get_Size: unsafe extern "system" fn( + this: *mut ::core::ffi::c_void, + result__: *mut u32, + ) -> ::windows_core::HRESULT, + pub HasKey: unsafe extern "system" fn( + this: *mut ::core::ffi::c_void, + key: *mut ::windows_core::AbiType, + result__: *mut bool, + ) -> ::windows_core::HRESULT, + pub K: ::core::marker::PhantomData, + pub V: ::core::marker::PhantomData, +} diff --git a/crates/tests/riddle/src/lib.rs b/crates/tests/riddle/src/lib.rs index 7649b367fc..fc67fa8861 100644 --- a/crates/tests/riddle/src/lib.rs +++ b/crates/tests/riddle/src/lib.rs @@ -1,3 +1,4 @@ +mod generic_interfaces; mod module_attributes; mod nested_module; mod nested_struct; diff --git a/crates/tests/riddle/tests/generic_interfaces.rdl b/crates/tests/riddle/tests/generic_interfaces.rdl new file mode 100644 index 0000000000..16457b1bf7 --- /dev/null +++ b/crates/tests/riddle/tests/generic_interfaces.rdl @@ -0,0 +1,21 @@ +#![winrt] + +mod Test { + interface IIterable { + fn First() -> IIterator; + } + interface IIterator { + fn get_Current() -> T; + fn get_HasCurrent() -> bool; + fn MoveNext() -> bool; + } + interface IKeyValuePair { + fn get_Key() -> K; + fn get_Value() -> V; + } + interface IMapView : IIterable> { + fn Lookup(key: K) -> V; + fn get_Size() -> u32; + fn HasKey(key: K) -> bool; + } +} diff --git a/crates/tests/riddle/tests/generic_interfaces.rs b/crates/tests/riddle/tests/generic_interfaces.rs new file mode 100644 index 0000000000..7bac25b697 --- /dev/null +++ b/crates/tests/riddle/tests/generic_interfaces.rs @@ -0,0 +1,36 @@ +use test_riddle::run_riddle; +use windows_metadata::*; + +#[test] +fn test() { + let files = run_riddle("generic_interfaces", "winrt", &[]); + let reader = &Reader::new(&files); + + let types: Vec = reader + .namespace_items("Test", &Default::default()) + .collect(); + + assert_eq!(types.len(), 4); + + let def = reader + .get_type_def(TypeName::new("Test", "IIterable")) + .next() + .unwrap(); + + assert_eq!(reader.type_def_interface_impls(def).count(), 0); + let generics: Vec = reader.type_def_generics(def).collect(); + assert_eq!(generics.len(), 1); + assert_eq!(reader.generic_param_name(generics[0]), "T"); + + let def = reader + .get_type_def(TypeName::new("Test", "IMapView")) + .next() + .unwrap(); + + let impls: Vec = reader.type_def_interface_impls(def).collect(); + assert_eq!(impls.len(), 1); + let generics: Vec = reader.type_def_generics(def).collect(); + assert_eq!(generics.len(), 2); + assert_eq!(reader.generic_param_name(generics[0]), "K"); + assert_eq!(reader.generic_param_name(generics[1]), "V"); +} diff --git a/crates/tests/riddle/tests/params.rs b/crates/tests/riddle/tests/params.rs index 1b42dade8f..a70675321c 100644 --- a/crates/tests/riddle/tests/params.rs +++ b/crates/tests/riddle/tests/params.rs @@ -12,14 +12,14 @@ fn test() { .expect("Type missing"); assert_eq!(reader.type_def_kind(def), TypeKind::Interface); - let generics = &reader.type_def_generics(def); + let generics = &vec![]; assert!(reader.type_def_fields(def).next().is_none()); let methods: Vec = reader.type_def_methods(def).collect(); assert_eq!(methods.len(), 14); assert_eq!(reader.method_def_name(methods[0]), "Nothing"); - let sig = reader.method_def_signature("Test", methods[0], generics); + let sig = reader.method_def_signature(methods[0], generics); assert_eq!(sig.return_type, Type::Void); assert!(sig.params.is_empty()); @@ -53,9 +53,9 @@ fn test() { } fn method(reader: &Reader, generics: &[Type], method: MethodDef, expected: Type) { - let sig = reader.method_def_signature("Test", method, generics); + let sig = reader.method_def_signature(method, generics); assert_eq!(sig.return_type, expected); assert_eq!(sig.params.len(), 2); - assert_eq!(sig.params[0].ty, expected); - assert_eq!(sig.params[1].ty, expected); + assert_eq!(sig.params[0], expected); + assert_eq!(sig.params[1], expected); } diff --git a/crates/tools/lib/src/lib.rs b/crates/tools/lib/src/lib.rs index 77eec8dbaf..b57d4b13da 100644 --- a/crates/tools/lib/src/lib.rs +++ b/crates/tools/lib/src/lib.rs @@ -52,7 +52,7 @@ fn combine_libraries( libraries: &mut BTreeMap>, ) { for item in reader.items(&Default::default()) { - let metadata::Item::Fn(method, namespace) = item else { + let metadata::Item::Fn(method, _) = item else { continue; }; @@ -69,7 +69,7 @@ fn combine_libraries( } if flags.contains(metadata::PInvokeAttributes::CallConvPlatformapi) { - let params = reader.method_def_size(&namespace, method); + let params = reader.method_def_size(method); libraries .entry(library) .or_default() diff --git a/crates/tools/riddle/src/main.rs b/crates/tools/riddle/src/main.rs index 94d9e98643..ff7657e2e2 100644 --- a/crates/tools/riddle/src/main.rs +++ b/crates/tools/riddle/src/main.rs @@ -60,9 +60,9 @@ Options: match kind { ArgKind::None => match arg.as_str() { - "--in" => kind = ArgKind::Input, - "--out" => kind = ArgKind::Output, - "--filter" => kind = ArgKind::Filter, + "-i" | "--in" => kind = ArgKind::Input, + "-o" | "--out" => kind = ArgKind::Output, + "-f" | "--filter" => kind = ArgKind::Filter, "--config" => kind = ArgKind::Config, "--format" => format = true, _ => return Err(Error::new(&format!("invalid option `{arg}`"))), @@ -244,7 +244,7 @@ fn read_rdl_file(path: &str) -> Result { .and_then(|file| file.into_winmd()) .map(|bytes| { // TODO: Write bytes to file if you need to debug the intermediate .winmd file like so: - // _ = write_to_file("temp.winmd", &bytes); + _ = write_to_file("temp.winmd", &bytes); // Unwrapping here is fine since `rdl_to_winmd` should have produced a valid winmd metadata::File::new(bytes).unwrap() diff --git a/crates/tools/riddle/src/metadata/mod.rs b/crates/tools/riddle/src/metadata/mod.rs index b54fac3131..5c4af7afeb 100644 --- a/crates/tools/riddle/src/metadata/mod.rs +++ b/crates/tools/riddle/src/metadata/mod.rs @@ -1,8 +1,589 @@ +use std::collections::*; pub use windows_metadata::*; +#[derive(Clone)] +pub struct Interface { + pub ty: Type, + pub kind: InterfaceKind, +} + +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum InterfaceKind { + None, + Default, + Overridable, + Static, + Base, +} + +#[derive(Copy, Clone, PartialEq, Eq)] +pub struct QueryPosition { + pub object: usize, + pub guid: usize, +} + +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum SignatureKind { + Query(QueryPosition), + QueryOptional(QueryPosition), + ResultValue, + ResultVoid, + ReturnStruct, + ReturnValue, + ReturnVoid, + PreserveSig, +} + +#[derive(Copy, Clone, Eq, PartialEq)] +pub enum SignatureParamKind { + ArrayFixed(usize), + ArrayRelativeLen(usize), + ArrayRelativeByteLen(usize), + ArrayRelativePtr(usize), + TryInto, + IntoParam, + OptionalPointer, + ValueType, + Blittable, + Other, +} + +impl SignatureParamKind { + fn is_array(&self) -> bool { + matches!( + self, + Self::ArrayFixed(_) + | Self::ArrayRelativeLen(_) + | Self::ArrayRelativeByteLen(_) + | Self::ArrayRelativePtr(_) + ) + } +} + +pub struct Signature { + pub def: MethodDef, + pub params: Vec, + pub return_type: Type, + pub call_flags: MethodCallAttributes, +} + +pub struct SignatureParam { + pub def: Param, + pub ty: Type, + pub kind: SignatureParamKind, +} + +#[derive(PartialEq, Eq, Debug)] +pub enum AsyncKind { + None, + Action, + ActionWithProgress, + Operation, + OperationWithProgress, +} + pub fn type_def_invoke_method(reader: &Reader, row: TypeDef) -> MethodDef { reader .type_def_methods(row) .find(|method| reader.method_def_name(*method) == "Invoke") .expect("`Invoke` method not found") } + +pub fn type_def_generics(reader: &Reader, def: TypeDef) -> Vec { + reader + .type_def_generics(def) + .map(Type::GenericParam) + .collect() +} + +// TODO: namespace should not be required - it's a hack to accomodate Win32 metadata +// TODO: this is very Rust-specific and Win32-metadata specific with all of its translation. Replace with literal signature parser that just returns slice of types. +pub fn method_def_signature( + reader: &Reader, + namespace: &str, + row: MethodDef, + generics: &[Type], +) -> Signature { + let mut blob = reader.row_blob(row, 4); + let call_flags = MethodCallAttributes(blob.read_usize() as u8); + let _param_count = blob.read_usize(); + let mut return_type = reader.type_from_blob(&mut blob, None, generics); + + let mut params: Vec = reader + .method_def_params(row) + .filter_map(|param| { + let param_is_const = reader.has_attribute(param, "ConstAttribute"); + if reader.param_sequence(param) == 0 { + if param_is_const { + return_type = return_type.clone().to_const_type(); + } + None + } else { + let is_output = reader.param_flags(param).contains(ParamAttributes::Out); + let mut ty = reader.type_from_blob(&mut blob, None, generics); + + if let Some(name) = param_or_enum(reader, param) { + let def = reader + .get_type_def(TypeName::new(namespace, &name)) + .next() + .expect("Enum not found"); + ty = Type::PrimitiveOrEnum( + Box::new(ty), + Box::new(Type::TypeDef(def, Vec::new())), + ); + } + + if param_is_const || !is_output { + ty = ty.to_const_type(); + } + if !is_output { + ty = ty.to_const_ptr(); + } + let kind = param_kind(reader, param); + Some(SignatureParam { + def: param, + ty, + kind, + }) + } + }) + .collect(); + + for position in 0..params.len() { + // Point len params back to the corresponding ptr params. + match params[position].kind { + SignatureParamKind::ArrayRelativeLen(relative) + | SignatureParamKind::ArrayRelativeByteLen(relative) => { + // The len params must be input only. + if !reader + .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; + } + } + SignatureParamKind::ArrayFixed(_) => { + if reader.has_attribute(params[position].def, "FreeWithAttribute") { + params[position].kind = SignatureParamKind::Other; + } + } + _ => {} + } + } + + let mut sets = BTreeMap::>::new(); + + // Finds sets of ptr params pointing at the same len param. + for (position, param) in params.iter().enumerate() { + match param.kind { + SignatureParamKind::ArrayRelativeLen(relative) + | SignatureParamKind::ArrayRelativeByteLen(relative) => { + sets.entry(relative).or_default().push(position); + } + _ => {} + } + } + + // Remove all sets. + for (len, ptrs) in sets { + if ptrs.len() > 1 { + params[len].kind = SignatureParamKind::Other; + for ptr in ptrs { + params[ptr].kind = SignatureParamKind::Other; + } + } + } + + // Remove any byte arrays that aren't byte-sized types. + for position in 0..params.len() { + if let SignatureParamKind::ArrayRelativeByteLen(relative) = params[position].kind { + if !params[position].ty.is_byte_size() { + params[position].kind = SignatureParamKind::Other; + params[relative].kind = SignatureParamKind::Other; + } + } + } + + for param in &mut params { + if param.kind == SignatureParamKind::Other { + if signature_param_is_convertible(reader, param) { + if type_is_non_exclusive_winrt_interface(reader, ¶m.ty) { + param.kind = SignatureParamKind::TryInto; + } else { + param.kind = SignatureParamKind::IntoParam; + } + } else { + let flags = reader.param_flags(param.def); + if param.ty.is_pointer() + && (flags.contains(ParamAttributes::Optional) + || reader.has_attribute(param.def, "ReservedAttribute")) + { + param.kind = SignatureParamKind::OptionalPointer; + } else if reader.type_is_primitive(¶m.ty) + && (!param.ty.is_pointer() || reader.type_is_blittable(¶m.ty.deref())) + { + param.kind = SignatureParamKind::ValueType; + } else if reader.type_is_blittable(¶m.ty) { + param.kind = SignatureParamKind::Blittable; + } + } + } + } + + Signature { + def: row, + params, + return_type, + call_flags, + } +} + +fn param_kind(reader: &Reader, row: Param) -> SignatureParamKind { + for attribute in reader.attributes(row) { + match reader.attribute_name(attribute) { + "NativeArrayInfoAttribute" => { + for (_, value) in reader.attribute_args(attribute) { + match value { + Value::I16(value) => { + return SignatureParamKind::ArrayRelativeLen(value as usize) + } + Value::I32(value) => return SignatureParamKind::ArrayFixed(value as usize), + _ => {} + } + } + } + "MemorySizeAttribute" => { + for (_, value) in reader.attribute_args(attribute) { + if let Value::I16(value) = value { + return SignatureParamKind::ArrayRelativeByteLen(value as usize); + } + } + } + _ => {} + } + } + SignatureParamKind::Other +} +// TODO: this is a terribly broken Win32 metadata attribute - need to get rid of it. +fn param_or_enum(reader: &Reader, row: Param) -> Option { + reader + .find_attribute(row, "AssociatedEnumAttribute") + .and_then(|attribute| { + for (_, arg) in reader.attribute_args(attribute) { + if let Value::String(name) = arg { + return Some(name); + } + } + None + }) +} + +pub fn signature_param_is_borrowed(reader: &Reader, param: &SignatureParam) -> bool { + type_is_borrowed(reader, ¶m.ty) +} + +pub fn signature_param_is_convertible(reader: &Reader, param: &SignatureParam) -> bool { + !reader.param_flags(param.def).contains(ParamAttributes::Out) + && !param.ty.is_winrt_array() + && !param.ty.is_pointer() + && !param.kind.is_array() + && (type_is_borrowed(reader, ¶m.ty) + || type_is_non_exclusive_winrt_interface(reader, ¶m.ty) + || type_is_trivially_convertible(reader, ¶m.ty)) +} +fn signature_param_is_retval(reader: &Reader, param: &SignatureParam) -> bool { + // The Win32 metadata uses `RetValAttribute` to call out retval methods but it is employed + // very sparingly, so this heuristic is used to apply the transformation more uniformly. + if reader.has_attribute(param.def, "RetValAttribute") { + return true; + } + if !param.ty.is_pointer() { + return false; + } + if param.ty.is_void() { + return false; + } + let flags = reader.param_flags(param.def); + if flags.contains(ParamAttributes::In) + || !flags.contains(ParamAttributes::Out) + || flags.contains(ParamAttributes::Optional) + || param.kind.is_array() + { + return false; + } + if param_kind(reader, param.def).is_array() { + return false; + } + // If it's bigger than 128 bits, best to pass as a reference. + if reader.type_size(¶m.ty.deref()) > 16 { + return false; + } + // Win32 callbacks are defined as `Option` so we don't include them here to avoid + // producing the `Result>` anti-pattern. + !type_is_callback(reader, ¶m.ty.deref()) +} +pub fn signature_kind(reader: &Reader, signature: &Signature) -> SignatureKind { + if reader.has_attribute(signature.def, "CanReturnMultipleSuccessValuesAttribute") { + return SignatureKind::PreserveSig; + } + match &signature.return_type { + Type::Void if signature_is_retval(reader, signature) => SignatureKind::ReturnValue, + Type::Void => SignatureKind::ReturnVoid, + Type::HRESULT => { + if signature.params.len() >= 2 { + if let Some(guid) = signature_param_is_query_guid(reader, &signature.params) { + if let Some(object) = signature_param_is_query_object(reader, &signature.params) + { + if reader + .param_flags(signature.params[object].def) + .contains(ParamAttributes::Optional) + { + return SignatureKind::QueryOptional(QueryPosition { object, guid }); + } else { + return SignatureKind::Query(QueryPosition { object, guid }); + } + } + } + } + if signature_is_retval(reader, signature) { + SignatureKind::ResultValue + } else { + SignatureKind::ResultVoid + } + } + Type::TypeDef(def, _) if reader.type_def_type_name(*def) == TypeName::NTSTATUS => { + SignatureKind::ResultVoid + } + Type::TypeDef(def, _) if reader.type_def_type_name(*def) == TypeName::WIN32_ERROR => { + SignatureKind::ResultVoid + } + Type::TypeDef(def, _) + if reader.type_def_type_name(*def) == TypeName::BOOL + && method_def_last_error(reader, signature.def) => + { + SignatureKind::ResultVoid + } + _ if reader.type_is_struct(&signature.return_type) => SignatureKind::ReturnStruct, + _ => SignatureKind::PreserveSig, + } +} +fn signature_is_retval(reader: &Reader, signature: &Signature) -> bool { + signature + .params + .last() + .map_or(false, |param| signature_param_is_retval(reader, param)) + && signature.params[..signature.params.len() - 1] + .iter() + .all(|param| { + let flags = reader.param_flags(param.def); + !flags.contains(ParamAttributes::Out) + }) +} +fn signature_param_is_query_guid(reader: &Reader, params: &[SignatureParam]) -> Option { + params.iter().rposition(|param| { + param.ty == Type::ConstPtr(Box::new(Type::GUID), 1) + && !reader.param_flags(param.def).contains(ParamAttributes::Out) + }) +} +fn signature_param_is_query_object(reader: &Reader, params: &[SignatureParam]) -> Option { + params.iter().rposition(|param| { + param.ty == Type::MutPtr(Box::new(Type::Void), 2) + && reader.has_attribute(param.def, "ComOutPtrAttribute") + }) +} + +fn method_def_last_error(reader: &Reader, row: MethodDef) -> bool { + if let Some(map) = reader.method_def_impl_map(row) { + reader + .impl_map_flags(map) + .contains(PInvokeAttributes::SupportsLastError) + } else { + false + } +} + +fn type_is_borrowed(reader: &Reader, ty: &Type) -> bool { + match ty { + Type::TypeDef(row, _) => !reader.type_def_is_blittable(*row), + Type::BSTR + | Type::PCSTR + | Type::PCWSTR + | Type::IInspectable + | Type::IUnknown + | Type::GenericParam(_) => true, + _ => false, + } +} + +pub fn type_is_non_exclusive_winrt_interface(reader: &Reader, ty: &Type) -> bool { + match ty { + Type::TypeDef(row, _) => { + let flags = reader.type_def_flags(*row); + if !flags.contains(TypeAttributes::WindowsRuntime) { + false + } else { + match reader.type_def_kind(*row) { + TypeKind::Interface => !reader.type_def_is_exclusive(*row), + TypeKind::Class => reader.has_attribute(*row, "ComposableAttribute"), + _ => false, + } + } + } + _ => false, + } +} + +fn type_is_trivially_convertible(reader: &Reader, ty: &Type) -> bool { + match ty { + Type::TypeDef(row, _) => match reader.type_def_kind(*row) { + TypeKind::Struct => reader.type_def_is_handle(*row), + _ => false, + }, + Type::PCSTR | Type::PCWSTR => true, + _ => false, + } +} + +fn type_is_callback(reader: &Reader, ty: &Type) -> bool { + match ty { + Type::TypeDef(row, _) => type_def_is_callback(reader, *row), + _ => false, + } +} + +fn type_def_is_callback(reader: &Reader, row: TypeDef) -> bool { + !reader + .type_def_flags(row) + .contains(TypeAttributes::WindowsRuntime) + && reader.type_def_kind(row) == TypeKind::Delegate +} + +pub fn type_has_callback(reader: &Reader, ty: &Type) -> bool { + match ty { + Type::TypeDef(row, _) => type_def_has_callback(reader, *row), + Type::Win32Array(ty, _) => type_has_callback(reader, ty), + _ => false, + } +} +pub fn type_def_has_callback(reader: &Reader, row: TypeDef) -> bool { + if type_def_is_callback(reader, row) { + return true; + } + if reader.type_def_kind(row) != TypeKind::Struct { + return false; + } + fn check(reader: &Reader, row: TypeDef) -> bool { + if reader + .type_def_fields(row) + .any(|field| type_has_callback(reader, &reader.field_type(field, Some(row)))) + { + return true; + } + false + } + let type_name = reader.type_def_type_name(row); + if type_name.namespace.is_empty() { + check(reader, row) + } else { + for row in reader.get_type_def(type_name) { + if check(reader, row) { + return true; + } + } + false + } +} + +pub fn type_interfaces(reader: &Reader, ty: &Type) -> Vec { + // TODO: collect into btree map and then return collected vec + // This will both sort the results and should make finding dupes faster + fn walk(reader: &Reader, result: &mut Vec, parent: &Type, is_base: bool) { + if let Type::TypeDef(row, generics) = parent { + for imp in reader.type_def_interface_impls(*row) { + let mut child = Interface { + ty: reader.interface_impl_type(imp, generics), + kind: if reader.has_attribute(imp, "DefaultAttribute") { + InterfaceKind::Default + } else { + InterfaceKind::None + }, + }; + + child.kind = if !is_base && child.kind == InterfaceKind::Default { + InterfaceKind::Default + } else if child.kind == InterfaceKind::Overridable { + continue; + } else if is_base { + InterfaceKind::Base + } else { + InterfaceKind::None + }; + let mut found = false; + for existing in result.iter_mut() { + if existing.ty == child.ty { + found = true; + if child.kind == InterfaceKind::Default { + existing.kind = child.kind + } + } + } + if !found { + walk(reader, result, &child.ty, is_base); + result.push(child); + } + } + } + } + let mut result = Vec::new(); + walk(reader, &mut result, ty, false); + if let Type::TypeDef(row, _) = ty { + if reader.type_def_kind(*row) == TypeKind::Class { + for base in reader.type_def_bases(*row) { + walk(reader, &mut result, &Type::TypeDef(base, Vec::new()), true); + } + for attribute in reader.attributes(*row) { + match reader.attribute_name(attribute) { + "StaticAttribute" | "ActivatableAttribute" => { + for (_, arg) in reader.attribute_args(attribute) { + if let Value::TypeName(type_name) = arg { + let def = reader + .get_type_def(TypeName::parse(&type_name)) + .next() + .expect("Type not found"); + result.push(Interface { + ty: Type::TypeDef(def, Vec::new()), + kind: InterfaceKind::Static, + }); + break; + } + } + } + _ => {} + } + } + } + } + result.sort_by(|a, b| type_name(reader, &a.ty).cmp(type_name(reader, &b.ty))); + result +} + +fn type_name<'a>(reader: &Reader<'a>, ty: &Type) -> &'a str { + match ty { + Type::TypeDef(row, _) => reader.type_def_name(*row), + _ => "", + } +} + +pub fn type_def_async_kind(reader: &Reader, row: TypeDef) -> AsyncKind { + match reader.type_def_type_name(row) { + TypeName::IAsyncAction => AsyncKind::Action, + TypeName::IAsyncActionWithProgress => AsyncKind::ActionWithProgress, + TypeName::IAsyncOperation => AsyncKind::Operation, + TypeName::IAsyncOperationWithProgress => AsyncKind::OperationWithProgress, + _ => AsyncKind::None, + } +} diff --git a/crates/tools/riddle/src/rdl/fmt.rs b/crates/tools/riddle/src/rdl/fmt.rs index da95fd5145..ef3c8eb6d0 100644 --- a/crates/tools/riddle/src/rdl/fmt.rs +++ b/crates/tools/riddle/src/rdl/fmt.rs @@ -85,10 +85,64 @@ impl Writer { } } + fn rdl_class(&mut self, member: &rdl::Class) { + self.attrs(&member.attributes); + self.word("class "); + self.word(&member.name); + + if !member.extends.is_empty() { + self.word(" : "); + + let mut first = true; + for path in &member.extends { + if first { + first = false; + } else { + self.word(", "); + } + self.type_path(path); + } + } + + self.word(";"); + self.newline(); + } + fn rdl_interface(&mut self, member: &rdl::Interface) { self.attrs(&member.attributes); self.word("interface "); self.word(&member.name); + + if !member.generics.is_empty() { + self.word("<"); + + let mut first = true; + for generic in &member.generics { + if first { + first = false; + } else { + self.word(", "); + } + self.word(generic); + } + + self.word(">"); + } + + if !member.extends.is_empty() { + self.word(" : "); + + let mut first = true; + for path in &member.extends { + if first { + first = false; + } else { + self.word(", "); + } + self.type_path(path); + } + } + self.word(" {"); self.newline(); self.indent += 1; @@ -205,8 +259,6 @@ impl Writer { self.word("}"); } - fn rdl_class(&mut self, _member: &rdl::Class) {} - fn trait_item_fn(&mut self, method: &syn::TraitItemFn) { self.attrs(&method.attrs); self.signature(&method.sig); @@ -337,6 +389,29 @@ impl Writer { pub fn path_segment(&mut self, segment: &syn::PathSegment) { self.ident(&segment.ident); + + if let syn::PathArguments::AngleBracketed(args) = &segment.arguments { + self.word("<"); + + let mut first = true; + for arg in &args.args { + if first { + first = false; + } else { + self.word(", "); + } + self.generic_argument(arg); + } + + self.word(">"); + } + } + + fn generic_argument(&mut self, arg: &syn::GenericArgument) { + match arg { + syn::GenericArgument::Type(ty) => self.ty(ty), + rest => unimplemented!("{rest:?}"), + } } fn item_use(&mut self, item: &syn::ItemUse) { diff --git a/crates/tools/riddle/src/rdl/from_reader.rs b/crates/tools/riddle/src/rdl/from_reader.rs index 431ba426d3..f877912e63 100644 --- a/crates/tools/riddle/src/rdl/from_reader.rs +++ b/crates/tools/riddle/src/rdl/from_reader.rs @@ -116,6 +116,7 @@ impl<'a> Writer<'a> { let file = rdl::File::parse_str(&tokens.into_string())?; crate::write_to_file(output, file.fmt()) + //crate::write_to_file(output, tokens.into_string()) } fn tree(&self, tree: &'a Tree) -> TokenStream { @@ -275,22 +276,24 @@ impl<'a> Writer<'a> { fn class_def(&self, def: metadata::TypeDef) -> TokenStream { let name = to_ident(self.reader.type_def_name(def)); + let implements = self.implements(def, &[]); quote! { - struct #name { - - } + class #name #implements; } } fn interface_def(&self, def: metadata::TypeDef) -> TokenStream { let name = to_ident(self.reader.type_def_name(def)); - let generics = &self.reader.type_def_generics(def); + let generics = &metadata::type_def_generics(self.reader, def); + let implements = self.implements(def, generics); let methods = self.reader.type_def_methods(def).map(|method| { let name = to_ident(self.reader.method_def_name(method)); - let signature = self.reader.method_def_signature( + // TODO: use reader.method_def_signature instead + let signature = metadata::method_def_signature( + self.reader, self.reader.type_def_namespace(def), method, generics, @@ -309,13 +312,49 @@ impl<'a> Writer<'a> { } }); + let generics = self.generics(generics); + quote! { - interface #name { + interface #name #generics #implements { #(#methods)* } } } + fn generics(&self, generics: &[metadata::Type]) -> TokenStream { + if generics.is_empty() { + quote! {} + } else { + let generics = generics.iter().map(|generic| self.ty(generic)); + + quote! { <#(#generics),*>} + } + } + + fn implements(&self, def: metadata::TypeDef, generics: &[metadata::Type]) -> TokenStream { + let mut types = Vec::::new(); + + // TODO: if a winrt composable class then start with base + // TODO: then list default interface first + // Then everything else + + for imp in self.reader.type_def_interface_impls(def) { + let ty = self.reader.interface_impl_type(imp, generics); + if self.reader.has_attribute(imp, "DefaultAttribute") { + types.insert(0, ty); + } else { + types.push(ty); + } + } + + if types.is_empty() { + quote! {} + } else { + let types = types.iter().map(|ty| self.ty(ty)); + quote! { : #(#types),* } + } + } + fn return_type(&self, ty: &metadata::Type) -> TokenStream { match ty { metadata::Type::Void => quote! {}, @@ -361,6 +400,14 @@ impl<'a> Writer<'a> { quote! { #namespace #name<#(#generics,)*> } } } + + metadata::Type::TypeRef(code) => { + let type_name = self.reader.type_def_or_ref(*code); + let namespace = self.namespace(type_name.namespace); + let name = to_ident(type_name.name); + quote! { #namespace #name } + } + metadata::Type::GenericParam(generic) => { self.reader.generic_param_name(*generic).into() } @@ -386,29 +433,42 @@ impl<'a> Writer<'a> { if namespace.is_empty() || self.namespace == namespace { quote! {} } else { - let mut relative = self.namespace.split('.').peekable(); - let mut namespace = namespace.split('.').peekable(); - let mut related = false; + // TODO: problem with making relative paths here is that we don't have the context to disambiguate - while relative.peek() == namespace.peek() { - related = true; + // let mut relative = self.namespace.split('.').peekable(); + // let mut namespace = namespace.split('.').peekable(); + // let mut related = false; - if relative.next().is_none() { - break; - } + // while relative.peek() == namespace.peek() { + // related = true; - namespace.next(); - } + // if relative.next().is_none() { + // break; + // } - let mut tokens = TokenStream::new(); + // namespace.next(); + // } - if related { - for _ in 0..relative.count() { - tokens.push_str("super::"); - } - } + // let mut tokens = TokenStream::new(); + + // if related { + // for _ in 0..relative.count() { + // tokens.push_str("super::"); + // } + // } + + // for namespace in namespace { + // tokens.push_str(namespace); + // tokens.push_str("::"); + // } + + // tokens + + // TODO: so instead we just gen it out in full + + let mut tokens = TokenStream::new(); - for namespace in namespace { + for namespace in namespace.split('.') { tokens.push_str(namespace); tokens.push_str("::"); } diff --git a/crates/tools/riddle/src/rdl/mod.rs b/crates/tools/riddle/src/rdl/mod.rs index 200f6fa7b1..b206866ec4 100644 --- a/crates/tools/riddle/src/rdl/mod.rs +++ b/crates/tools/riddle/src/rdl/mod.rs @@ -113,7 +113,7 @@ pub struct Field { pub struct Class { pub name: String, pub attributes: Vec, - pub extends: Vec, + pub extends: Vec, } #[derive(Clone, Debug)] @@ -126,7 +126,9 @@ pub struct Function { pub struct Interface { pub winrt: bool, pub name: String, + pub generics: Vec, pub attributes: Vec, + pub extends: Vec, pub methods: Vec, } @@ -254,8 +256,8 @@ impl Class { if input.peek(syn::Token![:]) { input.parse::()?; - while input.peek(syn::Ident) { - extends.push(input.parse::()?); + while !input.peek(syn::Token![;]) { + extends.push(input.parse()?); _ = input.parse::(); } } @@ -278,15 +280,40 @@ impl Interface { ) -> syn::Result { input.parse::()?; let name = input.parse::()?.to_string(); + + let mut generics = Vec::new(); + + if input.peek(syn::Token![<]) { + input.parse::()?; + while input.peek(syn::Ident) { + generics.push(input.parse::()?.to_string()); + _ = input.parse::(); + } + + input.parse::]>()?; + } + + let mut extends = Vec::new(); + + if input.peek(syn::Token![:]) { + input.parse::()?; + while !input.peek(syn::token::Brace) { + extends.push(input.parse()?); + _ = input.parse::(); + } + } + let content; syn::braced!(content in input); let mut methods = vec![]; while !content.is_empty() { - methods.push(content.parse::()?); + methods.push(content.parse()?); } Ok(Self { winrt, attributes, + generics, + extends, name, methods, }) diff --git a/crates/tools/riddle/src/rdl/to_winmd.rs b/crates/tools/riddle/src/rdl/to_winmd.rs index ecc78a2cf6..fc80b2d7a6 100644 --- a/crates/tools/riddle/src/rdl/to_winmd.rs +++ b/crates/tools/riddle/src/rdl/to_winmd.rs @@ -1,5 +1,6 @@ use super::*; -use crate::{rdl, winmd, Result}; +use crate::winmd::{self, writer}; +use crate::{rdl, Result}; // TODO: store span in winmd so that errors resolving type references can be traced back to file/line/column use std::collections::HashMap; @@ -106,27 +107,72 @@ fn write_interface( writer.tables.TypeDef.push(winmd::TypeDef { Extends: 0, - FieldList: 0, - Flags: flags.0, + FieldList: writer.tables.Field.len() as u32, MethodList: writer.tables.MethodDef.len() as u32, + Flags: flags.0, TypeName: writer.strings.insert(name), TypeNamespace: writer.strings.insert(namespace), }); + for (number, generic) in member.generics.iter().enumerate() { + writer.tables.GenericParam.push(writer::GenericParam { + Number: number as u16, + Flags: 0, + Owner: writer::TypeOrMethodDef::TypeDef(writer.tables.TypeDef.len() as u32 - 1) + .encode(), + Name: writer.strings.insert(generic), + }); + } + + for type_path in &member.extends { + let ty = syn_type_path(namespace, &member.generics, type_path); + + let reference = match &ty { + winmd::Type::TypeRef(type_name) if type_name.generics.is_empty() => { + writer.insert_type_ref(&type_name.namespace, &type_name.name) + } + winmd::Type::TypeRef(_) => writer.insert_type_spec(ty), + rest => unimplemented!("{rest:?}"), + }; + + writer.tables.InterfaceImpl.push(writer::InterfaceImpl { + Class: writer.tables.TypeDef.len() as u32 - 1, + Interface: reference, + }); + } + for method in &member.methods { - let sig = syn_signature(namespace, &method.sig); - let signature = writer.insert_method_sig(&sig); + let signature = syn_signature(namespace, &member.generics, &method.sig); + + let params: Vec = signature + .params + .iter() + .map(|param| param.ty.clone()) + .collect(); + + let signature_blob = writer.insert_method_sig( + metadata::MethodCallAttributes(0), + &signature.return_type, + ¶ms, + ); + + let flags = metadata::MethodAttributes::Abstract + | metadata::MethodAttributes::HideBySig + | metadata::MethodAttributes::HideBySig + | metadata::MethodAttributes::NewSlot + | metadata::MethodAttributes::Public + | metadata::MethodAttributes::Virtual; writer.tables.MethodDef.push(winmd::MethodDef { RVA: 0, ImplFlags: 0, - Flags: 0, + Flags: flags.0, Name: writer.strings.insert(&method.sig.ident.to_string()), - Signature: signature, + Signature: signature_blob, ParamList: writer.tables.Param.len() as u32, }); - for (sequence, param) in sig.params.iter().enumerate() { + for (sequence, param) in signature.params.iter().enumerate() { writer.tables.Param.push(winmd::Param { Flags: 0, Sequence: (sequence + 1) as u16, @@ -139,7 +185,6 @@ fn write_interface( fn write_struct(writer: &mut winmd::Writer, namespace: &str, name: &str, member: &rdl::Struct) { let mut flags = metadata::TypeAttributes::Public | metadata::TypeAttributes::Sealed - | metadata::TypeAttributes::Import | metadata::TypeAttributes::SequentialLayout; if member.winrt { @@ -151,18 +196,19 @@ fn write_struct(writer: &mut winmd::Writer, namespace: &str, name: &str, member: writer.tables.TypeDef.push(winmd::TypeDef { Extends: extends, FieldList: writer.tables.Field.len() as u32, + MethodList: writer.tables.MethodDef.len() as u32, Flags: flags.0, - MethodList: 0, TypeName: writer.strings.insert(name), TypeNamespace: writer.strings.insert(namespace), }); for field in &member.fields { - let ty = syn_type(namespace, &field.ty); + let flags = metadata::FieldAttributes::Public; + let ty = syn_type(namespace, &[], &field.ty); let signature = writer.insert_field_sig(&ty); writer.tables.Field.push(winmd::Field { - Flags: 0, + Flags: flags.0, Name: writer.strings.insert(&field.name), Signature: signature, }); @@ -171,9 +217,25 @@ fn write_struct(writer: &mut winmd::Writer, namespace: &str, name: &str, member: fn write_enum(_writer: &mut winmd::Writer, _namespace: &str, _name: &str, _member: &rdl::Enum) {} -fn write_class(_writer: &mut winmd::Writer, _namespace: &str, _name: &str, _member: &rdl::Class) {} +fn write_class(writer: &mut winmd::Writer, namespace: &str, name: &str, _member: &rdl::Class) { + let flags = metadata::TypeAttributes::Public + | metadata::TypeAttributes::Sealed + | metadata::TypeAttributes::WindowsRuntime; + + let extends = writer.insert_type_ref("System", "Object"); -fn syn_signature(namespace: &str, sig: &syn::Signature) -> winmd::Signature { + writer.tables.TypeDef.push(winmd::TypeDef { + Extends: extends, + // Even though ECMA-335 says these can be "null", bugs in ILDASM necessitate this to avoid "misreading" the list terminators. + FieldList: writer.tables.Field.len() as u32, + MethodList: writer.tables.MethodDef.len() as u32, + Flags: flags.0, + TypeName: writer.strings.insert(name), + TypeNamespace: writer.strings.insert(namespace), + }); +} + +fn syn_signature(namespace: &str, generics: &[String], sig: &syn::Signature) -> winmd::Signature { let params = sig .inputs .iter() @@ -183,7 +245,7 @@ fn syn_signature(namespace: &str, sig: &syn::Signature) -> winmd::Signature { syn::Pat::Ident(pat_ident) => pat_ident.ident.to_string(), rest => unimplemented!("{rest:?}"), }; - let ty = syn_type(namespace, &pat_type.ty); + let ty = syn_type(namespace, generics, &pat_type.ty); winmd::SignatureParam { name, ty } } rest => unimplemented!("{rest:?}"), @@ -191,7 +253,7 @@ fn syn_signature(namespace: &str, sig: &syn::Signature) -> winmd::Signature { .collect(); let return_type = if let syn::ReturnType::Type(_, ty) = &sig.output { - syn_type(namespace, ty) + syn_type(namespace, generics, ty) } else { winmd::Type::Void }; @@ -203,9 +265,9 @@ fn syn_signature(namespace: &str, sig: &syn::Signature) -> winmd::Signature { } } -fn syn_type(namespace: &str, ty: &syn::Type) -> winmd::Type { +fn syn_type(namespace: &str, generics: &[String], ty: &syn::Type) -> winmd::Type { match ty { - syn::Type::Path(ty) => syn_type_path(namespace, ty), + syn::Type::Path(ty) => syn_type_path(namespace, generics, ty), syn::Type::Ptr(ptr) => syn_type_ptr(namespace, ptr), syn::Type::Array(array) => syn_type_array(namespace, array), rest => unimplemented!("{rest:?}"), @@ -213,7 +275,7 @@ fn syn_type(namespace: &str, ty: &syn::Type) -> winmd::Type { } fn syn_type_array(namespace: &str, array: &syn::TypeArray) -> winmd::Type { - let ty = syn_type(namespace, &array.elem); + let ty = syn_type(namespace, &[], &array.elem); if let syn::Expr::Lit(lit) = &array.len { if let syn::Lit::Int(lit) = &lit.lit { @@ -227,7 +289,7 @@ fn syn_type_array(namespace: &str, array: &syn::TypeArray) -> winmd::Type { } fn syn_type_ptr(namespace: &str, ptr: &syn::TypePtr) -> winmd::Type { - let ty = syn_type(namespace, &ptr.elem); + let ty = syn_type(namespace, &[], &ptr.elem); if ptr.mutability.is_some() { ty.into_mut_ptr() } else { @@ -235,71 +297,99 @@ fn syn_type_ptr(namespace: &str, ptr: &syn::TypePtr) -> winmd::Type { } } -fn syn_type_path(namespace: &str, ty: &syn::TypePath) -> winmd::Type { +fn syn_type_path(namespace: &str, generics: &[String], ty: &syn::TypePath) -> winmd::Type { if ty.qself.is_none() { - return syn_path(namespace, &ty.path); + return syn_path(namespace, generics, &ty.path); } unimplemented!() } -fn syn_path(namespace: &str, path: &syn::Path) -> winmd::Type { +fn syn_path(namespace: &str, generics: &[String], path: &syn::Path) -> winmd::Type { if let Some(segment) = path.segments.first() { - if path.segments.len() == 1 { + if path.segments.len() == 1 && segment.arguments.is_empty() { let name = segment.ident.to_string(); - return match name.as_str() { - "void" => winmd::Type::Void, - "bool" => winmd::Type::Bool, - "char" => winmd::Type::Char, - "i8" => winmd::Type::I8, - "u8" => winmd::Type::U8, - "i16" => winmd::Type::I16, - "u16" => winmd::Type::U16, - "i32" => winmd::Type::I32, - "u32" => winmd::Type::U32, - "i64" => winmd::Type::I64, - "u64" => winmd::Type::U64, - "f32" => winmd::Type::F32, - "f64" => winmd::Type::F64, - "isize" => winmd::Type::ISize, - "usize" => winmd::Type::USize, - "HSTRING" => winmd::Type::String, - "GUID" => winmd::Type::GUID, - "IUnknown" => winmd::Type::IUnknown, - "IInspectable" => winmd::Type::IInspectable, - "HRESULT" => winmd::Type::HRESULT, - "PSTR" => winmd::Type::PSTR, - "PWSTR" => winmd::Type::PWSTR, - "PCSTR" => winmd::Type::PCSTR, - "PCWSTR" => winmd::Type::PCWSTR, - "BSTR" => winmd::Type::BSTR, - _ => winmd::Type::TypeRef(winmd::TypeName { - namespace: namespace.to_string(), - name, - generics: vec![], - }), + if let Some(number) = generics.iter().position(|generic| generic == &name) { + return winmd::Type::GenericParam(number as u16); + } + + match name.as_str() { + "void" => return winmd::Type::Void, + "bool" => return winmd::Type::Bool, + "char" => return winmd::Type::Char, + "i8" => return winmd::Type::I8, + "u8" => return winmd::Type::U8, + "i16" => return winmd::Type::I16, + "u16" => return winmd::Type::U16, + "i32" => return winmd::Type::I32, + "u32" => return winmd::Type::U32, + "i64" => return winmd::Type::I64, + "u64" => return winmd::Type::U64, + "f32" => return winmd::Type::F32, + "f64" => return winmd::Type::F64, + "isize" => return winmd::Type::ISize, + "usize" => return winmd::Type::USize, + "HSTRING" => return winmd::Type::String, + "GUID" => return winmd::Type::GUID, + "IUnknown" => return winmd::Type::IUnknown, + "IInspectable" => return winmd::Type::IInspectable, + "HRESULT" => return winmd::Type::HRESULT, + "PSTR" => return winmd::Type::PSTR, + "PWSTR" => return winmd::Type::PWSTR, + "PCSTR" => return winmd::Type::PCSTR, + "PCWSTR" => return winmd::Type::PCWSTR, + "BSTR" => return winmd::Type::BSTR, + _ => {} }; } } // TODO: Here we assume that paths are absolute since there's no way to disambiguate between nested and absolute paths - // The canonicalize function preprocesses the IDL to make this work + // The canonicalize function (should maybe) preprocesses the IDL to make this work let mut builder = vec![]; for segment in &path.segments { let segment = segment.ident.to_string(); - builder.push(segment); + + if segment == "super" { + if builder.is_empty() { + for segment in namespace.split('.') { + builder.push(segment.to_string()); + } + } + builder.pop(); + } else { + builder.push(segment); + } } - // Unwrapping as there are more one segments - let (name, namespace) = builder.split_last().unwrap(); - let namespace = namespace.join("."); + // Unwrapping is fine as there should always be at least one segment. + let (name, type_namespace) = builder.split_last().unwrap(); + let type_namespace = if type_namespace.is_empty() { + namespace.to_string() + } else { + type_namespace.join(".") + }; + let mut type_generics = vec![]; + + if let Some(segment) = path.segments.last() { + if let syn::PathArguments::AngleBracketed(args) = &segment.arguments { + for arg in &args.args { + match arg { + syn::GenericArgument::Type(ty) => { + type_generics.push(syn_type(namespace, generics, ty)) + } + rest => unimplemented!("{rest:?}"), + } + } + } + } winmd::Type::TypeRef(winmd::TypeName { - namespace, + namespace: type_namespace, name: name.to_string(), - generics: vec![], + generics: type_generics, }) } diff --git a/crates/tools/riddle/src/rust/cfg.rs b/crates/tools/riddle/src/rust/cfg.rs index b68246fa57..ba5e9d0dd1 100644 --- a/crates/tools/riddle/src/rust/cfg.rs +++ b/crates/tools/riddle/src/rust/cfg.rs @@ -60,11 +60,7 @@ pub fn type_def_cfg_impl<'a>(reader: &'a Reader, def: TypeDef, generics: &[Type] type_def_cfg_combine(reader, def, generics, cfg); for method in reader.type_def_methods(def) { - signature_cfg_combine( - reader, - &reader.method_def_signature(reader.type_def_namespace(def), method, generics), - cfg, - ); + signature_cfg_combine(reader, &reader.method_def_signature(method, generics), cfg); } } @@ -81,7 +77,7 @@ pub fn type_def_cfg_impl<'a>(reader: &'a Reader, def: TypeDef, generics: &[Type] .contains(TypeAttributes::WindowsRuntime) { for interface in reader.type_def_interfaces(def, generics) { - if let Type::TypeDef(def, generics) = interface.ty { + if let Type::TypeDef(def, generics) = interface { combine(reader, def, &generics, &mut cfg); } } @@ -140,11 +136,7 @@ pub fn type_def_cfg_combine<'a>( } TypeKind::Delegate => signature_cfg_combine( reader, - &reader.method_def_signature( - type_name.namespace, - type_def_invoke_method(reader, row), - generics, - ), + &reader.method_def_signature(type_def_invoke_method(reader, row), generics), cfg, ), _ => {} @@ -152,18 +144,18 @@ pub fn type_def_cfg_combine<'a>( } } -pub fn signature_cfg<'a>(reader: &'a Reader, signature: &Signature) -> Cfg<'a> { +pub fn signature_cfg<'a>(reader: &'a Reader, method: MethodDef) -> Cfg<'a> { let mut cfg = Cfg::default(); - signature_cfg_combine(reader, signature, &mut cfg); - cfg_add_attributes(reader, &mut cfg, signature.def); + signature_cfg_combine(reader, &reader.method_def_signature(method, &[]), &mut cfg); + cfg_add_attributes(reader, &mut cfg, method); cfg } -fn signature_cfg_combine<'a>(reader: &'a Reader, signature: &Signature, cfg: &mut Cfg<'a>) { +fn signature_cfg_combine<'a>(reader: &'a Reader, signature: &MethodDefSig, cfg: &mut Cfg<'a>) { type_cfg_combine(reader, &signature.return_type, cfg); signature .params .iter() - .for_each(|param| type_cfg_combine(reader, ¶m.ty, cfg)); + .for_each(|param| type_cfg_combine(reader, param, cfg)); } fn cfg_add_attributes>(reader: &Reader, cfg: &mut Cfg, row: R) { diff --git a/crates/tools/riddle/src/rust/classes.rs b/crates/tools/riddle/src/rust/classes.rs index 568d52c8fa..c5f7219e12 100644 --- a/crates/tools/riddle/src/rust/classes.rs +++ b/crates/tools/riddle/src/rust/classes.rs @@ -21,9 +21,7 @@ fn gen_class(writer: &Writer, def: TypeDef) -> TokenStream { } let name = to_ident(writer.reader.type_def_name(def)); - let interfaces = writer - .reader - .type_interfaces(&Type::TypeDef(def, Vec::new())); + let interfaces = type_interfaces(writer.reader, &Type::TypeDef(def, Vec::new())); let mut methods = quote! {}; let mut method_names = MethodNames::new(); diff --git a/crates/tools/riddle/src/rust/com_methods.rs b/crates/tools/riddle/src/rust/com_methods.rs index 8b31d37f31..af1c2a8f70 100644 --- a/crates/tools/riddle/src/rust/com_methods.rs +++ b/crates/tools/riddle/src/rust/com_methods.rs @@ -9,16 +9,18 @@ pub fn writer( virtual_names: &mut MethodNames, base_count: usize, ) -> TokenStream { - let signature = - writer - .reader - .method_def_signature(writer.reader.type_def_namespace(def), method, &[]); + let signature = method_def_signature( + writer.reader, + writer.reader.type_def_namespace(def), + method, + &[], + ); let name = method_names.add(writer, method); let vname = virtual_names.add(writer, method); let generics = writer.constraint_generics(&signature.params); let where_clause = writer.where_clause(&signature.params); - let mut cfg = signature_cfg(writer.reader, &signature); + let mut cfg = signature_cfg(writer.reader, method); cfg.add_feature(writer.reader.type_def_namespace(def)); let doc = writer.cfg_method_doc(&cfg); let features = writer.cfg_features(&cfg); @@ -33,7 +35,7 @@ pub fn writer( bases.combine("e! { .base__ }); } - let kind = writer.reader.signature_kind(&signature); + let kind = signature_kind(writer.reader, &signature); match kind { SignatureKind::Query(_) => { let args = writer.win32_args(&signature.params, kind); @@ -166,7 +168,7 @@ pub fn writer( } pub fn gen_upcall(writer: &Writer, sig: &Signature, inner: TokenStream) -> TokenStream { - match writer.reader.signature_kind(sig) { + match signature_kind(writer.reader, sig) { SignatureKind::ResultValue => { let invoke_args = sig.params[..sig.params.len() - 1] .iter() diff --git a/crates/tools/riddle/src/rust/delegates.rs b/crates/tools/riddle/src/rust/delegates.rs index 7689c7af61..2590b8444e 100644 --- a/crates/tools/riddle/src/rust/delegates.rs +++ b/crates/tools/riddle/src/rust/delegates.rs @@ -16,10 +16,12 @@ fn gen_callback(writer: &Writer, def: TypeDef) -> TokenStream { let name = to_ident(writer.reader.type_def_name(def)); let method = type_def_invoke_method(writer.reader, def); - let signature = - writer - .reader - .method_def_signature(writer.reader.type_def_namespace(def), method, &[]); + let signature = method_def_signature( + writer.reader, + writer.reader.type_def_namespace(def), + method, + &[], + ); let return_type = writer.return_sig(&signature); let cfg = type_def_cfg(writer.reader, def, &[]); @@ -55,7 +57,7 @@ fn gen_win_delegate(writer: &Writer, def: TypeDef) -> TokenStream { let vtbl = name.join("_Vtbl"); let boxed = name.join("Box"); - let generics = &writer.reader.type_def_generics(def); + let generics = &type_def_generics(writer.reader, def); let phantoms = writer.generic_phantoms(generics); let named_phantoms = writer.generic_named_phantoms(generics); let constraints = writer.generic_constraints(generics); @@ -64,10 +66,12 @@ fn gen_win_delegate(writer: &Writer, def: TypeDef) -> TokenStream { let ident = writer.type_def_name(def, generics); let method = type_def_invoke_method(writer.reader, def); - let signature = - writer - .reader - .method_def_signature(writer.reader.type_def_namespace(def), method, generics); + let signature = method_def_signature( + writer.reader, + writer.reader.type_def_namespace(def), + method, + generics, + ); let fn_constraint = gen_fn_constraint(writer, def, &signature); let cfg = type_def_cfg(writer.reader, def, generics); diff --git a/crates/tools/riddle/src/rust/functions.rs b/crates/tools/riddle/src/rust/functions.rs index 5a582c20f9..6a68484985 100644 --- a/crates/tools/riddle/src/rust/functions.rs +++ b/crates/tools/riddle/src/rust/functions.rs @@ -25,8 +25,8 @@ pub fn writer(writer: &Writer, namespace: &str, def: MethodDef) -> TokenStream { } fn gen_sys_function(writer: &Writer, namespace: &str, def: MethodDef) -> TokenStream { - let signature = writer.reader.method_def_signature(namespace, def, &[]); - let cfg = signature_cfg(writer.reader, &signature); + let signature = method_def_signature(writer.reader, namespace, def, &[]); + let cfg = signature_cfg(writer.reader, def); let mut tokens = writer.cfg_features(&cfg); tokens.combine(&gen_link(writer, namespace, &signature, &cfg)); tokens @@ -34,16 +34,16 @@ fn gen_sys_function(writer: &Writer, namespace: &str, def: MethodDef) -> TokenSt fn gen_win_function(writer: &Writer, namespace: &str, def: MethodDef) -> TokenStream { let name = to_ident(writer.reader.method_def_name(def)); - let signature = writer.reader.method_def_signature(namespace, def, &[]); + let signature = method_def_signature(writer.reader, namespace, def, &[]); let generics = writer.constraint_generics(&signature.params); let where_clause = writer.where_clause(&signature.params); let abi_return_type = writer.return_sig(&signature); - let cfg = signature_cfg(writer.reader, &signature); + let cfg = signature_cfg(writer.reader, def); let doc = writer.cfg_doc(&cfg); let features = writer.cfg_features(&cfg); let link = gen_link(writer, namespace, &signature, &cfg); - let kind = writer.reader.signature_kind(&signature); + let kind = signature_kind(writer.reader, &signature); match kind { SignatureKind::Query(_) => { let args = writer.win32_args(&signature.params, kind); diff --git a/crates/tools/riddle/src/rust/implements.rs b/crates/tools/riddle/src/rust/implements.rs index d27c3951ab..bf500c1207 100644 --- a/crates/tools/riddle/src/rust/implements.rs +++ b/crates/tools/riddle/src/rust/implements.rs @@ -2,12 +2,12 @@ use super::*; pub fn writer(writer: &Writer, def: TypeDef) -> TokenStream { if writer.reader.type_def_kind(def) != TypeKind::Interface - || (!writer.implement && !writer.reader.type_def_can_implement(def)) + || (!writer.implement && writer.reader.has_attribute(def, "ExclusiveToAttribute")) { return quote! {}; } - let generics = &writer.reader.type_def_generics(def); + let generics = &type_def_generics(writer.reader, def); let type_ident = to_ident(writer.reader.type_def_name(def)); let impl_ident = type_ident.join("_Impl"); let vtbl_ident = type_ident.join("_Vtbl"); @@ -52,10 +52,7 @@ pub fn writer(writer: &Writer, def: TypeDef) -> TokenStream { .contains(TypeAttributes::WindowsRuntime) { // TODO: this awkward wrapping of TypeDefs needs fixing - for interface in writer - .reader - .type_interfaces(&Type::TypeDef(def, generics.to_vec())) - { + for interface in type_interfaces(writer.reader, &Type::TypeDef(def, generics.to_vec())) { if let Type::TypeDef(def, generics) = interface.ty { requires.combine(&gen_required_trait(writer, def, &generics)); } @@ -71,7 +68,8 @@ pub fn writer(writer: &Writer, def: TypeDef) -> TokenStream { let method_traits = writer.reader.type_def_methods(def).map(|method| { let name = method_names.add(writer, method); - let signature = writer.reader.method_def_signature( + let signature = method_def_signature( + writer.reader, writer.reader.type_def_namespace(def), method, generics, @@ -86,7 +84,7 @@ pub fn writer(writer: &Writer, def: TypeDef) -> TokenStream { let method_impls = writer.reader.type_def_methods(def).map(|method| { let name = method_names.add(writer, method); - let signature = writer.reader.method_def_signature(writer.reader.type_def_namespace(def), method, generics); + let signature = method_def_signature(writer.reader,writer.reader.type_def_namespace(def), method, generics); let vtbl_signature = writer.vtbl_signature(def, generics, &signature); let invoke_upcall = if writer.reader.type_def_flags(def).contains(TypeAttributes::WindowsRuntime) { winrt_methods::gen_upcall(writer, &signature, quote! { this.#name }) } else { com_methods::gen_upcall(writer, &signature, quote! { this.#name }) }; diff --git a/crates/tools/riddle/src/rust/interfaces.rs b/crates/tools/riddle/src/rust/interfaces.rs index 51c4c2d8a1..3bd36005ca 100644 --- a/crates/tools/riddle/src/rust/interfaces.rs +++ b/crates/tools/riddle/src/rust/interfaces.rs @@ -22,7 +22,7 @@ fn gen_sys_interface(writer: &Writer, def: TypeDef) -> TokenStream { } fn gen_win_interface(writer: &Writer, def: TypeDef) -> TokenStream { - let generics = &writer.reader.type_def_generics(def); + let generics = &type_def_generics(writer.reader, def); let ident = writer.type_def_name(def, generics); let is_exclusive = writer.reader.type_def_is_exclusive(def); let phantoms = writer.generic_phantoms(generics); @@ -30,9 +30,7 @@ fn gen_win_interface(writer: &Writer, def: TypeDef) -> TokenStream { let cfg = type_def_cfg(writer.reader, def, &[]); let doc = writer.cfg_doc(&cfg); let features = writer.cfg_features(&cfg); - let interfaces = writer - .reader - .type_interfaces(&Type::TypeDef(def, generics.to_vec())); + let interfaces = type_interfaces(writer.reader, &Type::TypeDef(def, generics.to_vec())); let vtables = writer.reader.type_def_vtables(def); let has_unknown_base = matches!(vtables.first(), Some(Type::IUnknown)); diff --git a/crates/tools/riddle/src/rust/iterators.rs b/crates/tools/riddle/src/rust/iterators.rs index 990da4b668..a2839da73c 100644 --- a/crates/tools/riddle/src/rust/iterators.rs +++ b/crates/tools/riddle/src/rust/iterators.rs @@ -151,9 +151,7 @@ pub fn writer( let wfc = writer.namespace("Windows.Foundation.Collections"); let mut iterable = None; - let interfaces = writer - .reader - .type_interfaces(&Type::TypeDef(def, generics.to_vec())); + let interfaces = type_interfaces(writer.reader, &Type::TypeDef(def, generics.to_vec())); // If the class or interface is not one of the well-known collection interfaces, we then see whether it // implements any one of them. Here is where we favor IVectorView/IVector over IIterable. diff --git a/crates/tools/riddle/src/rust/standalone.rs b/crates/tools/riddle/src/rust/standalone.rs index 7d0e76fb53..795fb6c34b 100644 --- a/crates/tools/riddle/src/rust/standalone.rs +++ b/crates/tools/riddle/src/rust/standalone.rs @@ -168,7 +168,7 @@ fn item_collect_standalone(reader: &Reader, item: Item, set: &mut BTreeSet type_collect_standalone(reader, &reader.field_type(def, None).to_const_type(), set) } Item::Fn(def, namespace) => { - let signature = reader.method_def_signature(&namespace, def, &[]); + let signature = method_def_signature(reader, &namespace, def, &[]); type_collect_standalone(reader, &signature.return_type, set); signature .params @@ -224,14 +224,14 @@ fn type_collect_standalone(reader: &Reader, ty: &Type, set: &mut BTreeSet) continue; } let signature = - reader.method_def_signature(reader.type_def_namespace(def), method, generics); + method_def_signature(reader, reader.type_def_namespace(def), method, generics); type_collect_standalone(reader, &signature.return_type, set); signature .params .iter() .for_each(|param| type_collect_standalone(reader, ¶m.ty, set)); } - for interface in reader.type_interfaces(&ty) { + for interface in type_interfaces(reader, &ty) { type_collect_standalone(reader, &interface.ty, set); } if reader.type_def_kind(def) == TypeKind::Struct diff --git a/crates/tools/riddle/src/rust/structs.rs b/crates/tools/riddle/src/rust/structs.rs index 44a14ebb3a..0799cb9868 100644 --- a/crates/tools/riddle/src/rust/structs.rs +++ b/crates/tools/riddle/src/rust/structs.rs @@ -181,7 +181,7 @@ fn gen_compare_traits(writer: &Writer, def: TypeDef, name: &TokenStream, cfg: &C if writer.sys || writer.reader.type_def_has_explicit_layout(def) || writer.reader.type_def_has_packing(def) - || writer.reader.type_def_has_callback(def) + || type_def_has_callback(writer.reader, def) { quote! {} } else { @@ -232,7 +232,7 @@ fn gen_debug(writer: &Writer, def: TypeDef, ident: &TokenStream, cfg: &Cfg) -> T let name = writer.reader.field_name(f); let ident = to_ident(name); let ty = writer.reader.field_type(f, Some(def)); - if writer.reader.type_has_callback(&ty) { + if type_has_callback(writer.reader, &ty) { None } else { Some(quote! { .field(#name, &self.#ident) }) diff --git a/crates/tools/riddle/src/rust/winrt_methods.rs b/crates/tools/riddle/src/rust/winrt_methods.rs index c756b37c97..7f9c8acfba 100644 --- a/crates/tools/riddle/src/rust/winrt_methods.rs +++ b/crates/tools/riddle/src/rust/winrt_methods.rs @@ -10,7 +10,8 @@ pub fn writer( method_names: &mut MethodNames, virtual_names: &mut MethodNames, ) -> TokenStream { - let signature = writer.reader.method_def_signature( + let signature = method_def_signature( + writer.reader, writer.reader.type_def_namespace(def), method, generic_types, @@ -21,7 +22,7 @@ pub fn writer( let vname = virtual_names.add(writer, method); let generics = writer.constraint_generics(params); let where_clause = writer.where_clause(params); - let mut cfg = signature_cfg(writer.reader, &signature); + let mut cfg = signature_cfg(writer.reader, method); type_def_cfg_combine(writer.reader, def, generic_types, &mut cfg); let doc = writer.cfg_method_doc(&cfg); let features = writer.cfg_features(&cfg); @@ -125,7 +126,7 @@ fn gen_winrt_params(writer: &Writer, params: &[SignatureParam]) -> TokenStream { { if param.ty.is_winrt_array() { result.combine("e! { #name: &[#default_type], }); - } else if writer.reader.signature_param_is_convertible(param) { + } else if signature_param_is_convertible(writer.reader, param) { let (position, _) = generic_params.next().unwrap(); let kind: TokenStream = format!("P{position}").into(); result.combine("e! { #name: #kind, }); @@ -162,9 +163,9 @@ fn gen_winrt_abi_args(writer: &Writer, params: &[SignatureParam]) -> TokenStream } else { quote! { #name.len() as u32, ::core::mem::transmute(#name.as_ptr()), } } - } else if writer.reader.signature_param_is_failible_param(param) { + } else if type_is_non_exclusive_winrt_interface(writer.reader, ¶m.ty) { quote! { #name.try_into_param()?.abi(), } - } else if writer.reader.signature_param_is_borrowed(param) { + } else if signature_param_is_borrowed(writer.reader, param) { quote! { #name.into_param().abi(), } } else if writer.reader.type_is_blittable(¶m.ty) { if param.ty.is_const_ref() { diff --git a/crates/tools/riddle/src/rust/writer.rs b/crates/tools/riddle/src/rust/writer.rs index 4a8fb17b22..909179fa50 100644 --- a/crates/tools/riddle/src/rust/writer.rs +++ b/crates/tools/riddle/src/rust/writer.rs @@ -298,7 +298,7 @@ impl<'a> Writer<'a> { ) -> impl Iterator + 'b { params .iter() - .filter(move |param| self.reader.signature_param_is_convertible(param)) + .filter(move |param| signature_param_is_convertible(self.reader, param)) .enumerate() } /// The generic param names (i.e., `T` in `fn foo()`) @@ -624,13 +624,13 @@ impl<'a> Writer<'a> { _phantoms: &TokenStream, features: &TokenStream, ) -> TokenStream { - let mut kind = self.reader.type_def_async_kind(def); + let mut kind = type_def_async_kind(self.reader, def); let mut async_generics = generics.to_vec(); if kind == AsyncKind::None { for interface in self.reader.type_def_interfaces(def, generics) { - if let Type::TypeDef(interface_def, interface_generics) = &interface.ty { - kind = self.reader.type_def_async_kind(*interface_def); + if let Type::TypeDef(interface_def, interface_generics) = &interface { + kind = type_def_async_kind(self.reader, *interface_def); if kind != AsyncKind::None { async_generics = interface_generics.to_vec(); break; @@ -904,12 +904,13 @@ impl<'a> Writer<'a> { continue; } let name = method_names.add(self, method); - let signature = self.reader.method_def_signature( + let signature = method_def_signature( + self.reader, self.reader.type_def_namespace(def), method, generics, ); - let mut cfg = signature_cfg(self.reader, &signature); + let mut cfg = signature_cfg(self.reader, method); let signature = self.vtbl_signature(def, generics, &signature); cfg.add_feature(self.reader.type_def_namespace(def)); let cfg_all = self.cfg_features(&cfg); @@ -1260,7 +1261,7 @@ impl<'a> Writer<'a> { quote! { (#this #(#params),*) -> ::windows_core::Result<#return_type> } } else { - let signature_kind = self.reader.signature_kind(signature); + let signature_kind = signature_kind(self.reader, signature); let mut params = quote! {}; if signature_kind == SignatureKind::ResultValue { diff --git a/crates/tools/riddle/src/tokens/token_stream.rs b/crates/tools/riddle/src/tokens/token_stream.rs index b8ba00873f..7bee9124e5 100644 --- a/crates/tools/riddle/src/tokens/token_stream.rs +++ b/crates/tools/riddle/src/tokens/token_stream.rs @@ -89,7 +89,7 @@ impl core::iter::FromIterator for TokenStream { fn from_iter>(iter: I) -> Self { iter.into_iter() .fold(None, |accum: Option, n| { - let mut ts = accum.unwrap_or_else(TokenStream::new); + let mut ts = accum.unwrap_or_default(); ts.combine(&n); Some(ts) }) diff --git a/crates/tools/riddle/src/winmd/from_reader.rs b/crates/tools/riddle/src/winmd/from_reader.rs index 05989d5be5..c355ee762d 100644 --- a/crates/tools/riddle/src/winmd/from_reader.rs +++ b/crates/tools/riddle/src/winmd/from_reader.rs @@ -20,13 +20,16 @@ pub fn from_reader( ))); } + // TODO: just use the reader directly since we now have everything in the reader, there's no need to abstract + // away the source format. Few reprs is always better. + for item in reader.items(filter) { // TODO: cover all variants let metadata::Item::Type(def) = item else { continue; }; - let generics = &reader.type_def_generics(def); + let generics = &metadata::type_def_generics(reader, def); let extends = if let Some(extends) = reader.type_def_extends(def) { writer.insert_type_ref(extends.namespace, extends.name) @@ -43,6 +46,34 @@ pub fn from_reader( TypeNamespace: writer.strings.insert(reader.type_def_namespace(def)), }); + for generic in reader.type_def_generics(def) { + writer.tables.GenericParam.push(writer::GenericParam { + Number: reader.generic_param_number(generic), + Flags: 0, + Owner: writer::TypeOrMethodDef::TypeDef(writer.tables.TypeDef.len() as u32 - 1) + .encode(), + Name: writer.strings.insert(reader.generic_param_name(generic)), + }); + } + + for imp in reader.type_def_interface_impls(def) { + let ty = reader.interface_impl_type(imp, generics); + let ty = winmd_type(reader, &ty); + + let reference = match &ty { + winmd::Type::TypeRef(type_name) if type_name.generics.is_empty() => { + writer.insert_type_ref(&type_name.namespace, &type_name.name) + } + winmd::Type::TypeRef(_) => writer.insert_type_spec(ty), + rest => unimplemented!("{rest:?}"), + }; + + writer.tables.InterfaceImpl.push(writer::InterfaceImpl { + Class: writer.tables.TypeDef.len() as u32 - 1, + Interface: reference, + }); + } + // TODO: if the class is "Apis" then should we sort the fields (constants) and methods (functions) for stability for field in reader.type_def_fields(def) { @@ -57,27 +88,31 @@ pub fn from_reader( } for method in reader.type_def_methods(def) { - let name = reader.method_def_name(method); - let sig = winmd_signature( - reader, - &reader.method_def_signature(reader.type_def_namespace(def), method, generics), - ); - let signature = writer.insert_method_sig(&sig); + let signature = reader.method_def_signature(method, generics); + let return_type = winmd_type(reader, &signature.return_type); + let param_types: Vec = signature + .params + .iter() + .map(|param| winmd_type(reader, param)) + .collect(); + + let signature = + writer.insert_method_sig(signature.call_flags, &return_type, ¶m_types); writer.tables.MethodDef.push(winmd::MethodDef { RVA: 0, - ImplFlags: 0, - Flags: 0, - Name: writer.strings.insert(name), + ImplFlags: reader.method_def_impl_flags(method).0, + Flags: reader.method_def_flags(method).0, + Name: writer.strings.insert(reader.method_def_name(method)), Signature: signature, ParamList: writer.tables.Param.len() as u32, }); - for (sequence, param) in sig.params.iter().enumerate() { + for param in reader.method_def_params(method) { writer.tables.Param.push(writer::Param { - Flags: 0, - Sequence: (sequence + 1) as u16, - Name: writer.strings.insert(¶m.name), + Flags: reader.param_flags(param).0, + Sequence: reader.param_sequence(param), + Name: writer.strings.insert(reader.param_name(param)), }); } } @@ -88,25 +123,7 @@ pub fn from_reader( crate::write_to_file(output, writer.into_stream()).map_err(|err| err.with_path(output)) } -fn winmd_signature(reader: &metadata::Reader, sig: &metadata::Signature) -> winmd::Signature { - let params = sig - .params - .iter() - .map(|param| { - let name = reader.param_name(param.def).to_string(); - let ty = winmd_type(reader, ¶m.ty); - winmd::SignatureParam { name, ty } - }) - .collect(); - - let return_type = winmd_type(reader, &sig.return_type); - winmd::Signature { - params, - return_type, - call_flags: 0, - } -} - +// TODO: keep the basic type conversion fn winmd_type(reader: &metadata::Reader, ty: &metadata::Type) -> winmd::Type { match ty { metadata::Type::Void => winmd::Type::Void, @@ -140,6 +157,23 @@ fn winmd_type(reader: &metadata::Reader, ty: &metadata::Type) -> winmd::Type { name: reader.type_def_name(*def).to_string(), generics: generics.iter().map(|ty| winmd_type(reader, ty)).collect(), }), + metadata::Type::GenericParam(generic) => { + winmd::Type::GenericParam(reader.generic_param_number(*generic)) + } + metadata::Type::ConstRef(ty) => winmd::Type::ConstRef(Box::new(winmd_type(reader, ty))), + metadata::Type::WinrtArrayRef(ty) => { + winmd::Type::WinrtArrayRef(Box::new(winmd_type(reader, ty))) + } + metadata::Type::WinrtArray(ty) => winmd::Type::WinrtArray(Box::new(winmd_type(reader, ty))), + metadata::Type::MutPtr(ty, pointers) => { + winmd::Type::MutPtr(Box::new(winmd_type(reader, ty)), *pointers) + } + metadata::Type::ConstPtr(ty, pointers) => { + winmd::Type::ConstPtr(Box::new(winmd_type(reader, ty)), *pointers) + } + metadata::Type::Win32Array(ty, len) => { + winmd::Type::Win32Array(Box::new(winmd_type(reader, ty)), *len) + } rest => unimplemented!("{rest:?}"), } } diff --git a/crates/tools/riddle/src/winmd/verify.rs b/crates/tools/riddle/src/winmd/verify.rs index 6283a9f3bb..dc892224eb 100644 --- a/crates/tools/riddle/src/winmd/verify.rs +++ b/crates/tools/riddle/src/winmd/verify.rs @@ -8,19 +8,19 @@ pub fn verify(reader: &metadata::Reader, filter: &metadata::Filter) -> crate::Re continue; }; - let generics = &reader.type_def_generics(def); + let generics = &metadata::type_def_generics(reader, def); reader .type_def_fields(def) .try_for_each(|field| not_type_ref(reader, &reader.field_type(field, Some(def))))?; reader.type_def_methods(def).try_for_each(|method| { - let sig = reader.method_def_signature(reader.type_def_namespace(def), method, generics); + let sig = reader.method_def_signature(method, generics); not_type_ref(reader, &sig.return_type)?; sig.params .iter() - .try_for_each(|param| not_type_ref(reader, ¶m.ty)) + .try_for_each(|param| not_type_ref(reader, param)) })?; } diff --git a/crates/tools/riddle/src/winmd/writer/codes.rs b/crates/tools/riddle/src/winmd/writer/codes.rs index 466b2549ca..c5aa789e0a 100644 --- a/crates/tools/riddle/src/winmd/writer/codes.rs +++ b/crates/tools/riddle/src/winmd/writer/codes.rs @@ -55,3 +55,17 @@ impl HasConstant { } } } + +/// A `TypeOrMethodDef` is an index into a certain table used to locate the owner of a generic parameter. +#[derive(Clone)] +pub enum TypeOrMethodDef { + TypeDef(u32), +} + +impl TypeOrMethodDef { + pub fn encode(&self) -> u32 { + match self { + Self::TypeDef(row) => (row + 1) << 1, + } + } +} diff --git a/crates/tools/riddle/src/winmd/writer/mod.rs b/crates/tools/riddle/src/winmd/writer/mod.rs index 3cbc29f2e1..63fc0eaf96 100644 --- a/crates/tools/riddle/src/winmd/writer/mod.rs +++ b/crates/tools/riddle/src/winmd/writer/mod.rs @@ -8,7 +8,7 @@ mod r#type; use super::*; use blobs::Blobs; -use codes::*; +pub use codes::*; use metadata::imp::*; pub use r#type::*; use std::collections::HashMap; @@ -21,7 +21,9 @@ pub struct Writer { pub strings: Strings, pub tables: Tables, pub scopes: HashMap, - pub references: HashMap>, + // TODO: is this faster than jsut using a single HashMap with a (String,String) key? + pub type_refs: HashMap>, + pub type_specs: HashMap, } impl Writer { @@ -31,7 +33,8 @@ impl Writer { strings: Default::default(), tables: Default::default(), scopes: Default::default(), - references: Default::default(), + type_refs: Default::default(), + type_specs: Default::default(), }; writer.tables.TypeDef.push(TypeDef { @@ -77,97 +80,25 @@ impl Writer { ) } - // fn insert_module_types(&mut self, module: &'a Module) { - // for (name, def) in &module.types { - // self.insert_type_def(&module.namespace, name, def); - // } - // module.modules.values().for_each(|module| self.insert_module_types(module)); - // } - - // fn insert_type_def(&mut self, namespace: &'a str, name: &'a str, def: &'a [TypeDef]) { - // for def in def { - // let extends = if let Some(extends) = &def.extends { self.insert_type_ref(&extends.namespace, &extends.name) } else { 0 }; - // self.tables.TypeDef.push(TypeDef { - // Flags: def.flags.0, - // TypeName: self.strings.insert(name), - // TypeNamespace: self.strings.insert(namespace), - // Extends: extends, - // FieldList: self.tables.Field.len() as u32, - // MethodList: self.tables.MethodDef.len() as u32, - // }); - // for field in &def.fields { - // let blob = self.insert_field_sig(&field.ty); - // let parent = self.tables.Field.push2(Field { Flags: field.flags.0, Name: self.strings.insert(&field.name), Signature: blob }); - // if let Some(value) = &field.value { - // let blob = self.insert_value_blob(value); - // self.tables.Constant.push(Constant { Type: value.to_code(), Parent: HasConstant::Field(parent).encode(), Value: blob }); - // } - // } - // for method in &def.methods { - // let blob = self.insert_method_sig(method); - // self.tables.MethodDef.push(MethodDef { RVA: 0, ImplFlags: 0, Flags: method.flags.0, Name: self.strings.insert(&method.name), Signature: blob, ParamList: self.tables.Param.len() as u32 }); - // for (sequence, param) in method.params.iter().enumerate() { - // self.tables.Param.push(Param { Flags: param.flags.0, Sequence: (sequence + 1) as u32, Name: self.strings.insert(¶m.name) }); - // } - // } - // } - // } - - // fn insert_value_blob(&mut self, value: &Value) -> u32 { - // // TODO: can either cache in Writer, like we do for scopes and references, or regenerate each time. - // // Profile once we can stress test this with field/method signatures. - - // let blob = match value { - // Value::I8(value) => value.to_le_bytes().to_vec(), - // Value::U8(value) => value.to_le_bytes().to_vec(), - // Value::I16(value) => value.to_le_bytes().to_vec(), - // Value::U16(value) => value.to_le_bytes().to_vec(), - // Value::I32(value) => value.to_le_bytes().to_vec(), - // Value::U32(value) => value.to_le_bytes().to_vec(), - // Value::I64(value) => value.to_le_bytes().to_vec(), - // Value::U64(value) => value.to_le_bytes().to_vec(), - // Value::F32(value) => value.to_le_bytes().to_vec(), - // Value::F64(value) => value.to_le_bytes().to_vec(), - // Value::String(value) => { - // let mut blob = vec![]; - // usize_blob(value.len(), &mut blob); - // blob.extend_from_slice(value.as_bytes()); - // blob - // } - // rest => unimplemented!("{rest:?}"), - // }; - - // self.blobs.insert(&blob) - // } - - // fn insert_method_sig(&mut self, method: &'a Method) -> u32 { - // // TODO: can either cache in Writer, like we do for scopes and references, or regenerate each time. - // // Profile once we can stress test this with field/method signatures. - - // let mut blob = vec![method.call_flags.0]; - // usize_blob(method.params.len(), &mut blob); - // self.type_blob(&method.return_type.ty, &mut blob); - // for param in &method.params { - // self.type_blob(¶m.ty, &mut blob); - // } - - // self.blobs.insert(&blob) - // } - - pub fn insert_method_sig(&mut self, sig: &Signature) -> u32 { - let mut blob = vec![sig.call_flags]; - usize_blob(sig.params.len(), &mut blob); - self.type_blob(&sig.return_type, &mut blob); - - for param in &sig.params { - self.type_blob(¶m.ty, &mut blob); + pub fn insert_method_sig( + &mut self, + call_flags: metadata::MethodCallAttributes, + return_type: &Type, + param_types: &[Type], + ) -> u32 { + let mut blob = vec![call_flags.0]; + usize_blob(param_types.len(), &mut blob); + self.type_blob(return_type, &mut blob); + + for ty in param_types { + self.type_blob(ty, &mut blob); } self.blobs.insert(&blob) } pub fn insert_field_sig(&mut self, ty: &Type) -> u32 { - // TODO: can either cache in Writer, like we do for scopes and references, or regenerate each time. + // TODO: can either cache in Writer, like we do for scopes and type_refs, or regenerate each time. // Profile once we can stress test this with field/method signatures. let mut blob = vec![0x6]; // FIELD @@ -186,7 +117,7 @@ impl Writer { MajorVersion: 4, PublicKeyOrToken: self .blobs - .insert(&[0xB7, 0x7A, 0x5C, 0x56, 0x19, 0x34, 0xE0, 0x89]), + .insert(&[0xB7, 0x7A, 0x5C, 0x56, 0x19, 0x34, 0xE0, 0x89]), // TODO: comment on this ..Default::default() }), ) @@ -194,7 +125,7 @@ impl Writer { self.scopes.insert(namespace.to_string(), scope); scope } else { - // TODO: may need to capture the original assembly info for external references. + // TODO: may need to capture the original assembly info for external type_refs. let scope = ResolutionScope::AssemblyRef(self.tables.AssemblyRef.push2(AssemblyRef { Name: self.strings.insert(namespace), MajorVersion: 0xFF, @@ -211,7 +142,7 @@ impl Writer { } pub fn insert_type_ref(&mut self, namespace: &str, name: &str) -> u32 { - if let Some(key) = self.references.get(namespace) { + if let Some(key) = self.type_refs.get(namespace) { if let Some(reference) = key.get(name) { return *reference; } @@ -225,13 +156,31 @@ impl Writer { ResolutionScope: scope, })) .encode(); - self.references + self.type_refs .entry(namespace.to_string()) .or_default() .insert(name.to_string(), reference); reference } + pub fn insert_type_spec(&mut self, ty: Type) -> u32 { + if let Some(key) = self.type_specs.get(&ty) { + return *key; + } + + let mut blob = vec![]; + self.type_blob(&ty, &mut blob); + let signature = self.blobs.insert(&blob); + + let reference = TypeDefOrRef::TypeSpec(self.tables.TypeSpec.push2(TypeSpec { + Signature: signature, + })) + .encode(); + + self.type_specs.insert(ty, reference); + reference + } + fn type_blob(&mut self, ty: &Type, blob: &mut Vec) { match ty { Type::Void => blob.push(ELEMENT_TYPE_VOID), @@ -262,9 +211,20 @@ impl Writer { usize_blob(code as usize, blob); } Type::TypeRef(ty) => { + if !ty.generics.is_empty() { + blob.push(ELEMENT_TYPE_GENERICINST); + } let code = self.insert_type_ref(&ty.namespace, &ty.name); blob.push(ELEMENT_TYPE_VALUETYPE); usize_blob(code as usize, blob); + + if !ty.generics.is_empty() { + usize_blob(ty.generics.len(), blob); + + for ty in &ty.generics { + self.type_blob(ty, blob); + } + } } Type::BSTR => { let code = self.insert_type_ref("Windows.Win32.Foundation", "BSTR"); @@ -322,7 +282,10 @@ impl Writer { } self.type_blob(ty, blob); } - rest => unimplemented!("{rest:?}"), + Type::GenericParam(index) => { + blob.push(ELEMENT_TYPE_VAR); + usize_blob(*index as usize, blob); + } } } } diff --git a/crates/tools/riddle/src/winmd/writer/tables.rs b/crates/tools/riddle/src/winmd/writer/tables.rs index 3c3948e7d1..3e590dde73 100644 --- a/crates/tools/riddle/src/winmd/writer/tables.rs +++ b/crates/tools/riddle/src/winmd/writer/tables.rs @@ -6,6 +6,7 @@ use metadata::imp::coded_index_size; #[derive(Default)] pub struct Tables { + // TODO: use BTreeSet for tables that have a primary key, unless they are naturally sorted. pub Assembly: Vec, pub AssemblyRef: Vec, pub ClassLayout: Vec, @@ -210,11 +211,15 @@ impl Tables { self.AssemblyRef.len(), self.TypeRef.len(), ]); + let type_def_or_ref = coded_index_size(&[self.TypeDef.len(), self.TypeRef.len(), self.TypeSpec.len()]); + let has_constant = coded_index_size(&[self.Field.len(), self.Param.len(), self.Property.len()]); + let type_or_method_def = coded_index_size(&[self.TypeDef.len(), self.MethodDef.len()]); + let valid_tables: u64 = 1 << 0 | // Module 1 << 0x01 | // TypeRef 1 << 0x02 | // TypeDef @@ -284,7 +289,7 @@ impl Tables { buffer.write_u32(x.TypeNamespace); } - for x in self.TypeDef { + for x in &self.TypeDef { buffer.write_u32(x.Flags); buffer.write_u32(x.TypeName); buffer.write_u32(x.TypeNamespace); @@ -314,12 +319,21 @@ impl Tables { buffer.write_u32(x.Name); } + for x in self.InterfaceImpl { + buffer.write_index(x.Class, self.TypeDef.len()); + buffer.write_code(x.Interface, type_def_or_ref); + } + for x in self.Constant { buffer.write_u16(x.Type); buffer.write_code(x.Parent, has_constant); buffer.write_u32(x.Value); } + for x in self.TypeSpec { + buffer.write_u32(x.Signature); + } + for x in self.Assembly { buffer.write_u32(x.HashAlgId); buffer.write_u16(x.MajorVersion); @@ -344,6 +358,33 @@ impl Tables { buffer.write_u32(x.HashValue); } + for x in self.GenericParam { + buffer.write_u16(x.Number); + buffer.write_u16(x.Flags); + buffer.write_code(x.Owner, type_or_method_def); + buffer.write_u32(x.Name); + } + + // TODO: sort GenericParam table prior to writing. This needs to be done for all tables with a primary index. See II.22 + + // TODO: do these get naturally sorted by virtue of how they're pushed into "tables" in type def order? + + // Table Primary Key Column + // ClassLayout Parent + // Constant Parent + // CustomAttribute Parent + // DeclSecurity Parent + // FieldLayout Field + // FieldMarshal Parent + // FieldRVA Field + // GenericParam Owner + // GenericParamConstraint Owner + // ImplMap MemberForwarded + // InterfaceImpl Class + // MethodImpl Class + // MethodSemantics Association + // NestedClass NestedClass + buffer.into_stream() } } diff --git a/crates/tools/riddle/src/winmd/writer/type.rs b/crates/tools/riddle/src/winmd/writer/type.rs index 03a085c030..3f0178654a 100644 --- a/crates/tools/riddle/src/winmd/writer/type.rs +++ b/crates/tools/riddle/src/winmd/writer/type.rs @@ -1,13 +1,13 @@ #![allow(dead_code, clippy::upper_case_acronyms)] -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct TypeName { pub namespace: String, pub name: String, pub generics: Vec, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub enum Type { Void, Bool, @@ -36,7 +36,7 @@ pub enum Type { BSTR, TypeName, TypeRef(TypeName), - GenericParam(String), + GenericParam(u16), MutPtr(Box, usize), ConstPtr(Box, usize), Win32Array(Box, usize),