diff options
author | Vitek Karas <vitek.karas@microsoft.com> | 2021-05-13 16:28:35 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-05-13 16:28:35 +0300 |
commit | 121115abe92f03c8b702732523098cb067777531 (patch) | |
tree | adee5b358949572b2a2123e4d19a6288f29fb309 | |
parent | 939d0ffd2720943149af7da0a922cf2913fb3e54 (diff) |
Implement DynamicallyAccessedMemberTypes.Interfaces (#2023)
Implement marking logic for Interfaces
- Transitively mark all interface implementations on a type
- Mark all interface implementations on all base types as well
Implement annotation propagation over Type.GetInterface
Add tests for marking behavior as well as annotation behavior
Workaround: Currently the Interfaces enum value can't be used by the linker itself
since it can't yet rely on high enough version of framework. Worked around this
by adding an overlay class and a const value. Once linker is upgraded to high enough
framework version, this workaround should be removed.
9 files changed, 629 insertions, 33 deletions
diff --git a/src/linker/Linker.Dataflow/DynamicallyAccessedMembersBinder.cs b/src/linker/Linker.Dataflow/DynamicallyAccessedMembersBinder.cs index f71e7c9d2..477174caa 100644 --- a/src/linker/Linker.Dataflow/DynamicallyAccessedMembersBinder.cs +++ b/src/linker/Linker.Dataflow/DynamicallyAccessedMembersBinder.cs @@ -10,12 +10,19 @@ using Mono.Cecil; namespace Mono.Linker { + // Temporary workaround - should be removed once linker can be upgraded to build against + // high enough version of the framework which has this enum value. + internal static class DynamicallyAccessedMemberTypesOverlay + { + public const DynamicallyAccessedMemberTypes Interfaces = (DynamicallyAccessedMemberTypes) 0x2000; + } + internal static class DynamicallyAccessedMembersBinder { - // Returns the members of the type bound by memberTypes. For MemberTypes.All, this returns a single null result. - // This sentinel value allows callers to handle the case where MemberTypes.All conceptually binds to the entire type - // including all recursive nested members. - public static IEnumerable<IMemberDefinition> GetDynamicallyAccessedMembers (this TypeDefinition typeDefinition, LinkContext context, DynamicallyAccessedMemberTypes memberTypes) + // Returns the members of the type bound by memberTypes. For DynamicallyAccessedMemberTypes.All, this returns a single null result. + // This sentinel value allows callers to handle the case where DynamicallyAccessedMemberTypes.All conceptually binds to the entire type + // including all recursive nested members. + public static IEnumerable<IMetadataTokenProvider> GetDynamicallyAccessedMembers (this TypeDefinition typeDefinition, LinkContext context, DynamicallyAccessedMemberTypes memberTypes) { if (memberTypes == DynamicallyAccessedMemberTypes.All) { yield return null; @@ -86,6 +93,11 @@ namespace Mono.Linker foreach (var e in typeDefinition.GetEventsOnTypeHierarchy (context, filter: null, bindingFlags: BindingFlags.Public)) yield return e; } + + if (memberTypes.HasFlag (DynamicallyAccessedMemberTypesOverlay.Interfaces)) { + foreach (var i in typeDefinition.GetAllInterfaceImplementations (context)) + yield return i; + } } public static IEnumerable<MethodDefinition> GetConstructorsOnType (this TypeDefinition type, Func<MethodDefinition, bool> filter, BindingFlags? bindingFlags = null) @@ -298,5 +310,22 @@ namespace Mono.Linker onBaseType = true; } } + + public static IEnumerable<InterfaceImplementation> GetAllInterfaceImplementations (this TypeDefinition type, LinkContext context) + { + while (type != null) { + foreach (var i in type.Interfaces) { + yield return i; + + TypeDefinition interfaceType = context.TryResolveTypeDefinition (i.InterfaceType); + if (interfaceType != null) { + foreach (var innerInterface in interfaceType.GetAllInterfaceImplementations (context)) + yield return innerInterface; + } + } + + type = context.TryResolveTypeDefinition (type.BaseType); + } + } } }
\ No newline at end of file diff --git a/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs b/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs index ff63e562b..6328b4a30 100644 --- a/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs +++ b/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs @@ -18,6 +18,17 @@ namespace Mono.Linker.Dataflow class ReflectionMethodBodyScanner : MethodBodyScanner { readonly MarkStep _markStep; + static readonly DynamicallyAccessedMemberTypes[] AllDynamicallyAccessedMemberTypes = GetAllDynamicallyAccessedMemberTypes (); + + static DynamicallyAccessedMemberTypes[] GetAllDynamicallyAccessedMemberTypes () + { + HashSet<DynamicallyAccessedMemberTypes> values = Enum.GetValues (typeof (DynamicallyAccessedMemberTypes)) + .Cast<DynamicallyAccessedMemberTypes> () + .ToHashSet (); + if (!values.Contains (DynamicallyAccessedMemberTypesOverlay.Interfaces)) + values.Add (DynamicallyAccessedMemberTypesOverlay.Interfaces); + return values.ToArray (); + } public static bool RequiresReflectionMethodBodyScannerForCallSite (LinkContext context, MethodReference calledMethod) { @@ -263,6 +274,7 @@ namespace Mono.Linker.Dataflow Type_GetNestedTypes, Type_GetMember, Type_GetMembers, + Type_GetInterface, Type_get_AssemblyQualifiedName, Type_get_UnderlyingSystemType, Type_get_BaseType, @@ -469,6 +481,15 @@ namespace Mono.Linker.Dataflow && calledMethod.HasThis => IntrinsicId.Type_GetMembers, + // System.Type.GetInterface (string) + // System.Type.GetInterface (string, bool) + "GetInterface" when calledMethod.IsDeclaredOnType ("System", "Type") + && calledMethod.HasParameterOfType (0, "System", "String") + && calledMethod.HasThis + && (calledMethod.Parameters.Count == 1 || + (calledMethod.Parameters.Count == 2 && calledMethod.HasParameterOfType (1, "System", "Boolean"))) + => IntrinsicId.Type_GetInterface, + // System.Type.AssemblyQualifiedName "get_AssemblyQualifiedName" when calledMethod.IsDeclaredOnType ("System", "Type") && !calledMethod.HasParameters @@ -1469,6 +1490,33 @@ namespace Mono.Linker.Dataflow break; // + // GetInterface (String) + // GetInterface (String, bool) + // + case IntrinsicId.Type_GetInterface: { + reflectionContext.AnalyzingPattern (); + + foreach (var value in methodParams[0].UniqueValues ()) { + // For now no support for marking a single interface by name. We would have to correctly support + // mangled names for generics to do that correctly. Simply mark all interfaces on the type for now. + + // Require Interfaces annotation + RequireDynamicallyAccessedMembers (ref reflectionContext, DynamicallyAccessedMemberTypesOverlay.Interfaces, value, calledMethodDefinition); + + // Interfaces is transitive, so the return values will always have at least Interfaces annotation + DynamicallyAccessedMemberTypes returnMemberTypes = DynamicallyAccessedMemberTypesOverlay.Interfaces; + + // Propagate All annotation across the call - All is a superset of Interfaces + if (value is LeafValueWithDynamicallyAccessedMemberNode annotatedNode + && annotatedNode.DynamicallyAccessedMemberTypes == DynamicallyAccessedMemberTypes.All) + returnMemberTypes = DynamicallyAccessedMemberTypes.All; + + methodReturnValue = MergePointValue.MergeValues (methodReturnValue, CreateMethodReturnValue (calledMethod, returnMemberTypes)); + } + } + break; + + // // System.Activator // // static CreateInstance (System.Type type) @@ -1738,7 +1786,7 @@ namespace Mono.Linker.Dataflow return true; } - private bool AnalyzeGenericInstatiationTypeArray (ValueNode arrayParam, ref ReflectionPatternContext reflectionContext, MethodReference calledMethod, IList<GenericParameter> genericParameters) + bool AnalyzeGenericInstatiationTypeArray (ValueNode arrayParam, ref ReflectionPatternContext reflectionContext, MethodReference calledMethod, IList<GenericParameter> genericParameters) { bool hasRequirements = false; foreach (var genericParameter in genericParameters) { @@ -1868,19 +1916,24 @@ namespace Mono.Linker.Dataflow reflectionContext.RecordHandledPattern (); } else if (uniqueValue is LeafValueWithDynamicallyAccessedMemberNode valueWithDynamicallyAccessedMember) { if (!valueWithDynamicallyAccessedMember.DynamicallyAccessedMemberTypes.HasFlag (requiredMemberTypes)) { - string missingMemberTypes = $"'{nameof (DynamicallyAccessedMemberTypes.All)}'"; + string missingMemberTypes = $"'{nameof (DynamicallyAccessedMemberTypes)}.{nameof (DynamicallyAccessedMemberTypes.All)}'"; if (requiredMemberTypes != DynamicallyAccessedMemberTypes.All) { - var missingMemberTypesList = Enum.GetValues (typeof (DynamicallyAccessedMemberTypes)) - .Cast<DynamicallyAccessedMemberTypes> () + var missingMemberTypesList = AllDynamicallyAccessedMemberTypes .Where (damt => (requiredMemberTypes & ~valueWithDynamicallyAccessedMember.DynamicallyAccessedMemberTypes & damt) == damt && damt != DynamicallyAccessedMemberTypes.None) - .Select (damt => damt.ToString ()).ToList (); + .ToList (); - if (missingMemberTypesList.Contains (nameof (DynamicallyAccessedMemberTypes.PublicConstructors)) && - missingMemberTypesList.SingleOrDefault (x => x == nameof (DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)) is var ppc && - ppc != null) + if (missingMemberTypesList.Contains (DynamicallyAccessedMemberTypes.PublicConstructors) && + missingMemberTypesList.SingleOrDefault (x => x == DynamicallyAccessedMemberTypes.PublicParameterlessConstructor) is var ppc && + ppc != DynamicallyAccessedMemberTypes.None) missingMemberTypesList.Remove (ppc); - missingMemberTypes = string.Join (", ", missingMemberTypesList.Select (mmt => $"'DynamicallyAccessedMemberTypes.{mmt}'")); + missingMemberTypes = string.Join (", ", missingMemberTypesList.Select (mmt => { + string mmtName = mmt == DynamicallyAccessedMemberTypesOverlay.Interfaces + ? nameof (DynamicallyAccessedMemberTypesOverlay.Interfaces) + : mmt.ToString (); + + return $"'{nameof (DynamicallyAccessedMemberTypes)}.{mmtName}'"; + })); } switch ((valueWithDynamicallyAccessedMember.SourceContext, targetContext)) { @@ -2144,7 +2197,7 @@ namespace Mono.Linker.Dataflow MarkField (ref reflectionContext, field, DependencyKind.DynamicallyAccessedMember); break; case TypeDefinition nestedType: - MarkNestedType (ref reflectionContext, nestedType, DependencyKind.DynamicallyAccessedMember); + MarkType (ref reflectionContext, nestedType, DependencyKind.DynamicallyAccessedMember); break; case PropertyDefinition property: MarkProperty (ref reflectionContext, property, DependencyKind.DynamicallyAccessedMember); @@ -2152,6 +2205,9 @@ namespace Mono.Linker.Dataflow case EventDefinition @event: MarkEvent (ref reflectionContext, @event, DependencyKind.DynamicallyAccessedMember); break; + case InterfaceImplementation interfaceImplementation: + MarkInterfaceImplementation (ref reflectionContext, interfaceImplementation, DependencyKind.DynamicallyAccessedMember); + break; case null: var source = reflectionContext.Source; reflectionContext.RecordRecognizedPattern (typeDefinition, () => _markStep.MarkEntireType (typeDefinition, includeBaseTypes: true, includeInterfaceTypes: true, new DependencyInfo (DependencyKind.DynamicallyAccessedMember, source), source)); @@ -2160,11 +2216,11 @@ namespace Mono.Linker.Dataflow } } - void MarkType (ref ReflectionPatternContext reflectionContext, TypeReference typeReference) + void MarkType (ref ReflectionPatternContext reflectionContext, TypeReference typeReference, DependencyKind dependencyKind = DependencyKind.AccessedViaReflection) { var source = reflectionContext.Source; TypeDefinition type = _context.TryResolveTypeDefinition (typeReference); - reflectionContext.RecordRecognizedPattern (type, () => _markStep.MarkTypeVisibleToReflection (typeReference, type, new DependencyInfo (DependencyKind.AccessedViaReflection, source), source)); + reflectionContext.RecordRecognizedPattern (type, () => _markStep.MarkTypeVisibleToReflection (typeReference, type, new DependencyInfo (dependencyKind, source), source)); } void MarkMethod (ref ReflectionPatternContext reflectionContext, MethodDefinition method, DependencyKind dependencyKind = DependencyKind.AccessedViaReflection) @@ -2174,13 +2230,6 @@ namespace Mono.Linker.Dataflow reflectionContext.RecordRecognizedPattern (method, () => _markStep.MarkMethodVisibleToReflection (method, new DependencyInfo (dependencyKind, source), new MessageOrigin (source, offset))); } - void MarkNestedType (ref ReflectionPatternContext reflectionContext, TypeDefinition nestedType, DependencyKind dependencyKind = DependencyKind.AccessedViaReflection) - { - var source = reflectionContext.Source; - TypeDefinition type = _context.TryResolveTypeDefinition (nestedType); - reflectionContext.RecordRecognizedPattern (nestedType, () => _markStep.MarkTypeVisibleToReflection (nestedType, type, new DependencyInfo (dependencyKind, source), source)); - } - void MarkField (ref ReflectionPatternContext reflectionContext, FieldDefinition field, DependencyKind dependencyKind = DependencyKind.AccessedViaReflection) { var source = reflectionContext.Source; @@ -2213,6 +2262,12 @@ namespace Mono.Linker.Dataflow }); } + void MarkInterfaceImplementation (ref ReflectionPatternContext reflectionContext, InterfaceImplementation interfaceImplementation, DependencyKind dependencyKind = DependencyKind.AccessedViaReflection) + { + var source = reflectionContext.Source; + reflectionContext.RecordRecognizedPattern (interfaceImplementation, () => _markStep.MarkInterfaceImplementation (interfaceImplementation, source, new DependencyInfo (dependencyKind, source))); + } + void MarkConstructorsOnType (ref ReflectionPatternContext reflectionContext, TypeDefinition type, Func<MethodDefinition, bool> filter, BindingFlags? bindingFlags = null) { foreach (var ctor in type.GetConstructorsOnType (filter, bindingFlags)) @@ -2231,7 +2286,7 @@ namespace Mono.Linker.Dataflow foreach (var nestedType in type.GetNestedTypesOnType (filter, bindingFlags)) { result.Add (nestedType); - MarkNestedType (ref reflectionContext, nestedType); + MarkType (ref reflectionContext, nestedType); } return result.ToArray (); diff --git a/src/linker/Linker.Dataflow/ReflectionPatternContext.cs b/src/linker/Linker.Dataflow/ReflectionPatternContext.cs index 564406409..3b4e08e53 100644 --- a/src/linker/Linker.Dataflow/ReflectionPatternContext.cs +++ b/src/linker/Linker.Dataflow/ReflectionPatternContext.cs @@ -67,7 +67,7 @@ namespace Mono.Linker.Dataflow } #pragma warning restore CA1822 - public void RecordRecognizedPattern (IMemberDefinition accessedItem, Action mark) + public void RecordRecognizedPattern (IMetadataTokenProvider accessedItem, Action mark) { #if DEBUG if (!_patternAnalysisAttempted) diff --git a/src/linker/Linker.Steps/MarkStep.cs b/src/linker/Linker.Steps/MarkStep.cs index 1a5ae1bc8..1997bc9d0 100644 --- a/src/linker/Linker.Steps/MarkStep.cs +++ b/src/linker/Linker.Steps/MarkStep.cs @@ -884,7 +884,7 @@ namespace Mono.Linker.Steps } } - IEnumerable<IMemberDefinition> members; + IEnumerable<IMetadataTokenProvider> members; if (dynamicDependency.MemberSignature is string memberSignature) { members = DocumentationSignatureParser.GetMembersByDocumentationSignature (type, memberSignature, acceptName: true); if (!members.Any ()) { @@ -903,7 +903,7 @@ namespace Mono.Linker.Steps MarkMembers (type, members, new DependencyInfo (DependencyKind.DynamicDependency, dynamicDependency.OriginalAttribute), context); } - void MarkMembers (TypeDefinition typeDefinition, IEnumerable<IMemberDefinition> members, DependencyInfo reason, IMemberDefinition sourceLocationMember) + void MarkMembers (TypeDefinition typeDefinition, IEnumerable<IMetadataTokenProvider> members, DependencyInfo reason, IMemberDefinition sourceLocationMember) { foreach (var member in members) { switch (member) { @@ -926,6 +926,9 @@ namespace Mono.Linker.Steps MarkEvent (@event, reason); MarkMethodsIf (@event.OtherMethods, m => true, reason, sourceLocationMember); break; + case InterfaceImplementation interfaceType: + MarkInterfaceImplementation (interfaceType, sourceLocationMember, reason); + break; case null: MarkEntireType (typeDefinition, includeBaseTypes: true, includeInterfaceTypes: true, reason, sourceLocationMember); break; @@ -3267,16 +3270,16 @@ namespace Mono.Linker.Steps return IsFullyPreserved (type); } - protected virtual void MarkInterfaceImplementation (InterfaceImplementation iface, TypeDefinition type) + protected internal virtual void MarkInterfaceImplementation (InterfaceImplementation iface, IMemberDefinition sourceLocation, DependencyInfo? reason = null) { if (Annotations.IsMarked (iface)) return; // Blame the type that has the interfaceimpl, expecting the type itself to get marked for other reasons. - MarkCustomAttributes (iface, new DependencyInfo (DependencyKind.CustomAttribute, iface), type); + MarkCustomAttributes (iface, new DependencyInfo (DependencyKind.CustomAttribute, iface), sourceLocation); // Blame the interface type on the interfaceimpl itself. - MarkType (iface.InterfaceType, new DependencyInfo (DependencyKind.InterfaceImplementationInterfaceType, iface), type); - Annotations.MarkProcessed (iface, new DependencyInfo (DependencyKind.InterfaceImplementationOnType, type)); + MarkType (iface.InterfaceType, reason ?? new DependencyInfo (DependencyKind.InterfaceImplementationInterfaceType, iface), sourceLocation); + Annotations.MarkProcessed (iface, reason ?? new DependencyInfo (DependencyKind.InterfaceImplementationOnType, sourceLocation)); } // diff --git a/test/Mono.Linker.Tests.Cases.Expectations/Helpers/DataFlowTypeExtensions.cs b/test/Mono.Linker.Tests.Cases.Expectations/Helpers/DataFlowTypeExtensions.cs index 9e9c484af..7f22b45c8 100644 --- a/test/Mono.Linker.Tests.Cases.Expectations/Helpers/DataFlowTypeExtensions.cs +++ b/test/Mono.Linker.Tests.Cases.Expectations/Helpers/DataFlowTypeExtensions.cs @@ -37,6 +37,8 @@ namespace Mono.Linker.Tests.Cases.Expectations.Helpers public static void RequiresNonPublicProperties ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicProperties)] this Type type) { } + public static void RequiresInterfaces ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.Interfaces)] this Type type) { } + public static void RequiresNone (this Type type) { } } } diff --git a/test/Mono.Linker.Tests.Cases/DataFlow/GetInterfaceDataFlow.cs b/test/Mono.Linker.Tests.Cases/DataFlow/GetInterfaceDataFlow.cs new file mode 100644 index 000000000..630fc3c14 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/DataFlow/GetInterfaceDataFlow.cs @@ -0,0 +1,142 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more 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.Helpers; + +namespace Mono.Linker.Tests.Cases.DataFlow +{ + [SkipKeptItemsValidation] + [ExpectedNoWarnings] + public class GetInterfaceDataFlow + { + public static void Main () + { + GetInterface_Name.Test (); + GetInterface_Name_IgnoreCase.Test (); + } + + class GetInterface_Name + { + [ExpectedWarning ("IL2070", nameof (Type.GetInterface), nameof (DynamicallyAccessedMemberTypes) + "." + nameof (DynamicallyAccessedMemberTypes.Interfaces))] + static void TestNoAnnotation (Type type) + { + type.GetInterface ("ITestInterface"); + } + + [ExpectedWarning ("IL2072", nameof (DataFlowTypeExtensions.RequiresAll), nameof (DynamicallyAccessedMemberTypes) + "." + nameof (DynamicallyAccessedMemberTypes.All))] + static void TestWithAnnotation ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.Interfaces)] Type type) + { + type.GetInterface ("ITestInterface").RequiresInterfaces (); + type.GetInterface ("ITestInterface").RequiresAll (); // Warns + } + + static void TestWithAll ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] Type type) + { + type.GetInterface ("ITestInterface").RequiresInterfaces (); + type.GetInterface ("ITestInterface").RequiresAll (); + } + + [ExpectedWarning ("IL2072", nameof (DataFlowTypeExtensions.RequiresAll), nameof (DynamicallyAccessedMemberTypes) + "." + nameof (DynamicallyAccessedMemberTypes.All))] + static void TestKnownType () + { + // Interfaces marking is transitive - meaning that it marks all interfaces in the entire hierarchy + // so the return value of GetInterface always has all interfaces on it already. + typeof (TestType).GetInterface ("ITestInterface").RequiresInterfaces (); + typeof (TestType).GetInterface ("ITestInterface").RequiresAll (); // Warns + } + + [ExpectedWarning ("IL2072", nameof (DataFlowTypeExtensions.RequiresAll), nameof (DynamicallyAccessedMemberTypes) + "." + nameof (DynamicallyAccessedMemberTypes.All))] + static void TestMultipleValues (int p, [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] Type typeWithAll) + { + Type type; + if (p == 0) + type = typeof (TestType); + else + type = typeWithAll; + + type.GetInterface ("ITestInterface").RequiresInterfaces (); + type.GetInterface ("ITestInterface").RequiresAll (); // Warns since only one of the values is guaranteed All + } + + public static void Test () + { + TestNoAnnotation (typeof (TestType)); + TestWithAnnotation (typeof (TestType)); + TestWithAnnotation (typeof (ITestInterface)); + TestWithAll (typeof (TestType)); + TestKnownType (); + TestMultipleValues (0, typeof (TestType)); + } + } + + class GetInterface_Name_IgnoreCase + { + [ExpectedWarning ("IL2070", nameof (Type.GetInterface), nameof (DynamicallyAccessedMemberTypes) + "." + nameof (DynamicallyAccessedMemberTypes.Interfaces))] + static void TestNoAnnotation (Type type) + { + type.GetInterface ("ITestInterface", false); + } + + [ExpectedWarning ("IL2072", nameof (DataFlowTypeExtensions.RequiresAll), nameof (DynamicallyAccessedMemberTypes) + "." + nameof (DynamicallyAccessedMemberTypes.All))] + static void TestWithAnnotation ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.Interfaces)] Type type) + { + type.GetInterface ("ITestInterface", false).RequiresInterfaces (); + type.GetInterface ("ITestInterface", false).RequiresAll (); // Warns + } + + static void TestWithAll ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] Type type) + { + type.GetInterface ("ITestInterface", false).RequiresInterfaces (); + type.GetInterface ("ITestInterface", false).RequiresAll (); + } + + [ExpectedWarning ("IL2072", nameof (DataFlowTypeExtensions.RequiresAll), nameof (DynamicallyAccessedMemberTypes) + "." + nameof (DynamicallyAccessedMemberTypes.All))] + static void TestKnownType () + { + // Interfaces marking is transitive - meaning that it marks all interfaces in the entire hierarchy + // so the return value of GetInterface always has all interfaces on it already. + typeof (TestType).GetInterface ("ITestInterface", false).RequiresInterfaces (); + typeof (TestType).GetInterface ("ITestInterface", false).RequiresAll (); + } + + [ExpectedWarning ("IL2072", nameof (DataFlowTypeExtensions.RequiresAll), nameof (DynamicallyAccessedMemberTypes) + "." + nameof (DynamicallyAccessedMemberTypes.All))] + static void TestMultipleValues (int p, [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] Type typeWithAll) + { + Type type; + if (p == 0) + type = typeof (TestType); + else + type = typeWithAll; + + type.GetInterface ("ITestInterface", false).RequiresInterfaces (); + type.GetInterface ("ITestInterface", false).RequiresAll (); // Warns since only one of the values is guaranteed All + } + + public static void Test () + { + TestNoAnnotation (typeof (TestType)); + TestWithAnnotation (typeof (TestType)); + TestWithAnnotation (typeof (ITestInterface)); + TestWithAll (typeof (TestType)); + TestKnownType (); + TestMultipleValues (0, typeof (TestType)); + } + } + + interface ITestInterface + { + } + + class TestType : ITestInterface + { + } + } +} diff --git a/test/Mono.Linker.Tests.Cases/DataFlow/MemberTypes.cs b/test/Mono.Linker.Tests.Cases/DataFlow/MemberTypes.cs index d0b51abf5..d28c3ed54 100644 --- a/test/Mono.Linker.Tests.Cases/DataFlow/MemberTypes.cs +++ b/test/Mono.Linker.Tests.Cases/DataFlow/MemberTypes.cs @@ -42,6 +42,7 @@ namespace Mono.Linker.Tests.Cases.DataFlow RequirePublicEvents (typeof (PublicEventsType)); RequireNonPublicEvents (typeof (NonPublicEventsType)); RequireAllEvents (typeof (AllEventsType)); + RequireInterfaces (typeof (InterfacesType)); RequireAll (typeof (AllType)); RequireAll (typeof (RequireAllWithRecursiveTypeReferences)); } @@ -1565,6 +1566,94 @@ namespace Mono.Linker.Tests.Cases.DataFlow static public event EventHandler<EventArgs> HideStaticEvent; } + [Kept] + private static void RequireInterfaces ( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] + [KeptAttributeAttribute(typeof(DynamicallyAccessedMembersAttribute))] + Type type) + { + } + + [Kept] + interface IInterfaceOnBaseBase + { + } + + [Kept] + [KeptInterface (typeof (IInterfaceOnBaseBase))] + interface IInterfacesOnBase : IInterfaceOnBaseBase + { + void OnBaseInterfaceMethod (); + } + + [Kept] + [KeptInterface (typeof (IInterfacesOnBase))] // Interface implementations are collected across all base types, so this one has to be included as well + [KeptInterface (typeof (IInterfaceOnBaseBase))] // Roslyn adds transitively implemented interfaces automatically + class InterfacesBaseType : IInterfacesOnBase + { + public void OnBaseInterfaceMethod () { } + } + + [Kept] + interface IInterfacesEmpty + { + } + + [Kept] + interface IInterfacesWithMethods + { + void InterfaceMethod (); + } + + [Kept] + interface IInterfaceGeneric<T> + { + void GenericMethod<U> (T t, U u); + } + + [Kept] + interface IInterfacesBase + { + void BaseMethod (); + } + + [Kept] + [KeptInterface (typeof (IInterfacesBase))] + interface IInterfacesDerived : IInterfacesBase + { + void DerivedMethod (); + } + + interface IInterfacesSuperDerived : IInterfacesDerived + { + void SuperDerivedMethod (); + } + + [Kept] + [KeptInterface (typeof (IInterfacesEmpty))] + [KeptInterface (typeof (IInterfacesWithMethods))] + [KeptInterface (typeof (IInterfacesBase))] // Roslyn adds transitively implemented interfaces automatically + [KeptInterface (typeof (IInterfacesDerived))] + [KeptInterface (typeof (IInterfaceGeneric<int>))] + [KeptBaseType (typeof (InterfacesBaseType))] + class InterfacesType : InterfacesBaseType, IInterfacesEmpty, IInterfacesWithMethods, IInterfacesDerived, IInterfaceGeneric<int> + { + public void InterfaceMethod () + { + } + + public void BaseMethod () + { + } + + public void DerivedMethod () + { + } + + public void GenericMethod<U> (int t, U u) + { + } + } [Kept] private static void RequireAll ( @@ -1575,7 +1664,29 @@ namespace Mono.Linker.Tests.Cases.DataFlow } [Kept] - class AllBaseType + interface IAllBaseGenericInterface<T> + { + [Kept] + void BaseInterfaceMethod (); + [Kept] + void BaseDefaultMethod () { } + } + + [Kept] + [KeptInterface (typeof (IAllBaseGenericInterface<Int64>))] + interface IAllDerivedInterface : IAllBaseGenericInterface<Int64> + { + [Kept] + void DerivedInterfaceMethod (); + + [Kept] + void DerivedDefaultMethod () { } + } + + [Kept] + [KeptInterface (typeof (IAllDerivedInterface))] + [KeptInterface (typeof (IAllBaseGenericInterface<Int64>))] + class AllBaseType : IAllDerivedInterface { // This is different from all of the above cases. // All means really everything - so we include everything on base class as well - including private stuff @@ -1639,6 +1750,11 @@ namespace Mono.Linker.Tests.Cases.DataFlow public static void HideStaticMethod () { } [Kept] + public void DerivedInterfaceMethod () { } + [Kept] + public void BaseInterfaceMethod () { } + + [Kept] [KeptBackingField] static public bool PublicStaticPropertyOnBase { [Kept] get; [Kept] set; } [Kept] diff --git a/test/Mono.Linker.Tests.Cases/DataFlow/MemberTypesRelationships.cs b/test/Mono.Linker.Tests.Cases/DataFlow/MemberTypesRelationships.cs new file mode 100644 index 000000000..a16ffd9ef --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/DataFlow/MemberTypesRelationships.cs @@ -0,0 +1,249 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more 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.Helpers; + +namespace Mono.Linker.Tests.Cases.DataFlow +{ + [SkipKeptItemsValidation] + [ExpectedNoWarnings] + public class MemberTypesRelationships + { + public static void Main () + { + TestPublicParameterlessConstructor (typeof (TestType)); + TestPublicConstructors (typeof (TestType)); + TestNonPublicConstructors (typeof (TestType)); + TestPublicMethods (typeof (TestType)); + TestNonPublicMethods (typeof (TestType)); + TestPublicFields (typeof (TestType)); + TestNonPublicFields (typeof (TestType)); + TestPublicNestedTypes (typeof (TestType)); + TestNonPublicNestedTypes (typeof (TestType)); + TestPublicProperties (typeof (TestType)); + TestNonPublicProperties (typeof (TestType)); + TestPublicEvents (typeof (TestType)); + TestNonPublicEvents (typeof (TestType)); + TestInterfaces (typeof (TestType)); + TestAll (typeof (TestType)); + } + + [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresPublicConstructors))] + [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresNonPublicConstructors))] + [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresPublicMethods))] + [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresAll))] + static void TestPublicParameterlessConstructor ( + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type type) + { + type.RequiresPublicParameterlessConstructor (); + type.RequiresPublicConstructors (); // Warns + type.RequiresNonPublicConstructors (); // Warns + type.RequiresNone (); + type.RequiresPublicMethods (); // Warns + type.RequiresAll (); // Warns + } + + [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresNonPublicConstructors))] + [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresPublicMethods))] + [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresAll))] + static void TestPublicConstructors ( + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors)] Type type) + { + type.RequiresPublicParameterlessConstructor (); + type.RequiresPublicConstructors (); + type.RequiresNonPublicConstructors (); // Warns + type.RequiresNone (); + type.RequiresPublicMethods (); // Warns + type.RequiresAll (); // Warns + } + + [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresPublicParameterlessConstructor))] + [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresPublicConstructors))] + [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresPublicMethods))] + [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresAll))] + static void TestNonPublicConstructors ( + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicConstructors)] Type type) + { + type.RequiresPublicParameterlessConstructor (); // Warns + type.RequiresPublicConstructors (); // Warns + type.RequiresNonPublicConstructors (); + type.RequiresNone (); + type.RequiresPublicMethods (); // Warns + type.RequiresAll (); // Warns + } + + [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresNonPublicMethods))] + [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresPublicConstructors))] + [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresAll))] + static void TestPublicMethods ( + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] Type type) + { + type.RequiresPublicMethods (); + type.RequiresNonPublicMethods (); // Warns + type.RequiresNone (); + type.RequiresPublicConstructors (); // Warns + type.RequiresAll (); // Warns + } + + [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresPublicMethods))] + [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresNonPublicConstructors))] + [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresAll))] + static void TestNonPublicMethods ( + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicMethods)] Type type) + { + type.RequiresPublicMethods (); // Warns + type.RequiresNonPublicMethods (); + type.RequiresNone (); + type.RequiresNonPublicConstructors (); // Warns + type.RequiresAll (); // Warns + } + + [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresNonPublicFields))] + [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresPublicConstructors))] + [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresAll))] + static void TestPublicFields ( + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] Type type) + { + type.RequiresPublicFields (); + type.RequiresNonPublicFields (); // Warns + type.RequiresNone (); + type.RequiresPublicConstructors (); // Warns + type.RequiresAll (); // Warns + } + + [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresPublicFields))] + [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresNonPublicConstructors))] + [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresAll))] + static void TestNonPublicFields ( + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicFields)] Type type) + { + type.RequiresPublicFields (); // Warns + type.RequiresNonPublicFields (); + type.RequiresNone (); + type.RequiresNonPublicConstructors (); // Warns + type.RequiresAll (); // Warns + } + + [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresNonPublicNestedTypes))] + [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresInterfaces))] + [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresAll))] + static void TestPublicNestedTypes ( + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicNestedTypes)] Type type) + { + type.RequiresPublicNestedTypes (); + type.RequiresNonPublicNestedTypes (); // Warns + type.RequiresNone (); + type.RequiresInterfaces (); // Warns + type.RequiresAll (); // Warns + } + + [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresPublicNestedTypes))] + [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresInterfaces))] + [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresAll))] + static void TestNonPublicNestedTypes ( + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicNestedTypes)] Type type) + { + type.RequiresPublicNestedTypes (); // Warns + type.RequiresNonPublicNestedTypes (); + type.RequiresNone (); + type.RequiresInterfaces (); // Warns + type.RequiresAll (); // Warns + } + + [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresNonPublicProperties))] + [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresPublicFields))] + [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresAll))] + static void TestPublicProperties ( + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicProperties)] Type type) + { + type.RequiresPublicProperties (); + type.RequiresNonPublicProperties (); // Warns + type.RequiresNone (); + type.RequiresPublicFields (); // Warns + type.RequiresAll (); // Warns + } + + [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresPublicProperties))] + [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresNonPublicFields))] + [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresAll))] + static void TestNonPublicProperties ( + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicProperties)] Type type) + { + type.RequiresPublicProperties (); // Warns + type.RequiresNonPublicProperties (); + type.RequiresNone (); + type.RequiresNonPublicFields (); // Warns + type.RequiresAll (); // Warns + } + + [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresNonPublicEvents))] + [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresPublicFields))] + [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresAll))] + static void TestPublicEvents ( + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicEvents)] Type type) + { + type.RequiresPublicEvents (); + type.RequiresNonPublicEvents (); // Warns + type.RequiresNone (); + type.RequiresPublicFields (); // Warns + type.RequiresAll (); // Warns + } + + [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresPublicEvents))] + [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresNonPublicFields))] + [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresAll))] + static void TestNonPublicEvents ( + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicEvents)] Type type) + { + type.RequiresPublicEvents (); // Warns + type.RequiresNonPublicEvents (); + type.RequiresNone (); + type.RequiresNonPublicFields (); // Warns + type.RequiresAll (); // Warns + } + + [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresPublicNestedTypes))] + [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresNonPublicNestedTypes))] + [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresAll))] + static void TestInterfaces ( + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.Interfaces)] Type type) + { + type.RequiresInterfaces (); + type.RequiresNone (); + type.RequiresPublicNestedTypes (); // Warns + type.RequiresNonPublicNestedTypes (); // Warns + type.RequiresAll (); // Warns + } + + static void TestAll ( + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] Type type) + { + type.RequiresAll (); + type.RequiresNone (); + type.RequiresPublicParameterlessConstructor (); + type.RequiresPublicConstructors (); + type.RequiresNonPublicConstructors (); + type.RequiresPublicMethods (); + type.RequiresNonPublicMethods (); + type.RequiresPublicFields (); + type.RequiresNonPublicFields (); + type.RequiresPublicNestedTypes (); + type.RequiresNonPublicNestedTypes (); + type.RequiresPublicProperties (); + type.RequiresNonPublicProperties (); + type.RequiresPublicEvents (); + type.RequiresNonPublicEvents (); + type.RequiresInterfaces (); + } + + class TestType { } + } +} diff --git a/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs b/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs index a4a4b1f64..bca2b26bb 100644 --- a/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs +++ b/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs @@ -205,7 +205,7 @@ namespace Mono.Linker.Tests.TestCasesRunner } } - Assert.IsEmpty (expectedInterfaces, $"Unexpected interfaces on {src}"); + Assert.IsEmpty (expectedInterfaces, $"Expected interfaces were not found on {src}"); } } |