diff --git a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs index 32fd98cbe039ca..9290b157920dd2 100644 --- a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs +++ b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs @@ -35,8 +35,8 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Reflection.Metadata.Ecma335; using System.Reflection.Runtime.TypeParsing; -using System.Runtime.CompilerServices; using System.Text.RegularExpressions; using ILLink.Shared; using ILLink.Shared.TrimAnalysis; @@ -2450,26 +2450,27 @@ void MarkNamedProperty (TypeDefinition type, string property_name, in Dependency void MarkInterfaceImplementations (TypeDefinition type) { - if (!type.HasInterfaces) + var ifaces = Annotations.GetRecursiveInterfaces (type); + if (ifaces is null) return; - - foreach (var iface in type.Interfaces) { + foreach (var (ifaceType, impls) in ifaces) { // Only mark interface implementations of interface types that have been marked. // This enables stripping of interfaces that are never used - if (ShouldMarkInterfaceImplementation (type, iface)) - MarkInterfaceImplementation (iface, new MessageOrigin (type)); + if (ShouldMarkInterfaceImplementationList (type, impls, ifaceType)) + MarkInterfaceImplementationList (impls, new MessageOrigin (type)); } } - protected virtual bool ShouldMarkInterfaceImplementation (TypeDefinition type, InterfaceImplementation iface) + + protected virtual bool ShouldMarkInterfaceImplementationList (TypeDefinition type, List ifaces, TypeReference ifaceType) { - if (Annotations.IsMarked (iface)) + if (ifaces.All (Annotations.IsMarked)) return false; if (!Context.IsOptimizationEnabled (CodeOptimizations.UnusedInterfaces, type)) return true; - if (Context.Resolve (iface.InterfaceType) is not TypeDefinition resolvedInterfaceType) + if (Context.Resolve (ifaceType) is not TypeDefinition resolvedInterfaceType) return false; if (Annotations.IsMarked (resolvedInterfaceType)) @@ -3764,8 +3765,7 @@ protected virtual void MarkInstruction (Instruction instruction, MethodDefinitio ScopeStack.UpdateCurrentScopeInstructionOffset (instruction.Offset); if (markForReflectionAccess) { MarkMethodVisibleToReflection (methodReference, new DependencyInfo (dependencyKind, method), ScopeStack.CurrentScope.Origin); - } - else { + } else { MarkMethod (methodReference, new DependencyInfo (dependencyKind, method), ScopeStack.CurrentScope.Origin); } break; @@ -3825,6 +3825,12 @@ protected virtual void MarkInstruction (Instruction instruction, MethodDefinitio } } + void MarkInterfaceImplementationList (List ifaces, MessageOrigin? origin = null, DependencyInfo? reason = null) + { + foreach (var iface in ifaces) { + MarkInterfaceImplementation (iface, origin, reason); + } + } protected internal virtual void MarkInterfaceImplementation (InterfaceImplementation iface, MessageOrigin? origin = null, DependencyInfo? reason = null) { diff --git a/src/tools/illink/src/linker/Linker/Annotations.cs b/src/tools/illink/src/linker/Linker/Annotations.cs index a7b3198265e812..a7f77cf0efae90 100644 --- a/src/tools/illink/src/linker/Linker/Annotations.cs +++ b/src/tools/illink/src/linker/Linker/Annotations.cs @@ -34,6 +34,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Reflection.Metadata.Ecma335; using ILLink.Shared.TrimAnalysis; using Mono.Cecil; using Mono.Cecil.Cil; @@ -717,5 +718,10 @@ public void EnqueueVirtualMethod (MethodDefinition method) if (FlowAnnotations.RequiresVirtualMethodDataFlowAnalysis (method) || HasLinkerAttribute (method)) VirtualMethodsWithAnnotationsToValidate.Add (method); } + + internal List<(TypeReference, List)>? GetRecursiveInterfaces (TypeDefinition type) + { + return TypeMapInfo.GetRecursiveInterfaces (type); + } } } diff --git a/src/tools/illink/src/linker/Linker/MethodReferenceComparer.cs b/src/tools/illink/src/linker/Linker/MethodReferenceComparer.cs new file mode 100644 index 00000000000000..3f4fa06684a07a --- /dev/null +++ b/src/tools/illink/src/linker/Linker/MethodReferenceComparer.cs @@ -0,0 +1,171 @@ +// 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 System; +using System.Collections.Generic; +using System.Diagnostics; +using Mono.Cecil; + +namespace Mono.Linker +{ + // Copied from https://github.com/jbevain/cecil/blob/master/Mono.Cecil/MethodReferenceComparer.cs + internal sealed class MethodReferenceComparer : EqualityComparer + { + // Initialized lazily for each thread + [ThreadStatic] + static List? xComparisonStack; + + [ThreadStatic] + static List? yComparisonStack; + + public readonly ITryResolveMetadata _resolver; + + public MethodReferenceComparer(ITryResolveMetadata resolver) + { + _resolver = resolver; + } + + public override bool Equals (MethodReference? x, MethodReference? y) + { + return AreEqual (x, y, _resolver); + } + + public override int GetHashCode (MethodReference obj) + { + return GetHashCodeFor (obj); + } + + public static bool AreEqual (MethodReference? x, MethodReference? y, ITryResolveMetadata resolver) + { + if (ReferenceEquals (x, y)) + return true; + + if (x is null ^ y is null) + return false; + + Debug.Assert (x is not null); + Debug.Assert (y is not null); + + if (x.HasThis != y.HasThis) + return false; + +#pragma warning disable RS0030 // MethodReference.HasParameters is banned - this code is copied from Cecil + if (x.HasParameters != y.HasParameters) + return false; +#pragma warning restore RS0030 + + if (x.HasGenericParameters != y.HasGenericParameters) + return false; + +#pragma warning disable RS0030 // MethodReference.HasParameters is banned - this code is copied from Cecil + if (x.Parameters.Count != y.Parameters.Count) + return false; +#pragma warning restore RS0030 + + if (x.Name != y.Name) + return false; + + if (!TypeReferenceEqualityComparer.AreEqual (x.DeclaringType, y.DeclaringType, resolver)) + return false; + + var xGeneric = x as GenericInstanceMethod; + var yGeneric = y as GenericInstanceMethod; + if (xGeneric != null || yGeneric != null) { + if (xGeneric == null || yGeneric == null) + return false; + + if (xGeneric.GenericArguments.Count != yGeneric.GenericArguments.Count) + return false; + + for (int i = 0; i < xGeneric.GenericArguments.Count; i++) + if (!TypeReferenceEqualityComparer.AreEqual (xGeneric.GenericArguments[i], yGeneric.GenericArguments[i], resolver)) + return false; + } + + var xResolved = resolver.TryResolve (x); + var yResolved = resolver.TryResolve (y); + + if (xResolved != yResolved) + return false; + + if (xResolved == null) { + // We couldn't resolve either method. In order for them to be equal, their parameter types _must_ match. But wait, there's a twist! + // There exists a situation where we might get into a recursive state: parameter type comparison might lead to comparing the same + // methods again if the parameter types are generic parameters whose owners are these methods. We guard against these by using a + // thread static list of all our comparisons carried out in the stack so far, and if we're in progress of comparing them already, + // we'll just say that they match. + + xComparisonStack ??= new List (); + + yComparisonStack ??= new List (); + + for (int i = 0; i < xComparisonStack.Count; i++) { + if (xComparisonStack[i] == x && yComparisonStack[i] == y) + return true; + } + + xComparisonStack.Add (x); + + try { + yComparisonStack.Add (y); + + try { +#pragma warning disable RS0030 // MethodReference.HasParameters is banned - this code is copied from Cecil + for (int i = 0; i < x.Parameters.Count; i++) { + if (!TypeReferenceEqualityComparer.AreEqual (x.Parameters[i].ParameterType, y.Parameters[i].ParameterType, resolver)) + return false; + } +#pragma warning restore RS0030 + } finally { + yComparisonStack.RemoveAt (yComparisonStack.Count - 1); + } + } finally { + xComparisonStack.RemoveAt (xComparisonStack.Count - 1); + } + } + + return true; + } + + public static bool AreSignaturesEqual (MethodReference x, MethodReference y, ITryResolveMetadata resolver, TypeComparisonMode comparisonMode = TypeComparisonMode.Exact) + { + if (x.HasThis != y.HasThis) + return false; + +#pragma warning disable RS0030 // MethodReference.HasParameters is banned - this code is copied from Cecil + if (x.Parameters.Count != y.Parameters.Count) + return false; +#pragma warning restore RS0030 + + if (x.GenericParameters.Count != y.GenericParameters.Count) + return false; + +#pragma warning disable RS0030 // MethodReference.HasParameters is banned - this code is copied from Cecil + for (var i = 0; i < x.Parameters.Count; i++) + if (!TypeReferenceEqualityComparer.AreEqual (x.Parameters[i].ParameterType, y.Parameters[i].ParameterType, resolver, comparisonMode)) + return false; +#pragma warning restore RS0030 + + if (!TypeReferenceEqualityComparer.AreEqual (x.ReturnType, y.ReturnType, resolver, comparisonMode)) + return false; + + return true; + } + + public static int GetHashCodeFor (MethodReference obj) + { + // a very good prime number + const int hashCodeMultiplier = 486187739; + + var genericInstanceMethod = obj as GenericInstanceMethod; + if (genericInstanceMethod != null) { + var hashCode = GetHashCodeFor (genericInstanceMethod.ElementMethod); + for (var i = 0; i < genericInstanceMethod.GenericArguments.Count; i++) + hashCode = hashCode * hashCodeMultiplier + TypeReferenceEqualityComparer.GetHashCodeFor (genericInstanceMethod.GenericArguments[i]); + return hashCode; + } + + return TypeReferenceEqualityComparer.GetHashCodeFor (obj.DeclaringType) * hashCodeMultiplier + obj.Name.GetHashCode (); + } + } +} diff --git a/src/tools/illink/src/linker/Linker/TypeComparisonMode.cs b/src/tools/illink/src/linker/Linker/TypeComparisonMode.cs new file mode 100644 index 00000000000000..e3f95bd148555e --- /dev/null +++ b/src/tools/illink/src/linker/Linker/TypeComparisonMode.cs @@ -0,0 +1,17 @@ +// 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. + +namespace Mono.Linker +{ + // Copied from https://github.com/jbevain/cecil/blob/master/Mono.Cecil/TypeComparisonMode.cs + internal enum TypeComparisonMode + { + Exact, + SignatureOnly, + + /// + /// Types can be in different assemblies, as long as the module, assembly, and type names match they will be considered equal + /// + SignatureOnlyLoose + } +} diff --git a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs index 7e68b71bcb7d08..d2813c8982c4f0 100644 --- a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs +++ b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs @@ -116,10 +116,12 @@ public void AddDefaultInterfaceImplementation (MethodDefinition @base, Interface default_interface_implementations.AddToList (@base, new OverrideInformation (@base, defaultImplementationMethod, interfaceImplementor)); } + Dictionary)>> interfaces = new (); protected virtual void MapType (TypeDefinition type) { MapVirtualMethods (type); MapInterfaceMethodsInTypeHierarchy (type); + interfaces[type] = GetRecursiveInterfaceImplementations (type); if (!type.HasNestedTypes) return; @@ -128,6 +130,50 @@ protected virtual void MapType (TypeDefinition type) MapType (nested); } + internal List<(TypeReference, List)>? GetRecursiveInterfaces (TypeDefinition type) + { + if (interfaces.TryGetValue (type, out var value)) + return value; + return null; + } + + List<(TypeReference, List)> GetRecursiveInterfaceImplementations (TypeDefinition type) + { + List<(TypeReference, List)> firstImplementationChain = new (); + + AddRecursiveInterfaces (type, [], firstImplementationChain, context); + Debug.Assert (firstImplementationChain.All (kvp => context.Resolve (kvp.Item1) == context.Resolve (kvp.Item2.Last ().InterfaceType))); + + return firstImplementationChain; + + static void AddRecursiveInterfaces (TypeReference typeRef, IEnumerable pathToType, List<(TypeReference, List)> firstImplementationChain, LinkContext Context) + { + var type = Context.TryResolve (typeRef); + if (type is null) + return; + // Get all explicit interfaces of this type + foreach (var iface in type.Interfaces) { + var interfaceType = iface.InterfaceType.TryInflateFrom (typeRef, Context); + if (interfaceType is null) { + continue; + } + if (!firstImplementationChain.Any (i => TypeReferenceEqualityComparer.AreEqual (i.Item1, interfaceType, Context))) { + firstImplementationChain.Add ((interfaceType, pathToType.Append (iface).ToList ())); + } + } + + // Recursive interfaces after all direct interfaces to preserve Inherit/Implement tree order + foreach (var iface in type.Interfaces) { + // If we can't resolve the interface type we can't find recursive interfaces + var ifaceDirectlyOnType = iface.InterfaceType.TryInflateFrom (typeRef, Context); + if (ifaceDirectlyOnType is null) { + continue; + } + AddRecursiveInterfaces (ifaceDirectlyOnType, pathToType.Append (iface), firstImplementationChain, Context); + } + } + } + void MapInterfaceMethodsInTypeHierarchy (TypeDefinition type) { if (!type.HasInterfaces) diff --git a/src/tools/illink/src/linker/Linker/TypeReferenceEqualityComparer.cs b/src/tools/illink/src/linker/Linker/TypeReferenceEqualityComparer.cs new file mode 100644 index 00000000000000..ee0e0d48ba9bc1 --- /dev/null +++ b/src/tools/illink/src/linker/Linker/TypeReferenceEqualityComparer.cs @@ -0,0 +1,270 @@ +// 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 System; +using System.Collections.Generic; +using System.Diagnostics; +using Mono.Cecil; + +namespace Mono.Linker +{ + // Copied from https://github.com/jbevain/cecil/blob/master/Mono.Cecil/TypeReferenceComparer.cs + internal sealed class TypeReferenceEqualityComparer : EqualityComparer + { + public readonly ITryResolveMetadata _resolver; + + public TypeReferenceEqualityComparer(ITryResolveMetadata resolver) + { + _resolver = resolver; + } + + public override bool Equals (TypeReference? x, TypeReference? y) + { + return AreEqual (x, y, _resolver); + } + + public override int GetHashCode (TypeReference obj) + { + return GetHashCodeFor (obj); + } + + public static bool AreEqual (TypeReference? a, TypeReference? b, ITryResolveMetadata resolver, TypeComparisonMode comparisonMode = TypeComparisonMode.Exact) + { + if (ReferenceEquals (a, b)) + return true; + + if (a == null || b == null) + return false; + + var aMetadataType = a.MetadataType; + var bMetadataType = b.MetadataType; + + if (aMetadataType == MetadataType.GenericInstance || bMetadataType == MetadataType.GenericInstance) { + if (aMetadataType != bMetadataType) + return false; + + return AreEqual ((GenericInstanceType) a, (GenericInstanceType) b, resolver, comparisonMode); + } + + if (aMetadataType == MetadataType.Array || bMetadataType == MetadataType.Array) { + if (aMetadataType != bMetadataType) + return false; + + var a1 = (ArrayType) a; + var b1 = (ArrayType) b; + if (a1.Rank != b1.Rank) + return false; + + return AreEqual (a1.ElementType, b1.ElementType, resolver, comparisonMode); + } + + if (aMetadataType == MetadataType.Var || bMetadataType == MetadataType.Var) { + if (aMetadataType != bMetadataType) + return false; + + return AreEqual ((GenericParameter) a, (GenericParameter) b, resolver, comparisonMode); + } + + if (aMetadataType == MetadataType.MVar || bMetadataType == MetadataType.MVar) { + if (aMetadataType != bMetadataType) + return false; + + return AreEqual ((GenericParameter) a, (GenericParameter) b, resolver, comparisonMode); + } + + if (aMetadataType == MetadataType.ByReference || bMetadataType == MetadataType.ByReference) { + if (aMetadataType != bMetadataType) + return false; + + return AreEqual (((ByReferenceType) a).ElementType, ((ByReferenceType) b).ElementType, resolver, comparisonMode); + } + + if (aMetadataType == MetadataType.Pointer || bMetadataType == MetadataType.Pointer) { + if (aMetadataType != bMetadataType) + return false; + + return AreEqual (((PointerType) a).ElementType, ((PointerType) b).ElementType, resolver, comparisonMode); + } + + if (aMetadataType == MetadataType.RequiredModifier || bMetadataType == MetadataType.RequiredModifier) { + if (aMetadataType != bMetadataType) + return false; + + var a1 = (RequiredModifierType) a; + var b1 = (RequiredModifierType) b; + + return AreEqual (a1.ModifierType, b1.ModifierType, resolver, comparisonMode) && AreEqual (a1.ElementType, b1.ElementType, resolver, comparisonMode); + } + + if (aMetadataType == MetadataType.OptionalModifier || bMetadataType == MetadataType.OptionalModifier) { + if (aMetadataType != bMetadataType) + return false; + + var a1 = (OptionalModifierType) a; + var b1 = (OptionalModifierType) b; + + return AreEqual (a1.ModifierType, b1.ModifierType, resolver, comparisonMode) && AreEqual (a1.ElementType, b1.ElementType, resolver, comparisonMode); + } + + if (aMetadataType == MetadataType.Pinned || bMetadataType == MetadataType.Pinned) { + if (aMetadataType != bMetadataType) + return false; + + return AreEqual (((PinnedType) a).ElementType, ((PinnedType) b).ElementType, resolver, comparisonMode); + } + + if (aMetadataType == MetadataType.Sentinel || bMetadataType == MetadataType.Sentinel) { + if (aMetadataType != bMetadataType) + return false; + + return AreEqual (((SentinelType) a).ElementType, ((SentinelType) b).ElementType, resolver, comparisonMode); + } + + if (!a.Name.Equals (b.Name) || !a.Namespace.Equals (b.Namespace)) + return false; + + var xDefinition = resolver.TryResolve (a); + var yDefinition = resolver.TryResolve (b); + if (xDefinition == null || yDefinition == null) + return false; + + // For loose signature the types could be in different assemblies, as long as the type names match we will consider them equal + if (comparisonMode == TypeComparisonMode.SignatureOnlyLoose) { + if (xDefinition.Module.Name != yDefinition.Module.Name) + return false; + + if (xDefinition.Module.Assembly.Name.Name != yDefinition.Module.Assembly.Name.Name) + return false; + + return xDefinition.FullName == yDefinition.FullName; + } + + return xDefinition == yDefinition; + } + + static bool AreEqual (GenericParameter a, GenericParameter b, ITryResolveMetadata resolver, TypeComparisonMode comparisonMode = TypeComparisonMode.Exact) + { + if (ReferenceEquals (a, b)) + return true; + + if (a.Position != b.Position) + return false; + + if (a.Type != b.Type) + return false; + + var aOwnerType = a.Owner as TypeReference; + if (aOwnerType != null && AreEqual (aOwnerType, b.Owner as TypeReference, resolver, comparisonMode)) + return true; + + var aOwnerMethod = a.Owner as MethodReference; + if (aOwnerMethod != null && comparisonMode != TypeComparisonMode.SignatureOnlyLoose && MethodReferenceComparer.AreEqual (aOwnerMethod, b.Owner as MethodReference, resolver)) + return true; + + return comparisonMode == TypeComparisonMode.SignatureOnly || comparisonMode == TypeComparisonMode.SignatureOnlyLoose; + } + + static bool AreEqual (GenericInstanceType a, GenericInstanceType b, ITryResolveMetadata resolver, TypeComparisonMode comparisonMode = TypeComparisonMode.Exact) + { + if (ReferenceEquals (a, b)) + return true; + + var aGenericArgumentsCount = a.GenericArguments.Count; + if (aGenericArgumentsCount != b.GenericArguments.Count) + return false; + + if (!AreEqual (a.ElementType, b.ElementType, resolver, comparisonMode)) + return false; + + for (int i = 0; i < aGenericArgumentsCount; i++) + if (!AreEqual (a.GenericArguments[i], b.GenericArguments[i], resolver, comparisonMode)) + return false; + + return true; + } + + public static int GetHashCodeFor (TypeReference obj) + { + // a very good prime number + const int hashCodeMultiplier = 486187739; + // prime numbers + const int genericInstanceTypeMultiplier = 31; + const int byReferenceMultiplier = 37; + const int pointerMultiplier = 41; + const int requiredModifierMultiplier = 43; + const int optionalModifierMultiplier = 47; + const int pinnedMultiplier = 53; + const int sentinelMultiplier = 59; + + var metadataType = obj.MetadataType; + + if (metadataType == MetadataType.GenericInstance) { + var genericInstanceType = (GenericInstanceType) obj; + var hashCode = GetHashCodeFor (genericInstanceType.ElementType) * hashCodeMultiplier + genericInstanceTypeMultiplier; + for (var i = 0; i < genericInstanceType.GenericArguments.Count; i++) + hashCode = hashCode * hashCodeMultiplier + GetHashCodeFor (genericInstanceType.GenericArguments[i]); + return hashCode; + } + + if (metadataType == MetadataType.Array) { + var arrayType = (ArrayType) obj; + return GetHashCodeFor (arrayType.ElementType) * hashCodeMultiplier + arrayType.Rank.GetHashCode (); + } + + if (metadataType == MetadataType.Var || metadataType == MetadataType.MVar) { + var genericParameter = (GenericParameter) obj; + var hashCode = genericParameter.Position.GetHashCode () * hashCodeMultiplier + ((int) metadataType).GetHashCode (); + + var ownerTypeReference = genericParameter.Owner as TypeReference; + if (ownerTypeReference != null) + return hashCode * hashCodeMultiplier + GetHashCodeFor (ownerTypeReference); + + var ownerMethodReference = genericParameter.Owner as MethodReference; + if (ownerMethodReference != null) + return hashCode * hashCodeMultiplier + MethodReferenceComparer.GetHashCodeFor (ownerMethodReference); + + throw new InvalidOperationException ("Generic parameter encountered with invalid owner"); + } + + if (metadataType == MetadataType.ByReference) { + var byReferenceType = (ByReferenceType) obj; + return GetHashCodeFor (byReferenceType.ElementType) * hashCodeMultiplier * byReferenceMultiplier; + } + + if (metadataType == MetadataType.Pointer) { + var pointerType = (PointerType) obj; + return GetHashCodeFor (pointerType.ElementType) * hashCodeMultiplier * pointerMultiplier; + } + + if (metadataType == MetadataType.RequiredModifier) { + var requiredModifierType = (RequiredModifierType) obj; + var hashCode = GetHashCodeFor (requiredModifierType.ElementType) * requiredModifierMultiplier; + hashCode = hashCode * hashCodeMultiplier + GetHashCodeFor (requiredModifierType.ModifierType); + return hashCode; + } + + if (metadataType == MetadataType.OptionalModifier) { + var optionalModifierType = (OptionalModifierType) obj; + var hashCode = GetHashCodeFor (optionalModifierType.ElementType) * optionalModifierMultiplier; + hashCode = hashCode * hashCodeMultiplier + GetHashCodeFor (optionalModifierType.ModifierType); + return hashCode; + } + + if (metadataType == MetadataType.Pinned) { + var pinnedType = (PinnedType) obj; + return GetHashCodeFor (pinnedType.ElementType) * hashCodeMultiplier * pinnedMultiplier; + } + + if (metadataType == MetadataType.Sentinel) { + var sentinelType = (SentinelType) obj; + return GetHashCodeFor (sentinelType.ElementType) * hashCodeMultiplier * sentinelMultiplier; + } + + if (metadataType == MetadataType.FunctionPointer) { + throw new NotImplementedException ("We currently don't handle function pointer types."); + } + + return obj.Namespace.GetHashCode () * hashCodeMultiplier + obj.FullName.GetHashCode (); + } + } +} diff --git a/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs b/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs index 5092fe1158e34b..6189eae6d794cb 100644 --- a/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs +++ b/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs @@ -151,6 +151,13 @@ void parseArrayDimensions (ArrayType at) return null; } + public static TypeReference? TryInflateFrom (this TypeReference typeToInflate, TypeReference maybeGenericInstanceProvider, ITryResolveMetadata resolver) + { + if (maybeGenericInstanceProvider is GenericInstanceType genericInstanceProvider) + return InflateGenericType (genericInstanceProvider, typeToInflate, resolver); + return typeToInflate; + } + public static IEnumerable<(TypeReference InflatedInterface, InterfaceImplementation OriginalImpl)> GetInflatedInterfaces (this TypeReference typeRef, ITryResolveMetadata resolver) { var typeDef = resolver.TryResolve (typeRef); diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.DefaultInterfaceMethodsTests.g.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.DefaultInterfaceMethodsTests.g.cs index 4b3f387a390151..19189bfcd01705 100644 --- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.DefaultInterfaceMethodsTests.g.cs +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.DefaultInterfaceMethodsTests.g.cs @@ -15,6 +15,12 @@ public Task DefaultInterfaceMethodCallIntoClass () return RunTest (allowMissingWarnings: true); } + [Fact] + public Task DimProvidedByRecursiveInterface () + { + return RunTest (allowMissingWarnings: true); + } + [Fact] public Task GenericDefaultInterfaceMethods () { @@ -39,6 +45,12 @@ public Task MostSpecificDefaultImplementationKeptStatic () return RunTest (allowMissingWarnings: true); } + [Fact] + public Task MultipleDimsProvidedByRecursiveInterface () + { + return RunTest (allowMissingWarnings: true); + } + [Fact] public Task SimpleDefaultInterfaceMethod () { @@ -51,6 +63,12 @@ public Task StaticDefaultInterfaceMethodOnStruct () return RunTest (allowMissingWarnings: true); } + [Fact] + public Task StaticDimProvidedByUnreferencedIfaceInHierarchy () + { + return RunTest (allowMissingWarnings: true); + } + [Fact] public Task UnusedDefaultInterfaceImplementation () { diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.RecursiveInterfacesTests.g.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.RecursiveInterfacesTests.g.cs new file mode 100644 index 00000000000000..d436348e800bc7 --- /dev/null +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.RecursiveInterfacesTests.g.cs @@ -0,0 +1,31 @@ +using System; +using System.Threading.Tasks; +using Xunit; + +namespace ILLink.RoslynAnalyzer.Tests.Inheritance.Interfaces +{ + public sealed partial class RecursiveInterfacesTests : LinkerTestBase + { + + protected override string TestSuiteName => "Inheritance.Interfaces.RecursiveInterfaces"; + + [Fact] + public Task GenericInterfaceImplementedRecursively () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task InterfaceImplementedRecursively () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task RecursiveInterfaceKept () + { + return RunTest (allowMissingWarnings: true); + } + + } +} diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.InterfacesTests.g.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.InterfacesTests.g.cs index 649b8449527f75..07680c1c9d5e6a 100644 --- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.InterfacesTests.g.cs +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.InterfacesTests.g.cs @@ -33,6 +33,12 @@ public Task InterfaceVariants () return RunTest (allowMissingWarnings: true); } + [Fact] + public Task InterfaceVariantsGeneric () + { + return RunTest (allowMissingWarnings: true); + } + [Fact] public Task InterfaceWithoutNewSlot () { diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/FeatureCheckDataFlow.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/FeatureCheckDataFlow.cs index d0d236997445ed..8c8c3baf5ab5c6 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/FeatureCheckDataFlow.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/FeatureCheckDataFlow.cs @@ -1155,6 +1155,7 @@ static void GuardedLocalFunction () public static void Test () { + // Use the IEnumerable to mark the IEnumerable methods GuardInIterator (); StateFlowsAcrossYield (); GuardInAsync (); diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/Dependencies/DimProvidedByRecursiveInterface.il b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/Dependencies/DimProvidedByRecursiveInterface.il new file mode 100644 index 00000000000000..c85892208989b3 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/Dependencies/DimProvidedByRecursiveInterface.il @@ -0,0 +1,86 @@ +// 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. + +.assembly extern mscorlib { } + +.assembly 'library' { } + +.class public auto ansi abstract sealed beforefieldinit Program + extends [mscorlib]System.Object +{ + // Nested Types + .class interface nested public auto ansi abstract beforefieldinit IFoo + { + // Methods + .method public hidebysig newslot abstract virtual + instance void Method () cil managed + { + } // end of method IFoo::Method + + } // end of class IFoo + + .class interface nested public auto ansi abstract beforefieldinit IBar + implements Program/IFoo + { + // Methods + .method public final hidebysig virtual + instance void Program.IFoo.Method () cil managed + { + .override method instance void Program/IFoo::Method() + // Method begins at RVA 0x2068 + // Code size 2 (0x2) + .maxstack 8 + + IL_0000: nop + IL_0001: ret + } // end of method IBar::Program.IFoo.Method + + } // end of class IBar + + .class interface nested public auto ansi abstract beforefieldinit IBaz + implements Program/IBar + { + } // end of class IBaz + + .class nested public auto ansi beforefieldinit MyFoo + extends [mscorlib]System.Object + implements Program/IBaz + { + // Methods + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x2076 + // Code size 8 (0x8) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method MyFoo::.ctor + + } // end of class MyFoo + + + // Methods + .method public hidebysig static + void CallMethod ( + class Program/IFoo foo + ) cil managed + { + .custom instance void [mscorlib]mscorlib.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( + 01 00 01 00 00 + ) + // Method begins at RVA 0x2050 + // Code size 9 (0x9) + .maxstack 8 + + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: callvirt instance void Program/IFoo::Method() + IL_0007: nop + IL_0008: ret + } // end of method Program::CallMethod + +} // end of class Program diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/Dependencies/MultipleDimsProvidedByRecursiveInterface.il b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/Dependencies/MultipleDimsProvidedByRecursiveInterface.il new file mode 100644 index 00000000000000..4937513f285310 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/Dependencies/MultipleDimsProvidedByRecursiveInterface.il @@ -0,0 +1,86 @@ +// 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. + +.assembly extern mscorlib { } + +.assembly 'library' { } + +.class public auto ansi abstract sealed beforefieldinit Program + extends [mscorlib]System.Object +{ + // Nested Types + .class interface nested public auto ansi abstract beforefieldinit I0 + { + // Methods + .method public hidebysig newslot abstract virtual + instance void Method () cil managed + { + } // end of method I0::Method + + } // end of class I0 + + .class interface nested public auto ansi abstract beforefieldinit I00 + implements Program/I0 + { + // Methods + .method public final hidebysig virtual + instance void Program.I0.Method () cil managed + { + .override method instance void Program/I0::Method() + // Method begins at RVA 0x2068 + // Code size 2 (0x2) + .maxstack 8 + + IL_0000: nop + IL_0001: ret + } // end of method I00::Program.I0.Method + } // end of class I00 + + .class interface nested public auto ansi abstract beforefieldinit I01 + implements Program/I0 + { + // Methods + .method public final hidebysig virtual + instance void Program.I0.Method () cil managed + { + .override method instance void Program/I0::Method() + // Method begins at RVA 0x2068 + // Code size 2 (0x2) + .maxstack 8 + + IL_0000: nop + IL_0001: ret + } // end of method I00::Program.I0.Method + } // end of class I00 + + .class interface nested public auto ansi abstract beforefieldinit I000 + implements Program/I00 + { + } // end of class I000 + + .class interface nested public auto ansi abstract beforefieldinit I010 + implements Program/I01 + { + } // end of class I000 + + .class nested public auto ansi beforefieldinit MyFoo + extends [mscorlib]System.Object + implements Program/I000, Program/I010 + { + // Methods + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x2076 + // Code size 8 (0x8) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method MyFoo::.ctor + + } // end of class MyFoo + +} // end of class Program diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/Dependencies/StaticDimProvidedByUnreferencedIfaceInHierarchy.il b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/Dependencies/StaticDimProvidedByUnreferencedIfaceInHierarchy.il new file mode 100644 index 00000000000000..949d5e6fbf4aba --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/Dependencies/StaticDimProvidedByUnreferencedIfaceInHierarchy.il @@ -0,0 +1,70 @@ +// 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. + +.assembly extern mscorlib { } + +.assembly 'library' { } + +.class public auto ansi abstract sealed beforefieldinit Program + extends [mscorlib]System.Object +{ + // Nested Types + .class interface nested public auto ansi abstract beforefieldinit IBase + { + // Methods + .method public hidebysig abstract virtual static + void Method () cil managed + { + } // end of method IBase::Method + + } // end of class IBase + + .class interface nested public auto ansi abstract beforefieldinit I2 + implements Program/IBase + { + // Methods + .method public hidebysig static + void Program.IBase.Method () cil managed + { + .override method void Program/IBase::Method() + // Method begins at RVA 0x205f + // Code size 2 (0x2) + .maxstack 8 + + IL_0000: nop + IL_0001: ret + } // end of method I2::Program.IBase.Method + + } // end of class I2 + + .class interface nested public auto ansi abstract beforefieldinit I3 + implements Program/I2 + { + } // end of class I3 + + .class interface nested public auto ansi abstract beforefieldinit I4 + implements Program/I3 + { + } // end of class I4 + + + // Methods + .method public hidebysig static + void CallMethod<(Program/IBase) T> () cil managed + { + .param constraint T, Program/IBase + .custom instance void [mscorlib]mscorlib.CompilerServices.NullableAttribute::.ctor(uint8) = ( + 01 00 01 00 00 + ) + // Method begins at RVA 0x2050 + // Code size 14 (0xe) + .maxstack 8 + + IL_0000: nop + IL_0001: constrained. !!T + IL_0007: call void Program/IBase::Method() + IL_000c: nop + IL_000d: ret + } // end of method Program::CallMethod + +} // end of class Program diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/DimProvidedByRecursiveInterface.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/DimProvidedByRecursiveInterface.cs new file mode 100644 index 00000000000000..743df7227a80c8 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/DimProvidedByRecursiveInterface.cs @@ -0,0 +1,67 @@ + + +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.DefaultInterfaceMethods +{ + [SetupLinkerArgument ("--skip-unresolved", "true")] + [TestCaseRequirements (TestRunCharacteristics.SupportsDefaultInterfaceMethods, "Requires support for default interface methods")] + [Define ("IL_ASSEMBLY_AVAILABLE")] + [SetupCompileBefore ("library.dll", new[] { "Dependencies/DimProvidedByRecursiveInterface.il" })] + [SkipILVerify] + +#if IL_ASSEMBLY_AVAILABLE + [KeptMemberInAssembly ("library.dll", typeof(Program.IFoo), "Method()")] + [KeptTypeInAssembly ("library.dll", typeof(Program.IBar))] + [KeptMemberInAssembly ("library.dll", typeof(Program.IBar), "Program.IFoo.Method()")] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.IBar), "library.dll", typeof (Program.IFoo))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.MyFoo), "library.dll", typeof (Program.IBaz))] + [KeptTypeInAssembly ("library.dll", typeof(Program.IBaz))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.IBaz), "library.dll", typeof (Program.IBar))] + [KeptMemberInAssembly ("library.dll", typeof(Program), "CallMethod(Program/IFoo)")] +#endif + class DimProvidedByRecursiveInterface + { + static void Main () + { +#if IL_ASSEMBLY_AVAILABLE + Program.IFoo foo = new Program.MyFoo (); + Program.CallMethod(foo); +#endif + } + } +} + + + +// public static class Program +// { +// [Kept] +// interface IFoo +// { +// void Method(); +// } + +// [Kept] +// interface IBar : IFoo +// { +// [Kept] +// void IFoo.Method() { } +// } + +// [Kept] +// interface IBaz: IBar /* not IFoo */ +// { +// } + +// [Kept] +// [KeptInterface(typeof(IBaz))] +// class MyFoo : IBaz /* not IBar, not IFoo */ +// { } + +// static void CallMethod(IFoo foo) +// { +// foo.Method(); +// } +// } diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/MultipleDimsProvidedByRecursiveInterface.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/MultipleDimsProvidedByRecursiveInterface.cs new file mode 100644 index 00000000000000..ccaddf8b5309f4 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/MultipleDimsProvidedByRecursiveInterface.cs @@ -0,0 +1,88 @@ + + +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.DefaultInterfaceMethods +{ + [SetupLinkerArgument ("--skip-unresolved", "true")] + [TestCaseRequirements (TestRunCharacteristics.SupportsDefaultInterfaceMethods, "Requires support for default interface methods")] + [Define ("IL_ASSEMBLY_AVAILABLE")] + [SetupCompileBefore ("library.dll", new[] { "Dependencies/MultipleDimsProvidedByRecursiveInterface.il" })] + [SkipILVerify] + +#if IL_ASSEMBLY_AVAILABLE + // Both DIMs on I01 and I00 should be kept because one is not more specific than another. + [KeptMemberInAssembly ("library.dll", typeof(Program.I0), "Method()")] + [KeptTypeInAssembly ("library.dll", typeof(Program.I00))] + [KeptMemberInAssembly ("library.dll", typeof(Program.I00), "Program.I0.Method()")] + // Bug: DIM resolution doesn't look at recursive interfaces + //[KeptMemberInAssembly ("library.dll", typeof(Program.I01), "Program.I0.Method()")] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.I00), "library.dll", typeof (Program.I0))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.MyFoo), "library.dll", typeof (Program.I000))] + [KeptTypeInAssembly ("library.dll", typeof(Program.I000))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.I000), "library.dll", typeof (Program.I00))] + // Bug: DIM resolution doesn't look at recursive interfaces + //[KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.MyFoo), "library.dll", typeof (Program.I010))] + //[KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.I010), "library.dll", typeof (Program.I01))] + //[KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.I01), "library.dll", typeof (Program.I0))] +#endif + class MultipleDimsProvidedByRecursiveInterface + { + static void Main () + { +#if IL_ASSEMBLY_AVAILABLE + Program.I0 foo = new Program.MyFoo (); + CallMethod(foo); +#endif + } +#if IL_ASSEMBLY_AVAILABLE + [Kept] + static void CallMethod(Program.I0 foo) + { + foo.Method(); + } +#endif + } +} + + + +// public static class Program +// { +// [Kept] +// interface I0 +// { +// void Method(); +// } + +// [Kept] +// interface I00 : I0 +// { +// [Kept] +// void I0.Method() { } +// } + +// [Kept] +// interface I000: I00 /* not I0 */ +// { +// } + +// [Kept] +// interface I01 : I0 +// { +// [Kept] +// void I0.Method() { } +// } + +// [Kept] +// interface I010: I01 /* not I0 */ +// { +// } + +// [Kept] +// [KeptInterface(typeof(I000))] +// class MyFoo : I000, I010 +// { } + +// } diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/StaticDimProvidedByUnreferencedIfaceInHierarchy.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/StaticDimProvidedByUnreferencedIfaceInHierarchy.cs new file mode 100644 index 00000000000000..2d10e78d146f35 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/StaticDimProvidedByUnreferencedIfaceInHierarchy.cs @@ -0,0 +1,69 @@ + + +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.DefaultInterfaceMethods +{ + [SetupLinkerArgument ("--skip-unresolved", "true")] + [TestCaseRequirements (TestRunCharacteristics.SupportsDefaultInterfaceMethods, "Requires support for default interface methods")] + [Define ("IL_ASSEMBLY_AVAILABLE")] + [SetupCompileBefore ("library.dll", new[] { "Dependencies/StaticDimProvidedByUnreferencedIfaceInHierarchy.il" })] + [SkipILVerify] + +#if IL_ASSEMBLY_AVAILABLE + [KeptMemberInAssembly ("library.dll", typeof(Program), "CallMethod<#1>()")] + [KeptTypeInAssembly ("library.dll", typeof(Program.IBase))] + [KeptMemberInAssembly ("library.dll", typeof(Program.IBase), "Method()")] + [KeptTypeInAssembly ("library.dll", typeof(Program.I4))] + [KeptTypeInAssembly ("library.dll", typeof(Program.I2))] + [KeptMemberInAssembly ("library.dll", typeof(Program.I2), "Program.IBase.Method()")] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.I2), "library.dll", typeof (Program.IBase))] + [KeptTypeInAssembly ("library.dll", typeof(Program.I3))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.I3), "library.dll", typeof (Program.I2))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.I4), "library.dll", typeof (Program.I3))] +#endif + class StaticDimProvidedByUnreferencedIfaceInHierarchy + { + static void Main () + { +#if IL_ASSEMBLY_AVAILABLE + Program.CallMethod(); +#endif + } + } +} + + + +// public static class Program +// { +// [Kept] +// interface IBase +// { +// [Kept] +// static abstract void Method(); +// } + +// [Kept] +// [KeptInterface(typeof(IBase)] +// interface I2 : IBase +// { +// [Kept] +// static void IBase.Method() { } +// } + +// [Kept] +// [KeptInterface(typeof(I2)] +// interface I3 : I2 { } + +// [Kept] +// [KeptInterface(typeof(I3)] +// interface I4 : I3 { } + +// [Kept] +// static void CallMethod() where T : IBase +// { +// T.Method(); +// } +// } diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceVariantsGeneric.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceVariantsGeneric.cs new file mode 100644 index 00000000000000..17f5aed992ebba --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceVariantsGeneric.cs @@ -0,0 +1,50 @@ +// 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 System; +using System.Collections; +using System.Collections.Generic; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Helpers; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces +{ + public class InterfaceVariantsGeneric + { + public static void Main () + { + G g = new C (); + g.M (1, 2.0f); + } + + [Kept] + interface G + { + [Kept] + void M (T t, U u); + } + [Kept] + public class MyT { } + [Kept] + public class MyU { } + [Kept] + [KeptInterface (typeof (G))] + [KeptInterface (typeof (G))] + [KeptInterface (typeof (G))] + [KeptMember (".ctor()")] + public class C : G, G, G + { + [Kept] + public void M (int t, float u) { } + + public void M (long t, double u) { } + + [Kept] + public void M (MyT t, MyU u) { } + + [Kept] + void G.M(long t, double u) { } + } + } +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/BaseProvidesInterfaceMember/GenericInterfaceWithMethodManyVariations.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/BaseProvidesInterfaceMember/GenericInterfaceWithMethodManyVariations.cs index 078f4b94c60ba6..79ad8eaaa58c2c 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/BaseProvidesInterfaceMember/GenericInterfaceWithMethodManyVariations.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/BaseProvidesInterfaceMember/GenericInterfaceWithMethodManyVariations.cs @@ -68,4 +68,4 @@ class Bar { } } -} \ No newline at end of file +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/GenericInterfaceImplementedRecursively.il b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/GenericInterfaceImplementedRecursively.il new file mode 100644 index 00000000000000..7ff59e54ad0658 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/GenericInterfaceImplementedRecursively.il @@ -0,0 +1,45 @@ +// 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. + +.assembly extern mscorlib { } + +.assembly 'library' { } + +.class public auto ansi abstract sealed beforefieldinit Program + extends [mscorlib]System.Object +{ + // Nested Types + .class interface nested public auto ansi abstract beforefieldinit IBase`1 + { + } // end of class IBase + + .class interface nested public auto ansi abstract beforefieldinit IMiddle`1 + implements class Program/IBase`1 + { + } // end of class IMiddle + + .class interface nested public auto ansi abstract beforefieldinit IDerived`1 + implements class Program/IMiddle`1 + { + } // end of class IDerived + + .class nested public auto ansi beforefieldinit C + extends [mscorlib]System.Object + implements class Program/IDerived`1 + { + // Methods + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x2066 + // Code size 8 (0x8) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method C::.ctor + + } // end of class C +} // end of class Program diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/InterfaceImplementedRecursively.il b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/InterfaceImplementedRecursively.il new file mode 100644 index 00000000000000..c1d2943997da6f --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/InterfaceImplementedRecursively.il @@ -0,0 +1,67 @@ +// 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. + +.assembly extern mscorlib { } + +.assembly 'library' { } + +.class public auto ansi abstract sealed beforefieldinit Program + extends [System.Runtime]System.Object +{ + // Nested Types + .class interface nested public auto ansi abstract beforefieldinit IBase + { + } // end of class IBase + + .class interface nested public auto ansi abstract beforefieldinit IMiddle + implements Program/IBase + { + } // end of class IMiddle + + .class interface nested public auto ansi abstract beforefieldinit IDerived + implements Program/IMiddle + { + } // end of class IDerived + + .class nested public auto ansi beforefieldinit C + extends [System.Runtime]System.Object + implements Program/IDerived + { + // Methods + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x2066 + // Code size 8 (0x8) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void [System.Runtime]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method C::.ctor + + } // end of class C + + + // Methods + .method public hidebysig static + void Main () cil managed + { + // Method begins at RVA 0x2050 + // Code size 10 (0xa) + .maxstack 1 + .locals init ( + [0] class Program/IBase b, + [1] class Program/C c + ) + + IL_0000: nop + IL_0001: ldnull + IL_0002: stloc.0 + IL_0003: newobj instance void Program/C::.ctor() + IL_0008: stloc.1 + IL_0009: ret + } // end of method Program::Main + +} // end of class Program diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/RecursiveInterfaceTwoImplementationPaths.il b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/RecursiveInterfaceTwoImplementationPaths.il new file mode 100644 index 00000000000000..854b0cd4a45b20 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/RecursiveInterfaceTwoImplementationPaths.il @@ -0,0 +1,62 @@ + +// 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. + +.assembly extern mscorlib { } + +.assembly 'library' { } + +.class public auto ansi abstract sealed beforefieldinit Library + extends [mscorlib]System.Object +{ + .class interface nested public auto ansi abstract beforefieldinit I0 + { + } // end of class I0 + + .class interface nested public auto ansi abstract beforefieldinit I00 + implements Library/I0 + { + } // end of class I00 + + .class interface nested public auto ansi abstract beforefieldinit I01 + implements Library/I0 + { + } // end of class I01 + + .class interface nested public auto ansi abstract beforefieldinit I000 + implements Library/I00 + { + } // end of class I000 + + .class interface nested public auto ansi abstract beforefieldinit I010 + implements Library/I01 + { + } // end of class I010 + + .class interface nested public auto ansi abstract beforefieldinit I0100 + implements Library/I010 + { + } // end of class I010 + + .class nested public auto ansi beforefieldinit MyClass + extends [mscorlib]System.Object + implements Library/I0100, + Library/I000 + { + // Methods + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x2076 + // Code size 8 (0x8) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method MyFoo::.ctor + + } // end of class MyFoo + +} // end of class Library diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/GenericInterfaceImplementedRecursively.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/GenericInterfaceImplementedRecursively.cs new file mode 100644 index 00000000000000..bb0d318cac1a1d --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/GenericInterfaceImplementedRecursively.cs @@ -0,0 +1,42 @@ +// 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 System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.RecursiveInterfaces +{ + [SetupLinkerArgument ("--skip-unresolved", "true")] + [TestCaseRequirements (TestRunCharacteristics.SupportsDefaultInterfaceMethods, "Requires support for default interface methods")] + [Define ("IL_ASSEMBLY_AVAILABLE")] + [SetupCompileBefore ("library.dll", new[] { "Dependencies/GenericInterfaceImplementedRecursively.il" })] + [SkipILVerify] +#if IL_ASSEMBLY_AVAILABLE + [KeptTypeInAssembly ("library.dll", typeof(Program.IBase<>))] + [KeptTypeInAssembly ("library.dll", typeof(Program.IMiddle<>))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.IMiddle<>), "library.dll", typeof (Program.IBase<>))] + [KeptTypeInAssembly ("library.dll", typeof(Program.IDerived<>))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.IDerived<>), "library.dll", typeof (Program.IMiddle<>))] + [KeptTypeInAssembly ("library.dll", typeof(Program.C))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.C), "library.dll", typeof (Program.IDerived))] +#endif + /// + /// This test case is to verify that the linker will keep all the metadata necessary for C to implement IBase when an interfaceImpl isn't directly on C. + /// + class GenericInterfaceImplementedRecursively + { + public static void Main() + { + +#if IL_ASSEMBLY_AVAILABLE + Program.IBase _ = null; + _ = new Program.C(); +#endif + } + } +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/InterfaceImplementedRecursively.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/InterfaceImplementedRecursively.cs new file mode 100644 index 00000000000000..89f59777c5fcd0 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/InterfaceImplementedRecursively.cs @@ -0,0 +1,49 @@ +// 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 System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.RecursiveInterfaces +{ + [SetupLinkerArgument ("--skip-unresolved", "true")] + [TestCaseRequirements (TestRunCharacteristics.SupportsDefaultInterfaceMethods, "Requires support for default interface methods")] + [Define ("IL_ASSEMBLY_AVAILABLE")] + [SetupCompileBefore ("library.dll", new[] { "Dependencies/InterfaceImplementedRecursively.il" })] + [SkipILVerify] +#if IL_ASSEMBLY_AVAILABLE + [KeptTypeInAssembly ("library.dll", typeof(Program.IBase))] + [KeptTypeInAssembly ("library.dll", typeof(Program.IMiddle))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.IMiddle), "library.dll", typeof (Program.IBase))] + [KeptTypeInAssembly ("library.dll", typeof(Program.IDerived))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.IDerived), "library.dll", typeof (Program.IMiddle))] + [KeptTypeInAssembly ("library.dll", typeof(Program.C))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.C), "library.dll", typeof (Program.IDerived))] +#endif + /// + /// This test case is to verify that the linker will keep all the metadata necessary for C to implement IBase when an interfaceImpl isn't directly on C. + /// + class InterfaceImplementedRecursively + { + public static void Main() + { + +#if IL_ASSEMBLY_AVAILABLE + Program.IBase b = null; + object c = new Program.C(); + +#endif + } + } + // interface IBase {} + // interface IMiddle : IBase {} + // interface IDerived : IMiddle {} + // class C : IDerived + // { + // } +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/RecursiveInterfaceKept.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/RecursiveInterfaceKept.cs new file mode 100644 index 00000000000000..56e08d67be74f7 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/RecursiveInterfaceKept.cs @@ -0,0 +1,36 @@ +// 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; + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.RecursiveInterfaces +{ + /// + /// This tests that when a type implements an interface recursively (via implementations on implemented interfaces), + /// the interface implementations kept are in type declaration order according to ECMA-335 12.2 + /// + [TestCaseRequirements (TestRunCharacteristics.SupportsDefaultInterfaceMethods, "Requires support for default interface methods")] + [Define ("IL_ASSEMBLY_AVAILABLE")] + [SetupCompileBefore ("library.dll", new[] { "Dependencies/RecursiveInterfaceTwoImplementationPaths.il" })] + [SkipILVerify] +#if IL_ASSEMBLY_AVAILABLE + [KeptTypeInAssembly ("library.dll", typeof(Library.MyClass))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Library.MyClass), "library.dll", typeof (Library.I0100))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Library.I0100), "library.dll", typeof (Library.I010))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Library.I010), "library.dll", typeof (Library.I01))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Library.I01), "library.dll", typeof (Library.I0))] + [RemovedTypeInAssembly("library.dll", typeof(Library.I00))] + [RemovedTypeInAssembly("library.dll", typeof(Library.I000))] + [RemovedInterfaceOnTypeInAssembly("library.dll", typeof (Library.MyClass), "library.dll", typeof (Library.I000))] +#endif + public class RecursiveInterfaceKept + { + public static void Main() + { +#if IL_ASSEMBLY_AVAILABLE + Library.I0 _ = new Library.MyClass(); +#endif + } + } +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/UnreachableBlock/CompilerGeneratedCodeSubstitutions.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/UnreachableBlock/CompilerGeneratedCodeSubstitutions.cs index 5b1edc37671ace..3c59c3e446aadb 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/UnreachableBlock/CompilerGeneratedCodeSubstitutions.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/UnreachableBlock/CompilerGeneratedCodeSubstitutions.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Threading.Tasks; using Mono.Linker.Tests.Cases.Expectations.Assertions; using Mono.Linker.Tests.Cases.Expectations.Metadata; @@ -174,7 +175,8 @@ static IEnumerable TestBranchWithYieldBefore () public static void Test () { - TestBranchWithNormalCall (); + // Use the IEnumerable to mark the IEnumerable methods + foreach (var _ in TestBranchWithNormalCall ()) ; TestBranchWithYieldAfter (); TestBranchWithYieldBefore (); } diff --git a/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs b/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs index 8ed15724cc1148..467c2272c1bed4 100644 --- a/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs +++ b/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs @@ -345,7 +345,7 @@ IEnumerable VerifyInterfaces (TypeDefinition src, TypeDefinition linked) } if (expectedInterfaces.Any ()) { - yield return $"Expected interfaces were not found on {src}"; + yield return $"Expected interfaces were not found on {src}. Expected to find: \n{string.Join(Environment.NewLine, expectedInterfaces)}\n"; } } } diff --git a/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs b/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs index 7e730cb0b59211..2f05eab1e4c666 100644 --- a/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs +++ b/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs @@ -101,6 +101,7 @@ public virtual void Check (TrimmedTestCaseResult linkResult) if (!HasActiveSkipKeptItemsValidationAttribute(linkResult.TestCase.FindTypeDefinition (original))) { CreateAssemblyChecker (original, linked, linkResult).Verify (); } + CreateILChecker ().Check(linkResult, original); } VerifyLinkingOfOtherAssemblies (original); @@ -279,7 +280,6 @@ protected virtual void AdditionalChecking (TrimmedTestCaseResult linkResult, Ass protected virtual void InitialChecking (TrimmedTestCaseResult linkResult, AssemblyDefinition original, AssemblyDefinition linked) { - CreateILChecker ().Check(linkResult, original); ValidateTypeRefsHaveValidAssemblyRefs (linked); }