From bdcd682679b21e0239a60933e0578dd5e3dd89ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Tue, 21 May 2024 13:56:43 +0200 Subject: [PATCH] Add support for exported types in illink XML formats Addresses the TODO in the native AOT port. Ran into inability of rooting things from mscorlib with the illink XML format on native AOT so decided to fix the TODO. (It was possible to work around with `Type.GetType` (that's a supported pattern) or rd.xml, but this should work with the descriptors too.) --- .../tools/Common/Compiler/ProcessLinkerXmlBase.cs | 9 ++++++--- .../Compiler/BodySubstitutionParser.cs | 2 +- .../Compiler/DescriptorMarker.cs | 8 ++++++++ .../TrimmingBehaviors/ILLinkDescriptor.cs | 13 +++++++++++++ .../SmokeTests/TrimmingBehaviors/Library.cs | 4 ++++ .../SmokeTests/TrimmingBehaviors/Library.csproj | 8 ++++++++ .../NonEmbedded.ILLink.Descriptor.xml | 4 ++++ .../SmokeTests/TrimmingBehaviors/ShimLibrary.cs | 6 ++++++ .../SmokeTests/TrimmingBehaviors/ShimLibrary.csproj | 11 +++++++++++ .../TrimmingBehaviors/TrimmingBehaviors.csproj | 5 +++++ 10 files changed, 66 insertions(+), 4 deletions(-) create mode 100644 src/tests/nativeaot/SmokeTests/TrimmingBehaviors/Library.cs create mode 100644 src/tests/nativeaot/SmokeTests/TrimmingBehaviors/Library.csproj create mode 100644 src/tests/nativeaot/SmokeTests/TrimmingBehaviors/ShimLibrary.cs create mode 100644 src/tests/nativeaot/SmokeTests/TrimmingBehaviors/ShimLibrary.csproj diff --git a/src/coreclr/tools/Common/Compiler/ProcessLinkerXmlBase.cs b/src/coreclr/tools/Common/Compiler/ProcessLinkerXmlBase.cs index 9bcc484b178a32..07b78f196d2e34 100644 --- a/src/coreclr/tools/Common/Compiler/ProcessLinkerXmlBase.cs +++ b/src/coreclr/tools/Common/Compiler/ProcessLinkerXmlBase.cs @@ -217,9 +217,10 @@ protected virtual void ProcessTypes(ModuleDesc assembly, XPathNavigator nav, boo continue; } - // TODO: Process exported types + MetadataType? type = CecilCompatibleTypeParser.GetType(assembly, fullname); - TypeDesc? type = CecilCompatibleTypeParser.GetType(assembly, fullname); + if (type != null && type.Module != assembly) + type = ProcessExportedType(type, assembly, typeNav); if (type == null) { @@ -234,6 +235,8 @@ protected virtual void ProcessTypes(ModuleDesc assembly, XPathNavigator nav, boo } } + protected virtual MetadataType? ProcessExportedType(MetadataType exported, ModuleDesc assembly, XPathNavigator nav) => exported; + private void MatchType(TypeDesc type, Regex regex, XPathNavigator nav) { StringBuilder sb = new StringBuilder(); @@ -759,7 +762,7 @@ public bool TryConvertValue(string value, TypeDesc type, out object? result) public class CecilCompatibleTypeParser { - public static TypeDesc? GetType(ModuleDesc assembly, string fullName) + public static MetadataType? GetType(ModuleDesc assembly, string fullName) { Debug.Assert(!string.IsNullOrEmpty(fullName)); var position = fullName.IndexOf('/'); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/BodySubstitutionParser.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/BodySubstitutionParser.cs index 979ade817b3d27..0cf8ec1638793a 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/BodySubstitutionParser.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/BodySubstitutionParser.cs @@ -40,7 +40,7 @@ protected override void ProcessAssembly(ModuleDesc assembly, XPathNavigator nav, ProcessTypes(assembly, nav, warnOnUnresolvedTypes); } - // protected override TypeDesc? ProcessExportedType(ExportedType exported, ModuleDesc assembly, XPathNavigator nav) => null; + protected override MetadataType ProcessExportedType(MetadataType exported, ModuleDesc assembly, XPathNavigator nav) => null; protected override bool ProcessTypePattern(string fullname, ModuleDesc assembly, XPathNavigator nav) => false; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DescriptorMarker.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DescriptorMarker.cs index e86c20c31a7837..32514dccc5eee6 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DescriptorMarker.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DescriptorMarker.cs @@ -162,6 +162,14 @@ private void MarkAndPreserve(TypeDesc type, XPathNavigator nav, TypePreserve pre } #endif + protected override MetadataType? ProcessExportedType(MetadataType exported, ModuleDesc assembly, XPathNavigator nav) + { + // Rooting module metadata roots type forwarder metadata for all types in the module that are reflection visible. + // (We don't track individual type forwarders right now.) + _dependencies.Add(_factory.ModuleMetadata(assembly), "Type used through forwarder"); + return base.ProcessExportedType(exported, assembly, nav); + } + protected override void ProcessType(TypeDesc type, XPathNavigator nav) { Debug.Assert(ShouldProcessElement(nav)); diff --git a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/ILLinkDescriptor.cs b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/ILLinkDescriptor.cs index 105ff1379df446..4e4404a5b89e90 100644 --- a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/ILLinkDescriptor.cs +++ b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/ILLinkDescriptor.cs @@ -17,6 +17,7 @@ public static int Run() ThrowIfMemberNotPresent(typeof(ILLinkDescriptor), nameof(PropertyKeptViaDescriptor)); ThrowIfMemberNotPresent(typeof(ILLinkDescriptor), nameof(EventKeptViaDescriptor)); ThrowIfTypeNotPresent(typeof(ILLinkDescriptor), nameof(NestedTypeKeptViaDescriptor)); + ThrowIfTypeNotPresent("LibraryClass, ShimLibrary"); ThrowIfTypePresent(typeof(ILLinkDescriptor), nameof(NestedTypeNonKept)); return 100; } @@ -51,6 +52,10 @@ class NestedTypeNonKept Justification = "That's the point")] private static bool IsTypePresent(Type testType, string typeName) => testType.GetNestedType(typeName, BindingFlags.NonPublic | BindingFlags.Public) != null; + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2057:UnrecognizedReflectionPattern", + Justification = "That's the point")] + private static bool IsTypePresent(string typeName) => Type.GetType(typeName) != null; + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern", Justification = "That's the point")] private static bool IsMemberPresent(Type testType, string memberName) { @@ -70,6 +75,14 @@ private static void ThrowIfTypeNotPresent(Type testType, string typeName) } } + private static void ThrowIfTypeNotPresent(string typeName) + { + if (!IsTypePresent(typeName)) + { + throw new Exception(typeName); + } + } + private static void ThrowIfTypePresent(Type testType, string typeName) { if (IsTypePresent(testType, typeName)) diff --git a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/Library.cs b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/Library.cs new file mode 100644 index 00000000000000..d7a9483709bc94 --- /dev/null +++ b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/Library.cs @@ -0,0 +1,4 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +public class LibraryClass { } diff --git a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/Library.csproj b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/Library.csproj new file mode 100644 index 00000000000000..71634a59882430 --- /dev/null +++ b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/Library.csproj @@ -0,0 +1,8 @@ + + + Library + + + + + diff --git a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/NonEmbedded.ILLink.Descriptor.xml b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/NonEmbedded.ILLink.Descriptor.xml index ef70498ef49735..c33d576e31c293 100644 --- a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/NonEmbedded.ILLink.Descriptor.xml +++ b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/NonEmbedded.ILLink.Descriptor.xml @@ -4,4 +4,8 @@ + + + + diff --git a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/ShimLibrary.cs b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/ShimLibrary.cs new file mode 100644 index 00000000000000..dbac9ac09317f3 --- /dev/null +++ b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/ShimLibrary.cs @@ -0,0 +1,6 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; + +[assembly: TypeForwardedTo(typeof(LibraryClass))] diff --git a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/ShimLibrary.csproj b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/ShimLibrary.csproj new file mode 100644 index 00000000000000..cc00bd360d38ef --- /dev/null +++ b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/ShimLibrary.csproj @@ -0,0 +1,11 @@ + + + Library + + + + + + + + diff --git a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/TrimmingBehaviors.csproj b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/TrimmingBehaviors.csproj index fa0c9d0e4d7b2b..c69f6e042a57b8 100644 --- a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/TrimmingBehaviors.csproj +++ b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/TrimmingBehaviors.csproj @@ -8,6 +8,11 @@ true + + + + +