diff --git a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets
index 5bc7d308a0c37c..7b2e26f8cc9961 100644
--- a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets
+++ b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets
@@ -277,6 +277,7 @@ The .NET Foundation licenses this file to you under the MIT license.
+
diff --git a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/ILCompilerOptions.cs b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/ILCompilerOptions.cs
index a8cc056f9aa4d9..681fd981a3f4d8 100644
--- a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/ILCompilerOptions.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/ILCompilerOptions.cs
@@ -22,5 +22,6 @@ public class ILCompilerOptions
public Dictionary WarningsAsErrors = new Dictionary();
public List SuppressedWarningCategories = new List();
public bool DisableGeneratedCodeHeuristics;
+ public string? TypeMapEntryAssembly;
}
}
diff --git a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingArgumentBuilder.cs b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingArgumentBuilder.cs
index 604795426163de..afdac96827e93a 100644
--- a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingArgumentBuilder.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingArgumentBuilder.cs
@@ -163,6 +163,10 @@ public virtual void AddAdditionalArgument(string flag, string[] values)
{
Options.FeatureSwitches.Add(values[0], bool.Parse(values[1]));
}
+ else if (flag == "--typemap-entry-assembly")
+ {
+ Options.TypeMapEntryAssembly = values[0];
+ }
else if (flag == "--singlewarn")
{
Options.SingleWarn = true;
diff --git a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingDriver.cs b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingDriver.cs
index e61725ac3ddaef..68b25cb1428b51 100644
--- a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingDriver.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingDriver.cs
@@ -147,8 +147,14 @@ public ILScanResults Trim(ILCompilerOptions options, TrimmingCustomizations? cus
InteropStubManager interopStubManager = new UsageBasedInteropStubManager(interopStateManager, pinvokePolicy, logger);
TypeMapManager typeMapManager = new UsageBasedTypeMapManager(TypeMapMetadata.Empty);
- if (entrypointModule is { Assembly: EcmaAssembly entryAssembly })
+ if (options.TypeMapEntryAssembly is not null
+ && typeSystemContext.ResolveAssembly(AssemblyNameInfo.Parse(options.TypeMapEntryAssembly), throwIfNotFound: true) is EcmaAssembly typeMapEntryAssembly)
{
+ typeMapManager = new UsageBasedTypeMapManager(TypeMapMetadata.CreateFromAssembly(typeMapEntryAssembly, typeSystemContext));
+ }
+ else if (entrypointModule is { Assembly: EcmaAssembly entryAssembly })
+ {
+ // Pass null for typeMappingEntryAssembly to use default entry assembly behavior in tests
typeMapManager = new UsageBasedTypeMapManager(TypeMapMetadata.CreateFromAssembly(entryAssembly, typeSystemContext));
}
diff --git a/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs b/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs
index 44794ca8be295f..4b63458781ea18 100644
--- a/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs
+++ b/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs
@@ -182,6 +182,8 @@ internal sealed class ILCompilerRootCommand : RootCommand
new("--generateunmanagedentrypoints") { DefaultValueFactory = _ => Array.Empty(), Description = "Generate unmanaged entrypoints for a given assembly" };
public Option DisableGeneratedCodeHeuristics { get; } =
new("--disable-generated-code-heuristics") { Description = "Disable heuristics for detecting compiler-generated code" };
+ public Option TypeMapEntryAssembly { get; } =
+ new("--typemap-entry-assembly") { Description = "Assembly name to use as entry point for TypeMap generation" };
public OptimizationMode OptimizationMode { get; private set; }
public ParseResult Result;
@@ -273,6 +275,7 @@ public ILCompilerRootCommand(string[] args) : base(".NET Native IL Compiler")
Options.Add(MakeReproPath);
Options.Add(UnmanagedEntryPointsAssemblies);
Options.Add(DisableGeneratedCodeHeuristics);
+ Options.Add(TypeMapEntryAssembly);
this.SetAction(result =>
{
diff --git a/src/coreclr/tools/aot/ILCompiler/Program.cs b/src/coreclr/tools/aot/ILCompiler/Program.cs
index 2054c723bc8a07..2c5b2c12266936 100644
--- a/src/coreclr/tools/aot/ILCompiler/Program.cs
+++ b/src/coreclr/tools/aot/ILCompiler/Program.cs
@@ -314,8 +314,16 @@ public int Run()
compilationRoots.Add(new ILCompiler.DependencyAnalysis.TrimmingDescriptorNode(linkTrimFilePath));
}
- if (entrypointModule is { Assembly: EcmaAssembly entryAssembly })
+ // Get TypeMappingEntryAssembly from command-line option if specified
+ string typeMappingEntryAssembly = Get(_command.TypeMapEntryAssembly);
+ if (typeMappingEntryAssembly is not null)
{
+ var typeMapEntryAssembly = (EcmaAssembly)typeSystemContext.ResolveAssembly(AssemblyNameInfo.Parse(typeMappingEntryAssembly), throwIfNotFound: true);
+ typeMapManager = new UsageBasedTypeMapManager(TypeMapMetadata.CreateFromAssembly(typeMapEntryAssembly, typeSystemContext));
+ }
+ else if (entrypointModule is { Assembly: EcmaAssembly entryAssembly })
+ {
+ // Fall back to entryassembly if not specified
typeMapManager = new UsageBasedTypeMapManager(TypeMapMetadata.CreateFromAssembly(entryAssembly, typeSystemContext));
}
}
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.cs
index a1fc75f100bdba..a644947b73463e 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.cs
@@ -189,7 +189,16 @@ private static unsafe CallbackContext CreateMaps(
delegate* unmanaged newExternalTypeEntry,
delegate* unmanaged newProxyTypeEntry)
{
- RuntimeAssembly? startingAssembly = (RuntimeAssembly?)Assembly.GetEntryAssembly();
+ RuntimeAssembly? startingAssembly;
+ if (AppContext.GetData("System.Runtime.InteropServices.TypeMappingEntryAssembly") is string entryAssemblyName)
+ {
+ startingAssembly = Assembly.Load(entryAssemblyName) as RuntimeAssembly;
+ }
+ else
+ {
+ startingAssembly = Assembly.GetEntryAssembly() as RuntimeAssembly;
+ }
+
if (startingAssembly is null)
{
throw new InvalidOperationException(SR.InvalidOperation_TypeMapMissingEntryAssembly);
diff --git a/src/tests/Interop/TypeMap/Lib5.cs b/src/tests/Interop/TypeMap/Lib5.cs
new file mode 100644
index 00000000000000..a21c629910771c
--- /dev/null
+++ b/src/tests/Interop/TypeMap/Lib5.cs
@@ -0,0 +1,20 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Runtime.InteropServices;
+
+// This library defines TypeMap entries that should be used when
+// TypeMapLib5 is specified as the TypeMappingEntryAssembly
+[assembly: TypeMap("lib5_type1", typeof(Lib5Type1))]
+[assembly: TypeMap("lib5_type2", typeof(Lib5Type2))]
+
+[assembly: TypeMapAssociation(typeof(Lib5Type1), typeof(Lib5Proxy1))]
+[assembly: TypeMapAssociation(typeof(Lib5Type2), typeof(Lib5Proxy2))]
+
+public class Lib5Type1 { }
+public class Lib5Type2 { }
+public class Lib5Proxy1 { }
+public class Lib5Proxy2 { }
+
+public class AlternateEntryPoint { }
diff --git a/src/tests/Interop/TypeMap/TypeMapEntryAssemblyApp.cs b/src/tests/Interop/TypeMap/TypeMapEntryAssemblyApp.cs
new file mode 100644
index 00000000000000..241f0c7d23da82
--- /dev/null
+++ b/src/tests/Interop/TypeMap/TypeMapEntryAssemblyApp.cs
@@ -0,0 +1,48 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using Xunit;
+
+// Note: This test does NOT define any TypeMap attributes.
+// The TypeMappingEntryAssembly is set to TypeMapLib5 via RuntimeHostConfigurationOption,
+// so the type maps should be resolved from TypeMapLib5 instead of this assembly.
+
+public class TypeMapEntryAssemblyTest
+{
+ [Fact]
+ public static void Validate_TypeMappingEntryAssembly_ExternalMap()
+ {
+ Console.WriteLine(nameof(Validate_TypeMappingEntryAssembly_ExternalMap));
+
+ // These types should be resolved from TypeMapLib5's type maps
+ IReadOnlyDictionary externalMap = TypeMapping.GetOrCreateExternalTypeMapping();
+
+ Assert.Equal(typeof(Lib5Type1), externalMap["lib5_type1"]);
+ Assert.Equal(typeof(Lib5Type2), externalMap["lib5_type2"]);
+
+ Assert.True(externalMap.TryGetValue("lib5_type1", out Type? type1));
+ Assert.Equal(typeof(Lib5Type1), type1);
+
+ Assert.True(externalMap.TryGetValue("lib5_type2", out Type? type2));
+ Assert.Equal(typeof(Lib5Type2), type2);
+
+ Assert.False(externalMap.TryGetValue("nonexistent", out Type? _));
+ }
+
+ [Fact]
+ public static void Validate_TypeMappingEntryAssembly_ProxyMap()
+ {
+ Console.WriteLine(nameof(Validate_TypeMappingEntryAssembly_ProxyMap));
+
+ // These proxy mappings should be resolved from TypeMapLib5's type maps
+ IReadOnlyDictionary proxyMap = TypeMapping.GetOrCreateProxyTypeMapping();
+
+ Assert.Equal(typeof(Lib5Proxy1), proxyMap[new Lib5Type1().GetType()]);
+ Assert.Equal(typeof(Lib5Proxy2), proxyMap[new Lib5Type2().GetType()]);
+
+ Assert.False(proxyMap.TryGetValue(typeof(string), out Type? _));
+ }
+}
diff --git a/src/tests/Interop/TypeMap/TypeMapEntryAssemblyApp.csproj b/src/tests/Interop/TypeMap/TypeMapEntryAssemblyApp.csproj
new file mode 100644
index 00000000000000..64d31fd2b30754
--- /dev/null
+++ b/src/tests/Interop/TypeMap/TypeMapEntryAssemblyApp.csproj
@@ -0,0 +1,27 @@
+
+
+ Exe
+ true
+ true
+ TypeMapLib5
+ true
+ true
+ true
+ true
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/tests/Interop/TypeMap/TypeMapLib5.csproj b/src/tests/Interop/TypeMap/TypeMapLib5.csproj
new file mode 100644
index 00000000000000..21f2c06d751915
--- /dev/null
+++ b/src/tests/Interop/TypeMap/TypeMapLib5.csproj
@@ -0,0 +1,14 @@
+
+
+ Library
+ true
+
+
+
+
+
+
+
+
+
+
diff --git a/src/tools/illink/src/ILLink.Tasks/build/Microsoft.NET.ILLink.targets b/src/tools/illink/src/ILLink.Tasks/build/Microsoft.NET.ILLink.targets
index 655cb28bb1c529..16198390f88f7b 100644
--- a/src/tools/illink/src/ILLink.Tasks/build/Microsoft.NET.ILLink.targets
+++ b/src/tools/illink/src/ILLink.Tasks/build/Microsoft.NET.ILLink.targets
@@ -80,6 +80,11 @@ Copyright (c) .NET Foundation. All rights reserved.
false
+
+
+ <_ExtraTrimmerArgs>$(_ExtraTrimmerArgs) --typemap-entry-assembly "$(TypeMapEntryAssembly)"
+
+
$(NoWarn);IL2121
diff --git a/src/tools/illink/src/linker/CompatibilitySuppressions.xml b/src/tools/illink/src/linker/CompatibilitySuppressions.xml
index eba23205563f29..92e46039ff27f3 100644
--- a/src/tools/illink/src/linker/CompatibilitySuppressions.xml
+++ b/src/tools/illink/src/linker/CompatibilitySuppressions.xml
@@ -1547,6 +1547,14 @@
ref/net10.0/illink.dll
lib/net10.0/illink.dll
+
+ CP0002
+ M:Mono.Linker.LinkContext.get_TypeMapEntryAssembly
+
+
+ CP0002
+ M:Mono.Linker.LinkContext.set_TypeMapEntryAssembly(System.String)
+
CP0008
T:Mono.Linker.LinkContext
diff --git a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs
index 8c4bcd08ff4e63..65a6f7937bc0f9 100644
--- a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs
+++ b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs
@@ -254,7 +254,18 @@ protected virtual void Initialize()
{
InitializeCorelibAttributeXml();
Context.Pipeline.InitializeMarkHandlers(Context, MarkContext);
- _typeMapHandler.Initialize(Context, this, Annotations.GetEntryPointAssembly());
+
+ // Check for TypeMappingEntryAssembly override
+ AssemblyDefinition? startingAssembly = null;
+ if (Context.TypeMapEntryAssembly is not null)
+ {
+ var assemblyName = AssemblyNameReference.Parse(Context.TypeMapEntryAssembly);
+ startingAssembly = Context.TryResolve(assemblyName);
+ }
+ // If resolution fails, fall back to entry point assembly
+ startingAssembly ??= Annotations.GetEntryPointAssembly();
+
+ _typeMapHandler.Initialize(Context, this, startingAssembly);
ProcessMarkedPending();
}
diff --git a/src/tools/illink/src/linker/Linker/Driver.cs b/src/tools/illink/src/linker/Linker/Driver.cs
index 701f19ebf5ac89..c1934fbf2ba7d8 100644
--- a/src/tools/illink/src/linker/Linker/Driver.cs
+++ b/src/tools/illink/src/linker/Linker/Driver.cs
@@ -465,6 +465,13 @@ protected int SetupContext(ILogger? customLogger = null)
continue;
+ case "--typemap-entry-assembly":
+ if (!GetStringParam(token, out string? typeMapEntryAssembly))
+ return -1;
+
+ context.TypeMapEntryAssembly = typeMapEntryAssembly;
+ continue;
+
case "--ignore-descriptors":
if (!GetBoolParam(token, l => context.IgnoreDescriptors = l))
return -1;
@@ -1509,34 +1516,35 @@ static void Usage()
Console.WriteLine();
Console.WriteLine("Trimming");
- Console.WriteLine(" --disable-opt NAME [ASM] Disable one of the default optimizations globaly or for a specific assembly name");
- Console.WriteLine(" beforefieldinit: Unused static fields are removed if there is no static ctor");
- Console.WriteLine(" ipconstprop: Interprocedural constant propagation on return values");
- Console.WriteLine(" overrideremoval: Overrides of virtual methods on types that are never instantiated are removed");
- Console.WriteLine(" unreachablebodies: Instance methods that are marked but not executed are converted to throws");
- Console.WriteLine(" unusedinterfaces: Removes interface types from declaration when not used");
- Console.WriteLine(" unusedtypechecks: Inlines never successful type checks");
- Console.WriteLine(" substitutefeatureguards: Substitutes properties annotated as FeatureGuard(typeof(RequiresUnreferencedCodeAttribute)) to false");
- Console.WriteLine(" --enable-opt NAME [ASM] Enable one of the additional optimizations globaly or for a specific assembly name");
- Console.WriteLine(" sealer: Any method or type which does not have override is marked as sealed");
- Console.WriteLine(" --explicit-reflection Adds to members never used through reflection DisablePrivateReflection attribute. Defaults to false");
- Console.WriteLine(" --feature FEATURE VALUE Apply any optimizations defined when this feature setting is a constant known at link time");
- Console.WriteLine(" --keep-com-interfaces Keep COM interfaces implemented by kept types. Defaults to true");
- Console.WriteLine(" --keep-compilers-resources Keep assembly resources used for F# compilation resources. Defaults to false");
- Console.WriteLine(" --keep-dep-attributes Keep attributes used for manual dependency tracking. Defaults to false");
- Console.WriteLine(" --keep-metadata NAME Keep metadata which would otherwise be removed if not used");
- Console.WriteLine(" all: Metadata for any member are all kept");
- Console.WriteLine(" parametername: All parameter names are kept");
- Console.WriteLine(" --new-mvid Generate a new guid for each linked assembly (short -g). Defaults to true");
- Console.WriteLine(" --strip-descriptors Remove XML descriptor resources for linked assemblies. Defaults to true");
- Console.WriteLine(" --strip-security Remove metadata and code related to Code Access Security. Defaults to true");
- Console.WriteLine(" --substitutions FILE Configuration file with field or methods substitution rules");
- Console.WriteLine(" --ignore-substitutions Skips reading embedded substitutions. Defaults to false");
- Console.WriteLine(" --strip-substitutions Remove XML substitution resources for linked assemblies. Defaults to true");
- Console.WriteLine(" --used-attrs-only Attribute usage is removed if the attribute type is not used. Defaults to false");
- Console.WriteLine(" --link-attributes FILE Supplementary custom attribute definitions for attributes controlling the trimming behavior.");
- Console.WriteLine(" --ignore-link-attributes Skips reading embedded attributes. Defaults to false");
- Console.WriteLine(" --strip-link-attributes Remove XML link attributes resources for linked assemblies. Defaults to true");
+ Console.WriteLine(" --disable-opt NAME [ASM] Disable one of the default optimizations globaly or for a specific assembly name");
+ Console.WriteLine(" beforefieldinit: Unused static fields are removed if there is no static ctor");
+ Console.WriteLine(" ipconstprop: Interprocedural constant propagation on return values");
+ Console.WriteLine(" overrideremoval: Overrides of virtual methods on types that are never instantiated are removed");
+ Console.WriteLine(" unreachablebodies: Instance methods that are marked but not executed are converted to throws");
+ Console.WriteLine(" unusedinterfaces: Removes interface types from declaration when not used");
+ Console.WriteLine(" unusedtypechecks: Inlines never successful type checks");
+ Console.WriteLine(" substitutefeatureguards: Substitutes properties annotated as FeatureGuard(typeof(RequiresUnreferencedCodeAttribute)) to false");
+ Console.WriteLine(" --enable-opt NAME [ASM] Enable one of the additional optimizations globaly or for a specific assembly name");
+ Console.WriteLine(" sealer: Any method or type which does not have override is marked as sealed");
+ Console.WriteLine(" --explicit-reflection Adds to members never used through reflection DisablePrivateReflection attribute. Defaults to false");
+ Console.WriteLine(" --feature FEATURE VALUE Apply any optimizations defined when this feature setting is a constant known at link time");
+ Console.WriteLine(" --typemap-entry-assembly NAME Set the assembly name to use as entry point for TypeMap generation.");
+ Console.WriteLine(" --keep-com-interfaces Keep COM interfaces implemented by kept types. Defaults to true");
+ Console.WriteLine(" --keep-compilers-resources Keep assembly resources used for F# compilation resources. Defaults to false");
+ Console.WriteLine(" --keep-dep-attributes Keep attributes used for manual dependency tracking. Defaults to false");
+ Console.WriteLine(" --keep-metadata NAME Keep metadata which would otherwise be removed if not used");
+ Console.WriteLine(" all: Metadata for any member are all kept");
+ Console.WriteLine(" parametername: All parameter names are kept");
+ Console.WriteLine(" --new-mvid Generate a new guid for each linked assembly (short -g). Defaults to true");
+ Console.WriteLine(" --strip-descriptors Remove XML descriptor resources for linked assemblies. Defaults to true");
+ Console.WriteLine(" --strip-security Remove metadata and code related to Code Access Security. Defaults to true");
+ Console.WriteLine(" --substitutions FILE Configuration file with field or methods substitution rules");
+ Console.WriteLine(" --ignore-substitutions Skips reading embedded substitutions. Defaults to false");
+ Console.WriteLine(" --strip-substitutions Remove XML substitution resources for linked assemblies. Defaults to true");
+ Console.WriteLine(" --used-attrs-only Attribute usage is removed if the attribute type is not used. Defaults to false");
+ Console.WriteLine(" --link-attributes FILE Supplementary custom attribute definitions for attributes controlling the trimming behavior.");
+ Console.WriteLine(" --ignore-link-attributes Skips reading embedded attributes. Defaults to false");
+ Console.WriteLine(" --strip-link-attributes Remove XML link attributes resources for linked assemblies. Defaults to true");
Console.WriteLine();
Console.WriteLine("Analyzer");
diff --git a/src/tools/illink/src/linker/Linker/LinkContext.cs b/src/tools/illink/src/linker/Linker/LinkContext.cs
index d6423e6dd23437..7ed63e024f3d81 100644
--- a/src/tools/illink/src/linker/Linker/LinkContext.cs
+++ b/src/tools/illink/src/linker/Linker/LinkContext.cs
@@ -121,6 +121,8 @@ public Pipeline Pipeline
public bool DisableGeneratedCodeHeuristics { get; set; }
+ public string? TypeMapEntryAssembly { get; set; }
+
///
/// Option to not special case EventSource.
/// Currently, values are hard-coded and does not have a command line option to control
diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/ReflectionTests.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/ReflectionTests.cs
index 8bc5974c8319af..2f0ee4a081df57 100644
--- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/ReflectionTests.cs
+++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/ReflectionTests.cs
@@ -184,6 +184,12 @@ public Task RuntimeReflectionExtensionsCalls()
return RunTest();
}
+ [Fact]
+ public Task TypeMapEntryAssembly()
+ {
+ return RunTest();
+ }
+
[Fact]
public Task TypeBaseTypeUseViaReflection()
{
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/Dependencies/TypeMapEntryAssemblyLib.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/Dependencies/TypeMapEntryAssemblyLib.cs
new file mode 100644
index 00000000000000..f38fc759b4125d
--- /dev/null
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/Dependencies/TypeMapEntryAssemblyLib.cs
@@ -0,0 +1,26 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Runtime.InteropServices;
+using Mono.Linker.Tests.Cases.Reflection.Dependencies;
+
+[assembly: TypeMap("entry_type1", typeof(EntryType1))]
+[assembly: TypeMap("entry_type2", typeof(EntryType2))]
+
+[assembly: TypeMapAssociation(typeof(EntrySource1), typeof(EntryProxy1))]
+[assembly: TypeMapAssociation(typeof(EntrySource2), typeof(EntryProxy2))]
+
+namespace Mono.Linker.Tests.Cases.Reflection.Dependencies
+{
+ public class TypeMapEntryAssemblyGroup { }
+
+ public class EntryType1 { }
+ public class EntryType2 { }
+
+ public class EntrySource1 { }
+ public class EntrySource2 { }
+
+ public class EntryProxy1 { }
+ public class EntryProxy2 { }
+}
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/Dependencies/TypeMapReferencedAssembly.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/Dependencies/TypeMapReferencedAssembly.cs
index 82ea66d3933a6b..1ccad3565ad20f 100644
--- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/Dependencies/TypeMapReferencedAssembly.cs
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/Dependencies/TypeMapReferencedAssembly.cs
@@ -18,7 +18,7 @@ namespace Mono.Linker.Tests.Cases.Reflection.Dependencies
{
public class TypeMapReferencedAssembly
{
- public static void Main()
+ public static void Run()
{
// Mark expected trim targets
_ = new TrimTarget1();
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeMap.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeMap.cs
index 9e6dbe1745c824..dbc3009a23b065 100644
--- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeMap.cs
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeMap.cs
@@ -73,7 +73,7 @@ namespace Mono.Linker.Tests.Cases.Reflection
[KeptAssembly("library.dll")]
[KeptAssembly("library2.dll")]
[KeptTypeInAssembly("library.dll", typeof(TypeMapReferencedAssembly))]
- [KeptMemberInAssembly("library.dll", typeof(TypeMapReferencedAssembly), "Main()")]
+ [KeptMemberInAssembly("library.dll", typeof(TypeMapReferencedAssembly), "Run()")]
[KeptTypeInAssembly("library.dll", typeof(TargetTypeUnconditional1), Tool = Tool.Trimmer)]
[KeptTypeInAssembly("library.dll", typeof(TrimTarget1))]
[KeptMemberInAssembly("library.dll", typeof(TrimTarget1), ".ctor()")]
@@ -179,7 +179,7 @@ static void ConstrainedStaticCall(T t) where T : IStaticInterface
Console.WriteLine(new ConstructedNoTypeCheckNoBoxStruct(42).Value);
- TypeMapReferencedAssembly.Main();
+ TypeMapReferencedAssembly.Run();
// TypeMapUniverses are independent between External and Proxy type maps.
// That is, if the External type map is used for a given universe, that doesn't keep the Proxy type map, and vice versa.
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeMapEntryAssembly.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeMapEntryAssembly.cs
new file mode 100644
index 00000000000000..1dcb2621345021
--- /dev/null
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeMapEntryAssembly.cs
@@ -0,0 +1,41 @@
+// Licensed to the .NET Foundation and contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Runtime.InteropServices;
+using Mono.Linker.Tests.Cases.Expectations.Assertions;
+using Mono.Linker.Tests.Cases.Expectations.Metadata;
+using Mono.Linker.Tests.Cases.Reflection.Dependencies;
+
+namespace Mono.Linker.Tests.Cases.Reflection
+{
+ [SetupCompileBefore("TypeMapEntryAssemblyLib.dll", new[] { "Dependencies/TypeMapEntryAssemblyLib.cs" })]
+ [SetupLinkerArgument("--typemap-entry-assembly", "TypeMapEntryAssemblyLib")]
+
+ // The TypeMapEntryAssemblyGroup is defined in TypeMapEntryAssemblyLib, so its attributes should be kept
+ [KeptTypeInAssembly("TypeMapEntryAssemblyLib.dll", typeof(TypeMapEntryAssemblyGroup))]
+ [KeptTypeInAssembly("TypeMapEntryAssemblyLib.dll", typeof(EntryType1))]
+ [KeptTypeInAssembly("TypeMapEntryAssemblyLib.dll", typeof(EntryType2))]
+ [KeptTypeInAssembly("TypeMapEntryAssemblyLib.dll", typeof(EntrySource1))]
+ [KeptTypeInAssembly("TypeMapEntryAssemblyLib.dll", typeof(EntrySource2))]
+ [KeptTypeInAssembly("TypeMapEntryAssemblyLib.dll", typeof(EntryProxy1))]
+ [KeptTypeInAssembly("TypeMapEntryAssemblyLib.dll", typeof(EntryProxy2))]
+
+ [KeptAttributeInAssembly("TypeMapEntryAssemblyLib.dll", typeof(TypeMapAttribute))]
+ [KeptAttributeInAssembly("TypeMapEntryAssemblyLib.dll", typeof(TypeMapAssociationAttribute))]
+
+ public class TypeMapEntryAssembly
+ {
+ public static void Main()
+ {
+ // Access the type map to ensure it gets used
+ var externalMap = TypeMapping.GetOrCreateExternalTypeMapping();
+ var proxyMap = TypeMapping.GetOrCreateProxyTypeMapping();
+
+ Console.WriteLine(externalMap);
+ Console.WriteLine(proxyMap);
+ _ = new EntrySource1();
+ _ = new EntrySource2();
+ }
+ }
+}