diff --git a/src/WinRT.Generator.Core/Extensions/SignatureComparerExtensions.cs b/src/WinRT.Generator.Core/Extensions/SignatureComparerExtensions.cs new file mode 100644 index 0000000000..9ecb901c03 --- /dev/null +++ b/src/WinRT.Generator.Core/Extensions/SignatureComparerExtensions.cs @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using AsmResolver.DotNet.Signatures; + +namespace WindowsRuntime.Generator; + +/// +/// Extensions for . +/// +internal static class SignatureComparerExtensions +{ + /// + /// Backing field for the extension property. + /// + private static readonly SignatureComparer IgnoreVersion = new(SignatureComparisonFlags.VersionAgnostic); + + extension(SignatureComparer) + { + /// + /// Gets a shared, version-agnostic , suitable for comparing + /// AsmResolver entities (e.g. as the + /// for a of ). + /// + public static SignatureComparer IgnoreVersion => IgnoreVersion; + } +} diff --git a/src/WinRT.Generator.Core/WinRT.Generator.Core.csproj b/src/WinRT.Generator.Core/WinRT.Generator.Core.csproj index 5e0d083a0e..ecbb167af5 100644 --- a/src/WinRT.Generator.Core/WinRT.Generator.Core.csproj +++ b/src/WinRT.Generator.Core/WinRT.Generator.Core.csproj @@ -40,5 +40,8 @@ + + + diff --git a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IObservableMap2.cs b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IObservableMap2.cs index ea4708ee8d..0654706c64 100644 --- a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IObservableMap2.cs +++ b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IObservableMap2.cs @@ -5,6 +5,7 @@ using AsmResolver.DotNet; using AsmResolver.DotNet.Signatures; using AsmResolver.PE.DotNet.Metadata.Tables; +using WindowsRuntime.Generator; using WindowsRuntime.InteropGenerator.Factories; using WindowsRuntime.InteropGenerator.Generation; using WindowsRuntime.InteropGenerator.Helpers; @@ -533,4 +534,4 @@ public static void ImplType( implType.Properties.Add(mapChangedTableProperty); } } -} \ No newline at end of file +} diff --git a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IObservableVector1.cs b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IObservableVector1.cs index 90707e9a96..7f325ee78a 100644 --- a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IObservableVector1.cs +++ b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IObservableVector1.cs @@ -5,6 +5,7 @@ using AsmResolver.DotNet; using AsmResolver.DotNet.Signatures; using AsmResolver.PE.DotNet.Metadata.Tables; +using WindowsRuntime.Generator; using WindowsRuntime.InteropGenerator.Factories; using WindowsRuntime.InteropGenerator.Generation; using WindowsRuntime.InteropGenerator.Helpers; @@ -521,4 +522,4 @@ public static void ImplType( emitState.TrackTypeDefinition(implType, vectorType, "Impl"); } } -} \ No newline at end of file +} diff --git a/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.Generics.cs b/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.Generics.cs index 6eed89eebd..28f6bcf97f 100644 --- a/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.Generics.cs +++ b/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.Generics.cs @@ -3,6 +3,7 @@ using AsmResolver.DotNet; using AsmResolver.DotNet.Signatures; +using WindowsRuntime.Generator; using WindowsRuntime.InteropGenerator.Errors; using WindowsRuntime.InteropGenerator.Generation; using WindowsRuntime.InteropGenerator.Helpers; @@ -636,4 +637,4 @@ private static void TryTrackManagedGenericTypeInstance( interopReferences: interopReferences, module: module); } -} \ No newline at end of file +} diff --git a/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.cs b/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.cs index c044ab3e13..7bc0a97670 100644 --- a/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.cs +++ b/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.cs @@ -7,6 +7,7 @@ using System.Linq; using AsmResolver.DotNet; using AsmResolver.DotNet.Signatures; +using WindowsRuntime.Generator; using WindowsRuntime.InteropGenerator.Errors; using WindowsRuntime.InteropGenerator.Generation; using WindowsRuntime.InteropGenerator.Helpers; @@ -562,4 +563,4 @@ private static bool TryAddExposedInterfaceType( return true; } -} \ No newline at end of file +} diff --git a/src/WinRT.Interop.Generator/Extensions/ComExtensions.cs b/src/WinRT.Interop.Generator/Extensions/ComExtensions.cs index 20719c0ae4..7df003065d 100644 --- a/src/WinRT.Interop.Generator/Extensions/ComExtensions.cs +++ b/src/WinRT.Interop.Generator/Extensions/ComExtensions.cs @@ -4,6 +4,7 @@ using System.Diagnostics.CodeAnalysis; using AsmResolver.DotNet; using AsmResolver.DotNet.Signatures; +using WindowsRuntime.Generator; using WindowsRuntime.InteropGenerator.References; namespace WindowsRuntime.InteropGenerator; @@ -72,4 +73,4 @@ public bool TryGetInterfaceInformationType(InteropReferences interopReferences, return true; } } -} \ No newline at end of file +} diff --git a/src/WinRT.Interop.Generator/Extensions/IHasCustomAttributeExtensions.cs b/src/WinRT.Interop.Generator/Extensions/IHasCustomAttributeExtensions.cs index b124170267..132090edf1 100644 --- a/src/WinRT.Interop.Generator/Extensions/IHasCustomAttributeExtensions.cs +++ b/src/WinRT.Interop.Generator/Extensions/IHasCustomAttributeExtensions.cs @@ -5,6 +5,7 @@ using AsmResolver; using AsmResolver.DotNet; using AsmResolver.DotNet.Signatures; +using WindowsRuntime.Generator; namespace WindowsRuntime.InteropGenerator; @@ -100,4 +101,4 @@ public static bool HasCustomAttribute(this IHasCustomAttribute member, ITypeDesc { return TryGetCustomAttribute(member, attributeType, out _); } -} \ No newline at end of file +} diff --git a/src/WinRT.Interop.Generator/Extensions/ModuleDefinitionExtensions.cs b/src/WinRT.Interop.Generator/Extensions/ModuleDefinitionExtensions.cs index c50cc44e7d..4e6e1ba2e6 100644 --- a/src/WinRT.Interop.Generator/Extensions/ModuleDefinitionExtensions.cs +++ b/src/WinRT.Interop.Generator/Extensions/ModuleDefinitionExtensions.cs @@ -9,6 +9,7 @@ using AsmResolver.DotNet; using AsmResolver.DotNet.Signatures; using AsmResolver.PE.DotNet.Metadata.Tables; +using WindowsRuntime.Generator; using WindowsRuntime.InteropGenerator.Helpers; using WindowsRuntime.InteropGenerator.Visitors; @@ -304,4 +305,4 @@ public static IEnumerable OrderByFullyQualifiedName(this IEnum { return modules.Order(ModuleDefinitionComparer.Instance); } -} \ No newline at end of file +} diff --git a/src/WinRT.Interop.Generator/Extensions/SignatureComparerExtensions.cs b/src/WinRT.Interop.Generator/Extensions/SignatureComparerExtensions.cs index a0b6ac98e9..6b9cf3b1e1 100644 --- a/src/WinRT.Interop.Generator/Extensions/SignatureComparerExtensions.cs +++ b/src/WinRT.Interop.Generator/Extensions/SignatureComparerExtensions.cs @@ -12,20 +12,8 @@ namespace WindowsRuntime.InteropGenerator; /// internal static class SignatureComparerExtensions { -#pragma warning disable IDE0052 // TODO: remove this once Roslyn bug is fixed - /// - /// Backing field for . - /// - private static readonly SignatureComparer IgnoreVersion = new(SignatureComparisonFlags.VersionAgnostic); -#pragma warning restore IDE0052 - extension(SignatureComparer comparer) { - /// - /// An immutable default instance of , with . - /// - public static SignatureComparer IgnoreVersion => IgnoreVersion; - /// /// Creates an instance for a pair of values. /// diff --git a/src/WinRT.Interop.Generator/Extensions/TypeDefinitionExtensions.cs b/src/WinRT.Interop.Generator/Extensions/TypeDefinitionExtensions.cs index 888c334b37..cb0849aef3 100644 --- a/src/WinRT.Interop.Generator/Extensions/TypeDefinitionExtensions.cs +++ b/src/WinRT.Interop.Generator/Extensions/TypeDefinitionExtensions.cs @@ -8,6 +8,7 @@ using AsmResolver.DotNet; using AsmResolver.DotNet.Signatures; using AsmResolver.PE.DotNet.Metadata.Tables; +using WindowsRuntime.Generator; #pragma warning disable IDE0046 @@ -344,4 +345,4 @@ public IEnumerable EnumerateGenericInstanceInterfa } } } -} \ No newline at end of file +} diff --git a/src/WinRT.Interop.Generator/Extensions/WindowsRuntimeExtensions.cs b/src/WinRT.Interop.Generator/Extensions/WindowsRuntimeExtensions.cs index ce70b8a1a2..581fdb3faf 100644 --- a/src/WinRT.Interop.Generator/Extensions/WindowsRuntimeExtensions.cs +++ b/src/WinRT.Interop.Generator/Extensions/WindowsRuntimeExtensions.cs @@ -8,6 +8,7 @@ using AsmResolver.DotNet; using AsmResolver.DotNet.Signatures; using AsmResolver.PE.DotNet.Metadata.Tables; +using WindowsRuntime.Generator; using WindowsRuntime.InteropGenerator.References; #pragma warning disable IDE0046 @@ -1186,4 +1187,4 @@ file static class WellKnownMetadataNames /// The "WindowsRuntimeComponentAssemblyAttribute" text. /// public static readonly Utf8String WindowsRuntimeComponentAssemblyAttribute = "WindowsRuntimeComponentAssemblyAttribute"u8; -} \ No newline at end of file +} diff --git a/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IReadOnlyDictionary2Impl.cs b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IReadOnlyDictionary2Impl.cs index 27c0f0fe4c..9f5262fc3f 100644 --- a/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IReadOnlyDictionary2Impl.cs +++ b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IReadOnlyDictionary2Impl.cs @@ -6,6 +6,7 @@ using AsmResolver.DotNet.Signatures; using AsmResolver.PE.DotNet.Cil; using AsmResolver.PE.DotNet.Metadata.Tables; +using WindowsRuntime.Generator; using WindowsRuntime.InteropGenerator.Generation; using WindowsRuntime.InteropGenerator.References; using static AsmResolver.PE.DotNet.Cil.CilOpCodes; @@ -512,4 +513,4 @@ public static MethodDefinition Split( return splitMethod; } } -} \ No newline at end of file +} diff --git a/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IReadOnlyList1Impl.cs b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IReadOnlyList1Impl.cs index 8abfa8e72d..1844bc4c0c 100644 --- a/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IReadOnlyList1Impl.cs +++ b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IReadOnlyList1Impl.cs @@ -6,6 +6,7 @@ using AsmResolver.DotNet.Signatures; using AsmResolver.PE.DotNet.Cil; using AsmResolver.PE.DotNet.Metadata.Tables; +using WindowsRuntime.Generator; using WindowsRuntime.InteropGenerator.Generation; using WindowsRuntime.InteropGenerator.References; using static AsmResolver.PE.DotNet.Cil.CilOpCodes; @@ -493,4 +494,4 @@ _ when elementType.IsTypeOfException(interopReferences) => interopReferences.IRe return getManyImplMethod; } } -} \ No newline at end of file +} diff --git a/src/WinRT.Interop.Generator/Generation/InteropGenerator.Discover.cs b/src/WinRT.Interop.Generator/Generation/InteropGenerator.Discover.cs index 42760ba3c2..54196bf645 100644 --- a/src/WinRT.Interop.Generator/Generation/InteropGenerator.Discover.cs +++ b/src/WinRT.Interop.Generator/Generation/InteropGenerator.Discover.cs @@ -10,6 +10,7 @@ using AsmResolver.DotNet; using AsmResolver.DotNet.Signatures; using AsmResolver.PE; +using WindowsRuntime.Generator; using WindowsRuntime.Generator.Errors; using WindowsRuntime.Generator.Extensions; using WindowsRuntime.Generator.References; @@ -618,4 +619,4 @@ private static string[] GetAssembliesToProcess(InteropGeneratorArgs args) return [.. assembliesToProcess]; } -} \ No newline at end of file +} diff --git a/src/WinRT.Interop.Generator/Generation/InteropGeneratorDiscoveryState.cs b/src/WinRT.Interop.Generator/Generation/InteropGeneratorDiscoveryState.cs index 979bba7ef9..7b747a0c0f 100644 --- a/src/WinRT.Interop.Generator/Generation/InteropGeneratorDiscoveryState.cs +++ b/src/WinRT.Interop.Generator/Generation/InteropGeneratorDiscoveryState.cs @@ -6,6 +6,7 @@ using System.Diagnostics.CodeAnalysis; using AsmResolver.DotNet; using AsmResolver.DotNet.Signatures; +using WindowsRuntime.Generator; using WindowsRuntime.InteropGenerator.Errors; using WindowsRuntime.InteropGenerator.Models; @@ -572,4 +573,4 @@ private void ThrowIfReadOnly() throw WellKnownInteropExceptions.DiscoveryStateChangeAfterMakeReadOnlyError(); } } -} \ No newline at end of file +} diff --git a/src/WinRT.Interop.Generator/Generation/InteropGeneratorEmitState.cs b/src/WinRT.Interop.Generator/Generation/InteropGeneratorEmitState.cs index 265b99f1e7..ab07191566 100644 --- a/src/WinRT.Interop.Generator/Generation/InteropGeneratorEmitState.cs +++ b/src/WinRT.Interop.Generator/Generation/InteropGeneratorEmitState.cs @@ -8,6 +8,7 @@ using AsmResolver.DotNet.Code.Cil; using AsmResolver.DotNet.Signatures; using AsmResolver.PE.DotNet.Cil; +using WindowsRuntime.Generator; using WindowsRuntime.InteropGenerator.Errors; using WindowsRuntime.InteropGenerator.Models; @@ -394,4 +395,4 @@ private void ThrowIfReadOnly() throw WellKnownInteropExceptions.EmitStateChangeAfterMakeReadOnlyError(); } } -} \ No newline at end of file +} diff --git a/src/WinRT.Interop.Generator/Helpers/TypeExclusions.cs b/src/WinRT.Interop.Generator/Helpers/TypeExclusions.cs index 74b5ab7994..2356b13fec 100644 --- a/src/WinRT.Interop.Generator/Helpers/TypeExclusions.cs +++ b/src/WinRT.Interop.Generator/Helpers/TypeExclusions.cs @@ -4,6 +4,7 @@ using System; using AsmResolver.DotNet; using AsmResolver.DotNet.Signatures; +using WindowsRuntime.Generator; using WindowsRuntime.InteropGenerator.References; namespace WindowsRuntime.InteropGenerator.Helpers; diff --git a/src/WinRT.Interop.Generator/Helpers/WindowsRuntimeTypeAnalyzer.cs b/src/WinRT.Interop.Generator/Helpers/WindowsRuntimeTypeAnalyzer.cs index 7831c0771d..85b31842cb 100644 --- a/src/WinRT.Interop.Generator/Helpers/WindowsRuntimeTypeAnalyzer.cs +++ b/src/WinRT.Interop.Generator/Helpers/WindowsRuntimeTypeAnalyzer.cs @@ -5,6 +5,7 @@ using System.Diagnostics.CodeAnalysis; using AsmResolver.DotNet; using AsmResolver.DotNet.Signatures; +using WindowsRuntime.Generator; using WindowsRuntime.InteropGenerator.References; namespace WindowsRuntime.InteropGenerator.Helpers; diff --git a/src/WinRT.Interop.Generator/Models/TypeSignatureEquatableSet.Builder.cs b/src/WinRT.Interop.Generator/Models/TypeSignatureEquatableSet.Builder.cs index 6f33602c2b..199d2b2ec6 100644 --- a/src/WinRT.Interop.Generator/Models/TypeSignatureEquatableSet.Builder.cs +++ b/src/WinRT.Interop.Generator/Models/TypeSignatureEquatableSet.Builder.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; using AsmResolver.DotNet.Signatures; +using WindowsRuntime.Generator; namespace WindowsRuntime.InteropGenerator.Models; @@ -122,4 +123,4 @@ public TypeSignatureEquatableSet MoveToEquatableSet() return new(set); } } -} \ No newline at end of file +} diff --git a/src/WinRT.Interop.Generator/Models/TypeSignatureEquatableSet.cs b/src/WinRT.Interop.Generator/Models/TypeSignatureEquatableSet.cs index 6620f2705b..f051e8c390 100644 --- a/src/WinRT.Interop.Generator/Models/TypeSignatureEquatableSet.cs +++ b/src/WinRT.Interop.Generator/Models/TypeSignatureEquatableSet.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; using AsmResolver.DotNet.Signatures; +using WindowsRuntime.Generator; using WindowsRuntime.InteropGenerator.Helpers; namespace WindowsRuntime.InteropGenerator.Models; @@ -220,4 +221,4 @@ IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } -} \ No newline at end of file +} diff --git a/src/WinRT.Interop.Generator/References/WellKnownInterfaceIIDs.cs b/src/WinRT.Interop.Generator/References/WellKnownInterfaceIIDs.cs index fafda0e0fb..a6384152ca 100644 --- a/src/WinRT.Interop.Generator/References/WellKnownInterfaceIIDs.cs +++ b/src/WinRT.Interop.Generator/References/WellKnownInterfaceIIDs.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using AsmResolver.DotNet; using AsmResolver.DotNet.Signatures; +using WindowsRuntime.Generator; using WindowsRuntime.InteropGenerator.Errors; using WindowsRuntime.InteropGenerator.Factories; diff --git a/src/WinRT.Interop.Generator/Resolvers/InteropImplTypeResolver.cs b/src/WinRT.Interop.Generator/Resolvers/InteropImplTypeResolver.cs index 1e6dd29662..f635942b8d 100644 --- a/src/WinRT.Interop.Generator/Resolvers/InteropImplTypeResolver.cs +++ b/src/WinRT.Interop.Generator/Resolvers/InteropImplTypeResolver.cs @@ -5,6 +5,7 @@ using AsmResolver.DotNet; using AsmResolver.DotNet.Signatures; using AsmResolver.PE.DotNet.Metadata.Tables; +using WindowsRuntime.Generator; using WindowsRuntime.InteropGenerator.Factories; using WindowsRuntime.InteropGenerator.Generation; using WindowsRuntime.InteropGenerator.References; diff --git a/src/WinRT.Interop.Generator/Resolvers/InteropInterfaceEntriesResolver.cs b/src/WinRT.Interop.Generator/Resolvers/InteropInterfaceEntriesResolver.cs index 0de717e4f6..6c2e6a6a84 100644 --- a/src/WinRT.Interop.Generator/Resolvers/InteropInterfaceEntriesResolver.cs +++ b/src/WinRT.Interop.Generator/Resolvers/InteropInterfaceEntriesResolver.cs @@ -7,6 +7,7 @@ using AsmResolver.DotNet; using AsmResolver.DotNet.Code.Cil; using AsmResolver.DotNet.Signatures; +using WindowsRuntime.Generator; using WindowsRuntime.InteropGenerator.Generation; using WindowsRuntime.InteropGenerator.Models; using WindowsRuntime.InteropGenerator.References; diff --git a/src/WinRT.Interop.Generator/Rewriters/InteropMethodRewriter.ManagedParameter.cs b/src/WinRT.Interop.Generator/Rewriters/InteropMethodRewriter.ManagedParameter.cs index c61d54484b..b547b1d2bd 100644 --- a/src/WinRT.Interop.Generator/Rewriters/InteropMethodRewriter.ManagedParameter.cs +++ b/src/WinRT.Interop.Generator/Rewriters/InteropMethodRewriter.ManagedParameter.cs @@ -6,6 +6,7 @@ using AsmResolver.DotNet.Collections; using AsmResolver.DotNet.Signatures; using AsmResolver.PE.DotNet.Cil; +using WindowsRuntime.Generator; using WindowsRuntime.InteropGenerator.Errors; using WindowsRuntime.InteropGenerator.Generation; using WindowsRuntime.InteropGenerator.References; @@ -116,4 +117,4 @@ public static void RewriteMethod( } } } -} \ No newline at end of file +} diff --git a/src/WinRT.Interop.Generator/Rewriters/InteropMethodRewriter.ManagedValue.cs b/src/WinRT.Interop.Generator/Rewriters/InteropMethodRewriter.ManagedValue.cs index fd61a5c9f7..f7a69c98e0 100644 --- a/src/WinRT.Interop.Generator/Rewriters/InteropMethodRewriter.ManagedValue.cs +++ b/src/WinRT.Interop.Generator/Rewriters/InteropMethodRewriter.ManagedValue.cs @@ -5,6 +5,7 @@ using AsmResolver.DotNet.Code.Cil; using AsmResolver.DotNet.Signatures; using AsmResolver.PE.DotNet.Cil; +using WindowsRuntime.Generator; using WindowsRuntime.InteropGenerator.Errors; using WindowsRuntime.InteropGenerator.Generation; using WindowsRuntime.InteropGenerator.References; @@ -99,4 +100,4 @@ public static void RewriteMethod( } } } -} \ No newline at end of file +} diff --git a/src/WinRT.Interop.Generator/Rewriters/InteropMethodRewriter.NativeParameter.cs b/src/WinRT.Interop.Generator/Rewriters/InteropMethodRewriter.NativeParameter.cs index ad3a7d7c42..0185da8c63 100644 --- a/src/WinRT.Interop.Generator/Rewriters/InteropMethodRewriter.NativeParameter.cs +++ b/src/WinRT.Interop.Generator/Rewriters/InteropMethodRewriter.NativeParameter.cs @@ -7,6 +7,7 @@ using AsmResolver.DotNet.Collections; using AsmResolver.DotNet.Signatures; using AsmResolver.PE.DotNet.Cil; +using WindowsRuntime.Generator; using WindowsRuntime.InteropGenerator.Errors; using WindowsRuntime.InteropGenerator.Generation; using WindowsRuntime.InteropGenerator.References; @@ -425,4 +426,4 @@ private static void RewriteBodyForTypeOfType( }); } } -} \ No newline at end of file +} diff --git a/src/WinRT.Interop.Generator/Rewriters/InteropMethodRewriter.ReturnValue.cs b/src/WinRT.Interop.Generator/Rewriters/InteropMethodRewriter.ReturnValue.cs index ae4462938c..02eed83a6a 100644 --- a/src/WinRT.Interop.Generator/Rewriters/InteropMethodRewriter.ReturnValue.cs +++ b/src/WinRT.Interop.Generator/Rewriters/InteropMethodRewriter.ReturnValue.cs @@ -5,6 +5,7 @@ using AsmResolver.DotNet.Code.Cil; using AsmResolver.DotNet.Signatures; using AsmResolver.PE.DotNet.Cil; +using WindowsRuntime.Generator; using WindowsRuntime.InteropGenerator.Errors; using WindowsRuntime.InteropGenerator.Generation; using WindowsRuntime.InteropGenerator.References; @@ -221,4 +222,4 @@ private static void RewriteBody( }); } } -} \ No newline at end of file +} diff --git a/src/WinRT.Projection.Generator/Generation/ProjectionGenerator.Generate.cs b/src/WinRT.Projection.Generator/Generation/ProjectionGenerator.Generate.cs index b64717ab0e..469a0a3333 100644 --- a/src/WinRT.Projection.Generator/Generation/ProjectionGenerator.Generate.cs +++ b/src/WinRT.Projection.Generator/Generation/ProjectionGenerator.Generate.cs @@ -109,6 +109,11 @@ private static void BuildWriterOptions( List excludes = []; List winmdInputs = []; + // Paths to the managed implementation assemblies of the component(s) being projected, scanned + // by the projection writer for implementation details (e.g. the static fields backing XAML + // dependency properties) that are not present in the .winmd metadata + List componentImplementationAssemblies = []; + // Filter out .winmd files from the resolver paths string[] resolverPaths = [.. args.ReferenceAssemblyPaths .Where(p => !p.EndsWith(".winmd", StringComparison.OrdinalIgnoreCase))]; @@ -137,9 +142,15 @@ private static void BuildWriterOptions( ModuleDefinition refModule = ModuleDefinition.FromFile(refPath, resolver.ReaderParameters); - if (IsComponentAssembly(refModule) && refModule.Assembly?.Name is Utf8String name) + if (IsComponentAssembly(refModule)) { - _ = componentAssemblyNameSet.Add(name.Value); + // Keep the path so the projection writer can inspect the managed implementation + componentImplementationAssemblies.Add(refPath); + + if (refModule.Assembly?.Name is Utf8String name) + { + _ = componentAssemblyNameSet.Add(name.Value); + } } } @@ -272,6 +283,7 @@ private static void BuildWriterOptions( Include = includes, Exclude = excludes, Component = componentMode, + ComponentImplementationAssemblyPaths = componentImplementationAssemblies, MaxDegreesOfParallelism = args.MaxDegreesOfParallelism, CancellationToken = args.Token, }; diff --git a/src/WinRT.Projection.Writer/Factories/ComponentFactory.cs b/src/WinRT.Projection.Writer/Factories/ComponentFactory.cs index 4c559d9c35..c2e29a11ba 100644 --- a/src/WinRT.Projection.Writer/Factories/ComponentFactory.cs +++ b/src/WinRT.Projection.Writer/Factories/ComponentFactory.cs @@ -50,12 +50,20 @@ public static void AddMetadataTypeEntry(ProjectionEmitContext context, TypeDefin /// /// Writes the per-runtime-class server-activation-factory type for component mode. /// + /// The writer to emit the factory class to. + /// The active projection emit context. + /// The activatable runtime class to emit a factory for. public static void WriteFactoryClass(IndentedTextWriter writer, ProjectionEmitContext context, TypeDefinition type) { (string typeNs, string typeName) = type.Names(); string projectedTypeName = TypedefNameWriter.BuildGlobalQualifiedName(typeNs, typeName); string factoryTypeName = $"{IdentifierEscaping.StripBackticks(typeName)}ServerActivationFactory"; + // The static constructor that forces the authored type's class constructor to run before + // activation is only needed when the type registers dependency properties, so consult the + // component's managed implementation assemblies (the .winmd doesn't carry those fields). + bool emitStaticConstructor = context.StaticConstructorAnalyzer.RequiresStaticConstructor(type.FullName); + // Writes the set of interfaces implemented by the factory class ('IActivationFactory' is always included) void WriteBaseInterfaceList(IndentedTextWriter writer) { @@ -90,6 +98,27 @@ void WriteActivateInstanceBody(IndentedTextWriter writer) } } + // Writes the static constructor that forces the projected type's class constructor to run + // before activation, so any dependency properties it registers (as static fields) are set + // up in time. Types that don't register any don't need it, so the callback emits nothing + // for them and the factory omits the constructor entirely, keeping the factory body a + // single interpolated template in either case + void WriteStaticConstructor(IndentedTextWriter writer) + { + if (!emitStaticConstructor) + { + return; + } + + writer.WriteLine(); + writer.WriteLine(isMultiline: true, $$""" + static {{factoryTypeName}}() + { + global::System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof({{projectedTypeName}}).TypeHandle); + } + """); + } + // Helper wrapper to write additional methods void WriteAdditionalActivationFactoryMethods(IndentedTextWriter writer) { @@ -101,12 +130,8 @@ void WriteAdditionalActivationFactoryMethods(IndentedTextWriter writer) internal sealed class {{factoryTypeName}} : {{WriteBaseInterfaceList}} { private static readonly {{factoryTypeName}} _factory = new(); + {{WriteStaticConstructor}} - static {{factoryTypeName}}() - { - global::System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof({{projectedTypeName}}).TypeHandle); - } - public static unsafe void* Make() { return global::WindowsRuntime.InteropServices.Marshalling.WindowsRuntimeInterfaceMarshaller diff --git a/src/WinRT.Projection.Writer/Generation/ProjectionEmitContext.cs b/src/WinRT.Projection.Writer/Generation/ProjectionEmitContext.cs index 1be2c4aa9e..405cc1ce0b 100644 --- a/src/WinRT.Projection.Writer/Generation/ProjectionEmitContext.cs +++ b/src/WinRT.Projection.Writer/Generation/ProjectionEmitContext.cs @@ -14,7 +14,12 @@ namespace WindowsRuntime.ProjectionWriter.Generation; /// The active projection settings. /// The metadata cache for the current generation. /// The namespace currently being emitted (or when not in a per-namespace pass). -internal sealed class ProjectionEmitContext(Settings settings, MetadataCache cache, string currentNamespace) +/// The analyzer over the component's managed implementation assemblies (component mode). +internal sealed class ProjectionEmitContext( + Settings settings, + MetadataCache cache, + string currentNamespace, + ComponentStaticConstructorAnalyzer staticConstructorAnalyzer) { /// /// Gets the active projection settings. @@ -53,6 +58,14 @@ internal sealed class ProjectionEmitContext(Settings settings, MetadataCache cac /// public AbiTypeKindResolver AbiTypeKindResolver { get; } = new AbiTypeKindResolver(cache); + /// + /// Gets the analyzer over the component's managed implementation assemblies (component mode). + /// It exposes implementation details that are absent from the .winmd metadata (e.g. the + /// static fields backing XAML dependency properties), computed on demand as types are + /// emitted. The same instance is shared by every emit context, so its memoization is reused. + /// + public ComponentStaticConstructorAnalyzer StaticConstructorAnalyzer { get; } = staticConstructorAnalyzer; + /// /// Gets a value indicating whether platform-attribute computation should suppress platforms /// that are less than or equal to . Used to apply class-scope platform diff --git a/src/WinRT.Projection.Writer/Generation/ProjectionGenerator.Component.cs b/src/WinRT.Projection.Writer/Generation/ProjectionGenerator.Component.cs index 68b1260e15..39e0d8f939 100644 --- a/src/WinRT.Projection.Writer/Generation/ProjectionGenerator.Component.cs +++ b/src/WinRT.Projection.Writer/Generation/ProjectionGenerator.Component.cs @@ -4,6 +4,8 @@ using System.Collections.Generic; using System.IO; using AsmResolver.DotNet; +using AsmResolver.DotNet.Signatures; +using WindowsRuntime.Generator; using WindowsRuntime.ProjectionWriter.Factories; using WindowsRuntime.ProjectionWriter.Metadata; using WindowsRuntime.ProjectionWriter.Writers; @@ -28,7 +30,7 @@ internal sealed partial class ProjectionGenerator /// private (HashSet ComponentActivatable, Dictionary> ByModule) DiscoverComponentActivatableTypes() { - HashSet componentActivatable = []; + HashSet componentActivatable = new(SignatureComparer.IgnoreVersion); Dictionary> componentByModule = []; if (!_settings.Component) @@ -53,7 +55,7 @@ internal sealed partial class ProjectionGenerator if (!componentByModule.TryGetValue(moduleName, out HashSet? set)) { - set = []; + set = new(SignatureComparer.IgnoreVersion); componentByModule[moduleName] = set; } diff --git a/src/WinRT.Projection.Writer/Generation/ProjectionGenerator.GeneratedIids.cs b/src/WinRT.Projection.Writer/Generation/ProjectionGenerator.GeneratedIids.cs index 62fd1b7349..1af69abb26 100644 --- a/src/WinRT.Projection.Writer/Generation/ProjectionGenerator.GeneratedIids.cs +++ b/src/WinRT.Projection.Writer/Generation/ProjectionGenerator.GeneratedIids.cs @@ -62,7 +62,7 @@ internal void WriteGeneratedInterfaceIidsFile() bool iidWritten = false; HashSet interfacesFromClassesEmitted = []; - ProjectionEmitContext guidContext = new(_settings, _cache, "ABI"); + ProjectionEmitContext guidContext = new(_settings, _cache, "ABI", _staticConstructorAnalyzer); using IndentedTextWriterOwner guidIndentedOwner = IndentedTextWriterPool.GetOrCreate(); IndentedTextWriter guidIndented = guidIndentedOwner.Writer; IidExpressionGenerator.WriteInterfaceIidsBegin(guidIndented); diff --git a/src/WinRT.Projection.Writer/Generation/ProjectionGenerator.Namespace.cs b/src/WinRT.Projection.Writer/Generation/ProjectionGenerator.Namespace.cs index 5275e36818..70d19b5363 100644 --- a/src/WinRT.Projection.Writer/Generation/ProjectionGenerator.Namespace.cs +++ b/src/WinRT.Projection.Writer/Generation/ProjectionGenerator.Namespace.cs @@ -27,7 +27,7 @@ internal bool ProcessNamespace(string ns, NamespaceMembers members, ProjectionGe ConcurrentBag> exclusiveToInterfaceEntries = state.ExclusiveToInterfaceEntries; ConcurrentDictionary authoredTypeNameToMetadataMap = state.AuthoredTypeNameToMetadataMap; HashSet componentActivatable = state.ComponentActivatable; - ProjectionEmitContext context = new(_settings, _cache, ns); + ProjectionEmitContext context = new(_settings, _cache, ns, _staticConstructorAnalyzer); using IndentedTextWriterOwner writerOwner = IndentedTextWriterPool.GetOrCreate(); IndentedTextWriter writer = writerOwner.Writer; diff --git a/src/WinRT.Projection.Writer/Generation/ProjectionGenerator.cs b/src/WinRT.Projection.Writer/Generation/ProjectionGenerator.cs index 3b07176cce..5f5d8a4383 100644 --- a/src/WinRT.Projection.Writer/Generation/ProjectionGenerator.cs +++ b/src/WinRT.Projection.Writer/Generation/ProjectionGenerator.cs @@ -48,6 +48,15 @@ internal sealed partial class ProjectionGenerator(Settings settings, MetadataCac private readonly MetadataCache _cache = cache; private readonly CancellationToken _token = token; + /// + /// Analyzes the component's managed implementation assemblies to decide which activation + /// factories must force the authored type's class constructor to run. Created once and shared + /// by every emit context, so its memoization cache is reused across namespaces. Empty (and + /// unused) outside component mode, where no implementation assemblies are provided. + /// + private readonly ComponentStaticConstructorAnalyzer _staticConstructorAnalyzer = + new(ComponentImplementationMetadata.Load(settings.ComponentImplementationAssemblies)); + /// /// Runs the projection-generation pipeline end-to-end. /// diff --git a/src/WinRT.Projection.Writer/Generation/Settings.cs b/src/WinRT.Projection.Writer/Generation/Settings.cs index 47490fd359..f5c2dc29c8 100644 --- a/src/WinRT.Projection.Writer/Generation/Settings.cs +++ b/src/WinRT.Projection.Writer/Generation/Settings.cs @@ -102,6 +102,14 @@ public TypeFilter AdditionFilter /// public bool Component { get; init; } + /// + /// Gets the paths to the managed implementation assemblies of the authored Windows Runtime + /// component(s) being projected. Used in component mode to inspect implementation details that + /// are absent from the input .winmd metadata (e.g. the static fields backing XAML + /// dependency properties). May be empty when those assemblies are not available. + /// + public HashSet ComponentImplementationAssemblies { get; } = []; + /// /// Gets or sets a value indicating whether [ExclusiveTo] interfaces are emitted as public rather than internal. /// diff --git a/src/WinRT.Projection.Writer/Metadata/ComponentImplementationMetadata.cs b/src/WinRT.Projection.Writer/Metadata/ComponentImplementationMetadata.cs new file mode 100644 index 0000000000..e4c049c718 --- /dev/null +++ b/src/WinRT.Projection.Writer/Metadata/ComponentImplementationMetadata.cs @@ -0,0 +1,75 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Collections.Generic; +using AsmResolver.DotNet; + +namespace WindowsRuntime.ProjectionWriter.Metadata; + +/// +/// Resolves authored Windows Runtime types from the managed implementation assemblies (.dll) +/// of the component(s) being projected, by full name. The managed assemblies carry implementation +/// details (such as the static fields backing XAML dependency properties) that are not +/// present in the .winmd metadata, so consumers resolve the managed type here and inspect it +/// directly. +/// +/// +/// Loading only builds a name index over the assemblies' types. No per-type analysis is performed +/// up front: callers resolve just the types they care about (see +/// ), keeping the cost proportional to what is used. +/// +internal sealed class ComponentImplementationMetadata +{ + /// The authored types indexed by full name (ordinal, the default string equality). + private readonly Dictionary _typesByFullName; + + private ComponentImplementationMetadata(Dictionary typesByFullName) + { + _typesByFullName = typesByFullName; + } + + /// + /// Loads the managed implementation assemblies and indexes their types by full name. When the + /// input is empty, the returned instance resolves nothing and always + /// returns . + /// + /// The managed implementation assembly paths to load. + /// The loaded metadata. + public static ComponentImplementationMetadata Load(IEnumerable assemblyPaths) + { + Dictionary typesByFullName = []; + + foreach (string assemblyPath in assemblyPaths) + { + ModuleDefinition module = ModuleDefinition.FromFile(assemblyPath); + + foreach (TypeDefinition type in module.GetAllTypes()) + { + string fullName = type.FullName; + + // Skip the '' pseudo-type and only index the first definition for a given + // full name (multiple component assemblies should never define the same type) + if (string.IsNullOrEmpty(fullName) || type.Name?.Value is "") + { + continue; + } + + _ = typesByFullName.TryAdd(fullName, type); + } + } + + return new ComponentImplementationMetadata(typesByFullName); + } + + /// + /// Resolves the authored type with the given full name from the loaded implementation + /// assemblies, or if no such type is present (e.g. a framework type, or + /// when no implementation assemblies were loaded). + /// + /// The full name of the type to resolve. + /// The resolved , or . + public TypeDefinition? Resolve(string fullName) + { + return _typesByFullName.GetValueOrDefault(fullName); + } +} diff --git a/src/WinRT.Projection.Writer/Metadata/ComponentStaticConstructorAnalyzer.cs b/src/WinRT.Projection.Writer/Metadata/ComponentStaticConstructorAnalyzer.cs new file mode 100644 index 0000000000..851092253c --- /dev/null +++ b/src/WinRT.Projection.Writer/Metadata/ComponentStaticConstructorAnalyzer.cs @@ -0,0 +1,127 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Collections.Concurrent; +using AsmResolver.DotNet; +using AsmResolver.DotNet.Signatures; +using WindowsRuntime.Generator; +using WindowsRuntime.ProjectionWriter.References; + +namespace WindowsRuntime.ProjectionWriter.Metadata; + +/// +/// Decides whether the activation factory generated for an authored Windows Runtime class must +/// force the class constructor to run before activation, i.e. whether the class (or an authored +/// base class) registers any XAML dependency properties. +/// +/// +/// Dependency properties are registered in static fields of type DependencyProperty, +/// so they only exist in the managed implementation assemblies, not in the .winmd metadata. +/// The analyzer resolves each queried type lazily via +/// and walks its base chain on demand, memoizing the result per visited type so that shared +/// hierarchies (e.g. several controls deriving from a common authored base) are traversed once. A +/// single instance is shared by all per-namespace emit contexts and queried from the parallel +/// emission work items, so the memoization cache is concurrent. +/// +internal sealed class ComponentStaticConstructorAnalyzer +{ + /// The resolver used to look up authored types (and their base types) by full name. + private readonly ComponentImplementationMetadata _metadata; + + /// + /// Memoizes, per visited type, whether it requires the class constructor to run. Concurrent + /// because the analyzer is queried from the parallel emission work items. Keyed with the + /// version-agnostic , the standard comparer for AsmResolver entities. + /// + private readonly ConcurrentDictionary _cache = new(SignatureComparer.IgnoreVersion); + + /// + /// Creates a new over the given metadata. + /// + /// The resolver for the component's managed implementation types. + public ComponentStaticConstructorAnalyzer(ComponentImplementationMetadata metadata) + { + _metadata = metadata; + } + + /// + /// Determines whether the activation factory for the authored type identified by + /// must force the type's class constructor to run before + /// activation. + /// + /// The full name of the authored type. + /// if the static constructor is required; otherwise, . + /// + /// If the type cannot be resolved from the scanned implementation assemblies, this returns + /// : we cannot prove the constructor is unnecessary, so it is kept (this + /// also covers the case where no implementation assemblies are available). + /// + public bool RequiresStaticConstructor(string typeFullName) + { + TypeDefinition? type = _metadata.Resolve(typeFullName); + + return type is null || RequiresStaticConstructor(type); + } + + /// + /// Memoized core: whether (or an authored base type) registers a + /// dependency property. + /// + /// The resolved authored type to inspect. + /// if the static constructor is required; otherwise, . + private bool RequiresStaticConstructor(TypeDefinition type) + { + if (_cache.TryGetValue(type, out bool cached)) + { + return cached; + } + + // The type itself registers a dependency property, or an authored base type does. The base + // walk stops at the first type outside the scanned assemblies (e.g. a framework base type), + // because framework dependency properties are registered by the framework itself. Inheritance + // can't cycle, so a plain recursion (memoized below) always terminates. + bool result = + DeclaresDependencyProperty(type) || + (type.BaseType is { } baseType && + _metadata.Resolve(baseType.FullName) is { } baseDefinition && + RequiresStaticConstructor(baseDefinition)); + + _cache[type] = result; + + return result; + } + + /// + /// Returns whether declares any static field whose type is the + /// XAML DependencyProperty (in either Microsoft.UI.Xaml or Windows.UI.Xaml). + /// + /// The type to inspect. + /// if the type declares such a field; otherwise, . + private static bool DeclaresDependencyProperty(TypeDefinition type) + { + foreach (FieldDefinition field in type.Fields) + { + if (!field.IsStatic) + { + continue; + } + + TypeSignature? fieldType = field.Signature?.FieldType; + + if (fieldType is null) + { + continue; + } + + (string ns, string name) = fieldType.Names(); + + if (name == WellKnownTypeNames.DependencyProperty && + (ns == WellKnownNamespaces.MicrosoftUIXaml || ns == WellKnownNamespaces.WindowsUIXaml)) + { + return true; + } + } + + return false; + } +} diff --git a/src/WinRT.Projection.Writer/ProjectionWriter.cs b/src/WinRT.Projection.Writer/ProjectionWriter.cs index 943dafdee0..e07785cd8b 100644 --- a/src/WinRT.Projection.Writer/ProjectionWriter.cs +++ b/src/WinRT.Projection.Writer/ProjectionWriter.cs @@ -57,6 +57,7 @@ public static void Run(ProjectionWriterOptions options) settings.Include.UnionWith(options.Include); settings.Exclude.UnionWith(options.Exclude); settings.AdditionExclude.UnionWith(options.AdditionExclude); + settings.ComponentImplementationAssemblies.UnionWith(options.ComponentImplementationAssemblyPaths); settings.MakeReadOnly(); diff --git a/src/WinRT.Projection.Writer/ProjectionWriterOptions.cs b/src/WinRT.Projection.Writer/ProjectionWriterOptions.cs index 6c9bd4e772..03a56008c0 100644 --- a/src/WinRT.Projection.Writer/ProjectionWriterOptions.cs +++ b/src/WinRT.Projection.Writer/ProjectionWriterOptions.cs @@ -46,6 +46,19 @@ public sealed class ProjectionWriterOptions /// public bool Component { get; init; } + /// + /// Optional paths to the managed implementation assemblies (.dll) of the authored + /// Windows Runtime component(s) being projected. Only meaningful in mode. + /// + /// + /// The input .winmd metadata does not carry implementation details such as the + /// static fields backing XAML dependency properties, so the writer consults these + /// managed assemblies to decide whether each generated activation factory needs to force the + /// authored type's class constructor to run before activation. When the list is empty (e.g. the + /// managed assembly is not available yet), the writer conservatively keeps that constructor. + /// + public IReadOnlyList ComponentImplementationAssemblyPaths { get; init; } = []; + /// /// Make exclusive-to interfaces public in the projection (default is internal). /// diff --git a/src/WinRT.Projection.Writer/References/WellKnownNamespaces.cs b/src/WinRT.Projection.Writer/References/WellKnownNamespaces.cs index 333546ce6f..7c8e5fca9a 100644 --- a/src/WinRT.Projection.Writer/References/WellKnownNamespaces.cs +++ b/src/WinRT.Projection.Writer/References/WellKnownNamespaces.cs @@ -37,4 +37,14 @@ internal static class WellKnownNamespaces /// The Windows.UI.Xaml.Interop namespace. /// public const string WindowsUIXamlInterop = "Windows.UI.Xaml.Interop"; + + /// + /// The Windows.UI.Xaml namespace (UWP XAML, where DependencyProperty lives in that mode). + /// + public const string WindowsUIXaml = "Windows.UI.Xaml"; + + /// + /// The Microsoft.UI.Xaml namespace (WinUI, where DependencyProperty lives in that mode). + /// + public const string MicrosoftUIXaml = "Microsoft.UI.Xaml"; } diff --git a/src/WinRT.Projection.Writer/References/WellKnownTypeNames.cs b/src/WinRT.Projection.Writer/References/WellKnownTypeNames.cs index 5e1435571c..12a84dae7d 100644 --- a/src/WinRT.Projection.Writer/References/WellKnownTypeNames.cs +++ b/src/WinRT.Projection.Writer/References/WellKnownTypeNames.cs @@ -52,4 +52,11 @@ internal static class WellKnownTypeNames /// The Windows SDK XAML TypeName struct (the WinMD source for ). /// public const string TypeName = "TypeName"; + + /// + /// The XAML DependencyProperty type (in either Microsoft.UI.Xaml for WinUI or + /// Windows.UI.Xaml for UWP XAML). Authored types register dependency properties as + /// static fields of this type. + /// + public const string DependencyProperty = "DependencyProperty"; } diff --git a/src/WinRT.Projection.Writer/WinRT.Projection.Writer.csproj b/src/WinRT.Projection.Writer/WinRT.Projection.Writer.csproj index 3c100fcc61..34eb111fcb 100644 --- a/src/WinRT.Projection.Writer/WinRT.Projection.Writer.csproj +++ b/src/WinRT.Projection.Writer/WinRT.Projection.Writer.csproj @@ -30,7 +30,7 @@ + - $(NoWarn);IDE0010;IDE0022;IDE0046;IDE0060;IDE0072 + $(NoWarn);IDE0010;IDE0022;IDE0028;IDE0046;IDE0060;IDE0072 + + + +