diff --git a/src/coreclr/tools/ILTrim.Tests/ILTrimExpectedFailures.txt b/src/coreclr/tools/ILTrim.Tests/ILTrimExpectedFailures.txt index 90c596ee59fa81..88163a1f32ffef 100644 --- a/src/coreclr/tools/ILTrim.Tests/ILTrimExpectedFailures.txt +++ b/src/coreclr/tools/ILTrim.Tests/ILTrimExpectedFailures.txt @@ -519,6 +519,7 @@ TypeForwarding.AttributesScopeUpdated TypeForwarding.MultiForwardedTypesWithCopyUsed TypeForwarding.MultiForwardedTypesWithLink TypeForwarding.NestedTypeForwarder +TypeForwarding.RootedForwarderWithExportedTypesIsHandled TypeForwarding.SecurityAttributeScope TypeForwarding.TypeForwardedIsUpdatedForMissingType TypeForwarding.TypeForwarderOnlyAssemblyCanBePreservedViaLinkXml @@ -537,6 +538,7 @@ TypeForwarding.UsedForwarderInCopyAssemblyKeptByUsedProperty TypeForwarding.UsedForwarderInCopyAssemblyKeptByUsedTypeAsGenericArg TypeForwarding.UsedForwarderInGenericIsDynamicallyAccessedWithAssemblyCopyUsed TypeForwarding.UsedForwarderIsDynamicallyAccessedWithAssemblyCopyUsed +TypeForwarding.UsedForwarderIsRemovedWhenReferencedByRootedAssembly TypeForwarding.UsedForwarderWithAssemblyCopy TypeForwarding.UsedForwarderWithAssemblyCopyUsed TypeForwarding.UsedForwarderWithAssemblyCopyUsedAndForwarderLibraryKept diff --git a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs index a817c864212b7c..6e8b5176787542 100644 --- a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs +++ b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs @@ -1489,6 +1489,8 @@ public virtual void MarkAssembly(AssemblyDefinition assembly, DependencyInfo rea disableMarkingOfCopyAssembliesValue != "true") { MarkEntireAssembly(assembly, assemblyOrigin); + // For copy/save assemblies, also mark scopes of type references, since they won't be rewritten. + TypeReferenceMarker.MarkTypeReferences(assembly, MarkingHelpers); } return; } @@ -1519,8 +1521,14 @@ void MarkEntireAssembly(AssemblyDefinition assembly, MessageOrigin origin) foreach (TypeDefinition type in module.Types) MarkEntireType(type, new DependencyInfo(DependencyKind.TypeInAssembly, assembly), origin); - // Mark scopes of type references and exported types. - TypeReferenceMarker.MarkTypeReferences(assembly, MarkingHelpers); + MarkExportedTypes(assembly, origin); + } + + void MarkExportedTypes(AssemblyDefinition assembly, MessageOrigin origin) + { + ModuleDefinition module = assembly.MainModule; + foreach (ExportedType exportedType in module.ExportedTypes) + MarkingHelpers.MarkExportedType(exportedType, module, new DependencyInfo(DependencyKind.ExportedType, assembly), origin); } sealed class TypeReferenceMarker : TypeReferenceWalker @@ -1546,7 +1554,6 @@ protected override void ProcessTypeReference(TypeReference type) protected override void ProcessExportedType(ExportedType exportedType) { - markingHelpers.MarkExportedType(exportedType, assembly.MainModule, new DependencyInfo(DependencyKind.ExportedType, assembly), new MessageOrigin(assembly)); markingHelpers.MarkForwardedScope(CreateTypeReferenceForExportedTypeTarget(exportedType), new MessageOrigin(assembly)); } diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/TypeForwarding/RootedForwarderWithExportedTypesIsHandled.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/TypeForwarding/RootedForwarderWithExportedTypesIsHandled.cs new file mode 100644 index 00000000000000..40dec0e75f74bc --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/TypeForwarding/RootedForwarderWithExportedTypesIsHandled.cs @@ -0,0 +1,34 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; +using Mono.Linker.Tests.Cases.TypeForwarding.Dependencies; + +namespace Mono.Linker.Tests.Cases.TypeForwarding +{ + // Actions: + // link - This assembly (default), Forwarder.dll and Implementation.dll + // Rooting Forwarder.dll with -a (link action) while it is itself a type forwarder: + // MarkExportedTypes marks the exported type entries without TypeReferenceMarker running, + // and the implementation assembly is still preserved because the forwarded type is used. + [SetupLinkerArgument("-a", "Forwarder")] + + [SetupCompileBefore("Forwarder.dll", new[] { "Dependencies/ReferenceImplementationLibrary.cs" }, defines: new[] { "INCLUDE_REFERENCE_IMPL" })] + + // After compiling the test case we then replace the reference impl with implementation + type forwarder + [SetupCompileAfter("Implementation.dll", new[] { "Dependencies/ImplementationLibrary.cs" })] + [SetupCompileAfter("Forwarder.dll", new[] { "Dependencies/ForwarderLibrary.cs" }, references: new[] { "Implementation.dll" })] + + [KeptAssembly("Forwarder.dll")] + [KeptTypeInAssembly("Forwarder.dll", typeof(ImplementationLibrary))] + [RemovedAssemblyReference("test", "Forwarder")] + [KeptMemberInAssembly("Implementation.dll", typeof(ImplementationLibrary), "GetSomeValue()")] + class RootedForwarderWithExportedTypesIsHandled + { + static void Main() + { + new ImplementationLibrary().GetSomeValue(); + } + } +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedForwarderIsRemovedWhenReferencedByRootedAssembly.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedForwarderIsRemovedWhenReferencedByRootedAssembly.cs new file mode 100644 index 00000000000000..5a08aade4bc96b --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/TypeForwarding/UsedForwarderIsRemovedWhenReferencedByRootedAssembly.cs @@ -0,0 +1,33 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; +using Mono.Linker.Tests.Cases.TypeForwarding.Dependencies; + +namespace Mono.Linker.Tests.Cases.TypeForwarding +{ + // Actions: + // link - This assembly (default), Forwarder.dll and Implementation.dll + // Rooting the test assembly with -a should not cause Forwarder.dll to be preserved, + // because type references are rewritten to point directly to Implementation.dll. + [SetupLinkerArgument("-a", "test")] + + [SetupCompileBefore("Forwarder.dll", new[] { "Dependencies/ReferenceImplementationLibrary.cs" }, defines: new[] { "INCLUDE_REFERENCE_IMPL" })] + + // After compiling the test case we then replace the reference impl with implementation + type forwarder + [SetupCompileAfter("Implementation.dll", new[] { "Dependencies/ImplementationLibrary.cs" })] + [SetupCompileAfter("Forwarder.dll", new[] { "Dependencies/ForwarderLibrary.cs" }, references: new[] { "Implementation.dll" })] + + [RemovedAssembly("Forwarder.dll")] + [RemovedAssemblyReference("test", "Forwarder")] + [KeptMemberInAssembly("Implementation.dll", typeof(ImplementationLibrary), "GetSomeValue()")] + [KeptMember(".ctor()")] + class UsedForwarderIsRemovedWhenReferencedByRootedAssembly + { + static void Main() + { + new ImplementationLibrary().GetSomeValue(); + } + } +}