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(); + } + } +}