Skip to content

Commit 917a0b1

Browse files
authored
Avoid Attribute.GetCustomAttributes() returning null for open generic type
* Revert "Revert "Avoid Attribute.GetCustomAttributes() returning null for open generic type (#65237)" (#66508)" This reverts commit f99ba2e. * Make DynamicMethod.GetCustomAttributes() return well-typed arrays. This change makes DynamicMethod.GetCustomAttributes() compatible with Attribute.GetCustomAttributes().
1 parent 4b93e52 commit 917a0b1

File tree

10 files changed

+211
-71
lines changed

10 files changed

+211
-71
lines changed

src/coreclr/System.Private.CoreLib/src/System/Attribute.CoreCLR.cs

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -434,10 +434,8 @@ private static AttributeUsageAttribute InternalGetAttributeUsage(Type type)
434434
SR.Format(SR.Format_AttributeUsage, type));
435435
}
436436

437-
private static Attribute[] CreateAttributeArrayHelper(Type elementType, int elementCount)
438-
{
439-
return (Attribute[])Array.CreateInstance(elementType, elementCount);
440-
}
437+
private static Attribute[] CreateAttributeArrayHelper(Type elementType, int elementCount) =>
438+
elementType.ContainsGenericParameters ? new Attribute[elementCount] : (Attribute[])Array.CreateInstance(elementType, elementCount);
441439
#endregion
442440

443441
#endregion
@@ -459,7 +457,7 @@ public static Attribute[] GetCustomAttributes(MemberInfo element!!, Type attribu
459457
{
460458
MemberTypes.Property => InternalGetCustomAttributes((PropertyInfo)element, attributeType, inherit),
461459
MemberTypes.Event => InternalGetCustomAttributes((EventInfo)element, attributeType, inherit),
462-
_ => (element.GetCustomAttributes(attributeType, inherit) as Attribute[])!,
460+
_ => (Attribute[])element.GetCustomAttributes(attributeType, inherit)
463461
};
464462
}
465463

@@ -474,7 +472,7 @@ public static Attribute[] GetCustomAttributes(MemberInfo element!!, bool inherit
474472
{
475473
MemberTypes.Property => InternalGetCustomAttributes((PropertyInfo)element, typeof(Attribute), inherit),
476474
MemberTypes.Event => InternalGetCustomAttributes((EventInfo)element, typeof(Attribute), inherit),
477-
_ => (element.GetCustomAttributes(typeof(Attribute), inherit) as Attribute[])!,
475+
_ => (Attribute[])element.GetCustomAttributes(typeof(Attribute), inherit)
478476
};
479477
}
480478

@@ -536,25 +534,23 @@ public static Attribute[] GetCustomAttributes(ParameterInfo element!!, Type attr
536534
if (element.Member == null)
537535
throw new ArgumentException(SR.Argument_InvalidParameterInfo, nameof(element));
538536

539-
540537
MemberInfo member = element.Member;
541538
if (member.MemberType == MemberTypes.Method && inherit)
542539
return InternalParamGetCustomAttributes(element, attributeType, inherit);
543540

544-
return (element.GetCustomAttributes(attributeType, inherit) as Attribute[])!;
541+
return (Attribute[])element.GetCustomAttributes(attributeType, inherit);
545542
}
546543

547544
public static Attribute[] GetCustomAttributes(ParameterInfo element!!, bool inherit)
548545
{
549546
if (element.Member == null)
550547
throw new ArgumentException(SR.Argument_InvalidParameterInfo, nameof(element));
551548

552-
553549
MemberInfo member = element.Member;
554550
if (member.MemberType == MemberTypes.Method && inherit)
555551
return InternalParamGetCustomAttributes(element, null, inherit);
556552

557-
return (element.GetCustomAttributes(typeof(Attribute), inherit) as Attribute[])!;
553+
return (Attribute[])element.GetCustomAttributes(typeof(Attribute), inherit);
558554
}
559555

560556
public static bool IsDefined(ParameterInfo element, Type attributeType)

src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/DynamicMethod.cs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -624,15 +624,18 @@ public override object Invoke(object? obj, BindingFlags invokeAttr, Binder? bind
624624
throw new ArgumentException(SR.Argument_MustBeRuntimeMethodInfo, "this");
625625
}
626626

627-
public override object[] GetCustomAttributes(Type attributeType, bool inherit)
627+
public override object[] GetCustomAttributes(Type attributeType!!, bool inherit)
628628
{
629-
if (attributeType == null)
630-
throw new ArgumentNullException(nameof(attributeType));
629+
if (attributeType.UnderlyingSystemType is not RuntimeType attributeRuntimeType)
630+
throw new ArgumentException(SR.Arg_MustBeType, nameof(attributeType));
631631

632-
if (attributeType.IsAssignableFrom(typeof(MethodImplAttribute)))
633-
return new object[] { new MethodImplAttribute((MethodImplOptions)GetMethodImplementationFlags()) };
634-
else
635-
return Array.Empty<object>();
632+
bool includeMethodImplAttribute = attributeType.IsAssignableFrom(typeof(MethodImplAttribute));
633+
object[] result = CustomAttribute.CreateAttributeArrayHelper(attributeRuntimeType, includeMethodImplAttribute ? 1 : 0);
634+
if (includeMethodImplAttribute)
635+
{
636+
result[0] = new MethodImplAttribute((MethodImplOptions)GetMethodImplementationFlags());
637+
}
638+
return result;
636639
}
637640

