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
+
+
+
+
+