diff --git a/src/tools/illink/src/linker/Linker.Dataflow/FlowAnnotations.cs b/src/tools/illink/src/linker/Linker.Dataflow/FlowAnnotations.cs index d8cee744c9c6b..b21b2aebe50ea 100644 --- a/src/tools/illink/src/linker/Linker.Dataflow/FlowAnnotations.cs +++ b/src/tools/illink/src/linker/Linker.Dataflow/FlowAnnotations.cs @@ -559,8 +559,8 @@ void ValidateMethodGenericParametersHaveNoAnnotations (DynamicallyAccessedMember void LogValidationWarning (IMetadataTokenProvider provider, IMetadataTokenProvider baseProvider, OverrideInformation ov) { - IMemberDefinition origin = (ov.IsOverrideOfInterfaceMember && ov.InterfaceImplementor.Implementor != ov.Override.DeclaringType) - ? ov.InterfaceImplementor.Implementor + IMemberDefinition origin = (ov.IsOverrideOfInterfaceMember && ov.RuntimeInterfaceImplementation.Implementor != ov.Override.DeclaringType) + ? ov.RuntimeInterfaceImplementation.Implementor : ov.Override; Debug.Assert (provider.GetType () == baseProvider.GetType ()); Debug.Assert (!(provider is GenericParameter genericParameter) || genericParameter.DeclaringMethod != null); diff --git a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs index 2e41771ee3963..193596246d5a7 100644 --- a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs +++ b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs @@ -32,6 +32,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; @@ -156,6 +157,7 @@ internal DynamicallyAccessedMembersTypeHierarchy DynamicallyAccessedMembersTypeH DependencyKind.ReturnTypeMarshalSpec, DependencyKind.DynamicInterfaceCastableImplementation, DependencyKind.XmlDescriptor, + DependencyKind.InterfaceImplementationOnType, }; static readonly DependencyKind[] _methodReasons = new DependencyKind[] { @@ -713,13 +715,6 @@ void MarkMethodIfNeededByBaseMethod (MethodDefinition method, MessageOrigin orig } } - /// - /// 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) - => IsInterfaceImplementationMarkedRecursively (type, interfaceType, Context); - /// /// 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 @@ -740,18 +735,19 @@ internal static bool IsInterfaceImplementationMarkedRecursively (TypeDefinition return true; } } - + if (type.BaseType is not null && context.Resolve (type.BaseType) is { } baseType) + return IsInterfaceImplementationMarkedRecursively (baseType, interfaceType, context); return false; } void ProcessDefaultImplementation (OverrideInformation ov, MessageOrigin origin) { Debug.Assert (ov.IsOverrideOfInterfaceMember); - if ((!ov.Override.IsStatic && !Annotations.IsInstantiated (ov.InterfaceImplementor.Implementor)) - || ov.Override.IsStatic && !Annotations.IsRelevantToVariantCasting (ov.InterfaceImplementor.Implementor)) + if ((!ov.Override.IsStatic && !Annotations.IsInstantiated (ov.RuntimeInterfaceImplementation.Implementor)) + || ov.Override.IsStatic && !Annotations.IsRelevantToVariantCasting (ov.RuntimeInterfaceImplementation.Implementor)) return; - MarkInterfaceImplementation (ov.InterfaceImplementor.InterfaceImplementation, origin); + MarkRuntimeInterfaceImplementation (ov.RuntimeInterfaceImplementation, origin); } void MarkMarshalSpec (IMarshalInfoProvider spec, in DependencyInfo reason, MessageOrigin origin) @@ -2327,38 +2323,37 @@ void MarkNamedProperty (TypeDefinition type, string property_name, in Dependency void MarkInterfaceImplementations (TypeDefinition type) { - var ifaces = Annotations.GetRecursiveInterfaces (type); - if (ifaces is null) - return; - foreach (var (ifaceType, impls) in ifaces) { + var ifaces = Annotations.GetRuntimeInterfaces (type); + foreach (var runtimeInterface in ifaces) { // Only mark interface implementations of interface types that have been marked. // This enables stripping of interfaces that are never used - if (ShouldMarkInterfaceImplementationList (type, impls, ifaceType)) - MarkInterfaceImplementationList (impls, new MessageOrigin (type)); + if (ShouldMarkRuntimeInterfaceImplementation (runtimeInterface)) + MarkRuntimeInterfaceImplementation (runtimeInterface, new MessageOrigin (type)); } } - - protected virtual bool ShouldMarkInterfaceImplementationList (TypeDefinition type, List ifaces, TypeReference ifaceType) + internal bool ShouldMarkRuntimeInterfaceImplementation (RuntimeInterfaceImplementation runtimeInterfaceImplementation) { - if (ifaces.All (Annotations.IsMarked)) + var implementingType = runtimeInterfaceImplementation.Implementor; + var ifaceType = runtimeInterfaceImplementation.InterfaceTypeDefinition; + if (Annotations.IsMarked (runtimeInterfaceImplementation)) return false; - if (!Context.IsOptimizationEnabled (CodeOptimizations.UnusedInterfaces, type)) + if (!Context.IsOptimizationEnabled (CodeOptimizations.UnusedInterfaces, implementingType)) return true; - if (Context.Resolve (ifaceType) is not TypeDefinition resolvedInterfaceType) + if (ifaceType is null) return false; - if (Annotations.IsMarked (resolvedInterfaceType)) + if (Annotations.IsMarked (ifaceType)) 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 (Context.KeepComInterfaces && (resolvedInterfaceType.IsImport || resolvedInterfaceType.IsWindowsRuntime)) + if (Context.KeepComInterfaces && (ifaceType.IsImport || ifaceType.IsWindowsRuntime)) return true; - return IsFullyPreserved (type); + return IsFullyPreserved (implementingType); } void MarkGenericParameterProvider (IGenericParameterProvider provider, MessageOrigin origin) @@ -2438,11 +2433,13 @@ bool IsInterfaceImplementationMethodNeededByTypeDueToInterface (OverrideInformat if (Annotations.IsMarked (method)) return false; + if (!Annotations.IsMarked (overrideInformation.RuntimeInterfaceImplementation.Implementor)) + 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))) + // We must check all possible ways the interface could be implemented by the type (through all recursive interface implementations, not just the primary one) + if (!overrideInformation.RuntimeInterfaceImplementation.IsAnyImplementationMarked (Annotations, Context)) 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 @@ -2458,12 +2455,12 @@ bool IsInterfaceImplementationMethodNeededByTypeDueToInterface (OverrideInformat // If the method is static and the implementing type is relevant to variant casting, mark the implementation method. // A static method may only be called through a constrained call if the type is relevant to variant casting. if (@base.IsStatic) - return Annotations.IsRelevantToVariantCasting (overrideInformation.InterfaceImplementor.Implementor) + return Annotations.IsRelevantToVariantCasting (overrideInformation.RuntimeInterfaceImplementation.Implementor) || IgnoreScope (@base.DeclaringType.Scope); // If the implementing type is marked as instantiated, mark the implementation method. // If the type is not instantiated, do not mark the implementation method - return Annotations.IsInstantiated (overrideInformation.InterfaceImplementor.Implementor); + return Annotations.IsInstantiated (overrideInformation.RuntimeInterfaceImplementation.Implementor); } static bool IsSpecialSerializationConstructor (MethodDefinition method) @@ -2526,31 +2523,15 @@ void MarkCustomMarshalerGetInstance (TypeDefinition type, in DependencyInfo reas void MarkICustomMarshalerMethods (TypeDefinition inputType, in DependencyInfo reason, MessageOrigin origin) { - TypeDefinition? type = inputType; - do { - if (!type.HasInterfaces) + var runtimeInterfaces = Annotations.GetRuntimeInterfaces (inputType); + foreach (var runtimeInterface in runtimeInterfaces) { + var iface_type = runtimeInterface.InterfaceTypeDefinition; + if (false == iface_type?.IsTypeOf ("System.Runtime.InteropServices", "ICustomMarshaler")) continue; + MarkMethodsIf (iface_type!.Methods, m => !m.IsStatic, reason, origin); - foreach (var iface in type.Interfaces) { - var iface_type = iface.InterfaceType; - if (!iface_type.IsTypeOf ("System.Runtime.InteropServices", "ICustomMarshaler")) - continue; - - // - // Instead of trying to guess where to find the interface declaration ILLink walks - // the list of implemented interfaces and resolve the declaration from there - // - var tdef = Context.Resolve (iface_type); - if (tdef == null) { - return; - } - - MarkMethodsIf (tdef.Methods, m => !m.IsStatic, reason, origin); - - MarkInterfaceImplementation (iface, new MessageOrigin (type)); - return; - } - } while ((type = Context.TryResolve (type.BaseType)) != null); + MarkRuntimeInterfaceImplementation (runtimeInterface, new MessageOrigin (inputType)); + } } bool IsNonEmptyStaticConstructor (MethodDefinition method) @@ -3246,13 +3227,11 @@ void MarkRuntimeInterfaceImplementation (MethodDefinition method, MethodReferenc if (!resolvedOverride.DeclaringType.IsInterface) return; var interfaceToBeImplemented = ov.DeclaringType; - - var ifaces = Annotations.GetRecursiveInterfaces (method.DeclaringType); - if (ifaces is null) - return; + var ifaces = Annotations.GetRuntimeInterfaces (method.DeclaringType); + Debug.Assert (ifaces.SingleOrDefault (i => TypeReferenceEqualityComparer.AreEqual (i.InflatedInterfaceType, interfaceToBeImplemented, Context)) is not null); foreach (var iface in ifaces) { - if (TypeReferenceEqualityComparer.AreEqual (iface.InterfaceType, interfaceToBeImplemented, Context)) { - MarkInterfaceImplementationList (iface.ImplementationChain, new MessageOrigin (method.DeclaringType)); + if (TypeReferenceEqualityComparer.AreEqual (iface.InflatedInterfaceType, interfaceToBeImplemented, Context)) { + MarkRuntimeInterfaceImplementation (iface, new MessageOrigin (method.DeclaringType)); return; } } @@ -3579,7 +3558,7 @@ void MarkInterfacesNeededByBodyStack (MethodIL methodIL) return; foreach (var (implementation, type) in implementations) - MarkInterfaceImplementation (implementation, new MessageOrigin (type)); + MarkRuntimeInterfaceImplementation (implementation, new MessageOrigin (type)); } bool InstructionRequiresReflectionMethodBodyScannerForFieldAccess (Instruction instruction) @@ -3687,10 +3666,13 @@ protected virtual void MarkInstruction (Instruction instruction, MethodDefinitio } } - void MarkInterfaceImplementationList (List ifaces, MessageOrigin origin, DependencyInfo? reason = null) + void MarkRuntimeInterfaceImplementation (RuntimeInterfaceImplementation runtimeInterface, MessageOrigin origin, DependencyInfo? reason = null) { - foreach (var iface in ifaces) { - MarkInterfaceImplementation (iface, origin, reason); + Annotations.Mark (runtimeInterface); + foreach (var iface in runtimeInterface.InterfaceImplementationChains) { + MarkType (iface.TypeWithInterfaceImplementation, reason ?? new DependencyInfo (DependencyKind.InterfaceImplementationOnType, origin.Provider), origin); + foreach (var impl in iface.InterfaceImplementations) + MarkInterfaceImplementation (impl, origin, reason); } } diff --git a/src/tools/illink/src/linker/Linker.Steps/ValidateVirtualMethodAnnotationsStep.cs b/src/tools/illink/src/linker/Linker.Steps/ValidateVirtualMethodAnnotationsStep.cs index aa9c7ee0d9f09..820d5ca1adc32 100644 --- a/src/tools/illink/src/linker/Linker.Steps/ValidateVirtualMethodAnnotationsStep.cs +++ b/src/tools/illink/src/linker/Linker.Steps/ValidateVirtualMethodAnnotationsStep.cs @@ -16,6 +16,8 @@ protected override void Process () var baseOverrideInformations = annotations.GetBaseMethods (method); if (baseOverrideInformations != null) { foreach (var baseOv in baseOverrideInformations) { + if (baseOv.IsOverrideOfInterfaceMember && !baseOv.RuntimeInterfaceImplementation.HasExplicitImplementation) + continue; annotations.FlowAnnotations.ValidateMethodAnnotationsAreSame (baseOv); ValidateMethodRequiresUnreferencedCodeAreSame (baseOv); } @@ -30,6 +32,9 @@ protected override void Process () if (annotations.VirtualMethodsWithAnnotationsToValidate.Contains (overrideInformation.Override)) continue; + if (overrideInformation.IsOverrideOfInterfaceMember && !overrideInformation.RuntimeInterfaceImplementation.HasExplicitImplementation) + continue; + annotations.FlowAnnotations.ValidateMethodAnnotationsAreSame (overrideInformation); ValidateMethodRequiresUnreferencedCodeAreSame (overrideInformation); } @@ -51,8 +56,8 @@ void ValidateMethodRequiresUnreferencedCodeAreSame (OverrideInformation ov) nameof (RequiresUnreferencedCodeAttribute), method.GetDisplayName (), baseMethod.GetDisplayName ()); - IMemberDefinition origin = (ov.IsOverrideOfInterfaceMember && ov.InterfaceImplementor.Implementor != method.DeclaringType) - ? ov.InterfaceImplementor.Implementor + IMemberDefinition origin = (ov.IsOverrideOfInterfaceMember && ov.RuntimeInterfaceImplementation.Implementor != method.DeclaringType) + ? ov.RuntimeInterfaceImplementation.Implementor : method; Context.LogWarning (origin, DiagnosticId.RequiresUnreferencedCodeAttributeMismatch, message); } diff --git a/src/tools/illink/src/linker/Linker/Annotations.cs b/src/tools/illink/src/linker/Linker/Annotations.cs index bfcfe56e7d71e..ba8ad7330f12a 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; @@ -71,6 +72,7 @@ public partial class AnnotationStore protected readonly HashSet indirectly_called = new HashSet (); protected readonly HashSet types_relevant_to_variant_casting = new HashSet (); readonly HashSet reflection_used = new (); + readonly HashSet _marked_runtime_interface_implementations = new (); public AnnotationStore (LinkContext context) { @@ -705,9 +707,13 @@ public void EnqueueVirtualMethod (MethodDefinition method) VirtualMethodsWithAnnotationsToValidate.Add (method); } - internal List<(TypeReference InterfaceType, List ImplementationChain)>? GetRecursiveInterfaces (TypeDefinition type) + internal ImmutableArray GetRuntimeInterfaces (TypeDefinition type) { return TypeMapInfo.GetRecursiveInterfaces (type); } + + internal bool IsMarked (RuntimeInterfaceImplementation runtimeInterface) => _marked_runtime_interface_implementations.Contains (runtimeInterface); + + internal void Mark (RuntimeInterfaceImplementation runtimeInterface) => _marked_runtime_interface_implementations.Add (runtimeInterface); } } diff --git a/src/tools/illink/src/linker/Linker/InterfaceImplementationChain.cs b/src/tools/illink/src/linker/Linker/InterfaceImplementationChain.cs new file mode 100644 index 0000000000000..f0e93e0668725 --- /dev/null +++ b/src/tools/illink/src/linker/Linker/InterfaceImplementationChain.cs @@ -0,0 +1,55 @@ +// 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.Collections.Immutable; +using System.Diagnostics; +using Mono.Cecil; + +namespace Mono.Linker +{ + internal sealed class InterfaceImplementationChain + { + /// + /// The type that has the InterfaceImplementation - either the or a base type of it. + /// + public TypeReference TypeWithInterfaceImplementation { get; } + + /// + /// The path of .interfaceimpl on or a base type that terminates with . + /// + public ImmutableArray InterfaceImplementations { get; } + + /// + /// Returns true if the interface implementation is directly on the implementing type. + /// + public bool IsExplicitInterfaceImplementation => InterfaceImplementations.Length == 1; + + public InterfaceImplementationChain (TypeReference typeWithInterfaceImplementation, ImmutableArray interfaceImplementation) + { + Debug.Assert (interfaceImplementation.Length > 0); + TypeWithInterfaceImplementation = typeWithInterfaceImplementation; + InterfaceImplementations = interfaceImplementation; + } + + /// + /// Returns true if all the .interfaceImpls in the chain and the are marked. + /// + /// + /// + public bool IsMarked (AnnotationStore annotations, ITryResolveMetadata context) + { + var typeDef = context.TryResolve (TypeWithInterfaceImplementation); + // If we have the .interfaceImpls on this type, it must be resolvable + Debug.Assert (typeDef is not null); + if (!annotations.IsMarked (typeDef)) + return false; + + foreach (var impl in InterfaceImplementations) { + if (!annotations.IsMarked (impl)) + return false; + } + + return true; + } + } +} diff --git a/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs b/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs deleted file mode 100644 index e981ce872703f..0000000000000 --- a/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs +++ /dev/null @@ -1,59 +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; -using System.Collections.Generic; -using System.Diagnostics; -using Mono.Cecil; - -namespace Mono.Linker -{ - public class InterfaceImplementor - { - /// - /// The type that implements . - /// - public TypeDefinition Implementor { get; } - /// - /// The .interfaceimpl on that points to - /// - public InterfaceImplementation InterfaceImplementation { get; } - /// - /// The type of the interface that is implemented by - /// - public TypeDefinition InterfaceType { get; } - - public InterfaceImplementor (TypeDefinition implementor, InterfaceImplementation interfaceImplementation, TypeDefinition interfaceType, IMetadataResolver resolver) - { - Implementor = implementor; - InterfaceImplementation = interfaceImplementation; - InterfaceType = interfaceType; - Debug.Assert(resolver.Resolve (interfaceImplementation.InterfaceType) == interfaceType); - } - - public static InterfaceImplementor Create(TypeDefinition implementor, TypeDefinition interfaceType, IMetadataResolver resolver) - { - foreach(InterfaceImplementation iface in implementor.Interfaces) { - if (resolver.Resolve(iface.InterfaceType) == interfaceType) { - return new InterfaceImplementor(implementor, iface, interfaceType, resolver); - } - } - - Queue ifacesToCheck = new (); - ifacesToCheck.Enqueue(implementor); - while (ifacesToCheck.Count > 0) { - var currentIface = ifacesToCheck.Dequeue (); - - 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); - } - } - 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/LinkContext.cs b/src/tools/illink/src/linker/Linker/LinkContext.cs index 0301f76f0a9f5..57c4763cf08c4 100644 --- a/src/tools/illink/src/linker/Linker/LinkContext.cs +++ b/src/tools/illink/src/linker/Linker/LinkContext.cs @@ -194,7 +194,7 @@ internal TypeNameResolver TypeNameResolver public SerializationMarker SerializationMarker { get; } public LinkContext (Pipeline pipeline, ILogger logger, string outputDirectory) - : this(pipeline, logger, outputDirectory, new UnintializedContextFactory ()) + : this (pipeline, logger, outputDirectory, new UnintializedContextFactory ()) { } diff --git a/src/tools/illink/src/linker/Linker/MethodBodyScanner.cs b/src/tools/illink/src/linker/Linker/MethodBodyScanner.cs index b3a230f194795..bf2fdb5d5f9a9 100644 --- a/src/tools/illink/src/linker/Linker/MethodBodyScanner.cs +++ b/src/tools/illink/src/linker/Linker/MethodBodyScanner.cs @@ -63,7 +63,7 @@ public InterfacesOnStackScanner (LinkContext context) this.context = context; } - public IEnumerable<(InterfaceImplementation, TypeDefinition)>? GetReferencedInterfaces (MethodIL methodIL) + public IEnumerable<(RuntimeInterfaceImplementation, TypeDefinition)>? GetReferencedInterfaces (MethodIL methodIL) { var possibleStackTypes = AllPossibleStackTypes (methodIL); if (possibleStackTypes.Count == 0) @@ -73,7 +73,7 @@ public InterfacesOnStackScanner (LinkContext context) if (interfaceTypes.Length == 0) return null; - var interfaceImplementations = new HashSet<(InterfaceImplementation, TypeDefinition)> (); + var interfaceImplementations = new HashSet<(RuntimeInterfaceImplementation, TypeDefinition)> (); // If a type could be on the stack in the body and an interface it implements could be on the stack on the body // then we need to mark that interface implementation. When this occurs it is not safe to remove the interface implementation from the type @@ -139,31 +139,14 @@ HashSet AllPossibleStackTypes (MethodIL methodIL) return types; } - void AddMatchingInterfaces (HashSet<(InterfaceImplementation, TypeDefinition)> results, TypeDefinition type, TypeDefinition[] interfaceTypes) + void AddMatchingInterfaces (HashSet<(RuntimeInterfaceImplementation, TypeDefinition)> results, TypeDefinition type, TypeDefinition[] interfaceTypes) { - if (!type.HasInterfaces) - return; - - foreach (var interfaceType in interfaceTypes) { - if (HasInterface (type, interfaceType, out InterfaceImplementation? implementation)) - results.Add ((implementation, type)); - } - } - - bool HasInterface (TypeDefinition type, TypeDefinition interfaceType, [NotNullWhen (true)] out InterfaceImplementation? implementation) - { - implementation = null; - if (!type.HasInterfaces) - return false; - - foreach (var iface in type.Interfaces) { - if (context.TryResolve (iface.InterfaceType) == interfaceType) { - implementation = iface; - return true; + var runtimeInterfaces = context.Annotations.GetRuntimeInterfaces (type); + foreach (var iface in runtimeInterfaces) { + if (interfaceTypes.Contains (iface.InterfaceTypeDefinition)) { + results.Add ((iface, type)); } } - - return false; } void AddFromGenericInstance (HashSet set, IGenericInstance instance) diff --git a/src/tools/illink/src/linker/Linker/OverrideInformation.cs b/src/tools/illink/src/linker/Linker/OverrideInformation.cs index 0727d5d25c19a..fd03529a704c8 100644 --- a/src/tools/illink/src/linker/Linker/OverrideInformation.cs +++ b/src/tools/illink/src/linker/Linker/OverrideInformation.cs @@ -1,9 +1,10 @@ // 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.Diagnostics; -using Mono.Cecil; using System.Diagnostics.CodeAnalysis; +using Mono.Cecil; namespace Mono.Linker { @@ -14,28 +15,32 @@ public class OverrideInformation public MethodDefinition Override { get; } - internal InterfaceImplementor? InterfaceImplementor { get; } + internal RuntimeInterfaceImplementation? RuntimeInterfaceImplementation { get; } - internal OverrideInformation (MethodDefinition @base, MethodDefinition @override, InterfaceImplementor? interfaceImplementor = null) + internal OverrideInformation (MethodDefinition @base, MethodDefinition @override, RuntimeInterfaceImplementation? runtimeInterface = null) { Base = @base; Override = @override; - InterfaceImplementor = interfaceImplementor; + RuntimeInterfaceImplementation = runtimeInterface; // 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 - || !@base.DeclaringType.IsInterface && interfaceImplementor == null); + Debug.Assert (@base.DeclaringType.IsInterface && runtimeInterface != null + || !@base.DeclaringType.IsInterface && runtimeInterface == null); // Ensure the interfaceImplementor is for the interface we expect - Debug.Assert (@base.DeclaringType.IsInterface ? interfaceImplementor!.InterfaceType == @base.DeclaringType : true); + Debug.Assert (@base.DeclaringType.IsInterface ? runtimeInterface!.InterfaceTypeDefinition == @base.DeclaringType : true); } public InterfaceImplementation? MatchingInterfaceImplementation - => InterfaceImplementor?.InterfaceImplementation; + => RuntimeInterfaceImplementation?.InterfaceImplementationChains[0].InterfaceImplementations[0]; public TypeDefinition? InterfaceType - => InterfaceImplementor?.InterfaceType; + => RuntimeInterfaceImplementation?.InterfaceTypeDefinition; - [MemberNotNullWhen (true, nameof (InterfaceImplementor), nameof (MatchingInterfaceImplementation))] + [MemberNotNullWhen (true, nameof (RuntimeInterfaceImplementation))] public bool IsOverrideOfInterfaceMember - => InterfaceImplementor != null; + => RuntimeInterfaceImplementation != null; + + [MemberNotNullWhen (true, nameof (RuntimeInterfaceImplementation))] + internal bool IsInterfaceMethodProvidedByBaseType + => IsOverrideOfInterfaceMember && (Override.DeclaringType != RuntimeInterfaceImplementation.Implementor); } } diff --git a/src/tools/illink/src/linker/Linker/RuntimeInterfaceImplementation.cs b/src/tools/illink/src/linker/Linker/RuntimeInterfaceImplementation.cs new file mode 100644 index 0000000000000..433a39e01be94 --- /dev/null +++ b/src/tools/illink/src/linker/Linker/RuntimeInterfaceImplementation.cs @@ -0,0 +1,69 @@ +// 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.Collections.Immutable; +using System.Diagnostics; +using System.Linq; +using Mono.Cecil; + +namespace Mono.Linker +{ + /// + /// Represents an implementation of an interface on a type that may be directly on the type or on a base type or implemented interface. + /// This type is considered to implement the interface at runtime, even though the interface may not be directly on the type. + /// + /// + /// This type should be used for marking, but should NOT be used to check if a runtime interface implementation has been marked. + /// This type represents the most direct way an interface may be implemented, but it may be implemented in a less direct way that is not represented here. + /// You should check all possible implementation 'paths' to determine if an interface is implemented, for example . + /// + internal sealed class RuntimeInterfaceImplementation + { + /// + /// The type that implements . + /// + public TypeDefinition Implementor { get; } + + /// + /// All the chains of .interfaceImpls that cause to implement + /// + public ImmutableArray InterfaceImplementationChains { get; } + + /// + /// The inflated interface type reference that is implemented by . + /// + public TypeReference InflatedInterfaceType { get; } + + /// + /// The of + /// + public TypeDefinition? InterfaceTypeDefinition { get; } + + /// + /// Returns true if an interface implementation is directly on the implementing type. + /// + public bool HasExplicitImplementation => InterfaceImplementationChains.Any (c => c.IsExplicitInterfaceImplementation && c.TypeWithInterfaceImplementation == Implementor); + + public RuntimeInterfaceImplementation (TypeDefinition implementor, TypeReference interfaceType, TypeDefinition? interfaceTypeDefinition, IEnumerable interfaceImplementations) + { + Implementor = implementor; + InterfaceImplementationChains = interfaceImplementations.ToImmutableArray (); + InflatedInterfaceType = interfaceType; + InterfaceTypeDefinition = interfaceTypeDefinition; + } + + public bool IsAnyImplementationMarked (AnnotationStore annotations, ITryResolveMetadata context) + { + if (annotations.IsMarked (this)) + return true; + foreach (var chain in InterfaceImplementationChains) { + if (chain.IsMarked (annotations, context)) { + return true; + } + } + return false; + } + } +} diff --git a/src/tools/illink/src/linker/Linker/RuntimeInterfacesAlgorithm.cs b/src/tools/illink/src/linker/Linker/RuntimeInterfacesAlgorithm.cs new file mode 100644 index 0000000000000..f83432d36cf33 --- /dev/null +++ b/src/tools/illink/src/linker/Linker/RuntimeInterfacesAlgorithm.cs @@ -0,0 +1,79 @@ +// 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.Collections.Immutable; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Mono.Cecil; + +namespace Mono.Linker.Linker +{ + internal struct RuntimeInterfacesAlgorithm + { + readonly ITryResolveMetadata _context; + readonly Dictionary> _runtimeInterfaceImpls = new (); + + public RuntimeInterfacesAlgorithm (ITryResolveMetadata context) + { + this._context = context; + } + + public ImmutableArray GetRuntimeInterfaceImplementations (TypeDefinition originalType) + { + if (_runtimeInterfaceImpls.TryGetValue (originalType, out var runtimeIfaces)) { + return runtimeIfaces; + } + + Dictionary> interfaceTypeToImplChainMap = new (new TypeReferenceEqualityComparer (_context)); + + foreach (var explicitIface in originalType.Interfaces) { + interfaceTypeToImplChainMap.AddToList (explicitIface.InterfaceType, new InterfaceImplementationChain (originalType, [explicitIface])); + + var resolvedInterfaceType = _context.TryResolve (explicitIface.InterfaceType); + if (resolvedInterfaceType is null) { + continue; + } + + // Add the recursive interfaces for each explicit interface, prepending the explicit interface on `originalType` to the chain + var recursiveIFaces = GetRuntimeInterfaceImplementations (resolvedInterfaceType); + foreach (var runtimeImpl in recursiveIFaces) { + // Inflate the interface type with the explicit interfaceImpl reference + var inflatedInterfaceType = runtimeImpl.InflatedInterfaceType.InflateFrom (explicitIface.InterfaceType as IGenericInstance); + foreach (var existingImpl in runtimeImpl.InterfaceImplementationChains) { + interfaceTypeToImplChainMap.AddToList (inflatedInterfaceType, new InterfaceImplementationChain (originalType, existingImpl.InterfaceImplementations.Insert (0, explicitIface))); + } + } + } + + if (originalType.BaseType is not null && _context.TryResolve (originalType.BaseType) is { } baseTypeDef) { + var baseTypeIfaces = GetRuntimeInterfaceImplementations (baseTypeDef); + foreach (var runtimeImpl in baseTypeIfaces) { + // Inflate the interface type with the base type reference + var inflatedInterfaceType = runtimeImpl.InflatedInterfaceType.InflateFrom (originalType.BaseType as IGenericInstance); + foreach (var impl in runtimeImpl.InterfaceImplementationChains) { + // Inflate the provider for the first .impl - this could be a different recursive base type for each chain + var inflatedImplProvider = impl.TypeWithInterfaceImplementation.InflateFrom (originalType.BaseType as IGenericInstance); + interfaceTypeToImplChainMap.AddToList (inflatedInterfaceType, new InterfaceImplementationChain (inflatedImplProvider, impl.InterfaceImplementations)); + } + } + } + + if (interfaceTypeToImplChainMap.Count == 0) + return ImmutableArray.Empty; + + // Build the ImmutableArray and cache it + ImmutableArray.Builder builder = ImmutableArray.CreateBuilder (interfaceTypeToImplChainMap.Count); + foreach (var kvp in interfaceTypeToImplChainMap) { + builder.Add (new (originalType, kvp.Key, _context.TryResolve (kvp.Key), kvp.Value)); + } + var runtimeInterfaces = builder.MoveToImmutable (); + _runtimeInterfaceImpls[originalType] = runtimeInterfaces; + + return runtimeInterfaces; + } + } +} diff --git a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs index 151a307707dd6..98fdd5b56ebea 100644 --- a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs +++ b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs @@ -29,27 +29,35 @@ // 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 Mono.Linker.Linker; namespace Mono.Linker { - public class TypeMapInfo { readonly HashSet assemblies = new HashSet (); - readonly LinkContext context; + readonly ITryResolveMetadata context; protected readonly Dictionary> base_methods = new Dictionary> (); protected readonly Dictionary> override_methods = new Dictionary> (); protected readonly Dictionary> default_interface_implementations = new Dictionary> (); + readonly RuntimeInterfacesAlgorithm _recursiveInterfaces; public TypeMapInfo (LinkContext context) { this.context = context; + _recursiveInterfaces = new (context); + } + + internal TypeMapInfo (ITryResolveMetadata context) + { + this.context = context; + _recursiveInterfaces = new (context); } public void EnsureProcessed (AssemblyDefinition assembly) @@ -100,28 +108,20 @@ public void EnsureProcessed (AssemblyDefinition assembly) return ret; } - public void AddBaseMethod (MethodDefinition method, MethodDefinition @base, InterfaceImplementor? interfaceImplementor) - { - base_methods.AddToList (method, new OverrideInformation (@base, method, interfaceImplementor)); - } - - public void AddOverride (MethodDefinition @base, MethodDefinition @override, InterfaceImplementor? interfaceImplementor = null) + internal void AddBaseMethod (MethodDefinition method, MethodDefinition @base, RuntimeInterfaceImplementation? runtimeInterface) { - override_methods.AddToList (@base, new OverrideInformation (@base, @override, interfaceImplementor)); + base_methods.AddToList (method, new OverrideInformation (@base, method, runtimeInterface)); } - public void AddDefaultInterfaceImplementation (MethodDefinition @base, InterfaceImplementor interfaceImplementor, MethodDefinition defaultImplementationMethod) + internal void AddOverride (MethodDefinition @base, MethodDefinition @override, RuntimeInterfaceImplementation? runtimeInterface = null) { - Debug.Assert(@base.DeclaringType.IsInterface); - default_interface_implementations.AddToList (@base, new OverrideInformation (@base, defaultImplementationMethod, interfaceImplementor)); + override_methods.AddToList (@base, new OverrideInformation (@base, @override, runtimeInterface)); } - Dictionary)>> interfaces = new (); protected virtual void MapType (TypeDefinition type) { MapVirtualMethods (type); MapInterfaceMethodsInTypeHierarchy (type); - interfaces[type] = GetRecursiveInterfaceImplementations (type); if (!type.HasNestedTypes) return; @@ -130,58 +130,21 @@ protected virtual void MapType (TypeDefinition type) MapType (nested); } - internal List<(TypeReference InterfaceType, List ImplementationChain)>? GetRecursiveInterfaces (TypeDefinition type) + internal ImmutableArray GetRecursiveInterfaces (TypeDefinition type) { - EnsureProcessed(type.Module.Assembly); - if (interfaces.TryGetValue (type, out var value)) - return value; - return null; - } - - List<(TypeReference InterfaceType, List ImplementationChain)> 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 we can't resolve the interface type we can't find recursive interfaces - if (type is null) - return; - // Get all explicit interfaces of this type - foreach (var iface in type.Interfaces) { - var interfaceType = iface.InterfaceType.InflateFrom (typeRef as IGenericInstance); - 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) { - var ifaceDirectlyOnType = iface.InterfaceType.InflateFrom (typeRef as IGenericInstance); - AddRecursiveInterfaces (ifaceDirectlyOnType, pathToType.Append (iface), firstImplementationChain, Context); - } - } + return _recursiveInterfaces.GetRuntimeInterfaceImplementations (type); } void MapInterfaceMethodsInTypeHierarchy (TypeDefinition type) { - if (!type.HasInterfaces) + var runtimeInterfaceImpls = _recursiveInterfaces.GetRuntimeInterfaceImplementations (type); + if (runtimeInterfaceImpls.Length == 0) return; // 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)) { - MethodDefinition? resolvedInterfaceMethod = context.TryResolve (interfaceMethod); - if (resolvedInterfaceMethod == null) - continue; - + foreach (var interfaceImpl in runtimeInterfaceImpls) { + foreach ((MethodReference interfaceMethod, MethodDefinition resolvedInterfaceMethod) in interfaceImpl.InflatedInterfaceType.GetMethods (context)) { // TODO-NICE: if the interface method is implemented explicitly (with an override), // we shouldn't need to run the below logic. This results in ILLink potentially // keeping more methods than needed. @@ -190,26 +153,28 @@ void MapInterfaceMethodsInTypeHierarchy (TypeDefinition type) || resolvedInterfaceMethod.IsFinal) continue; + // Note: methods with overrides are annotated in MapVirtualMethods + // Static methods on interfaces must be implemented only via explicit method-impl record // not by a signature match. So there's no point in running this logic for static methods. if (!resolvedInterfaceMethod.IsStatic) { // 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, interfaceImpl); 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, interfaceImpl); continue; } } // Look for a default implementation last. - FindAndAddDefaultInterfaceImplementations (type, type, resolvedInterfaceMethod, interfaceImpl.OriginalImpl); + FindAndAddDefaultInterfaceImplementations (interfaceImpl, resolvedInterfaceMethod); } } } @@ -239,7 +204,7 @@ void MapVirtualMethod (MethodDefinition method) if (@base == null) return; - Debug.Assert(!@base.DeclaringType.IsInterface); + Debug.Assert (!@base.DeclaringType.IsInterface); AnnotateMethods (@base, method); } @@ -251,17 +216,20 @@ void MapOverrides (MethodDefinition method) if (baseMethod == null) continue; if (baseMethod.DeclaringType.IsInterface) { - AnnotateMethods (baseMethod, method, InterfaceImplementor.Create (method.DeclaringType, baseMethod.DeclaringType, context)); + var runtimeInterfaces = _recursiveInterfaces.GetRuntimeInterfaceImplementations (method.DeclaringType); + Debug.Assert (runtimeInterfaces.Length > 0); + var runtimeInterface = runtimeInterfaces.Single (i => TypeReferenceEqualityComparer.AreEqual (i.InflatedInterfaceType, baseMethodRef.DeclaringType, context)); + AnnotateMethods (baseMethod, method, runtimeInterface); } else { AnnotateMethods (baseMethod, method); } } } - void AnnotateMethods (MethodDefinition @base, MethodDefinition @override, InterfaceImplementor? interfaceImplementor = null) + void AnnotateMethods (MethodDefinition @base, MethodDefinition @override, RuntimeInterfaceImplementation? runtimeInterface = null) { - AddBaseMethod (@override, @base, interfaceImplementor); - AddOverride (@base, @override, interfaceImplementor); + AddBaseMethod (@override, @base, runtimeInterface); + AddOverride (@base, @override, runtimeInterface); } MethodDefinition? GetBaseMethodInTypeHierarchy (MethodDefinition method) @@ -323,23 +291,23 @@ 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 (RuntimeInterfaceImplementation originalInterfaceImpl, MethodDefinition interfaceMethodDef) { // Go over all interfaces, trying to find a method that is an explicit MethodImpl of the // interface method in question. + var runtimeIfaces = _recursiveInterfaces.GetRuntimeInterfaceImplementations (originalInterfaceImpl.Implementor); + if (runtimeIfaces.Length == 0) + return; - foreach (var interfaceImpl in typeThatMayHaveDIM.Interfaces) { - var potentialImplInterface = context.TryResolve (interfaceImpl.InterfaceType); - if (potentialImplInterface == null) + foreach (var runtimeIface in runtimeIfaces) { + var potentialImplInterface = runtimeIface.InterfaceTypeDefinition; + if (potentialImplInterface is null) continue; - bool foundImpl = false; - foreach (var potentialImplMethod in potentialImplInterface.Methods) { - if (potentialImplMethod == interfaceMethodToBeImplemented && - !potentialImplMethod.IsAbstract) { - AddDefaultInterfaceImplementation (interfaceMethodToBeImplemented, new (typeThatImplementsInterface, originalInterfaceImpl, interfaceMethodToBeImplemented.DeclaringType, context), potentialImplMethod); - foundImpl = true; + if (potentialImplMethod == interfaceMethodDef && !potentialImplMethod.IsAbstract) { + // The interface method definition provides a default implementation. + AddDefaultInterfaceImplementation (interfaceMethodDef, originalInterfaceImpl, potentialImplMethod); break; } @@ -347,34 +315,31 @@ void FindAndAddDefaultInterfaceImplementations (TypeDefinition typeThatImplement continue; // This method is an override of something. Let's see if it's the method we are looking for. + bool foundImpl = false; foreach (var baseMethod in potentialImplMethod.Overrides) { - if (context.TryResolve (baseMethod) == interfaceMethodToBeImplemented) { - AddDefaultInterfaceImplementation (interfaceMethodToBeImplemented, new (typeThatImplementsInterface, originalInterfaceImpl, interfaceMethodToBeImplemented.DeclaringType, context), @potentialImplMethod); + if (context.TryResolve (baseMethod) == interfaceMethodDef) { + AddDefaultInterfaceImplementation (interfaceMethodDef, originalInterfaceImpl, @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); - } + void AddDefaultInterfaceImplementation (MethodDefinition @base, RuntimeInterfaceImplementation runtimeInterface, MethodDefinition defaultImplementationMethod) + { + Debug.Assert (@base.DeclaringType.IsInterface); + default_interface_implementations.AddToList (@base, new OverrideInformation (@base, defaultImplementationMethod, runtimeInterface)); } } MethodDefinition? TryMatchMethod (TypeReference type, MethodReference method) { - foreach (var candidate in type.GetMethods (context)) { - var md = context.TryResolve (candidate); - if (md?.IsVirtual != true) + foreach (var (candidate, md) in type.GetMethods (context)) { + if (!md.IsVirtual) continue; - if (MethodMatch (candidate, method)) return md; } diff --git a/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs b/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs index c5704fbb04142..9ef8418023d64 100644 --- a/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs +++ b/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs @@ -279,7 +279,7 @@ private static GenericInstanceType MakeGenericType (IGenericInstance genericInst return result; } - public static IEnumerable GetMethods (this TypeReference type, ITryResolveMetadata resolver) + public static IEnumerable<(MethodReference, MethodDefinition)> GetMethods (this TypeReference type, ITryResolveMetadata resolver) { TypeDefinition? typeDef = resolver.TryResolve (type); if (typeDef?.HasMethods != true) @@ -287,10 +287,10 @@ public static IEnumerable GetMethods (this TypeReference type, if (type is GenericInstanceType genericInstanceType) { foreach (var methodDef in typeDef.Methods) - yield return MakeMethodReferenceForGenericInstanceType (genericInstanceType, methodDef); + yield return (MakeMethodReferenceForGenericInstanceType (genericInstanceType, methodDef), methodDef); } else { foreach (var method in typeDef.Methods) - yield return method; + yield return (method, method); } } @@ -414,7 +414,8 @@ public static TypeReference WithoutModifiers (this TypeReference type) // Check whether this type represents a "named type" (i.e. a type that has a name and can be resolved to a TypeDefinition), // not an array, pointer, byref, or generic parameter. Conceptually this is supposed to represent the same idea as Roslyn's // INamedTypeSymbol, or ILC's DefType/MetadataType. - public static bool IsNamedType (this TypeReference typeReference) { + public static bool IsNamedType (this TypeReference typeReference) + { if (typeReference.IsDefinition || typeReference.IsGenericInstance) return true; diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresCapabilityTests.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresCapabilityTests.cs index 3f722ad634e3d..db08bb45f445a 100644 --- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresCapabilityTests.cs +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresCapabilityTests.cs @@ -123,5 +123,11 @@ public Task SuppressRequires () { return RunTest (nameof (SuppressRequires)); } + + [Fact] + public Task BaseProvidesInterfaceMethodRequiresMismatch () + { + return RunTest (nameof (BaseProvidesInterfaceMethodRequiresMismatch)); + } } } 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 19189bfcd0170..dc2440a552f56 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 @@ -63,6 +63,12 @@ public Task StaticDefaultInterfaceMethodOnStruct () return RunTest (allowMissingWarnings: true); } + [Fact] + public Task StaticDimOnDerivedInterface () + { + return RunTest (allowMissingWarnings: true); + } + [Fact] public Task StaticDimProvidedByUnreferencedIfaceInHierarchy () { 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 index 35cc0b65c0417..1dbdf7bd5c9ac 100644 --- 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 @@ -9,6 +9,12 @@ public sealed partial class RecursiveInterfacesTests : LinkerTestBase protected override string TestSuiteName => "Inheritance.Interfaces.RecursiveInterfaces"; + [Fact] + public Task BaseTypeMarkedInterfaceDerivedNotKept () + { + return RunTest (allowMissingWarnings: true); + } + [Fact] public Task GenericInterfaceImplementedRecursively () { @@ -27,6 +33,18 @@ public Task OverrideOfRecursiveInterfaceIsRemoved () return RunTest (allowMissingWarnings: true); } + [Fact] + public Task RecursiveGenericInterfaces () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task RecursiveGenericInterfacesStatic () + { + return RunTest (allowMissingWarnings: true); + } + [Fact] public Task RecursiveInterfaceKept () { diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/BaseTypeMapInfoAttribute.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/BaseTypeMapInfoAttribute.cs new file mode 100644 index 0000000000000..a9bcbcd5b341e --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/BaseTypeMapInfoAttribute.cs @@ -0,0 +1,7 @@ +// 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.Tests.Cases.Expectations.Assertions +{ + public class BaseTypeMapInfoAttribute : BaseExpectedLinkedBehaviorAttribute { } +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/HasRuntimeInterfaceAttribute.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/HasRuntimeInterfaceAttribute.cs new file mode 100644 index 0000000000000..8d77a80e146a7 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/HasRuntimeInterfaceAttribute.cs @@ -0,0 +1,23 @@ +// 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; + +namespace Mono.Linker.Tests.Cases.Expectations.Assertions +{ + [AttributeUsage (AttributeTargets.Class, AllowMultiple = true, Inherited = false)] + public class HasRuntimeInterfaceAttribute : Attribute + { + public HasRuntimeInterfaceAttribute (Type interfaceType, params Type[] implementationChains) + { + } + + public HasRuntimeInterfaceAttribute (string interfaceType, params string[] implementationChains) + { + } + } +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/IsOverrideOfAttribute.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/IsOverrideOfAttribute.cs new file mode 100644 index 0000000000000..e468492a000b3 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/IsOverrideOfAttribute.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Mono.Linker.Tests.Cases.Expectations.Assertions +{ + [AttributeUsage (AttributeTargets.Method, AllowMultiple = true, Inherited = false)] + public class IsOverrideOfAttribute : BaseTypeMapInfoAttribute + { + public IsOverrideOfAttribute (string methodFullName) + { } + } +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/MethodOverridesMethodInAssemblyAttribute.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/MethodOverridesMethodInAssemblyAttribute.cs new file mode 100644 index 0000000000000..7760f981833c4 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/MethodOverridesMethodInAssemblyAttribute.cs @@ -0,0 +1,22 @@ +// 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; + +namespace Mono.Linker.Tests.Cases.Expectations.Assertions +{ + class MethodOverridesMethodInAssemblyAttribute : BaseTypeMapInfoAttribute + { + public MethodOverridesMethodInAssemblyAttribute ( + string overridingAssemblyName, + string overridingTypeName, + string overridingMethodName, + string baseAssemblyName, + string baseMethodName) + { } + } +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/RuntimeInterfaceOnTypeInAssembly.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/RuntimeInterfaceOnTypeInAssembly.cs new file mode 100644 index 0000000000000..b0425ffc221d9 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/RuntimeInterfaceOnTypeInAssembly.cs @@ -0,0 +1,15 @@ +// 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; + +namespace Mono.Linker.Tests.Cases.Expectations.Assertions +{ + [AttributeUsage (AttributeTargets.Class, AllowMultiple = true, Inherited = false)] + public class RuntimeInterfaceOnTypeInAssembly : BaseTypeMapInfoAttribute + { + public RuntimeInterfaceOnTypeInAssembly (string dllName, string type, string interfaceType, params string[] implementationChains) + { + } + } +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/StaticDimOnDerivedInterface.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/StaticDimOnDerivedInterface.cs new file mode 100644 index 0000000000000..0f45b8aec3aff --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/StaticDimOnDerivedInterface.cs @@ -0,0 +1,57 @@ +// 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; + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.DefaultInterfaceMethods +{ + class StaticDimOnDerivedInterface + { + [Kept] + public static void Main () + { + ITest testImpl = new TestClass (); + testImpl.Invoke (); + + CallStaticInvoke (); + } + + [Kept] + public interface ITestBase + { + [Kept] + static virtual string InvokeStatic () => throw new NotSupportedException (); + + [Kept] + string Invoke () => throw new NotSupportedException (); + } + + [Kept] + [KeptInterface (typeof (ITestBase))] + public interface ITest : ITestBase + { + [Kept] + static string ITestBase.InvokeStatic () => "Test"; + + [Kept] + string ITestBase.Invoke () => "Test"; + } + + [Kept] + [KeptInterface (typeof (ITest))] + [KeptInterface (typeof (ITestBase))] + public class TestClass : ITest + { + [Kept] + public TestClass () { } + } + + [Kept] + public static string CallStaticInvoke () where T : ITestBase => T.InvokeStatic (); + } +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/UnusedDefaultInterfaceImplementation.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/UnusedDefaultInterfaceImplementation.cs index 23b029f2a3477..b89a50708476c 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/UnusedDefaultInterfaceImplementation.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/UnusedDefaultInterfaceImplementation.cs @@ -20,8 +20,11 @@ interface IFoo void InterfaceMethod (); } + [Kept] // Should be removable + [KeptInterface (typeof (IFoo))] interface IDefaultImpl : IFoo { + [Kept] void IFoo.InterfaceMethod () { } @@ -29,6 +32,7 @@ void IFoo.InterfaceMethod () [Kept] [KeptInterface (typeof (IFoo))] + [KeptInterface (typeof (IDefaultImpl))] // Should be removable class Foo : IDefaultImpl { [Kept] diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceVariants.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceVariants.cs index b19208bd563d6..26519f7f37e71 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceVariants.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceVariants.cs @@ -136,15 +136,19 @@ public class UninstantiatedPublicClassWithInterface : internal UninstantiatedPublicClassWithInterface () { } [Kept] + [IsOverrideOf ("System.Void Mono.Linker.Tests.Cases.Inheritance.Interfaces.InterfaceVariants/IPublicInterface::PublicInterfaceMethod()")] public void PublicInterfaceMethod () { } [Kept] + [IsOverrideOf ("System.Void Mono.Linker.Tests.Cases.Inheritance.Interfaces.InterfaceVariants/IPublicInterface::ExplicitImplementationPublicInterfaceMethod()")] void IPublicInterface.ExplicitImplementationPublicInterfaceMethod () { } [Kept] + [IsOverrideOf ("System.Void Mono.Linker.Tests.Cases.Inheritance.Interfaces.InterfaceVariants/IPublicStaticInterface::PublicStaticInterfaceMethod()")] public static void PublicStaticInterfaceMethod () { } [Kept] + [IsOverrideOf ("System.Void Mono.Linker.Tests.Cases.Inheritance.Interfaces.InterfaceVariants/IPublicStaticInterface::ExplicitImplementationPublicStaticInterfaceMethod()")] static void IPublicStaticInterface.ExplicitImplementationPublicStaticInterfaceMethod () { } public void InternalInterfaceMethod () { } diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/BaseProvidesInterfaceMember/GenericInterfaceWithMethodManyBaseInterfaces.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/BaseProvidesInterfaceMember/GenericInterfaceWithMethodManyBaseInterfaces.cs index f9b338ead868e..c85b9871a2f58 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/BaseProvidesInterfaceMember/GenericInterfaceWithMethodManyBaseInterfaces.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/BaseProvidesInterfaceMember/GenericInterfaceWithMethodManyBaseInterfaces.cs @@ -17,10 +17,15 @@ interface IFoo void Method (T arg); } + [Kept] // Should be able to be removed + [KeptInterface (typeof (IFoo<>))] interface IFoo2 : IFoo { } + [Kept] // Should be able to be removed + [KeptInterface (typeof (IFoo<>))] + [KeptInterface (typeof (IFoo2<>))] interface IFoo3 : IFoo2 { } @@ -39,8 +44,10 @@ public void Method (object arg) [KeptMember (".ctor()")] [KeptBaseType (typeof (BaseFoo))] [KeptInterface (typeof (IFoo))] + [KeptInterface (typeof (IFoo2))] // Should be removable + [KeptInterface (typeof (IFoo3))] // Should be removable class FooWithBase : BaseFoo, IFoo3 { } } -} \ No newline at end of file +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/BaseProvidesInterfaceMember/SimpleMethod.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/BaseProvidesInterfaceMember/SimpleMethod.cs index a395d4197c111..34b817a76e25b 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/BaseProvidesInterfaceMember/SimpleMethod.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/BaseProvidesInterfaceMember/SimpleMethod.cs @@ -22,6 +22,7 @@ interface IFoo class BaseFoo { [Kept] + [IsOverrideOf ("System.Void Mono.Linker.Tests.Cases.Inheritance.Interfaces.OnReferenceType.BaseProvidesInterfaceMember.SimpleMethod/IFoo::Method()")] public void Method () { } @@ -35,4 +36,4 @@ class FooWithBase : BaseFoo, IFoo { } } -} \ No newline at end of file +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/ClassImplementingInterfaceMethodsNested.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/ClassImplementingInterfaceMethodsNested.cs index c449911f6e3b4..14b2a7c76ce6b 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/ClassImplementingInterfaceMethodsNested.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/ClassImplementingInterfaceMethodsNested.cs @@ -17,6 +17,8 @@ interface IFoo void Foo (); } + [Kept] + [KeptInterface (typeof (IFoo))] interface IBar : IFoo { void Bar (); @@ -25,6 +27,7 @@ interface IBar : IFoo [Kept] [KeptMember (".ctor()")] [KeptInterface (typeof (IFoo))] + [KeptInterface (typeof (IBar))] class A : IBar { [Kept] @@ -37,4 +40,4 @@ public void Bar () } } } -} \ No newline at end of file +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/NestedInterfaces1.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/NestedInterfaces1.cs index 434a4174bbea6..9101c793b9617 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/NestedInterfaces1.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/NestedInterfaces1.cs @@ -46,6 +46,7 @@ interface IBase3 : IBase2 /// [Kept] [KeptInterface (typeof (IBase3))] + [KeptInterface (typeof (IBase2))] // Should be removable [KeptInterface (typeof (IBase))] class Foo : IBase3 { @@ -55,4 +56,4 @@ public void Method () } } } -} \ No newline at end of file +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/NestedInterfaces2.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/NestedInterfaces2.cs index 93f50accac117..a23078cd33c29 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/NestedInterfaces2.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/NestedInterfaces2.cs @@ -25,6 +25,8 @@ interface IBase { } + [Kept] // Should be removable + [KeptInterface (typeof (IBase))] interface IBase2 : IBase { void Method (); @@ -32,12 +34,14 @@ interface IBase2 : IBase [Kept] [KeptInterface (typeof (IBase))] + [KeptInterface (typeof (IBase2))] // Should be removable interface IBase3 : IBase2 { } [Kept] [KeptInterface (typeof (IBase3))] + [KeptInterface (typeof (IBase2))] // Should be removable [KeptInterface (typeof (IBase))] class Foo : IBase3 { @@ -46,4 +50,4 @@ public void Method () } } } -} \ No newline at end of file +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/NestedInterfaces3.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/NestedInterfaces3.cs index 5bc09dc62f22b..40f259dd79da9 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/NestedInterfaces3.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/NestedInterfaces3.cs @@ -25,6 +25,8 @@ interface IBase { } + [Kept] // Should be removable + [KeptInterface (typeof (IBase))] interface IBase2 : IBase { void Method (); @@ -32,12 +34,14 @@ interface IBase2 : IBase [Kept] [KeptInterface (typeof (IBase))] + [KeptInterface (typeof (IBase2))] // Should be removable interface IBase3 : IBase2 { } [Kept] [KeptInterface (typeof (IBase))] + [KeptInterface (typeof (IBase2))] // Should be removable class Base : IBase2 { public void Method () @@ -48,6 +52,7 @@ public void Method () [Kept] [KeptBaseType (typeof (Base))] [KeptInterface (typeof (IBase3))] + [KeptInterface (typeof (IBase2))] // Should be removable [KeptInterface (typeof (IBase))] class Foo : Base, IBase3, IBase2 { @@ -56,4 +61,4 @@ public void Method () } } } -} \ No newline at end of file +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnValueType/StructImplementingInterfaceMethodsNested.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnValueType/StructImplementingInterfaceMethodsNested.cs index 8e438aeda7c8e..8f685def3fa92 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnValueType/StructImplementingInterfaceMethodsNested.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnValueType/StructImplementingInterfaceMethodsNested.cs @@ -17,6 +17,8 @@ interface IFoo void Foo (); } + [Kept] + [KeptInterface (typeof (IFoo))] interface IBar : IFoo { void Bar (); @@ -24,6 +26,7 @@ interface IBar : IFoo [Kept] [KeptInterface (typeof (IFoo))] + [KeptInterface (typeof (IBar))] struct A : IBar { [Kept] @@ -36,4 +39,4 @@ public void Bar () } } } -} \ No newline at end of file +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/BaseTypeMarkedInterfaceDerivedNotKept.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/BaseTypeMarkedInterfaceDerivedNotKept.cs new file mode 100644 index 0000000000000..999d0c2855d42 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/BaseTypeMarkedInterfaceDerivedNotKept.cs @@ -0,0 +1,56 @@ +// 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; + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.RecursiveInterfaces +{ + class BaseTypeMarkedInterfaceDerivedNotKept + { + [Kept] + public static void Main () + { + IFoo foo = new UsedDerived (); + foo.Method (); + } + + [Kept] + [KeptInterface (typeof (IFoo))] + [KeptMember (".ctor()")] + abstract class BaseType : IFoo + { + [Kept] + public abstract void Method (); + } + + [Kept] + [KeptBaseType (typeof (BaseType))] + [KeptMember (".ctor()")] + class UsedDerived : BaseType + { + [Kept] + public override void Method () + { + } + } + + class UnusedDerived : BaseType + { + public override void Method () + { + } + } + + [Kept] + interface IFoo + { + [Kept] + void Method (); + } + } +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/RecursiveGenericInterfaces.il b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/RecursiveGenericInterfaces.il new file mode 100644 index 0000000000000..b0cde69b93de9 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/RecursiveGenericInterfaces.il @@ -0,0 +1,322 @@ +.assembly extern mscorlib { } + +.assembly 'library' { } + +.class interface public auto ansi abstract beforefieldinit IBase`3 +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( + 01 00 01 00 00 + ) + .param type T + .custom instance void [mscorlib]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( + 01 00 02 00 00 + ) + .param type U + .custom instance void [mscorlib]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( + 01 00 02 00 00 + ) + .param type V + .custom instance void [mscorlib]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( + 01 00 02 00 00 + ) + // Methods + .method public hidebysig newslot abstract virtual + instance !T GetT () cil managed + { + } // end of method IBase`3::GetT + + .method public hidebysig newslot virtual + instance !U GetU () cil managed + { + // Method begins at RVA 0x2050 + // Code size 10 (0xa) + .maxstack 1 + .locals init ( + [0] !U + ) + + IL_0000: ldloca.s 0 + IL_0002: initobj !U + IL_0008: ldloc.0 + IL_0009: ret + } // end of method IBase`3::GetU + + .method public hidebysig newslot virtual + instance !V GetV () cil managed + { + // Method begins at RVA 0x2068 + // Code size 10 (0xa) + .maxstack 1 + .locals init ( + [0] !V + ) + + IL_0000: ldloca.s 0 + IL_0002: initobj !V + IL_0008: ldloc.0 + IL_0009: ret + } // end of method IBase`3::GetV + +} // end of class IBase`3 + +.class interface public auto ansi abstract beforefieldinit IMiddle`2 + implements class IBase`3, + class IBase`3 +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( + 01 00 02 00 00 + ) + // Methods + .method private hidebysig virtual + instance int32 'IBase.GetV' () cil managed + { + .override method instance !2 class IBase`3::GetV() + // Method begins at RVA 0x207e + // Code size 2 (0x2) + .maxstack 8 + + IL_0000: ldnull + IL_0001: throw + } // end of method IMiddle`2::'IBase.GetV' + + .method private hidebysig virtual + instance float32 'IBase.GetV' () cil managed + { + .override method instance !2 class IBase`3::GetV() + // Method begins at RVA 0x207e + // Code size 2 (0x2) + .maxstack 8 + + IL_0000: ldnull + IL_0001: throw + } // end of method IMiddle`2::'IBase.GetV' + +} // end of class IMiddle`2 + +.class interface public auto ansi abstract beforefieldinit IDerived`1 + implements class IMiddle`2, + class IMiddle`2 + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( + 01 00 02 00 00 + ) + // Methods + .method private hidebysig virtual + instance int32 'IBase.GetV' () cil managed + { + .override method instance !2 class IBase`3::GetV() + // Method begins at RVA 0x207e + // Code size 2 (0x2) + .maxstack 8 + + IL_0000: ldnull + IL_0001: throw + } // end of method IDerived`1::'IBase.GetV' + + .method private hidebysig virtual + instance float32 'IBase.GetV' () cil managed + { + .override method instance !2 class IBase`3::GetV() + // Method begins at RVA 0x207e + // Code size 2 (0x2) + .maxstack 8 + + IL_0000: ldnull + IL_0001: throw + } // end of method IDerived`1::'IBase.GetV' + + .method private hidebysig virtual + instance int64 'IBase.GetU' () cil managed + { + .override method instance !1 class IBase`3::GetU() + // Method begins at RVA 0x207e + // Code size 2 (0x2) + .maxstack 8 + + IL_0000: ldnull + IL_0001: throw + } // end of method IDerived`1::'IBase.GetU' + + .method private hidebysig virtual + instance int64 'IBase.GetU' () cil managed + { + .override method instance !1 class IBase`3::GetU() + // Method begins at RVA 0x207e + // Code size 2 (0x2) + .maxstack 8 + + IL_0000: ldnull + IL_0001: throw + } // end of method IDerived`1::'IBase.GetU' + + .method private hidebysig virtual + instance float64 'IBase.GetU' () cil managed + { + .override method instance !1 class IBase`3::GetU() + // Method begins at RVA 0x207e + // Code size 2 (0x2) + .maxstack 8 + + IL_0000: ldnull + IL_0001: throw + } // end of method IDerived`1::'IBase.GetU' + + .method private hidebysig virtual + instance float64 'IBase.GetU' () cil managed + { + .override method instance !1 class IBase`3::GetU() + // Method begins at RVA 0x207e + // Code size 2 (0x2) + .maxstack 8 + + IL_0000: ldnull + IL_0001: throw + } // end of method IDerived`1::'IBase.GetU' + +} // end of class IDerived`1 + +.class public auto ansi beforefieldinit MyClass + extends [mscorlib]System.Object + implements class IDerived`1, + class IDerived`1 +{ + // Methods + .method private hidebysig newslot virtual + instance string 'IBase.GetT' () cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( + 01 00 01 00 00 + ) + .override method instance !0 class IBase`3::GetT() + // Method begins at RVA 0x207e + // Code size 2 (0x2) + .maxstack 8 + + IL_0000: ldnull + IL_0001: throw + } // end of method MyClass::'IBase.GetT' + + .method private hidebysig newslot virtual + instance string 'IBase.GetT' () cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( + 01 00 01 00 00 + ) + .override method instance !0 class IBase`3::GetT() + // Method begins at RVA 0x207e + // Code size 2 (0x2) + .maxstack 8 + + IL_0000: ldnull + IL_0001: throw + } // end of method MyClass::'IBase.GetT' + + .method private hidebysig newslot virtual + instance string 'IBase.GetT' () cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( + 01 00 01 00 00 + ) + .override method instance !0 class IBase`3::GetT() + // Method begins at RVA 0x207e + // Code size 2 (0x2) + .maxstack 8 + + IL_0000: ldnull + IL_0001: throw + } // end of method MyClass::'IBase.GetT' + + .method private hidebysig newslot virtual + instance string 'IBase.GetT' () cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( + 01 00 01 00 00 + ) + .override method instance !0 class IBase`3::GetT() + // Method begins at RVA 0x207e + // Code size 2 (0x2) + .maxstack 8 + + IL_0000: ldnull + IL_0001: throw + } // end of method MyClass::'IBase.GetT' + + .method private hidebysig newslot virtual + instance char 'IBase.GetT' () cil managed + { + .override method instance !0 class IBase`3::GetT() + // Method begins at RVA 0x207e + // Code size 2 (0x2) + .maxstack 8 + + IL_0000: ldnull + IL_0001: throw + } // end of method MyClass::'IBase.GetT' + + .method private hidebysig newslot virtual + instance char 'IBase.GetT' () cil managed + { + .override method instance !0 class IBase`3::GetT() + // Method begins at RVA 0x207e + // Code size 2 (0x2) + .maxstack 8 + + IL_0000: ldnull + IL_0001: throw + } // end of method MyClass::'IBase.GetT' + + .method private hidebysig newslot virtual + instance char 'IBase.GetT' () cil managed + { + .override method instance !0 class IBase`3::GetT() + // Method begins at RVA 0x207e + // Code size 2 (0x2) + .maxstack 8 + + IL_0000: ldnull + IL_0001: throw + } // end of method MyClass::'IBase.GetT' + + .method private hidebysig newslot virtual + instance char 'IBase.GetT' () cil managed + { + .override method instance !0 class IBase`3::GetT() + // Method begins at RVA 0x207e + // Code size 2 (0x2) + .maxstack 8 + + IL_0000: ldnull + IL_0001: throw + } // end of method MyClass::'IBase.GetT' + + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x2081 + // Code size 7 (0x7) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: ret + } // end of method MyClass::.ctor + +} // end of class MyClass + +.class public auto ansi beforefieldinit MyDerivedClass + extends MyClass +{ + // Methods + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x20c0 + // Code size 7 (0x7) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void MyClass::.ctor() + IL_0006: ret + } // end of method MyDerivedClass::.ctor + +} // end of class MyDerivedClass diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/RecursiveGenericInterfacesStatic.il b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/RecursiveGenericInterfacesStatic.il new file mode 100644 index 0000000000000..9a37ab31140c3 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/RecursiveGenericInterfacesStatic.il @@ -0,0 +1,322 @@ +.assembly extern mscorlib { } + +.assembly 'library' { } + +.class interface public auto ansi abstract beforefieldinit IBase`3 +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( + 01 00 01 00 00 + ) + .param type T + .custom instance void [mscorlib]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( + 01 00 02 00 00 + ) + .param type U + .custom instance void [mscorlib]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( + 01 00 02 00 00 + ) + .param type V + .custom instance void [mscorlib]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( + 01 00 02 00 00 + ) + // Methods + .method public hidebysig newslot abstract virtual + static !T GetT () cil managed + { + } // end of method IBase`3::GetT + + .method public hidebysig newslot virtual + static !U GetU () cil managed + { + // Method begins at RVA 0x2050 + // Code size 10 (0xa) + .maxstack 1 + .locals init ( + [0] !U + ) + + IL_0000: ldloca.s 0 + IL_0002: initobj !U + IL_0008: ldloc.0 + IL_0009: ret + } // end of method IBase`3::GetU + + .method public hidebysig newslot virtual + static !V GetV () cil managed + { + // Method begins at RVA 0x2068 + // Code size 10 (0xa) + .maxstack 1 + .locals init ( + [0] !V + ) + + IL_0000: ldloca.s 0 + IL_0002: initobj !V + IL_0008: ldloc.0 + IL_0009: ret + } // end of method IBase`3::GetV + +} // end of class IBase`3 + +.class interface public auto ansi abstract beforefieldinit IMiddle`2 + implements class IBase`3, + class IBase`3 +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( + 01 00 02 00 00 + ) + // Methods + .method private hidebysig + static int32 'IBase.GetV' () cil managed + { + .override method !2 class IBase`3::GetV() + // Method begins at RVA 0x207e + // Code size 2 (0x2) + .maxstack 8 + + IL_0000: ldnull + IL_0001: throw + } // end of method IMiddle`2::'IBase.GetV' + + .method private hidebysig + static float32 'IBase.GetV' () cil managed + { + .override method !2 class IBase`3::GetV() + // Method begins at RVA 0x207e + // Code size 2 (0x2) + .maxstack 8 + + IL_0000: ldnull + IL_0001: throw + } // end of method IMiddle`2::'IBase.GetV' + +} // end of class IMiddle`2 + +.class interface public auto ansi abstract beforefieldinit IDerived`1 + implements class IMiddle`2, + class IMiddle`2 + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( + 01 00 02 00 00 + ) + // Methods + .method private hidebysig + static int32 'IBase.GetV' () cil managed + { + .override method !2 class IBase`3::GetV() + // Method begins at RVA 0x207e + // Code size 2 (0x2) + .maxstack 8 + + IL_0000: ldnull + IL_0001: throw + } // end of method IDerived`1::'IBase.GetV' + + .method private hidebysig + static float32 'IBase.GetV' () cil managed + { + .override method !2 class IBase`3::GetV() + // Method begins at RVA 0x207e + // Code size 2 (0x2) + .maxstack 8 + + IL_0000: ldnull + IL_0001: throw + } // end of method IDerived`1::'IBase.GetV' + + .method private hidebysig + static int64 'IBase.GetU' () cil managed + { + .override method !1 class IBase`3::GetU() + // Method begins at RVA 0x207e + // Code size 2 (0x2) + .maxstack 8 + + IL_0000: ldnull + IL_0001: throw + } // end of method IDerived`1::'IBase.GetU' + + .method private hidebysig + static int64 'IBase.GetU' () cil managed + { + .override method !1 class IBase`3::GetU() + // Method begins at RVA 0x207e + // Code size 2 (0x2) + .maxstack 8 + + IL_0000: ldnull + IL_0001: throw + } // end of method IDerived`1::'IBase.GetU' + + .method private hidebysig + static float64 'IBase.GetU' () cil managed + { + .override method !1 class IBase`3::GetU() + // Method begins at RVA 0x207e + // Code size 2 (0x2) + .maxstack 8 + + IL_0000: ldnull + IL_0001: throw + } // end of method IDerived`1::'IBase.GetU' + + .method private hidebysig + static float64 'IBase.GetU' () cil managed + { + .override method !1 class IBase`3::GetU() + // Method begins at RVA 0x207e + // Code size 2 (0x2) + .maxstack 8 + + IL_0000: ldnull + IL_0001: throw + } // end of method IDerived`1::'IBase.GetU' + +} // end of class IDerived`1 + +.class public auto ansi beforefieldinit MyClass + extends [mscorlib]System.Object + implements class IDerived`1, + class IDerived`1 +{ + // Methods + .method private hidebysig newslot + static string 'IBase.GetT' () cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( + 01 00 01 00 00 + ) + .override method !0 class IBase`3::GetT() + // Method begins at RVA 0x207e + // Code size 2 (0x2) + .maxstack 8 + + IL_0000: ldnull + IL_0001: throw + } // end of method MyClass::'IBase.GetT' + + .method private hidebysig newslot + static string 'IBase.GetT' () cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( + 01 00 01 00 00 + ) + .override method !0 class IBase`3::GetT() + // Method begins at RVA 0x207e + // Code size 2 (0x2) + .maxstack 8 + + IL_0000: ldnull + IL_0001: throw + } // end of method MyClass::'IBase.GetT' + + .method private hidebysig newslot + static string 'IBase.GetT' () cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( + 01 00 01 00 00 + ) + .override method !0 class IBase`3::GetT() + // Method begins at RVA 0x207e + // Code size 2 (0x2) + .maxstack 8 + + IL_0000: ldnull + IL_0001: throw + } // end of method MyClass::'IBase.GetT' + + .method private hidebysig newslot + static string 'IBase.GetT' () cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( + 01 00 01 00 00 + ) + .override method !0 class IBase`3::GetT() + // Method begins at RVA 0x207e + // Code size 2 (0x2) + .maxstack 8 + + IL_0000: ldnull + IL_0001: throw + } // end of method MyClass::'IBase.GetT' + + .method private hidebysig newslot + static char 'IBase.GetT' () cil managed + { + .override method !0 class IBase`3::GetT() + // Method begins at RVA 0x207e + // Code size 2 (0x2) + .maxstack 8 + + IL_0000: ldnull + IL_0001: throw + } // end of method MyClass::'IBase.GetT' + + .method private hidebysig newslot + static char 'IBase.GetT' () cil managed + { + .override method !0 class IBase`3::GetT() + // Method begins at RVA 0x207e + // Code size 2 (0x2) + .maxstack 8 + + IL_0000: ldnull + IL_0001: throw + } // end of method MyClass::'IBase.GetT' + + .method private hidebysig newslot + static char 'IBase.GetT' () cil managed + { + .override method !0 class IBase`3::GetT() + // Method begins at RVA 0x207e + // Code size 2 (0x2) + .maxstack 8 + + IL_0000: ldnull + IL_0001: throw + } // end of method MyClass::'IBase.GetT' + + .method private hidebysig newslot + static char 'IBase.GetT' () cil managed + { + .override method !0 class IBase`3::GetT() + // Method begins at RVA 0x207e + // Code size 2 (0x2) + .maxstack 8 + + IL_0000: ldnull + IL_0001: throw + } // end of method MyClass::'IBase.GetT' + + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x2081 + // Code size 7 (0x7) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: ret + } // end of method MyClass::.ctor + +} // end of class MyClass + +.class public auto ansi beforefieldinit MyDerivedClass + extends MyClass +{ + // Methods + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x20c0 + // Code size 7 (0x7) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void MyClass::.ctor() + IL_0006: ret + } // end of method MyDerivedClass::.ctor + +} // end of class MyDerivedClass 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 index 6e703f79d4650..71a772029587a 100644 --- 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 @@ -16,14 +16,14 @@ namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.RecursiveInterfaces [Define ("IL_ASSEMBLY_AVAILABLE")] [SetupCompileBefore ("library.dll", new[] { "Dependencies/GenericInterfaceImplementedRecursively.il" })] [SkipILVerify] + [KeptInterfaceOnTypeInAssembly ("library.dll", "Program/IMiddle`1", "library.dll", "Program/IBase`1")] + [KeptInterfaceOnTypeInAssembly ("library.dll", "Program/IDerived`1", "library.dll", "Program/IMiddle`1")] + [KeptInterfaceOnTypeInAssembly ("library.dll", "Program/C", "library.dll", "Program/IDerived`1")] #if IL_ASSEMBLY_AVAILABLE [KeptTypeInAssembly ("library.dll", typeof(Program.IBase<>))] [KeptTypeInAssembly ("library.dll", typeof(Program.IMiddle<>))] - [KeptInterfaceOnTypeInAssembly ("library.dll", "Program/IMiddle`1", "library.dll", "Program/IBase`1")] [KeptTypeInAssembly ("library.dll", typeof(Program.IDerived<>))] - [KeptInterfaceOnTypeInAssembly ("library.dll", "Program/IDerived`1", "library.dll", "Program/IMiddle`1")] [KeptTypeInAssembly ("library.dll", typeof(Program.C))] - [KeptInterfaceOnTypeInAssembly ("library.dll", "Program/C", "library.dll", "Program/IDerived`1")] #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. diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/OverrideOfRecursiveInterfaceIsRemoved.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/OverrideOfRecursiveInterfaceIsRemoved.cs index 4beafaa1d9d96..6ef9d8848beef 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/OverrideOfRecursiveInterfaceIsRemoved.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/OverrideOfRecursiveInterfaceIsRemoved.cs @@ -30,6 +30,9 @@ namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.RecursiveInterfaces [RemovedOverrideOnMethodInAssembly ("library.dll", "Program/A", "O", "System.Void Program/IMiddleUnused::O()")] [RemovedMemberInAssembly ("library.dll", "Program/IBaseUnused", "M()")] [RemovedMemberInAssembly ("library.dll", "Program/IMiddleUnused", "O()")] + [RuntimeInterfaceOnTypeInAssembly ("library.dll", "Program/A", "Program/IDerived", new string[] { "Program/IDerived" })] + [RuntimeInterfaceOnTypeInAssembly ("library.dll", "Program/A", "Program/IBaseUsed", new string[] { "Program/IDerived", "Program/IMiddleUnused", "Program/IBaseUsed" })] + [RuntimeInterfaceOnTypeInAssembly ("library.dll", "Program/A", "Program/IBaseUnused", new string[] { "Program/IDerived", "Program/IMiddleUnused", "Program/IBaseUnused" })] public class OverrideOfRecursiveInterfaceIsRemoved { [Kept] diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/RecursiveGenericInterfaces.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/RecursiveGenericInterfaces.cs new file mode 100644 index 0000000000000..398e0fbdb5ee0 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/RecursiveGenericInterfaces.cs @@ -0,0 +1,95 @@ +// 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/RecursiveGenericInterfaces.il" })] + [KeptAllTypesAndMembersInAssembly ("library.dll")] + [KeptOverrideOnMethodInAssembly ("library.dll", "IMiddle`2", "IBase.GetV", "V IBase`3::GetV()")] + [KeptOverrideOnMethodInAssembly ("library.dll", "IMiddle`2", "IBase.GetV", "V IBase`3::GetV()")] + [KeptOverrideOnMethodInAssembly ("library.dll", "IDerived`1", "IBase.GetV", "V IBase`3::GetV()")] + [KeptOverrideOnMethodInAssembly ("library.dll", "IDerived`1", "IBase.GetV", "V IBase`3::GetV()")] + [KeptOverrideOnMethodInAssembly ("library.dll", "IDerived`1", "IBase.GetU", "U IBase`3::GetU()")] + [KeptOverrideOnMethodInAssembly ("library.dll", "IDerived`1", "IBase.GetU", "U IBase`3::GetU()")] + [KeptOverrideOnMethodInAssembly ("library.dll", "IDerived`1", "IBase.GetU", "U IBase`3::GetU()")] + [KeptOverrideOnMethodInAssembly ("library.dll", "IDerived`1", "IBase.GetU", "U IBase`3::GetU()")] + [KeptOverrideOnMethodInAssembly ("library.dll", "MyClass", "IBase.GetT", "T IBase`3::GetT()")] + [KeptOverrideOnMethodInAssembly ("library.dll", "MyClass", "IBase.GetT", "T IBase`3::GetT()")] + [KeptOverrideOnMethodInAssembly ("library.dll", "MyClass", "IBase.GetT", "T IBase`3::GetT()")] + [KeptOverrideOnMethodInAssembly ("library.dll", "MyClass", "IBase.GetT", "T IBase`3::GetT()")] + [KeptOverrideOnMethodInAssembly ("library.dll", "MyClass", "IBase.GetT", "T IBase`3::GetT()")] + [KeptOverrideOnMethodInAssembly ("library.dll", "MyClass", "IBase.GetT", "T IBase`3::GetT()")] + [KeptOverrideOnMethodInAssembly ("library.dll", "MyClass", "IBase.GetT", "T IBase`3::GetT()")] + [KeptOverrideOnMethodInAssembly ("library.dll", "MyClass", "IBase.GetT", "T IBase`3::GetT()")] + [RuntimeInterfaceOnTypeInAssembly ("library.dll", "MyClass", "IBase`3", ["IDerived`1", "IMiddle`2", "IBase`3"])] + [RuntimeInterfaceOnTypeInAssembly ("library.dll", "MyClass", "IBase`3", ["IDerived`1", "IMiddle`2", "IBase`3"])] + public class RecursiveGenericInterfaces + { + [Kept] + public static void Main () + { +#if IL_ASSEMBLY_AVAILABLE + UseIBase (new MyDerivedClass ()); + } + + [Kept] + public static void UseIBase (IBase myBase) + { + myBase.GetT (); + myBase.GetU (); + myBase.GetV (); +#endif + } + + //public interface IBase + //{ + // T GetT (); + // U GetU () => default; + // V GetV () => default; + //} + + //public interface IMiddle : IBase, IBase + //{ + // int IBase.GetV () => 12; + // float IBase.GetV () => 12.0f; + //} + + //public interface IDerived : IMiddle, IMiddle + //{ + // int IBase.GetV () => 12; + // float IBase.GetV () => 12.0f; + + // long IBase.GetU () => 12; + // long IBase.GetU () => 12; + + // double IBase.GetU () => 12; + // double IBase.GetU () => 12; + //} + + //public class MyClass : IDerived, IDerived + //{ + // string IBase.GetT () => throw new NotImplementedException (); + // string IBase.GetT () => throw new NotImplementedException (); + // string IBase.GetT () => throw new NotImplementedException (); + // string IBase.GetT () => throw new NotImplementedException (); + // char IBase.GetT () => throw new NotImplementedException (); + // char IBase.GetT () => throw new NotImplementedException (); + // char IBase.GetT () => throw new NotImplementedException (); + // char IBase.GetT () => throw new NotImplementedException (); + //} + + //public class MyDerivedClass : MyClass + //{ } + } +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/RecursiveGenericInterfacesStatic.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/RecursiveGenericInterfacesStatic.cs new file mode 100644 index 0000000000000..b01ee019c6b93 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/RecursiveGenericInterfacesStatic.cs @@ -0,0 +1,115 @@ +// 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 | TestRunCharacteristics.SupportsStaticInterfaceMethods, "Requires support for default and static interface methods")] + [Define ("IL_ASSEMBLY_AVAILABLE")] + [SetupCompileBefore ("library.dll", new[] { "Dependencies/RecursiveGenericInterfacesStatic.il" })] + [KeptMemberInAssembly ("library.dll", "IBase`3", "GetT()", "GetU()", "GetV()")] + [KeptTypeInAssembly ("library.dll", "IMiddle`2")] + // Below isn't strictly necessary, but we keep them since the interface is generic and we haven't hardened generic interface handling to only keep the single closest DIM. + // We use method definition to match the .override to the required DIM. However, one DIM might be for a different generic instance than we are searching for. + // Because of this, we keep all generic interface DIMs that may be the DIM we need. + [KeptMemberInAssembly ("library.dll", "IMiddle`2", "IBase.GetV()")] + [KeptInterfaceOnTypeInAssembly ("library.dll", "IMiddle`2", "library.dll", "IBase`3")] + [KeptInterfaceOnTypeInAssembly ("library.dll", "IMiddle`2", "library.dll", "IBase`3")] + [KeptOverrideOnMethodInAssembly ("library.dll", "IMiddle`2", "IBase.GetV", "V IBase`3::GetV()")] + [KeptTypeInAssembly ("library.dll", "IDerived`1")] + [KeptMemberInAssembly ("library.dll", "IDerived`1", + "IBase.GetV()", + "IBase.GetU()")] + [KeptInterfaceOnTypeInAssembly ("library.dll", "IDerived`1", "library.dll", "IMiddle`2")] + [KeptInterfaceOnTypeInAssembly ("library.dll", "IDerived`1", "library.dll", "IMiddle`2")] + [KeptOverrideOnMethodInAssembly ("library.dll", "IDerived`1", "IBase.GetV", "V IBase`3::GetV()")] + [KeptOverrideOnMethodInAssembly ("library.dll", "IDerived`1", "IBase.GetU", "U IBase`3::GetU()")] + [KeptTypeInAssembly ("library.dll", "MyClass")] + [KeptMemberInAssembly ("library.dll", "MyClass", + "IBase.GetT()", + "IBase.GetT()", + "IBase.GetT()", + "IBase.GetT()", + "IBase.GetT()", + "IBase.GetT()", + "IBase.GetT()", + "IBase.GetT()")] + [KeptInterfaceOnTypeInAssembly ("library.dll", "MyClass", "library.dll", "IDerived`1")] + [KeptInterfaceOnTypeInAssembly ("library.dll", "MyClass", "library.dll", "IDerived`1")] + [KeptOverrideOnMethodInAssembly ("library.dll", "MyClass", "IBase.GetT", "T IBase`3::GetT()")] + [KeptOverrideOnMethodInAssembly ("library.dll", "MyClass", "IBase.GetT", "T IBase`3::GetT()")] + [KeptOverrideOnMethodInAssembly ("library.dll", "MyClass", "IBase.GetT", "T IBase`3::GetT()")] + [KeptOverrideOnMethodInAssembly ("library.dll", "MyClass", "IBase.GetT", "T IBase`3::GetT()")] + [KeptOverrideOnMethodInAssembly ("library.dll", "MyClass", "IBase.GetT", "T IBase`3::GetT()")] + [KeptOverrideOnMethodInAssembly ("library.dll", "MyClass", "IBase.GetT", "T IBase`3::GetT()")] + [KeptOverrideOnMethodInAssembly ("library.dll", "MyClass", "IBase.GetT", "T IBase`3::GetT()")] + [KeptOverrideOnMethodInAssembly ("library.dll", "MyClass", "IBase.GetT", "T IBase`3::GetT()")] + public class RecursiveGenericInterfacesStatic + { + [Kept] + public static void Main () + { + +#if IL_ASSEMBLY_AVAILABLE + UseIBase (); + _ = new MyDerivedClass (); + } + + [Kept] + public static void UseIBase () where TBase: IBase + { + TBase.GetT (); + TBase.GetU (); + TBase.GetV (); +#endif + } + + //public interface IBase + //{ + // static abstract T GetT (); + // static virtual U GetU () => default; + // static virtual V GetV () => default; + //} + + //public interface IMiddle : IBase, IBase + //{ + // static int IBase.GetV () => 12; + // static float IBase.GetV () => 12.0f; + //} + + //public interface IDerived : IMiddle, IMiddle + //{ + // static int IBase.GetV () => 12; + // static float IBase.GetV () => 12.0f; + + // static long IBase.GetU () => 12; + // static long IBase.GetU () => 12; + + // static double IBase.GetU () => 12; + // static double IBase.GetU () => 12; + //} + + //public class MyClass : IDerived, IDerived + //{ + // static string IBase.GetT () => throw new NotImplementedException (); + // static string IBase.GetT () => throw new NotImplementedException (); + // static string IBase.GetT () => throw new NotImplementedException (); + // static string IBase.GetT () => throw new NotImplementedException (); + // static char IBase.GetT () => throw new NotImplementedException (); + // static char IBase.GetT () => throw new NotImplementedException (); + // static char IBase.GetT () => throw new NotImplementedException (); + // static char IBase.GetT () => throw new NotImplementedException (); + //} + + //public class MyDerivedClass : MyClass + //{ } + } +} 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 index 56e08d67be74f..833ace2676c41 100644 --- 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 @@ -20,13 +20,13 @@ namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.RecursiveInterfaces [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))] + [KeptTypeInAssembly("library.dll", typeof(Library.I00))] + [KeptTypeInAssembly("library.dll", typeof(Library.I000))] + [KeptInterfaceOnTypeInAssembly("library.dll", typeof (Library.MyClass), "library.dll", typeof (Library.I000))] #endif public class RecursiveInterfaceKept { - public static void Main() + public static void Main () { #if IL_ASSEMBLY_AVAILABLE Library.I0 _ = new Library.MyClass(); diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/BaseProvidesInterfaceMethod.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/BaseProvidesInterfaceMethod.cs index 081430458926b..6fe4ac5d8fd55 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/BaseProvidesInterfaceMethod.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/BaseProvidesInterfaceMethod.cs @@ -11,6 +11,7 @@ class BaseProvidesInterfaceMethod public static void Main () { CallMethod (); + CallMN (); } [Kept] @@ -38,5 +39,40 @@ class Base // Compiler generates private explicit implementation that calls Base.Method() class Derived : Base, IFoo { } + + [Kept] + static void CallMN () where T : I + { + T.M (); + T.N (); + } + + [Kept] + interface I + { + [Kept] + static abstract string M (); + [Kept] + static abstract string N (); + } + + [Kept] + [KeptInterface (typeof (I))] + class B : I + { + [Kept] + public static string M () => "B.M"; + [Kept] + static string I.N () => "B's I.N"; + } + + [Kept] + [KeptBaseType (typeof (B))] + [KeptInterface (typeof (I))] + class D : B, I + { + [Kept] + static string I.N () => "D's I.N"; + } } } diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/RemovedInterfaceImplementationRemovedOverride.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/RemovedInterfaceImplementationRemovedOverride.cs index 01541dd73a17b..499a08d521857 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/RemovedInterfaceImplementationRemovedOverride.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/RemovedInterfaceImplementationRemovedOverride.cs @@ -22,6 +22,8 @@ public static void Main () GenericInterfaceGenericType.Test (); PrivateExplicitImplementationReflectedOver.Test (); InheritedInterfaces.Test (); + DimWithOverride.Test (); + Generics.Test (); } [Kept] @@ -274,5 +276,92 @@ class UsedDirectly : IDerived public static void M () { } } } + + [Kept] + public class DimWithOverride + { + [Kept] + public static void Test () + { + CallM (); + } + + [Kept] + static void CallM () where T : IBase + { + T.M (); + } + + [Kept] + interface IBase + { + [Kept] + static abstract void M (); + } + + [Kept] + [KeptInterface (typeof (IBase))] + interface IDerived : IBase + { + [Kept] + static void IBase.M () { } + } + + [Kept] + [KeptInterface (typeof (IDerived))] + [KeptInterface (typeof (IBase))] + class MyClass : IDerived + { + } + + [Kept] + [KeptBaseType (typeof (MyClass))] + [KeptInterface (typeof (IBase))] + class MyDerivedClass : MyClass, IBase + { + } + } + + [Kept] + public class Generics + { + [Kept] + public static void Test () + { + UseIGeneric (); + } + + [Kept] + static void UseIGeneric () where T : IGeneric + { + T.GetT (); + } + + [Kept] + interface IGeneric + { + [Kept] + static abstract T GetT (); + } + + [Kept] + [KeptInterface (typeof (IGeneric))] + [KeptInterface (typeof (IGeneric))] + [KeptInterface (typeof (IGeneric))] + class MyType : IGeneric, IGeneric, IGeneric + { + [Kept] + [KeptOverride (typeof (IGeneric))] + static int IGeneric.GetT () => 0; + + [Kept] + [KeptOverride (typeof (IGeneric))] + static float IGeneric.GetT () => 0; + + [Kept] + [KeptOverride (typeof (IGeneric))] + static string IGeneric.GetT () => ""; + } + } } } diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/RequiresCapability/BaseProvidesInterfaceMethodRequiresMismatch.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/RequiresCapability/BaseProvidesInterfaceMethodRequiresMismatch.cs new file mode 100644 index 0000000000000..0ee0ad00f44e8 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/RequiresCapability/BaseProvidesInterfaceMethodRequiresMismatch.cs @@ -0,0 +1,168 @@ +// 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.CodeAnalysis; +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.RequiresCapability +{ + [ExpectedNoWarnings] + [SkipKeptItemsValidation] + [SetupCompileBefore ("library.dll", new[] { "Dependencies/AbstractBaseWithNoMethodsImplementsInterface.il" })] + [Define ("IL_ASSEMBLY_AVAILABLE")] + // + [LogContains ("IL2046: AbstractBaseWithNoMethodsImplementsInterface.Derived.M()", ProducedBy = Tool.None)] + class BaseProvidesInterfaceMethodRequiresMismatch + { + [RequiresUnreferencedCode ("Message")] + public static void Main () + { + BaseDoesNotImplementINoRuc.Run (); + BaseDoesNotImplementIRuc.Run (); + BaseImplementsINoRuc.Run (); + ThreeLevelsInheritance.Run (); + DerivedOverridesImplementingMethod.Run (); +#if IL_ASSEMBLY_AVAILABLE + AbstractBaseWithNoMethodsImplementsInterface.Run (); +#endif + } + + interface INoRuc + { + void M (); + } + + interface IRuc + { + [RequiresUnreferencedCode ("Message")] + void MRuc (); + } + + public class BaseDoesNotImplementINoRuc + { + public class C + { + [RequiresUnreferencedCode ("Message")] + public void M () + { + } + } + + [ExpectedWarning ("IL2046", "C.M()", "INoRuc.M()")] + public class D : C, INoRuc + { + } + + public static void Run () + { + ((INoRuc) new D ()).M (); + } + } + + public class BaseDoesNotImplementIRuc + { + public class C + { + public void MRuc () { } + } + + [ExpectedWarning ("IL2046", "C.MRuc()", "IRuc.MRuc()")] + public class D : C, IRuc + { + } + + [RequiresUnreferencedCode ("Message")] + public static void Run () + { + ((IRuc) new D ()).MRuc (); + } + } + + public class BaseImplementsINoRuc + { + class C : INoRuc + { + [RequiresUnreferencedCode ("Message")] + [ExpectedWarning ("IL2046", "C.M()", "INoRuc.M()")] + public void M () + { + } + } + + [ExpectedWarning ("IL2046", "C.M()", "INoRuc.M()")] + class D : C, INoRuc + { + } + + class D2 : C + { + } + + public static void Run () + { + ((INoRuc) new D ()).M (); + ((INoRuc) new D2 ()).M (); + } + } + + public class ThreeLevelsInheritance + { + public class Base + { + [RequiresUnreferencedCode ("Message")] + public void M () { } + } + + [ExpectedWarning ("IL2046", "Base.M()", "INoRuc.M()")] + public class Middle : Base, INoRuc + { + } + + public class Derived : Middle + { + } + + public static void Run () + { + ((INoRuc) new Derived ()).M (); + } + } + + public class DerivedOverridesImplementingMethod + { + class C : INoRuc + { + [RequiresUnreferencedCode ("Message")] + [ExpectedWarning ("IL2046", "C.M()", "INoRuc.M()")] + public virtual void M () + { + } + } + + class D : C, INoRuc + { + [RequiresUnreferencedCode ("Message")] + [ExpectedWarning ("IL2046", "D.M()", "INoRuc.M()")] + public override void M () { } + } + + class D2 : C + { + [RequiresUnreferencedCode ("Message")] + public override void M () { } + } + + public static void Run () + { + ((INoRuc) new D ()).M (); + ((INoRuc) new D2 ()).M (); + } + } + } +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/RequiresCapability/Dependencies/AbstractBaseWithNoMethodsImplementsInterface.il b/src/tools/illink/test/Mono.Linker.Tests.Cases/RequiresCapability/Dependencies/AbstractBaseWithNoMethodsImplementsInterface.il new file mode 100644 index 0000000000000..cb8797d62f91a --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/RequiresCapability/Dependencies/AbstractBaseWithNoMethodsImplementsInterface.il @@ -0,0 +1,101 @@ +// 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 beforefieldinit AbstractBaseWithNoMethodsImplementsInterface + extends [mscorlib]System.Object +{ + // Methods + .method public hidebysig static + void Run () cil managed + { + .custom instance void [System.Runtime]System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute::.ctor(string) = ( + 01 00 03 52 55 43 00 00 + ) + // Method begins at RVA 0x2050 + // Code size 16 (0x10) + .maxstack 8 + + IL_0000: newobj instance void AbstractBaseWithNoMethodsImplementsInterface/Derived::.ctor() + IL_0005: castclass AbstractBaseWithNoMethodsImplementsInterface/I + IL_000a: callvirt instance void AbstractBaseWithNoMethodsImplementsInterface/I::M() + IL_000f: ret + } // end of method Program::Main + + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x2061 + // Code size 7 (0x7) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: ret + } // end of method Program::.ctor + + + // Nested Types + .class nested assembly auto ansi abstract beforefieldinit Base + extends [mscorlib]System.Object + implements AbstractBaseWithNoMethodsImplementsInterface/I + { + // Methods + .method family hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x2061 + // Code size 7 (0x7) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: ret + } // end of method Base::.ctor + + } // end of class Base + + .class interface nested assembly auto ansi abstract beforefieldinit I + { + // Methods + .method public hidebysig newslot abstract virtual + instance void M () cil managed + { + .custom instance void [mscorlib]System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute::.ctor(string) = ( + 01 00 03 52 55 43 00 00 // "RUC" + ) + } // end of method I::M + + } // end of class I + + .class nested assembly auto ansi beforefieldinit Derived + extends AbstractBaseWithNoMethodsImplementsInterface/Base + { + // Methods + .method public virtual hidebysig + instance void M () cil managed + { + // Method begins at RVA 0x2069 + .maxstack 8 + + IL_0000: ret + } // end of method Derived::M + + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x2075 + // Code size 7 (0x7) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void AbstractBaseWithNoMethodsImplementsInterface/Base::.ctor() + IL_0006: ret + } // end of method Derived::.ctor + + } // end of class Derived + +} // end of class Program 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 7d984ef2e4863..893aca48a5eb4 100644 --- a/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs +++ b/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs @@ -17,6 +17,7 @@ namespace Mono.Linker.Tests.TestCasesRunner partial class AssemblyChecker { readonly AssemblyDefinition originalAssembly, linkedAssembly; + TypeMapInfo _originalTypeMapInfo = new TypeMapInfo (new TestResolver ()); readonly TrimmedTestCaseResult linkedTestCase; HashSet linkedMembers; @@ -25,11 +26,12 @@ partial class AssemblyChecker readonly HashSet verifiedGeneratedTypes = new HashSet (); bool checkNames; - public AssemblyChecker (AssemblyDefinition original, AssemblyDefinition linked, TrimmedTestCaseResult linkedTestCase) + public AssemblyChecker (AssemblyDefinition original, AssemblyDefinition linked, TrimmedTestCaseResult linkedTestCase, TypeMapInfo typeMapInfo) { this.originalAssembly = original; this.linkedAssembly = linked; this.linkedTestCase = linkedTestCase; + this._originalTypeMapInfo = typeMapInfo; checkNames = original.MainModule.GetTypeReferences ().Any (attr => attr.Name == nameof (RemovedNameValueAttribute)); @@ -43,6 +45,8 @@ public void Verify () IEnumerable GetFailures () { + foreach (var err in VerifyTypeMapInfo (originalAssembly)) + yield return err; foreach (var err in VerifyExportedTypes (originalAssembly, linkedAssembly)) yield return err; foreach (var err in VerifyCustomAttributes (originalAssembly, linkedAssembly)) @@ -99,6 +103,44 @@ IEnumerable GetFailures () } } + private IEnumerable VerifyTypeMapInfo (AssemblyDefinition originalAssembly) + { + foreach (var type in originalAssembly.AllDefinedTypes ()) { + foreach (var att in type.CustomAttributes) { + switch (att.AttributeType.Name) { + case nameof (HasRuntimeInterfaceAttribute): + var expectedInterfaceTypeName = att.ConstructorArguments[0].Value as string ?? ((TypeReference) att.ConstructorArguments[0].Value).FullName; + var expectedInterfaceChain = ((CustomAttributeArgument[]) att.ConstructorArguments[1].Value).Select (t => t.Value as string ?? ((TypeReference) t.Value).FullName); + var errs = TypeMapInfoValidation.ValidateRuntimeInterfaces (_originalTypeMapInfo, type, expectedInterfaceTypeName, expectedInterfaceChain); + foreach (var err in errs) + yield return err; + break; + + default: + break; + } + } + foreach (var method in type.Methods) { + foreach (var att in method.CustomAttributes) { + switch (att.AttributeType.Name) { + case nameof (IsOverrideOfAttribute): + string expectedOverriddenMethodName = att.ConstructorArguments.Count switch { + 1 => (string) att.ConstructorArguments[0].Value, + _ => throw new NotImplementedException ($"Unexpected number of arguments in {att.AttributeType.Name} attribute: {att.ConstructorArguments.Count}") + }; + var errs = TypeMapInfoValidation.ValidateMethodIsOverrideOf (_originalTypeMapInfo, method, expectedOverriddenMethodName); + foreach (var err in errs) + yield return err; + + break; + default: + break; + } + } + } + } + } + static bool IsBackingField (FieldDefinition field) => field.Name.StartsWith ("<") && field.Name.EndsWith (">k__BackingField"); protected virtual IEnumerable VerifyModule (ModuleDefinition original, ModuleDefinition linked) @@ -1186,7 +1228,7 @@ IEnumerable VerifyGenericParameterConstraints (GenericParameter src, Gen yield return $"Mismatch in generic parameter constraints on {src} of {src.Owner}. Input has constraints?: {src.HasConstraints}, Output has constraints?: {linked.HasConstraints}"; yield break; } - + if (!src.HasConstraints) yield break; @@ -1222,7 +1264,8 @@ IEnumerable VerifyGenericParameterConstraints (GenericParameter src, Gen yield return string.Join (Environment.NewLine, $"Custom attributes on `{src}' generic parameter constraints are not matching:", missing, extra); } - static bool IsKeptAttributeOnConstraint (CustomAttribute attr) { + static bool IsKeptAttributeOnConstraint (CustomAttribute attr) + { if (attr.AttributeType.Name != nameof (KeptAttributeOnConstraintAttribute)) return false; 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 acf9bf7a2b14d..07404a6ec617f 100644 --- a/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs +++ b/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs @@ -31,6 +31,7 @@ public class ResultChecker readonly TypeNameResolver _linkedTypeNameResolver; readonly ReaderParameters _originalReaderParameters; readonly ReaderParameters _linkedReaderParameters; + readonly TypeMapInfo _originalTypeMapInfo; public ResultChecker () : this (new TestCaseAssemblyResolver (), new TestCaseAssemblyResolver (), @@ -51,6 +52,7 @@ public ResultChecker (BaseAssemblyResolver originalsResolver, BaseAssemblyResolv _linkedTypeNameResolver = new TypeNameResolver (new TestResolver (), new TestAssemblyNameResolver (_linkedResolver)); _originalReaderParameters = originalReaderParameters; _linkedReaderParameters = linkedReaderParameters; + _originalTypeMapInfo = new TypeMapInfo (new TestResolver ()); } protected static void ValidateTypeRefsHaveValidAssemblyRefs (AssemblyDefinition linked) @@ -92,6 +94,9 @@ public virtual void Check (TrimmedTestCaseResult linkResult) var original = ResolveOriginalsAssembly (linkResult.ExpectationsAssemblyPath.FileNameWithoutExtension); VerifyExitCode (linkResult, original); + var errs = VerifyTypeMapOfOtherAssemblies (original); + if (errs.Any ()) + Assert.Fail ($"Errors found in type map validation:\n{string.Join ("\n", errs)}"); if (!HasAttribute (original, nameof (NoLinkedOutputAttribute))) { Assert.IsTrue (linkResult.OutputAssemblyPath.FileExists (), $"The linked output assembly was not found. Expected at {linkResult.OutputAssemblyPath}"); @@ -145,7 +150,7 @@ void VerifyILOfOtherAssemblies (TrimmedTestCaseResult linkResult) protected virtual AssemblyChecker CreateAssemblyChecker (AssemblyDefinition original, AssemblyDefinition linked, TrimmedTestCaseResult linkedTestCase) { - return new AssemblyChecker (original, linked, linkedTestCase); + return new AssemblyChecker (original, linked, linkedTestCase, _originalTypeMapInfo); } void InitializeResolvers (TrimmedTestCaseResult linkedResult) @@ -290,6 +295,35 @@ protected virtual void InitialChecking (TrimmedTestCaseResult linkResult, Assemb ValidateTypeRefsHaveValidAssemblyRefs (linked); } + IEnumerable VerifyTypeMapOfOtherAssemblies (AssemblyDefinition original) + { + var checks = BuildTypeMapInfoCheckTable (original); + List errs = []; + try { + foreach (var assemblyName in checks.Keys) { + var assembly = ResolveOriginalsAssembly (assemblyName); + foreach (var check in checks[assemblyName]) { + switch (check.AttributeType.Name) { + case nameof (RuntimeInterfaceOnTypeInAssembly): + object type = check.ConstructorArguments[1].Value; + TypeDefinition typeDef = GetOriginalTypeFromInAssemblyAttribute (assemblyName, type); + string interfaceName = (check.ConstructorArguments[2].Value as TypeReference)?.FullName ?? (string) check.ConstructorArguments[2].Value; + var expectedImplChain = (check.ConstructorArguments[3].Value as CustomAttributeArgument[]) + .Select (caa => (caa.Value as TypeReference)?.FullName ?? (string) caa.Value); + errs.AddRange (TypeMapInfoValidation.ValidateRuntimeInterfaces (_originalTypeMapInfo, typeDef, interfaceName, expectedImplChain)); + break; + default: + break; + } + } + + } + } catch (AssemblyResolutionException e) { + Assert.Fail ($"Failed to resolve linked assembly `{e.AssemblyReference.Name}`. It must not exist in any of the input directories:\n\t{_originalsResolver.GetSearchDirectories ().Aggregate ((buff, s) => $"{buff}\n\t{s}")}\n"); + } + return errs; + } + void VerifyLinkingOfOtherAssemblies (AssemblyDefinition original) { var checks = BuildOtherAssemblyCheckTable (original); @@ -318,7 +352,7 @@ void VerifyLinkingOfOtherAssemblies (AssemblyDefinition original) TypeReference linkedTypeRef = null; try { _linkedTypeNameResolver.TryResolveTypeName (linkedAssembly, expectedTypeName, out linkedTypeRef, out _); - } catch (AssemblyResolutionException) {} + } catch (AssemblyResolutionException) { } TypeDefinition linkedType = linkedTypeRef?.Resolve (); if (linkedType == null && linkedAssembly.MainModule.HasExportedTypes) { @@ -963,8 +997,8 @@ void VerifyLoggedMessages (AssemblyDefinition original, TrimmingTestLogger logge if (actualName.StartsWith (expectedMember.DeclaringType.FullName) && (actualName.Contains ("<" + expectedMember.Name + ">") || - actualName.EndsWith ("get_" + expectedMember.Name) || - actualName.EndsWith ("set_" + expectedMember.Name))) { + actualName.EndsWith ("get_" + expectedMember.Name) || + actualName.EndsWith ("set_" + expectedMember.Name))) { expectedWarningFound = true; unmatchedMessages.Remove (loggedMessage); break; @@ -1256,18 +1290,38 @@ protected TypeDefinition GetOriginalTypeFromInAssemblyAttribute (string assembly return originalType; } + + bool IsTypeInOtherAssemblyAssertion (CustomAttribute attr) + { + if (false == attr.AttributeType.Resolve ()?.DerivesFrom (nameof (BaseInAssemblyAttribute))) + return false; + + Tool? toolTarget = (Tool?) (int?) attr.GetPropertyValue ("Tool"); + if (toolTarget is not null && !toolTarget.Value.HasFlag (Tool.Trimmer)) + return false; + + return true; + } + + bool IsTypeMapInfoInOtherAssemblyAssertion (CustomAttribute attr) + { + return attr.AttributeType.Resolve ()?.DerivesFrom (nameof (BaseTypeMapInfoAttribute)) ?? false; + } + + Dictionary> BuildTypeMapInfoCheckTable (AssemblyDefinition original) + => BuildChecksTable (original, IsTypeMapInfoInOtherAssemblyAssertion); + Dictionary> BuildOtherAssemblyCheckTable (AssemblyDefinition original) + => BuildChecksTable (original, IsTypeInOtherAssemblyAssertion); + + Dictionary> BuildChecksTable (AssemblyDefinition original, Func predicate) { var checks = new Dictionary> (); foreach (var typeWithRemoveInAssembly in original.AllDefinedTypes ()) { - foreach (var attr in typeWithRemoveInAssembly.CustomAttributes.Where (IsTypeInOtherAssemblyAssertion)) { - var assemblyName = (string) attr.ConstructorArguments[0].Value; - - Tool? toolTarget = (Tool?) (int?) attr.GetPropertyValue ("Tool"); - if (toolTarget is not null && !toolTarget.Value.HasFlag (Tool.Trimmer)) - continue; + foreach (var attr in typeWithRemoveInAssembly.CustomAttributes.Where (predicate)) { + var assemblyName = (string) attr.ConstructorArguments[0].Value; if (!checks.TryGetValue (assemblyName, out List checksForAssembly)) checks[assemblyName] = checksForAssembly = new List (); @@ -1283,10 +1337,6 @@ protected virtual void UnhandledOtherAssemblyAssertion (string expectedTypeName, throw new NotImplementedException ($"Type {expectedTypeName}, has an unknown other assembly attribute of type {checkAttrInAssembly.AttributeType}"); } - bool IsTypeInOtherAssemblyAssertion (CustomAttribute attr) - { - return attr.AttributeType.Resolve ()?.DerivesFrom (nameof (BaseInAssemblyAttribute)) ?? false; - } static bool HasAttribute (ICustomAttributeProvider caProvider, string attributeName) { diff --git a/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/TypeMapInfoValidation.cs b/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/TypeMapInfoValidation.cs new file mode 100644 index 0000000000000..d5249aa8ec89e --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/TypeMapInfoValidation.cs @@ -0,0 +1,52 @@ +// 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 Microsoft.CodeAnalysis.CSharp.Syntax; +using Mono.Cecil; +using Mono.Linker.Tests.Cases.Inheritance.AbstractClasses.NoKeptCtor.OverrideRemoval; + +namespace Mono.Linker.Tests.TestCasesRunner +{ + internal class TypeMapInfoValidation + { + public static IEnumerable ValidateRuntimeInterfaces (TypeMapInfo typeMapInfo, TypeDefinition typeDef, string expectedInterfaceName, IEnumerable expectedImplChain) + { + var runtimeInterfaces = typeMapInfo.GetRecursiveInterfaces (typeDef); + if (runtimeInterfaces is []) { + yield return ($"Expected type `{typeDef}` to have runtime interface `{expectedInterfaceName}`, but it has none"); + yield break; + } + var runtimeInterface = runtimeInterfaces.SingleOrDefault (i => i.InflatedInterfaceType.FullName == expectedInterfaceName); + if (runtimeInterface == default) { + yield return ($"Expected type `{typeDef}` to have runtime interface `{expectedInterfaceName}`"); + yield break; + } + + if (expectedImplChain.Any ()) { + var matchingImplementationChainCount = runtimeInterface.InterfaceImplementationChains + .Count (chain => ImplChainMatches (chain, expectedImplChain)); + if (matchingImplementationChainCount == 0) { + yield return ($"Type {typeDef.FullName} does not have expected implementation chain for runtime interface `{expectedInterfaceName}`: {string.Join ("->", expectedImplChain.Select (i => $"`{i}`"))}"); + yield break; + } + } + + static bool ImplChainMatches (InterfaceImplementationChain chain, IEnumerable expectedChain) + { + return chain.InterfaceImplementations.Select (i => i.InterfaceType.FullName).SequenceEqual (expectedChain); + } + } + + public static IEnumerable ValidateMethodIsOverrideOf (TypeMapInfo typeMapInfo, MethodDefinition methodDef, string expectedOverriddenMethodName) + { + var overriddenMethods = typeMapInfo.GetBaseMethods (methodDef); + if (overriddenMethods is null || !overriddenMethods.Select (o => o.Base.FullName).Contains (expectedOverriddenMethodName)) + yield return $"Expected method `{methodDef}` to be an override of `{expectedOverriddenMethodName}`"; + } + } +} diff --git a/src/tools/illink/test/Trimming.Tests.Shared/TestResolver.cs b/src/tools/illink/test/Trimming.Tests.Shared/TestResolver.cs index af37fcfbe6487..2d76fc7a8c894 100644 --- a/src/tools/illink/test/Trimming.Tests.Shared/TestResolver.cs +++ b/src/tools/illink/test/Trimming.Tests.Shared/TestResolver.cs @@ -5,8 +5,14 @@ namespace Mono.Linker.Tests.TestCasesRunner { - struct TestResolver : ITryResolveMetadata + struct TestResolver : ITryResolveMetadata, IMetadataResolver { + public TypeDefinition Resolve (TypeReference type) => type.Resolve (); + + public FieldDefinition Resolve (FieldReference field) => field.Resolve (); + + public MethodDefinition Resolve (MethodReference method) => method.Resolve (); + public MethodDefinition TryResolve (MethodReference methodReference) => methodReference.Resolve (); public TypeDefinition TryResolve (TypeReference typeReference) => typeReference.Resolve ();