638641
public override object[] GetCustomAttributes(bool inherit)

src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeCustomAttributeData.cs

Lines changed: 41 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -899,7 +899,7 @@ internal static object[] GetCustomAttributes(RuntimeType type, RuntimeType caTyp
899899
Debug.Assert(caType is not null);
900900

901901
if (type.GetElementType() is not null)
902-
return (caType.IsValueType) ? Array.Empty<object>() : CreateAttributeArrayHelper(caType, 0);
902+
return CreateAttributeArrayHelper(caType, 0);
903903

904904
if (type.IsGenericType && !type.IsGenericTypeDefinition)
905905
type = (type.GetGenericTypeDefinition() as RuntimeType)!;
@@ -919,8 +919,6 @@ internal static object[] GetCustomAttributes(RuntimeType type, RuntimeType caTyp
919919

920920
RuntimeType.ListBuilder<object> result = default;
921921
bool mustBeInheritable = false;
922-
bool useObjectArray = (caType.IsValueType || caType.ContainsGenericParameters);
923-
RuntimeType arrayType = useObjectArray ? (RuntimeType)typeof(object) : caType;
924922

925923
for (int i = 0; i < pcas.Count; i++)
926924
result.Add(pcas[i]);
@@ -932,7 +930,7 @@ internal static object[] GetCustomAttributes(RuntimeType type, RuntimeType caTyp
932930
type = (type.BaseType as RuntimeType)!;
933931
}
934932

935-
object[] typedResult = CreateAttributeArrayHelper(arrayType, result.Count);
933+
object[] typedResult = CreateAttributeArrayHelper(caType, result.Count);
936934
for (int i = 0; i < result.Count; i++)
937935
{
938936
typedResult[i] = result[i];
@@ -963,8 +961,6 @@ internal static object[] GetCustomAttributes(RuntimeMethodInfo method, RuntimeTy
963961

964962
RuntimeType.ListBuilder<object> result = default;
965963
bool mustBeInheritable = false;
966-
bool useObjectArray = (caType.IsValueType || caType.ContainsGenericParameters);
967-
RuntimeType arrayType = useObjectArray ? (RuntimeType)typeof(object) : caType;
968964

969965
for (int i = 0; i < pcas.Count; i++)
970966
result.Add(pcas[i]);
@@ -976,7 +972,7 @@ internal static object[] GetCustomAttributes(RuntimeMethodInfo method, RuntimeTy
976972
method = method.GetParentDefinition()!;
977973
}
978974

979-
object[] typedResult = CreateAttributeArrayHelper(arrayType, result.Count);
975+
object[] typedResult = CreateAttributeArrayHelper(caType, result.Count);
980976
for (int i = 0; i < result.Count; i++)
981977
{
982978
typedResult[i] = result[i];
@@ -1123,16 +1119,13 @@ private static bool IsCustomAttributeDefined(
11231119
}
11241120

11251121
private static object[] GetCustomAttributes(
1126-
RuntimeModule decoratedModule, int decoratedMetadataToken, int pcaCount, RuntimeType? attributeFilterType)
1122+
RuntimeModule decoratedModule, int decoratedMetadataToken, int pcaCount, RuntimeType attributeFilterType)
11271123
{
11281124
RuntimeType.ListBuilder<object> attributes = default;
11291125

11301126
AddCustomAttributes(ref attributes, decoratedModule, decoratedMetadataToken, attributeFilterType, false, default);
11311127

1132-
bool useObjectArray = attributeFilterType is null || attributeFilterType.IsValueType || attributeFilterType.ContainsGenericParameters;
1133-
RuntimeType arrayType = useObjectArray ? (RuntimeType)typeof(object) : attributeFilterType!;
1134-
1135-
object[] result = CreateAttributeArrayHelper(arrayType, attributes.Count + pcaCount);
1128+
object[] result = CreateAttributeArrayHelper(attributeFilterType, attributes.Count + pcaCount);
11361129
for (int i = 0; i < attributes.Count; i++)
11371130
{
11381131
result[i] = attributes[i];
@@ -1439,6 +1432,42 @@ internal static AttributeUsageAttribute GetAttributeUsage(RuntimeType decoratedA
14391432

14401433
return attributeUsageAttribute ?? AttributeUsageAttribute.Default;
14411434
}
1435+
1436+
internal static object[] CreateAttributeArrayHelper(RuntimeType caType, int elementCount)
1437+
{
1438+
bool useAttributeArray = false;
1439+
bool useObjectArray = false;
1440+
1441+
if (caType == typeof(Attribute))
1442+
{
1443+
useAttributeArray = true;
1444+
}
1445+
else if (caType.IsValueType)
1446+
{
1447+
useObjectArray = true;
1448+
}
1449+
else if (caType.ContainsGenericParameters)
1450+
{
1451+
if (caType.IsSubclassOf(typeof(Attribute)))
1452+
{
1453+
useAttributeArray = true;
1454+
}
1455+
else
1456+
{
1457+
useObjectArray = true;
1458+
}
1459+
}
1460+
1461+
if (useAttributeArray)
1462+
{
1463+
return elementCount == 0 ? Array.Empty<Attribute>() : new Attribute[elementCount];
1464+
}
1465+
if (useObjectArray)
1466+
{
1467+
return elementCount == 0 ? Array.Empty<object>() : new object[elementCount];
1468+
}
1469+
return elementCount == 0 ? caType.GetEmptyArray() : (object[])Array.CreateInstance(caType, elementCount);
1470+
}
14421471
#endregion
14431472

14441473
#region Private Static FCalls
@@ -1476,17 +1505,6 @@ private static void GetPropertyOrFieldData(
14761505
module, &pBlobStart, (byte*)blobEnd, out name, out isProperty, out type, out value);
14771506
blobStart = (IntPtr)pBlobStart;
14781507
}
1479-
1480-
private static object[] CreateAttributeArrayHelper(RuntimeType elementType, int elementCount)
1481-
{
1482-
// If we have 0 elements, don't allocate a new array
1483-
if (elementCount == 0)
1484-
{
1485-
return elementType.GetEmptyArray();
1486-
}
1487-
1488-
return (object[])Array.CreateInstance(elementType, elementCount);
1489-
}
14901508
#endregion
14911509
}
14921510

src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeParameterInfo.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -509,12 +509,12 @@ public override object[] GetCustomAttributes(bool inherit)
509509

510510
public override object[] GetCustomAttributes(Type attributeType!!, bool inherit)
511511
{
512-
if (MdToken.IsNullToken(m_tkParamDef))
513-
return Array.Empty<object>();
514-
515512
if (attributeType.UnderlyingSystemType is not RuntimeType attributeRuntimeType)
516513
throw new ArgumentException(SR.Arg_MustBeType, nameof(attributeType));
517514

515+
if (MdToken.IsNullToken(m_tkParamDef))
516+
return CustomAttribute.CreateAttributeArrayHelper(attributeRuntimeType, 0);
517+
518518
return CustomAttribute.GetCustomAttributes(this, attributeRuntimeType);
519519
}
520520

src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Attribute.CoreRT.cs

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -141,19 +141,9 @@ private static Attribute[] Instantiate(IEnumerable<CustomAttributeData> cads, Ty
141141
attributes.Add(instantiatedAttribute);
142142
}
143143
int count = attributes.Count;
144-
Attribute[] result;
145-
try
146-
{
147-
result = (Attribute[])Array.CreateInstance(actualElementType, count);
148-
}
149-
catch (NotSupportedException) when (actualElementType.ContainsGenericParameters)
150-
{
151-
// This is here for desktop compatibility (using try-catch as control flow to avoid slowing down the mainline case.)
152-
// GetCustomAttributes() normally returns an array of the exact attribute type requested except when
153-
// the requested type is an open type. Its ICustomAttributeProvider counterpart would return an Object[] array but that's
154-
// not possible with this api's return type so it returns null instead.
155-
return null;
156-
}
144+
Attribute[] result = actualElementType.ContainsGenericParameters
145+
? new Attribute[count]
146+
: (Attribute[])Array.CreateInstance(actualElementType, count);
157147
attributes.CopyTo(result, 0);
158148
return result;
159149
}

src/libraries/System.Reflection/tests/ParameterInfoTests.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,14 @@ public void CustomAttributesInheritanceTest(int paramIndex, bool exists, int exp
245245
Assert.Equal(expectedMyAttributeValue, exists ? myAttribute.Value : expectedMyAttributeValue);
246246
}
247247

248+
[Fact]
249+
public static void GetCustomAttributesOnParameterWithNullMetadataTokenReturnsArrayOfCorrectType()
250+
{
251+
var parameterWithNullMetadataToken = typeof(int[]).GetProperty(nameof(Array.Length)).GetMethod.ReturnParameter;
252+
Assert.Equal(typeof(Attribute[]), Attribute.GetCustomAttributes(parameterWithNullMetadataToken).GetType());
253+
Assert.Equal(typeof(MyAttribute[]), Attribute.GetCustomAttributes(parameterWithNullMetadataToken, typeof(MyAttribute)).GetType());
254+
}
255+
248256
[Fact]
249257
public void VerifyGetCustomAttributesData()
250258
{

0 commit comments

Comments
 (0)