diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomType.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomType.cs index 95ee34ab4504b..56ab55515ce19 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomType.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomType.cs @@ -44,6 +44,7 @@ public override bool IsInstanceOfType([NotNullWhen(true)] object? o) return IsAssignableFrom(objectType); } + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] public override PropertyInfo[] GetProperties(BindingFlags bindingAttr) { // list of properties on this type according to the underlying ReflectionContext @@ -139,6 +140,7 @@ public override PropertyInfo[] GetProperties(BindingFlags bindingAttr) return binder.SelectProperty(bindingAttr, matchingProperties.ToArray(), returnType, types, modifiers); } + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] public override MethodInfo[] GetMethods(BindingFlags bindingAttr) { // list of methods on this type according to the underlying ReflectionContext diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingType.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingType.cs index bd7ef3ff90eae..5bad1592b9ba4 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingType.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingType.cs @@ -325,11 +325,17 @@ public override EventInfo[] GetEvents(BindingFlags bindingAttr) return _typeInfo.GetEvents(bindingAttr); } +#if NET8_0_OR_GREATER + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] +#endif public override FieldInfo? GetField(string name, BindingFlags bindingAttr) { return _typeInfo.GetField(name, bindingAttr); } +#if NET8_0_OR_GREATER + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] +#endif public override FieldInfo[] GetFields(BindingFlags bindingAttr) { return _typeInfo.GetFields(bindingAttr); @@ -345,6 +351,14 @@ public override Type[] GetInterfaces() return _typeInfo.GetInterfaces(); } +#if NET8_0_OR_GREATER + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields | + DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods | + DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents | + DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | + DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | + DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes)] +#endif public override MemberInfo[] GetMembers(BindingFlags bindingAttr) { return _typeInfo.GetMembers(bindingAttr); @@ -358,6 +372,9 @@ public override MemberInfo[] GetMembers(BindingFlags bindingAttr) _typeInfo.GetMethod(name, bindingAttr, binder, callConvention, types, modifiers); } +#if NET8_0_OR_GREATER + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] +#endif public override MethodInfo[] GetMethods(BindingFlags bindingAttr) { return _typeInfo.GetMethods(bindingAttr); @@ -373,6 +390,9 @@ public override Type[] GetNestedTypes(BindingFlags bindingAttr) return _typeInfo.GetNestedTypes(bindingAttr); } +#if NET8_0_OR_GREATER + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] +#endif public override PropertyInfo[] GetProperties(BindingFlags bindingAttr) { return _typeInfo.GetProperties(bindingAttr); diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingType.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingType.cs index 0ac9bb0de89f3..f61ea789bd738 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingType.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingType.cs @@ -206,11 +206,13 @@ public override EventInfo[] GetEvents(BindingFlags bindingAttr) return _projector.Project(base.GetEvents(bindingAttr), _projector.ProjectEvent); } + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] public override FieldInfo? GetField(string name, BindingFlags bindingAttr) { return _projector.ProjectField(base.GetField(name, bindingAttr)); } + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] public override FieldInfo[] GetFields(BindingFlags bindingAttr) { return _projector.Project(base.GetFields(bindingAttr), _projector.ProjectField); @@ -226,6 +228,12 @@ public override Type[] GetInterfaces() return _projector.Project(base.GetInterfaces(), _projector.ProjectType); } + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields | + DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods | + DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents | + DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | + DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | + DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes)] public override MemberInfo[] GetMembers(BindingFlags bindingAttr) { MethodInfo[] methods = GetMethods(bindingAttr); @@ -264,6 +272,7 @@ public override MemberInfo[] GetMembers(BindingFlags bindingAttr) return _projector.ProjectMethod(base.GetMethodImpl(name, bindingAttr, binder, callConvention, types, modifiers)); } + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] public override MethodInfo[] GetMethods(BindingFlags bindingAttr) { return _projector.Project(base.GetMethods(bindingAttr), _projector.ProjectMethod); @@ -279,6 +288,7 @@ public override Type[] GetNestedTypes(BindingFlags bindingAttr) return _projector.Project(base.GetNestedTypes(bindingAttr), _projector.ProjectType); } + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] public override PropertyInfo[] GetProperties(BindingFlags bindingAttr) { return _projector.Project(base.GetProperties(bindingAttr), _projector.ProjectProperty); diff --git a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs index 32fd98cbe039c..713e1d2dcace5 100644 --- a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs +++ b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs @@ -36,7 +36,6 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection.Runtime.TypeParsing; -using System.Runtime.CompilerServices; using System.Text.RegularExpressions; using ILLink.Shared; using ILLink.Shared.TrimAnalysis; @@ -779,45 +778,6 @@ void MarkMethodIfNeededByBaseMethod (MethodDefinition method) } } - /// - /// Returns true if implements and the interface implementation is marked, - /// or if any marked interface implementations on are interfaces that implement and that interface implementation is marked - /// - bool IsInterfaceImplementationMarkedRecursively (TypeDefinition type, TypeDefinition interfaceType) - { - if (type.HasInterfaces) { - foreach (var intf in type.Interfaces) { - TypeDefinition? resolvedInterface = Context.Resolve (intf.InterfaceType); - if (resolvedInterface == null) - continue; - - if (Annotations.IsMarked (intf) && RequiresInterfaceRecursively (resolvedInterface, interfaceType)) - return true; - } - } - - return false; - } - - bool RequiresInterfaceRecursively (TypeDefinition typeToExamine, TypeDefinition interfaceType) - { - if (typeToExamine == interfaceType) - return true; - - if (typeToExamine.HasInterfaces) { - foreach (var iface in typeToExamine.Interfaces) { - var resolved = Context.TryResolve (iface.InterfaceType); - if (resolved == null) - continue; - - if (RequiresInterfaceRecursively (resolved, interfaceType)) - return true; - } - } - - return false; - } - void ProcessDefaultImplementation (OverrideInformation ov) { Debug.Assert (ov.IsOverrideOfInterfaceMember); @@ -825,7 +785,9 @@ void ProcessDefaultImplementation (OverrideInformation ov) || ov.Override.IsStatic && !Annotations.IsRelevantToVariantCasting (ov.InterfaceImplementor.Implementor)) return; - MarkInterfaceImplementation (ov.InterfaceImplementor.InterfaceImplementation); + foreach (var ifaceImpl in ov.InterfaceImplementor.MostDirectInterfaceImplementationPath) { + MarkInterfaceImplementation (ifaceImpl); + } } void MarkMarshalSpec (IMarshalInfoProvider spec, in DependencyInfo reason) @@ -2450,37 +2412,35 @@ void MarkNamedProperty (TypeDefinition type, string property_name, in Dependency void MarkInterfaceImplementations (TypeDefinition type) { - if (!type.HasInterfaces) - return; - - foreach (var iface in type.Interfaces) { - // 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)); + foreach (var interfaceImplementor in Annotations.GetRecursiveInterfaces (type)) { + if (ShouldMarkInterfaceImplementation (interfaceImplementor)) { + foreach (InterfaceImplementation interfaceImpl in interfaceImplementor.MostDirectInterfaceImplementationPath) { + MarkInterfaceImplementation (interfaceImpl, new MessageOrigin (type)); + } + } } } - protected virtual bool ShouldMarkInterfaceImplementation (TypeDefinition type, InterfaceImplementation iface) + protected virtual bool ShouldMarkInterfaceImplementation (InterfaceImplementor interfaceImplementor) { - if (Annotations.IsMarked (iface)) + if (interfaceImplementor.IsMostDirectImplementationMarked (Annotations)) return false; - if (!Context.IsOptimizationEnabled (CodeOptimizations.UnusedInterfaces, type)) + if (!Context.IsOptimizationEnabled (CodeOptimizations.UnusedInterfaces, interfaceImplementor.Implementor)) return true; - if (Context.Resolve (iface.InterfaceType) is not TypeDefinition resolvedInterfaceType) + if (interfaceImplementor.InterfaceType is null) return false; - if (Annotations.IsMarked (resolvedInterfaceType)) + if (Annotations.IsMarked (interfaceImplementor.InterfaceType)) return true; // It's hard to know if a com or windows runtime interface will be needed from managed code alone, // so as a precaution we will mark these interfaces once the type is instantiated - if (resolvedInterfaceType.IsImport || resolvedInterfaceType.IsWindowsRuntime) + if (interfaceImplementor.InterfaceType.IsImport || interfaceImplementor.InterfaceType.IsWindowsRuntime) return true; - return IsFullyPreserved (type); + return IsFullyPreserved (interfaceImplementor.Implementor); } void MarkGenericParameterProvider (IGenericParameterProvider provider) @@ -2560,12 +2520,10 @@ bool IsInterfaceImplementationMethodNeededByTypeDueToInterface (OverrideInformat if (Annotations.IsMarked (method)) return false; - // If the interface implementation is not marked, do not mark the implementation method - // A type that doesn't implement the interface isn't required to have methods that implement the interface. - InterfaceImplementation? iface = overrideInformation.InterfaceImplementor.InterfaceImplementation; - if (!((iface is not null && Annotations.IsMarked (iface)) - || IsInterfaceImplementationMarkedRecursively (method.DeclaringType, @base.DeclaringType))) + // If there is no chain of interface implementations that make the type implement the interface, the method isn't required + if (!overrideInformation.InterfaceImplementor.IsMarked(Annotations)) { return false; + } // If the interface method is not marked and the interface doesn't come from a preserved scope, do not mark the implementation method // Unmarked interface methods from link assemblies will be removed so the implementing method does not need to be kept. diff --git a/src/tools/illink/src/linker/Linker/Annotations.cs b/src/tools/illink/src/linker/Linker/Annotations.cs index a7b3198265e81..6a21a921e76f9 100644 --- a/src/tools/illink/src/linker/Linker/Annotations.cs +++ b/src/tools/illink/src/linker/Linker/Annotations.cs @@ -31,6 +31,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; @@ -717,5 +718,7 @@ public void EnqueueVirtualMethod (MethodDefinition method) if (FlowAnnotations.RequiresVirtualMethodDataFlowAnalysis (method) || HasLinkerAttribute (method)) VirtualMethodsWithAnnotationsToValidate.Add (method); } + + internal ImmutableArray GetRecursiveInterfaces (TypeDefinition type) => TypeMapInfo.GetRecursiveInterfaces (type); } } diff --git a/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs b/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs index e981ce872703f..321584cb74e27 100644 --- a/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs +++ b/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs @@ -2,9 +2,9 @@ // 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 System.Collections.Immutable; using System.Diagnostics; +using System.Linq; using Mono.Cecil; namespace Mono.Linker @@ -15,45 +15,164 @@ public class InterfaceImplementor /// The type that implements . /// public TypeDefinition Implementor { get; } + /// - /// The .interfaceimpl on that points to + /// The type of the interface that is implemented by + /// Null if the type could not be resolved /// - public InterfaceImplementation InterfaceImplementation { get; } + public TypeDefinition? InterfaceType { get; } + /// - /// The type of the interface that is implemented by + /// A to the with the generic parameters substituted. /// - public TypeDefinition InterfaceType { get; } + public TypeReference InflatedInterface { get; } - public InterfaceImplementor (TypeDefinition implementor, InterfaceImplementation interfaceImplementation, TypeDefinition interfaceType, IMetadataResolver resolver) + /// + /// The graphs of s that make implement the . + /// There can be many ways a type implements an interface if an explicit interface implementation is given for an interface that is also implemented recursively due to another interface implementation. + /// It will be in the following order: + /// 1. Explicit interface implementation on + /// 2. Explicit interface implementation on a base type of + /// 3. Recursive interface implementations on an explicitly implemented interface on or it's base types + /// + public readonly ImmutableArray InterfaceImplementationNodes; + + public bool HasExplicitImplementation => InterfaceImplementationNodes[0].Length == 0; + + public InterfaceImplementor (TypeDefinition implementor, TypeDefinition? interfaceType, TypeReference inflatedInterface, ImmutableArray implNode, LinkContext context) { Implementor = implementor; - InterfaceImplementation = interfaceImplementation; InterfaceType = interfaceType; - Debug.Assert(resolver.Resolve (interfaceImplementation.InterfaceType) == interfaceType); + InterfaceImplementationNodes = implNode; + InflatedInterface = inflatedInterface; + Debug.Assert (context.Resolve (inflatedInterface) == interfaceType); + Debug.Assert (implNode.Length != 0); + Debug.Assert (implNode.All (i => interfaceType == context.Resolve (i.GetLast ().InterfaceType))); + } + + /// + /// An Enumerable over the most direct chain to the + /// + public ShortestInterfaceImplementationChainEnumerator MostDirectInterfaceImplementationPath => new (this); + + public struct ShortestInterfaceImplementationChainEnumerator + { + InterfaceImplementationNode _current; + bool _hasMoved; + public ShortestInterfaceImplementationChainEnumerator (InterfaceImplementor implementor) + { + _current = implementor.InterfaceImplementationNodes[0]; + _hasMoved = false; + } + public ShortestInterfaceImplementationChainEnumerator GetEnumerator () => this; + public InterfaceImplementation Current => _current.InterfaceImplementation; + + public bool MoveNext () + { + if (!_hasMoved) { + _hasMoved = true; + return true; + } + if (_current.Next.Length == 0) + return false; + _current = _current.Next[0]; + return true; + } + } + + /// + /// Returns true if the most direct implementation of is marked. may still have a different recursive implementation marked. + /// + public bool IsMostDirectImplementationMarked (AnnotationStore annotations) + { + return InterfaceImplementationNodes[0].IsMostDirectImplementationMarked (annotations); } - public static InterfaceImplementor Create(TypeDefinition implementor, TypeDefinition interfaceType, IMetadataResolver resolver) + /// + /// Returns true if implements via any of the possible interface implementation chains. + /// + public bool IsMarked (AnnotationStore annotations) { - foreach(InterfaceImplementation iface in implementor.Interfaces) { - if (resolver.Resolve(iface.InterfaceType) == interfaceType) { - return new InterfaceImplementor(implementor, iface, interfaceType, resolver); + foreach (var i in InterfaceImplementationNodes) { + if (i.IsMarked (annotations)) + return true; + } + return false; + } + + /// + /// Represents a node in the graph of a type implementing an interface. + /// + public sealed class InterfaceImplementationNode + { + /// + /// The that is on that is part of the chain of interface implementations. + /// + public InterfaceImplementation InterfaceImplementation { get; } + + /// + /// The type that has in its . + /// + public TypeDefinition InterfaceImplementationProvider { get; } + + /// + /// The s that are on the type pointed to by that lead to the interface type. + /// + public ImmutableArray Next { get; } + + /// + /// The number of interface implementations on the most direct way the interface is implemented from + /// + public int Length { + get { + if (_length != -1) + return _length; + if (Next.Length == 0) + return _length = 0; + return _length = Next[0].Length + 1; } } + int _length = -1; + + public InterfaceImplementationNode (InterfaceImplementation interfaceImplementation, TypeDefinition interfaceImplementationProvider, ImmutableArray next) + { + InterfaceImplementation = interfaceImplementation; + InterfaceImplementationProvider = interfaceImplementationProvider; + Next = next; + } + + public bool IsMostDirectImplementationMarked (AnnotationStore annotations) + { + if (!annotations.IsMarked (InterfaceImplementation)) + return false; + if (Next.Length == 0) + return true; + return Next[0].IsMostDirectImplementationMarked (annotations); + } + + public bool IsMarked (AnnotationStore annotations) + { + if (!annotations.IsMarked (InterfaceImplementation)) + return false; + + if (Next.Length == 0) + return true; - Queue ifacesToCheck = new (); - ifacesToCheck.Enqueue(implementor); - while (ifacesToCheck.Count > 0) { - var currentIface = ifacesToCheck.Dequeue (); + foreach (var impl in Next) { + if (impl.IsMarked (annotations)) + return true; + } + return false; + } - foreach(InterfaceImplementation ifaceImpl in currentIface.Interfaces) { - var iface = resolver.Resolve (ifaceImpl.InterfaceType); - if (iface == interfaceType) { - return new InterfaceImplementor(implementor, ifaceImpl, interfaceType, resolver); - } - ifacesToCheck.Enqueue (iface); + public InterfaceImplementation GetLast () + { + var curr = this; + while (curr.Next.Length > 0) { + curr = curr.Next[0]; } + return curr.InterfaceImplementation; } - throw new InvalidOperationException ($"Type '{implementor.FullName}' does not implement interface '{interfaceType.FullName}' directly or through any interfaces"); } } } diff --git a/src/tools/illink/src/linker/Linker/OverrideInformation.cs b/src/tools/illink/src/linker/Linker/OverrideInformation.cs index 0727d5d25c19a..c306a3c111eb1 100644 --- a/src/tools/illink/src/linker/Linker/OverrideInformation.cs +++ b/src/tools/illink/src/linker/Linker/OverrideInformation.cs @@ -10,10 +10,20 @@ namespace Mono.Linker [DebuggerDisplay ("{Override}")] public class OverrideInformation { + /// + /// The method that is being overridden or implemented + /// public MethodDefinition Base { get; } + /// + /// The method that overrides . For interface methods, this may be a method on a base type, or a default interface method. + /// public MethodDefinition Override { get; } + /// + /// For overrides of interface methods, includes info about the interface / implementor pair that correspong to the base / override pair + /// The may be a method on , one of it's base types, or a default interface method on one of the interfaces implemented by it. + /// internal InterfaceImplementor? InterfaceImplementor { get; } internal OverrideInformation (MethodDefinition @base, MethodDefinition @override, InterfaceImplementor? interfaceImplementor = null) @@ -22,14 +32,23 @@ internal OverrideInformation (MethodDefinition @base, MethodDefinition @override Override = @override; InterfaceImplementor = interfaceImplementor; // Ensure we have an interface implementation if the base method is from an interface and the override method is on a class - Debug.Assert(@base.DeclaringType.IsInterface && interfaceImplementor != null + Debug.Assert (@base.DeclaringType.IsInterface && interfaceImplementor != null || !@base.DeclaringType.IsInterface && interfaceImplementor == null); // Ensure the interfaceImplementor is for the interface we expect Debug.Assert (@base.DeclaringType.IsInterface ? interfaceImplementor!.InterfaceType == @base.DeclaringType : true); } - public InterfaceImplementation? MatchingInterfaceImplementation - => InterfaceImplementor?.InterfaceImplementation; + public InterfaceImplementation? MatchingInterfaceImplementation { + get { + if (InterfaceImplementor is null) + return null; + InterfaceImplementation? last = null; + foreach (InterfaceImplementation curr in InterfaceImplementor!.MostDirectInterfaceImplementationPath) { + last = curr; + } + return last; + } + } public TypeDefinition? InterfaceType => InterfaceImplementor?.InterfaceType; diff --git a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs index 7e68b71bcb7d0..a5304569192be 100644 --- a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs +++ b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs @@ -29,16 +29,16 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // -using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using Mono.Cecil; +using static Mono.Linker.InterfaceImplementor; namespace Mono.Linker { - public class TypeMapInfo { readonly HashSet assemblies = new HashSet (); @@ -46,6 +46,7 @@ public class TypeMapInfo protected readonly Dictionary> base_methods = new Dictionary> (); protected readonly Dictionary> override_methods = new Dictionary> (); protected readonly Dictionary> default_interface_implementations = new Dictionary> (); + readonly Dictionary> _interfaces = new (); public TypeMapInfo (LinkContext context) { @@ -87,6 +88,18 @@ public void EnsureProcessed (AssemblyDefinition assembly) return bases; } + InterfaceImplementor? GetInterfaceImplementor (TypeDefinition implementor, TypeDefinition interfaceType) + { + if (!_interfaces.TryGetValue (implementor, out var implrs)) + return null; + + foreach (var iface in implrs) { + if (iface.InterfaceType == interfaceType) + return iface; + } + return null; + } + /// /// Returns a list of all default interface methods that implement for a type. /// ImplementingType is the type that implements the interface, @@ -96,28 +109,38 @@ public void EnsureProcessed (AssemblyDefinition assembly) /// The interface method to find default implementations for public IEnumerable? GetDefaultInterfaceImplementations (MethodDefinition baseMethod) { + EnsureProcessed (baseMethod.Module.Assembly); default_interface_implementations.TryGetValue (baseMethod, out var ret); return ret; } - public void AddBaseMethod (MethodDefinition method, MethodDefinition @base, InterfaceImplementor? interfaceImplementor) + internal ImmutableArray GetRecursiveInterfaces (TypeDefinition type) + { + EnsureProcessed (type.Module.Assembly); + return _interfaces.TryGetValue (type, out var value) ? value : []; + } + + void AddBaseMethod (MethodDefinition method, OverrideInformation overrideInformation) { - base_methods.AddToList (method, new OverrideInformation (@base, method, interfaceImplementor)); + Debug.Assert (overrideInformation.Override == method); + base_methods.AddToList (method, overrideInformation); } - public void AddOverride (MethodDefinition @base, MethodDefinition @override, InterfaceImplementor? interfaceImplementor = null) + void AddOverrideOfMethod (MethodDefinition @base, OverrideInformation overrideInformation) { - override_methods.AddToList (@base, new OverrideInformation (@base, @override, interfaceImplementor)); + Debug.Assert (overrideInformation.Base == @base); + override_methods.AddToList (@base, overrideInformation); } - public void AddDefaultInterfaceImplementation (MethodDefinition @base, InterfaceImplementor interfaceImplementor, MethodDefinition defaultImplementationMethod) + void AddDefaultInterfaceImplementation (MethodDefinition @base, InterfaceImplementor interfaceImplementor, MethodDefinition defaultImplementationMethod) { - Debug.Assert(@base.DeclaringType.IsInterface); + Debug.Assert (@base.DeclaringType.IsInterface); default_interface_implementations.AddToList (@base, new OverrideInformation (@base, defaultImplementationMethod, interfaceImplementor)); } - protected virtual void MapType (TypeDefinition type) + void MapType (TypeDefinition type) { + MapInterfacesOnType (type); MapVirtualMethods (type); MapInterfaceMethodsInTypeHierarchy (type); @@ -128,6 +151,109 @@ protected virtual void MapType (TypeDefinition type) MapType (nested); } + void MapInterfacesOnType (TypeDefinition type) + { + if (_interfaces.ContainsKey (type)) + return; + + // Map from inflated interface type => list of every way to recursively implement that interface in 'type declaration order' according to ECMA 335 12.2 + Dictionary> waysToImplementIface = new (InflatedInterfaceComparer); + + // Get all explicit interfaces of this type + foreach (var directIface in type.Interfaces) { + InterfaceImplementationNode directlyImplementedNode = new InterfaceImplementationNode (directIface, type, []); + TypeReference inflatedDirectIface = directIface.InterfaceType.TryInflateFrom (type, context)!; + waysToImplementIface.AddToList (inflatedDirectIface, directlyImplementedNode); + } + + // Add interfaces on base type + if (type.BaseType is { } baseType && context.Resolve (baseType) is { } baseDef) { + MapInterfacesOnType (baseDef); + var baseInterfaces = _interfaces[baseDef]; + foreach (var item in baseInterfaces) { + var inflatedInterface = item.InflatedInterface.TryInflateFrom (type.BaseType, context); + Debug.Assert (inflatedInterface is not null); + foreach (var node in item.InterfaceImplementationNodes) { + waysToImplementIface.AddToList (inflatedInterface, node); + } + } + } + + // Recursive interfaces next to preserve Inherit/Implement tree order + foreach (var directIface in type.Interfaces) { + // If we can't resolve the interface type we can't find recursive interfaces + var ifaceDirectlyOnType = context.Resolve (directIface.InterfaceType); + if (ifaceDirectlyOnType is null) { + continue; + } + MapInterfacesOnType (ifaceDirectlyOnType); + TypeReference inflatedDirectIface = directIface.InterfaceType.TryInflateFrom (type, context)!; + var recursiveInterfaces = _interfaces[ifaceDirectlyOnType]; + foreach (var recursiveInterface in recursiveInterfaces) { + var implToRecursiveIfaceChain = new InterfaceImplementationNode (directIface, type, recursiveInterface.InterfaceImplementationNodes); + // Inflate the generic arguments up to the terminal interfaceImpl to get the inflated interface type implemented by this type + TypeReference inflatedRecursiveInterface = inflatedDirectIface; + foreach (var interfaceImpl in recursiveInterface.MostDirectInterfaceImplementationPath) { + inflatedRecursiveInterface = interfaceImpl.InterfaceType.TryInflateFrom (inflatedRecursiveInterface, context)!; + } + waysToImplementIface.AddToList (inflatedRecursiveInterface, implToRecursiveIfaceChain); + } + } + ImmutableArray.Builder builder = ImmutableArray.CreateBuilder (waysToImplementIface.Count); + foreach (var kvp in waysToImplementIface) { + builder.Add (new InterfaceImplementor (type, context.Resolve (kvp.Key), kvp.Key, kvp.Value.ToImmutableArray (), context)); + } + _interfaces.Add (type, builder.MoveToImmutable ()); + } + + /// + /// Used only to compare inflated interfaces in MapInterfacesOnType + /// + EqualityComparer InflatedInterfaceComparer => _inflatedInterfaceComparer ??= EqualityComparer.Create (InterfaceTypeEquals, t => context.Resolve (t)?.GetHashCode () ?? 0); + EqualityComparer? _inflatedInterfaceComparer; + + /// + /// Compares two TypeReferences to interface types and determines if they are equivalent references, taking into account generic arguments and element types. + /// + bool InterfaceTypeEquals (TypeReference? type, TypeReference? other) + { + Debug.Assert (type is not null && other is not null); + Debug.Assert (context.TryResolve (type)?.IsInterface is null or true); + Debug.Assert (context.TryResolve (other)?.IsInterface is null or true); + return TypeEquals (type, other); + + bool TypeEquals (TypeReference type1, TypeReference type2) + { + if (type1 == type2) + return true; + + if (context.TryResolve (type1) != context.TryResolve (type2)) + return false; + + if (type1 is GenericInstanceType genericInstance1) { + if (type2 is not GenericInstanceType genericInstance2) + return false; + if (genericInstance1.HasGenericParameters != genericInstance2.HasGenericParameters) + return false; + if (genericInstance1.GenericParameters.Count != genericInstance2.GenericParameters.Count + || genericInstance2.GenericArguments.Count != genericInstance2.GenericArguments.Count) + return false; + for (var i = 0; i < genericInstance1.GenericArguments.Count; ++i) { + if (!TypeEquals (genericInstance1.GenericArguments[i], genericInstance2.GenericArguments[i])) + return false; + } + return true; + } + + if (type1 is TypeSpecification typeSpec1) { + if (type2 is not TypeSpecification typeSpec2) + return false; + return TypeEquals (typeSpec1.ElementType, typeSpec2.ElementType); + } + return type1.FullName == type2.FullName; + } + } + void MapInterfaceMethodsInTypeHierarchy (TypeDefinition type) { if (!type.HasInterfaces) @@ -135,8 +261,9 @@ void MapInterfaceMethodsInTypeHierarchy (TypeDefinition type) // Foreach interface and for each newslot virtual method on the interface, try // to find the method implementation and record it. - foreach (var interfaceImpl in type.GetInflatedInterfaces (context)) { - foreach (MethodReference interfaceMethod in interfaceImpl.InflatedInterface.GetMethods (context)) { + var allInflatedInterface = _interfaces[type]; + foreach (var interfaceImplementor in allInflatedInterface) { + foreach (MethodReference interfaceMethod in interfaceImplementor.InflatedInterface.GetMethods (context)) { MethodDefinition? resolvedInterfaceMethod = context.TryResolve (interfaceMethod); if (resolvedInterfaceMethod == null) continue; @@ -155,20 +282,19 @@ void MapInterfaceMethodsInTypeHierarchy (TypeDefinition type) // Try to find an implementation with a name/sig match on the current type MethodDefinition? exactMatchOnType = TryMatchMethod (type, interfaceMethod); if (exactMatchOnType != null) { - AnnotateMethods (resolvedInterfaceMethod, exactMatchOnType, new (type, interfaceImpl.OriginalImpl, resolvedInterfaceMethod.DeclaringType, context)); + AnnotateMethods (resolvedInterfaceMethod, exactMatchOnType, interfaceImplementor); continue; } // Next try to find an implementation with a name/sig match in the base hierarchy var @base = GetBaseMethodInTypeHierarchy (type, interfaceMethod); if (@base != null) { - AnnotateMethods (resolvedInterfaceMethod, @base, new (type, interfaceImpl.OriginalImpl, resolvedInterfaceMethod.DeclaringType, context)); + AnnotateMethods (resolvedInterfaceMethod, @base, interfaceImplementor); continue; } } - // Look for a default implementation last. - FindAndAddDefaultInterfaceImplementations (type, type, resolvedInterfaceMethod, interfaceImpl.OriginalImpl); + FindAndAddDefaultInterfaceImplementations (type, resolvedInterfaceMethod, interfaceImplementor); } } } @@ -198,7 +324,7 @@ void MapVirtualMethod (MethodDefinition method) if (@base == null) return; - Debug.Assert(!@base.DeclaringType.IsInterface); + Debug.Assert (!@base.DeclaringType.IsInterface); AnnotateMethods (@base, method); } @@ -210,7 +336,9 @@ void MapOverrides (MethodDefinition method) if (baseMethod == null) continue; if (baseMethod.DeclaringType.IsInterface) { - AnnotateMethods (baseMethod, method, InterfaceImplementor.Create (method.DeclaringType, baseMethod.DeclaringType, context)); + var implr = GetInterfaceImplementor (method.DeclaringType, baseMethod.DeclaringType); + Debug.Assert (implr != null); + AnnotateMethods (baseMethod, method, implr); } else { AnnotateMethods (baseMethod, method); } @@ -219,8 +347,9 @@ void MapOverrides (MethodDefinition method) void AnnotateMethods (MethodDefinition @base, MethodDefinition @override, InterfaceImplementor? interfaceImplementor = null) { - AddBaseMethod (@override, @base, interfaceImplementor); - AddOverride (@base, @override, interfaceImplementor); + OverrideInformation ov = new OverrideInformation (@base, @override, interfaceImplementor); + AddBaseMethod (@override, ov); + AddOverrideOfMethod (@base, ov); } MethodDefinition? GetBaseMethodInTypeHierarchy (MethodDefinition method) @@ -282,22 +411,20 @@ void AnnotateMethods (MethodDefinition @base, MethodDefinition @override, Interf /// /// The InterfaceImplementation on that points to the DeclaringType of . /// - void FindAndAddDefaultInterfaceImplementations (TypeDefinition typeThatImplementsInterface, TypeDefinition typeThatMayHaveDIM, MethodDefinition interfaceMethodToBeImplemented, InterfaceImplementation originalInterfaceImpl) + void FindAndAddDefaultInterfaceImplementations (TypeDefinition typeThatImplementsInterface, MethodDefinition interfaceMethodToBeImplemented, InterfaceImplementor implr) { - // Go over all interfaces, trying to find a method that is an explicit MethodImpl of the - // interface method in question. - - foreach (var interfaceImpl in typeThatMayHaveDIM.Interfaces) { - var potentialImplInterface = context.TryResolve (interfaceImpl.InterfaceType); - if (potentialImplInterface == null) - continue; - + foreach (var potentialDimProviderImplementation in _interfaces[typeThatImplementsInterface]) { bool foundImpl = false; + if (potentialDimProviderImplementation.InterfaceType is null) + continue; + // If this interface doesn't implement the interface with the method we're looking for, it can't provide a default implementation. + if (GetInterfaceImplementor (potentialDimProviderImplementation.InterfaceType, interfaceMethodToBeImplemented.DeclaringType) is null) + continue; - foreach (var potentialImplMethod in potentialImplInterface.Methods) { + foreach (var potentialImplMethod in potentialDimProviderImplementation.InterfaceType.Methods) { if (potentialImplMethod == interfaceMethodToBeImplemented && !potentialImplMethod.IsAbstract) { - AddDefaultInterfaceImplementation (interfaceMethodToBeImplemented, new (typeThatImplementsInterface, originalInterfaceImpl, interfaceMethodToBeImplemented.DeclaringType, context), potentialImplMethod); + AddDefaultInterfaceImplementation (interfaceMethodToBeImplemented, implr, potentialImplMethod); foundImpl = true; break; } @@ -308,22 +435,15 @@ void FindAndAddDefaultInterfaceImplementations (TypeDefinition typeThatImplement // This method is an override of something. Let's see if it's the method we are looking for. foreach (var baseMethod in potentialImplMethod.Overrides) { if (context.TryResolve (baseMethod) == interfaceMethodToBeImplemented) { - AddDefaultInterfaceImplementation (interfaceMethodToBeImplemented, new (typeThatImplementsInterface, originalInterfaceImpl, interfaceMethodToBeImplemented.DeclaringType, context), @potentialImplMethod); + AddDefaultInterfaceImplementation (interfaceMethodToBeImplemented, implr, @potentialImplMethod); foundImpl = true; break; } } - if (foundImpl) { break; } } - - // We haven't found a MethodImpl on the current interface, but one of the interfaces - // this interface requires could still provide it. - if (!foundImpl) { - FindAndAddDefaultInterfaceImplementations (typeThatImplementsInterface, potentialImplInterface, interfaceMethodToBeImplemented, originalInterfaceImpl); - } } } diff --git a/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs b/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs index 5092fe1158e34..b886ad8aaa4d8 100644 --- a/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs +++ b/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs @@ -170,6 +170,13 @@ void parseArrayDimensions (ArrayType at) } } + public static TypeReference? TryInflateFrom (this TypeReference type, TypeReference maybeGenericInstanceProvider, ITryResolveMetadata resolver) + { + if (maybeGenericInstanceProvider is GenericInstanceType git) + return InflateGenericType (git, type, resolver); + return type; + } + public static TypeReference? InflateGenericType (GenericInstanceType genericInstanceProvider, TypeReference typeToInflate, ITryResolveMetadata resolver) { if (typeToInflate is ArrayType arrayType) { 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 4b3f387a39015..19189bfcd0170 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 0000000000000..d436348e800bc --- /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 649b8449527f7..2e1a2bbcb3454 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 @@ -15,12 +15,6 @@ public Task CanDisableUnusedInterfaces () return RunTest (allowMissingWarnings: true); } - [Fact] - public Task InterfaceImplementedThroughBaseInterface () - { - return RunTest (allowMissingWarnings: true); - } - [Fact] public Task InterfaceOnUninstantiatedTypeRemoved () { 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 d0d236997445e..9fc2b77d7a487 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 @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Runtime.CompilerServices; using System.Threading.Tasks; using ILLink.RoslynAnalyzer; @@ -1155,7 +1156,8 @@ static void GuardedLocalFunction () public static void Test () { - GuardInIterator (); + // Use the IEnumerable to mark the IEnumerable methods + foreach (var _ in GuardInIterator ()) ; StateFlowsAcrossYield (); GuardInAsync (); StateFlowsAcrossAwait (); 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 0000000000000..c85892208989b --- /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 0000000000000..4937513f28531 --- /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 0000000000000..949d5e6fbf4ab --- /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 0000000000000..743df7227a80c --- /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 0000000000000..7d027825ec409 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/MultipleDimsProvidedByRecursiveInterface.cs @@ -0,0 +1,86 @@ + + +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()")] + [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))] + [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 0000000000000..2d10e78d146f3 --- /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/Dependencies/InterfaceImplementedThroughBaseInterface.il b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/Dependencies/InterfaceImplementedThroughBaseInterface.il deleted file mode 100644 index 61080f8b7d066..0000000000000 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/Dependencies/InterfaceImplementedThroughBaseInterface.il +++ /dev/null @@ -1,48 +0,0 @@ -// 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 interface public auto ansi abstract beforefieldinit IBase -{ - // Methods - .method public hidebysig newslot abstract virtual - instance void M () cil managed - { - } // end of method IBase::M - -} // end of class IBase - -.class interface public auto ansi abstract beforefieldinit IDerived - implements IBase -{ -} // end of class IDerived - -.class public auto ansi beforefieldinit C - extends [System.Runtime]System.Object - implements IDerived -{ - // Methods - .method private final hidebysig newslot virtual - instance void IBase.M () cil managed - { - .override method instance void IBase::M() - // Method begins at RVA 0x2050 - // Code size 2 (0x2) - .maxstack 8 - - IL_0001: ret - } // end of method C::IBase.M - - .method public hidebysig specialname rtspecialname - instance void .ctor () cil managed - { - // Method begins at RVA 0x2053 - // Code size 8 (0x8) - .maxstack 8 - - IL_0007: ret - } // end of method C::.ctor -} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceImplementedThroughBaseInterface.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceImplementedThroughBaseInterface.cs deleted file mode 100644 index e701fb9c28ba6..0000000000000 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceImplementedThroughBaseInterface.cs +++ /dev/null @@ -1,34 +0,0 @@ -// 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 -{ - [SetupLinkerArgument ("--skip-unresolved", "true")] - [SetupLinkerArgument ("-a", "test.exe", "library")] - [SetupLinkerArgument ("-a", "library.dll", "library")] - [TestCaseRequirements (TestRunCharacteristics.SupportsDefaultInterfaceMethods, "Requires support for default interface methods")] - [Define ("IL_ASSEMBLY_AVAILABLE")] - [SetupCompileBefore ("library.dll", new[] { "Dependencies/InterfaceImplementedThroughBaseInterface.il" })] - [SkipILVerify] - -#if IL_ASSEMBLY_AVAILABLE - [KeptMemberInAssembly ("library.dll", typeof(C), "IBase.M()")] -#endif - [KeptMember(".ctor()")] - public class InterfaceImplementedThroughBaseInterface - { - public static void Main () - { - } - } -} - - 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 078f4b94c60ba..79ad8eaaa58c2 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 0000000000000..7ff59e54ad065 --- /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 0000000000000..c1d2943997da6 --- /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 0000000000000..854b0cd4a45b2 --- /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 0000000000000..bb0d318cac1a1 --- /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 0000000000000..89f59777c5fcd --- /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 0000000000000..56e08d67be74f --- /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 5b1edc37671ac..3c59c3e446aad 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 8ed15724cc114..467c2272c1bed 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"; } } }