From 62e6b596bb29c84c300395caaae2989cb891a98d Mon Sep 17 00:00:00 2001 From: Michael Adelson Date: Mon, 18 Apr 2022 08:20:12 -0400 Subject: [PATCH 1/3] Allow custom attribute filtering with an open generic type. Filtering on an open generic type retrieves all attributes that are or are derived from constructed instances of that type. fix #64169 --- .../Reflection/RuntimeCustomAttributeData.cs | 19 ++++++++++++++- .../NonPortable/CustomAttributeSearcher.cs | 24 +++++++++++++++++++ .../System.Runtime/tests/System/Attributes.cs | 18 +++++++++++++- .../GenericAttribute/GenericAttributeTests.cs | 14 +++++------ 4 files changed, 66 insertions(+), 9 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeCustomAttributeData.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeCustomAttributeData.cs index d28edee240381..2922230c7ff30 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeCustomAttributeData.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeCustomAttributeData.cs @@ -1289,7 +1289,7 @@ private static bool FilterCustomAttributeRecord( attributeType = (decoratedModule.ResolveType(scope.GetParentToken(caCtorToken), null, null) as RuntimeType)!; // Test attribute type against user provided attribute type filter - if (!attributeFilterType.IsAssignableFrom(attributeType)) + if (!MatchesTypeFilter(attributeType, attributeFilterType)) return false; // Ensure if attribute type must be inheritable that it is inheritable @@ -1370,6 +1370,23 @@ private static bool FilterCustomAttributeRecord( GC.KeepAlive(ctorWithParameters); return result; } + + private static bool MatchesTypeFilter(RuntimeType attributeType, RuntimeType attributeFilterType) + { + if (attributeFilterType.IsGenericTypeDefinition) + { + for (RuntimeType? type = attributeType; type != null; type = (RuntimeType?)type.BaseType) + { + if (type.IsConstructedGenericType && type.GetGenericTypeDefinition() == attributeFilterType) + { + return true; + } + } + return false; + } + + return attributeFilterType.IsAssignableFrom(attributeType); + } #endregion #region Private Static Methods diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Extensions/NonPortable/CustomAttributeSearcher.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Extensions/NonPortable/CustomAttributeSearcher.cs index bc9a9d770bc65..ab54005c11a74 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Extensions/NonPortable/CustomAttributeSearcher.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Extensions/NonPortable/CustomAttributeSearcher.cs @@ -74,6 +74,30 @@ public IEnumerable GetMatchingCustomAttributes(E element, T return true; }; } + else if (optionalAttributeTypeFilter.IsGenericTypeDefinition) + { + passesFilter = + delegate (Type actualType) + { + if (actualType.IsConstructedGenericType && actualType.GetGenericTypeDefinition() == optionalAttributeTypeFilter) + { + return true; + } + + if (!typeFilterKnownToBeSealed) + { + for (Type? type = actualType.BaseType; type != null; type = type.BaseType) + { + if (type.IsConstructedGenericType && type.GetGenericTypeDefinition() == optionalAttributeTypeFilter) + { + return true; + } + } + } + + return false; + }; + } else { passesFilter = diff --git a/src/libraries/System.Runtime/tests/System/Attributes.cs b/src/libraries/System.Runtime/tests/System/Attributes.cs index ed213cddcefe2..16d999a9aec15 100644 --- a/src/libraries/System.Runtime/tests/System/Attributes.cs +++ b/src/libraries/System.Runtime/tests/System/Attributes.cs @@ -295,10 +295,21 @@ public static void GetCustomAttributesWorksWithOpenAndClosedGenericTypesForEvent GenericAttributesTestHelper(t => Attribute.GetCustomAttributes(@event, t)); } + [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/56887", TestRuntimes.Mono)] + public static void GetCustomAttributesOnOpenGenericTypeRetrievesDerivedAttributes() + { + Attribute[] attributes = Attribute.GetCustomAttributes(typeof(HasGenericAttribute), typeof(GenericAttribute<>)); + Assert.Equal(2, attributes.Length); + Assert.Equal(1, attributes.OfType().Count()); + Assert.Equal(1, attributes.OfType>().Count()); + } + private static void GenericAttributesTestHelper(Func getCustomAttributes) { Attribute[] openGenericAttributes = getCustomAttributes(typeof(GenericAttribute<>)); - Assert.Empty(openGenericAttributes); + Assert.True(openGenericAttributes.Length >= 1); + Assert.Equal(1, openGenericAttributes.OfType>().Count()); Attribute[] closedGenericAttributes = getCustomAttributes(typeof(GenericAttribute)); Assert.Equal(1, closedGenericAttributes.Length); @@ -915,6 +926,11 @@ public class GenericAttribute : Attribute { } + public class DerivesFromGenericAttribute : GenericAttribute + { + } + + [DerivesFromGeneric] [GenericAttribute] public class HasGenericAttribute { diff --git a/src/tests/reflection/GenericAttribute/GenericAttributeTests.cs b/src/tests/reflection/GenericAttribute/GenericAttributeTests.cs index 3462830f1ed1b..f82d2bb99b95b 100644 --- a/src/tests/reflection/GenericAttribute/GenericAttributeTests.cs +++ b/src/tests/reflection/GenericAttribute/GenericAttributeTests.cs @@ -18,8 +18,8 @@ static int Main(string[] args) Assert(((ICustomAttributeProvider)assembly).IsDefined(typeof(SingleAttribute), true)); Assert(CustomAttributeExtensions.IsDefined(assembly, typeof(SingleAttribute))); Assert(((ICustomAttributeProvider)assembly).IsDefined(typeof(SingleAttribute), true)); - Assert(!CustomAttributeExtensions.GetCustomAttributes(assembly, typeof(SingleAttribute<>)).GetEnumerator().MoveNext()); - Assert(!CustomAttributeExtensions.GetCustomAttributes(assembly, typeof(SingleAttribute<>)).GetEnumerator().MoveNext()); + Assert(CustomAttributeExtensions.GetCustomAttributes(assembly, typeof(SingleAttribute<>)).GetEnumerator().MoveNext()); + Assert(CustomAttributeExtensions.GetCustomAttributes(assembly, typeof(SingleAttribute<>)).GetEnumerator().MoveNext()); */ // Uncomment when https://github.com/dotnet/runtime/issues/66168 is resolved @@ -27,8 +27,8 @@ static int Main(string[] args) // AssertAny(CustomAttributeExtensions.GetCustomAttributes(module), a => a is SingleAttribute); // Assert(CustomAttributeExtensions.GetCustomAttributes(module, typeof(SingleAttribute)).GetEnumerator().MoveNext()); // Assert(CustomAttributeExtensions.GetCustomAttributes(module, typeof(SingleAttribute)).GetEnumerator().MoveNext()); - // Assert(!CustomAttributeExtensions.GetCustomAttributes(module, typeof(SingleAttribute<>)).GetEnumerator().MoveNext()); - // Assert(!CustomAttributeExtensions.GetCustomAttributes(module, typeof(SingleAttribute<>)).GetEnumerator().MoveNext()); + // Assert(CustomAttributeExtensions.GetCustomAttributes(module, typeof(SingleAttribute<>)).GetEnumerator().MoveNext()); + // Assert(CustomAttributeExtensions.GetCustomAttributes(module, typeof(SingleAttribute<>)).GetEnumerator().MoveNext()); TypeInfo programTypeInfo = typeof(Class).GetTypeInfo(); Assert(CustomAttributeExtensions.GetCustomAttribute>(programTypeInfo) != null); @@ -161,9 +161,9 @@ static int Main(string[] args) AssertAny(b10, a => (a as MultiAttribute)?.Value == typeof(Class)); AssertAny(b10, a => (a as MultiAttribute)?.Value == typeof(Class.Derive)); - Assert(!CustomAttributeExtensions.GetCustomAttributes(programTypeInfo, typeof(MultiAttribute<>), false).GetEnumerator().MoveNext()); - Assert(!CustomAttributeExtensions.GetCustomAttributes(programTypeInfo, typeof(MultiAttribute<>), true).GetEnumerator().MoveNext()); - Assert(!((ICustomAttributeProvider)programTypeInfo).GetCustomAttributes(typeof(MultiAttribute<>), true).GetEnumerator().MoveNext()); + Assert(CustomAttributeExtensions.GetCustomAttributes(programTypeInfo, typeof(MultiAttribute<>), false).GetEnumerator().MoveNext()); + Assert(CustomAttributeExtensions.GetCustomAttributes(programTypeInfo, typeof(MultiAttribute<>), true).GetEnumerator().MoveNext()); + Assert(((ICustomAttributeProvider)programTypeInfo).GetCustomAttributes(typeof(MultiAttribute<>), true).GetEnumerator().MoveNext()); // Test coverage for CustomAttributeData api surface var a1_data = CustomAttributeData.GetCustomAttributes(programTypeInfo); From 5dcd8c54e2d633d3dfe11174d0e23306b6d1f0c3 Mon Sep 17 00:00:00 2001 From: Michael Adelson Date: Mon, 18 Apr 2022 20:40:28 -0400 Subject: [PATCH 2/3] retrigger checks From d35758580b9cee1efc0887b95f99be85c7f4458a Mon Sep 17 00:00:00 2001 From: Michael Adelson Date: Thu, 21 Apr 2022 07:03:17 -0400 Subject: [PATCH 3/3] Add additional generic attribute test cases. See https://github.com/dotnet/runtime/pull/68158#discussion_r854354623 --- .../System.Runtime/tests/System/Attributes.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Runtime/tests/System/Attributes.cs b/src/libraries/System.Runtime/tests/System/Attributes.cs index 16d999a9aec15..00c7997c5f6d4 100644 --- a/src/libraries/System.Runtime/tests/System/Attributes.cs +++ b/src/libraries/System.Runtime/tests/System/Attributes.cs @@ -300,9 +300,15 @@ public static void GetCustomAttributesWorksWithOpenAndClosedGenericTypesForEvent public static void GetCustomAttributesOnOpenGenericTypeRetrievesDerivedAttributes() { Attribute[] attributes = Attribute.GetCustomAttributes(typeof(HasGenericAttribute), typeof(GenericAttribute<>)); + Assert.Equal(3, attributes.Length); + Assert.Equal(1, attributes.Count(a => a.GetType() == typeof(DerivesFromGenericAttribute))); + Assert.Equal(1, attributes.Count(a => a.GetType() == typeof(GenericAttribute))); + Assert.Equal(1, attributes.Count(a => a.GetType() == typeof(GenericAttribute))); + + attributes = Attribute.GetCustomAttributes(typeof(HasGenericAttribute), typeof(GenericAttribute)); Assert.Equal(2, attributes.Length); - Assert.Equal(1, attributes.OfType().Count()); - Assert.Equal(1, attributes.OfType>().Count()); + Assert.Equal(1, attributes.Count(a => a.GetType() == typeof(DerivesFromGenericAttribute))); + Assert.Equal(1, attributes.Count(a => a.GetType() == typeof(GenericAttribute))); } private static void GenericAttributesTestHelper(Func getCustomAttributes) @@ -922,6 +928,7 @@ public class NameableAttribute : Attribute, INameable [Nameable] public class ExampleWithAttribute { } + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] public class GenericAttribute : Attribute { } @@ -932,6 +939,7 @@ public class DerivesFromGenericAttribute : GenericAttribute [DerivesFromGeneric] [GenericAttribute] + [GenericAttribute] public class HasGenericAttribute { [GenericAttribute]