diff --git a/src/coreclr/System.Private.CoreLib/src/System/Attribute.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Attribute.CoreCLR.cs index bd108893d6c22f..504d5249b8d6fb 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Attribute.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Attribute.CoreCLR.cs @@ -13,40 +13,6 @@ public abstract partial class Attribute #region Private Statics #region PropertyInfo - private static Attribute[] InternalGetCustomAttributes(PropertyInfo element, Type type, bool inherit) - { - Debug.Assert(element != null); - Debug.Assert(type != null); - Debug.Assert(type.IsSubclassOf(typeof(Attribute)) || type == typeof(Attribute)); - - // walk up the hierarchy chain - Attribute[] attributes = (Attribute[])element.GetCustomAttributes(type, inherit); - - if (!inherit) - return attributes; - - // create the hashtable that keeps track of inherited types - Dictionary types = new Dictionary(11); - - // create an array list to collect all the requested attibutes - List attributeList = new List(); - CopyToArrayList(attributeList, attributes, types); - - // if this is an index we need to get the parameter types to help disambiguate - Type[] indexParamTypes = GetIndexParameterTypes(element); - - - PropertyInfo? baseProp = GetParentDefinition(element, indexParamTypes); - while (baseProp != null) - { - attributes = GetCustomAttributes(baseProp, type, false); - AddAttributesToList(attributeList, attributes, types); - baseProp = GetParentDefinition(baseProp, indexParamTypes); - } - Attribute[] array = CreateAttributeArrayHelper(type, attributeList.Count); - attributeList.CopyTo(array, 0); - return array; - } private static bool InternalIsDefined(PropertyInfo element, Type attributeType, bool inherit) { @@ -56,7 +22,7 @@ private static bool InternalIsDefined(PropertyInfo element, Type attributeType, if (inherit) { - AttributeUsageAttribute usage = InternalGetAttributeUsage(attributeType); + AttributeUsageAttribute usage = attributeType.GetAttributeUsageAttribute(); if (!usage.Inherited) return false; @@ -64,53 +30,20 @@ private static bool InternalIsDefined(PropertyInfo element, Type attributeType, // if this is an index we need to get the parameter types to help disambiguate Type[] indexParamTypes = GetIndexParameterTypes(element); - PropertyInfo? baseProp = GetParentDefinition(element, indexParamTypes); + PropertyInfo? baseProp = CustomAttribute.GetParentDefinition(element, indexParamTypes); while (baseProp != null) { if (baseProp.IsDefined(attributeType, false)) return true; - baseProp = GetParentDefinition(baseProp, indexParamTypes); + baseProp = CustomAttribute.GetParentDefinition(baseProp, indexParamTypes); } } return false; } - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2075:UnrecognizedReflectionPattern", - Justification = "rtPropAccessor.DeclaringType is guaranteed to have the specified property because " + - "rtPropAccessor.GetParentDefinition() returned a non-null MethodInfo.")] - private static PropertyInfo? GetParentDefinition(PropertyInfo property, Type[] propertyParameters) - { - Debug.Assert(property != null); - - // for the current property get the base class of the getter and the setter, they might be different - // note that this only works for RuntimeMethodInfo - MethodInfo? propAccessor = property.GetGetMethod(true) ?? property.GetSetMethod(true); - - RuntimeMethodInfo? rtPropAccessor = propAccessor as RuntimeMethodInfo; - - if (rtPropAccessor != null) - { - rtPropAccessor = rtPropAccessor.GetParentDefinition(); - - if (rtPropAccessor != null) - { - // There is a public overload of Type.GetProperty that takes both a BingingFlags enum and a return type. - // However, we cannot use that because it doesn't accept null for "types". - return rtPropAccessor.DeclaringType!.GetProperty( - property.Name, - BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly, - null, // will use default binder - property.PropertyType, - propertyParameters, // used for index properties - null); - } - } - - return null; - } #endregion @@ -131,12 +64,12 @@ private static Attribute[] InternalGetCustomAttributes(EventInfo element, Type t List attributeList = new List(); CopyToArrayList(attributeList, attributes, types); - EventInfo? baseEvent = GetParentDefinition(element); + EventInfo? baseEvent = CustomAttribute.GetParentDefinition(element); while (baseEvent != null) { attributes = GetCustomAttributes(baseEvent, type, false); AddAttributesToList(attributeList, attributes, types); - baseEvent = GetParentDefinition(baseEvent); + baseEvent = CustomAttribute.GetParentDefinition(baseEvent); } Attribute[] array = CreateAttributeArrayHelper(type, attributeList.Count); attributeList.CopyTo(array, 0); @@ -146,26 +79,6 @@ private static Attribute[] InternalGetCustomAttributes(EventInfo element, Type t return attributes; } - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2075:UnrecognizedReflectionPattern", - Justification = "rtAdd.DeclaringType is guaranteed to have the specified event because " + - "rtAdd.GetParentDefinition() returned a non-null MethodInfo.")] - private static EventInfo? GetParentDefinition(EventInfo ev) - { - Debug.Assert(ev != null); - - // note that this only works for RuntimeMethodInfo - MethodInfo? add = ev.GetAddMethod(true); - - RuntimeMethodInfo? rtAdd = add as RuntimeMethodInfo; - - if (rtAdd != null) - { - rtAdd = rtAdd.GetParentDefinition(); - if (rtAdd != null) - return rtAdd.DeclaringType!.GetEvent(ev.Name!); - } - return null; - } private static bool InternalIsDefined(EventInfo element, Type attributeType, bool inherit) { @@ -177,18 +90,18 @@ private static bool InternalIsDefined(EventInfo element, Type attributeType, boo if (inherit) { - AttributeUsageAttribute usage = InternalGetAttributeUsage(attributeType); + AttributeUsageAttribute usage = attributeType.GetAttributeUsageAttribute(); if (!usage.Inherited) return false; - EventInfo? baseEvent = GetParentDefinition(element); + EventInfo? baseEvent = CustomAttribute.GetParentDefinition(element); while (baseEvent != null) { if (baseEvent.IsDefined(attributeType, false)) return true; - baseEvent = GetParentDefinition(baseEvent); + baseEvent = CustomAttribute.GetParentDefinition(baseEvent); } } @@ -198,124 +111,6 @@ private static bool InternalIsDefined(EventInfo element, Type attributeType, boo #endregion #region ParameterInfo - private static ParameterInfo? GetParentDefinition(ParameterInfo param) - { - Debug.Assert(param != null); - - // note that this only works for RuntimeMethodInfo - RuntimeMethodInfo? rtMethod = param.Member as RuntimeMethodInfo; - - if (rtMethod != null) - { - rtMethod = rtMethod.GetParentDefinition(); - - if (rtMethod != null) - { - // Find the ParameterInfo on this method - int position = param.Position; - if (position == -1) - { - return rtMethod.ReturnParameter; - } - else - { - ParameterInfo[] parameters = rtMethod.GetParameters(); - return parameters[position]; // Point to the correct ParameterInfo of the method - } - } - } - return null; - } - - private static Attribute[] InternalParamGetCustomAttributes(ParameterInfo param, Type? type, bool inherit) - { - Debug.Assert(param != null); - - // For ParameterInfo's we need to make sure that we chain through all the MethodInfo's in the inheritance chain that - // have this ParameterInfo defined. .We pick up all the CustomAttributes for the starting ParameterInfo. We need to pick up only attributes - // that are marked inherited from the remainder of the MethodInfo's in the inheritance chain. - // For MethodInfo's on an interface we do not do an inheritance walk so the default ParameterInfo attributes are returned. - // For MethodInfo's on a class we walk up the inheritance chain but do not look at the MethodInfo's on the interfaces that the - // class inherits from and return the respective ParameterInfo attributes - - List disAllowMultiple = new List(); - object?[] objAttr; - - type ??= typeof(Attribute); - - objAttr = param.GetCustomAttributes(type, false); - - for (int i = 0; i < objAttr.Length; i++) - { - Type objType = objAttr[i]!.GetType(); - AttributeUsageAttribute attribUsage = InternalGetAttributeUsage(objType); - if (!attribUsage.AllowMultiple) - disAllowMultiple.Add(objType); - } - - // Get all the attributes that have Attribute as the base class - Attribute[] ret; - if (objAttr.Length == 0) - ret = CreateAttributeArrayHelper(type, 0); - else - ret = (Attribute[])objAttr; - - if (param.Member.DeclaringType is null) // This is an interface so we are done. - return ret; - - if (!inherit) - return ret; - - ParameterInfo? baseParam = GetParentDefinition(param); - - while (baseParam != null) - { - objAttr = baseParam.GetCustomAttributes(type, false); - - int count = 0; - for (int i = 0; i < objAttr.Length; i++) - { - Type objType = objAttr[i]!.GetType(); - AttributeUsageAttribute attribUsage = InternalGetAttributeUsage(objType); - - if ((attribUsage.Inherited) && (!disAllowMultiple.Contains(objType))) - { - if (!attribUsage.AllowMultiple) - disAllowMultiple.Add(objType); - count++; - } - else - objAttr[i] = null; - } - - // Get all the attributes that have Attribute as the base class - Attribute[] attributes = CreateAttributeArrayHelper(type, count); - - count = 0; - for (int i = 0; i < objAttr.Length; i++) - { - if (objAttr[i] is object attr) - { - attributes[count] = (Attribute)attr; - count++; - } - } - - Attribute[] temp = ret; - ret = CreateAttributeArrayHelper(type, temp.Length + count); - Array.Copy(temp, ret, temp.Length); - - int offset = temp.Length; - - for (int i = 0; i < attributes.Length; i++) - ret[offset + i] = attributes[i]; - - baseParam = GetParentDefinition(baseParam); - } - - return ret; - } - private static bool InternalParamIsDefined(ParameterInfo param, Type type, bool inherit) { Debug.Assert(param != null); @@ -333,7 +128,7 @@ private static bool InternalParamIsDefined(ParameterInfo param, Type type, bool if (param.Member.DeclaringType is null || !inherit) // This is an interface so we are done. return false; - ParameterInfo? baseParam = GetParentDefinition(param); + ParameterInfo? baseParam = CustomAttribute.GetParentDefinition(param); while (baseParam != null) { @@ -342,13 +137,13 @@ private static bool InternalParamIsDefined(ParameterInfo param, Type type, bool for (int i = 0; i < objAttr.Length; i++) { Type objType = objAttr[i].GetType(); - AttributeUsageAttribute attribUsage = InternalGetAttributeUsage(objType); + AttributeUsageAttribute attribUsage = objType.GetAttributeUsageAttribute(); if ((objAttr[i] is Attribute) && (attribUsage.Inherited)) return true; } - baseParam = GetParentDefinition(baseParam); + baseParam = CustomAttribute.GetParentDefinition(baseParam); } return false; @@ -366,7 +161,7 @@ private static void CopyToArrayList(List attributeList, Attribute[] a Type attrType = attributes[i].GetType(); if (!types.ContainsKey(attrType)) - types[attrType] = InternalGetAttributeUsage(attrType); + types[attrType] = attrType.GetAttributeUsageAttribute(); } } @@ -397,7 +192,7 @@ private static void AddAttributesToList(List attributeList, Attribute if (usage == null) { // the type has never been seen before if it's inheritable add it to the list - usage = InternalGetAttributeUsage(attrType); + usage = attrType.GetAttributeUsageAttribute(); types[attrType] = usage; if (usage.Inherited) @@ -411,21 +206,6 @@ private static void AddAttributesToList(List attributeList, Attribute } } - private static AttributeUsageAttribute InternalGetAttributeUsage(Type type) - { - // Check if the custom attributes is Inheritable - object[] obj = type.GetCustomAttributes(typeof(AttributeUsageAttribute), false); - - if (obj.Length == 1) - return (AttributeUsageAttribute)obj[0]; - - if (obj.Length == 0) - return AttributeUsageAttribute.Default; - - throw new FormatException( - SR.Format(SR.Format_AttributeUsage, type)); - } - private static Attribute[] CreateAttributeArrayHelper(Type elementType, int elementCount) { return (Attribute[])Array.CreateInstance(elementType, elementCount); @@ -455,7 +235,6 @@ public static Attribute[] GetCustomAttributes(MemberInfo element, Type attribute return element.MemberType switch { - MemberTypes.Property => InternalGetCustomAttributes((PropertyInfo)element, attributeType, inherit), MemberTypes.Event => InternalGetCustomAttributes((EventInfo)element, attributeType, inherit), _ => (element.GetCustomAttributes(attributeType, inherit) as Attribute[])!, }; @@ -473,7 +252,6 @@ public static Attribute[] GetCustomAttributes(MemberInfo element, bool inherit) return element.MemberType switch { - MemberTypes.Property => InternalGetCustomAttributes((PropertyInfo)element, typeof(Attribute), inherit), MemberTypes.Event => InternalGetCustomAttributes((EventInfo)element, typeof(Attribute), inherit), _ => (element.GetCustomAttributes(typeof(Attribute), inherit) as Attribute[])!, }; @@ -506,20 +284,21 @@ public static bool IsDefined(MemberInfo element, Type attributeType, bool inheri public static Attribute? GetCustomAttribute(MemberInfo element, Type attributeType) { - return GetCustomAttribute(element, attributeType, true); + return GetCustomAttribute(element, attributeType, inherit: true); } public static Attribute? GetCustomAttribute(MemberInfo element, Type attributeType, bool inherit) { - Attribute[] attrib = GetCustomAttributes(element, attributeType, inherit); + if (element == null) + throw new ArgumentNullException(nameof(element)); - if (attrib == null || attrib.Length == 0) - return null; + if (attributeType == null) + throw new ArgumentNullException(nameof(attributeType)); - if (attrib.Length == 1) - return attrib[0]; + if (!attributeType.IsSubclassOf(typeof(Attribute)) && attributeType != typeof(Attribute)) + throw new ArgumentException(SR.Argument_MustHaveAttributeBaseClass); - throw new AmbiguousMatchException(SR.RFLCT_AmbigCust); + return element.GetCustomAttribute(attributeType, inherit); } #endregion @@ -549,11 +328,6 @@ public static Attribute[] GetCustomAttributes(ParameterInfo element, Type attrib if (element.Member == null) throw new ArgumentException(SR.Argument_InvalidParameterInfo, nameof(element)); - - MemberInfo member = element.Member; - if (member.MemberType == MemberTypes.Method && inherit) - return InternalParamGetCustomAttributes(element, attributeType, inherit); - return (element.GetCustomAttributes(attributeType, inherit) as Attribute[])!; } @@ -565,11 +339,6 @@ public static Attribute[] GetCustomAttributes(ParameterInfo element, bool inheri if (element.Member == null) throw new ArgumentException(SR.Argument_InvalidParameterInfo, nameof(element)); - - MemberInfo member = element.Member; - if (member.MemberType == MemberTypes.Method && inherit) - return InternalParamGetCustomAttributes(element, null, inherit); - return (element.GetCustomAttributes(typeof(Attribute), inherit) as Attribute[])!; } @@ -616,20 +385,16 @@ public static bool IsDefined(ParameterInfo element, Type attributeType, bool inh public static Attribute? GetCustomAttribute(ParameterInfo element, Type attributeType, bool inherit) { - // Returns an Attribute of base class/inteface attributeType on the ParameterInfo or null if none exists. - // throws an AmbiguousMatchException if there are more than one defined. - Attribute[] attrib = GetCustomAttributes(element, attributeType, inherit); - - if (attrib == null || attrib.Length == 0) - return null; + if (element == null) + throw new ArgumentNullException(nameof(element)); - if (attrib.Length == 0) - return null; + if (attributeType == null) + throw new ArgumentNullException(nameof(attributeType)); - if (attrib.Length == 1) - return attrib[0]; + if (!attributeType.IsSubclassOf(typeof(Attribute)) && attributeType != typeof(Attribute)) + throw new ArgumentException(SR.Argument_MustHaveAttributeBaseClass); - throw new AmbiguousMatchException(SR.RFLCT_AmbigCust); + return element.GetCustomAttribute(attributeType, inherit); } #endregion @@ -694,17 +459,16 @@ public static bool IsDefined(Module element, Type attributeType, bool inherit) public static Attribute? GetCustomAttribute(Module element, Type attributeType, bool inherit) { - // Returns an Attribute of base class/inteface attributeType on the Module or null if none exists. - // throws an AmbiguousMatchException if there are more than one defined. - Attribute[] attrib = GetCustomAttributes(element, attributeType, inherit); + if (element == null) + throw new ArgumentNullException(nameof(element)); - if (attrib == null || attrib.Length == 0) - return null; + if (attributeType == null) + throw new ArgumentNullException(nameof(attributeType)); - if (attrib.Length == 1) - return attrib[0]; + if (!attributeType.IsSubclassOf(typeof(Attribute)) && attributeType != typeof(Attribute)) + throw new ArgumentException(SR.Argument_MustHaveAttributeBaseClass); - throw new AmbiguousMatchException(SR.RFLCT_AmbigCust); + return element.GetCustomAttribute(attributeType, inherit); } #endregion @@ -769,17 +533,16 @@ public static bool IsDefined(Assembly element, Type attributeType, bool inherit) public static Attribute? GetCustomAttribute(Assembly element, Type attributeType, bool inherit) { - // Returns an Attribute of base class/inteface attributeType on the Assembly or null if none exists. - // throws an AmbiguousMatchException if there are more than one defined. - Attribute[] attrib = GetCustomAttributes(element, attributeType, inherit); + if (element == null) + throw new ArgumentNullException(nameof(element)); - if (attrib == null || attrib.Length == 0) - return null; + if (attributeType == null) + throw new ArgumentNullException(nameof(attributeType)); - if (attrib.Length == 1) - return attrib[0]; + if (!attributeType.IsSubclassOf(typeof(Attribute)) && attributeType != typeof(Attribute)) + throw new ArgumentException(SR.Argument_MustHaveAttributeBaseClass); - throw new AmbiguousMatchException(SR.RFLCT_AmbigCust); + return element.GetCustomAttribute(attributeType, inherit); } #endregion diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Assembly.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Assembly.CoreCLR.cs index 15d27ce8bd3f62..776038199fe6d9 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Assembly.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Assembly.CoreCLR.cs @@ -98,5 +98,20 @@ public static Assembly GetCallingAssembly() [MethodImpl(MethodImplOptions.InternalCall)] internal static extern uint GetAssemblyCount(); + + internal virtual Attribute? GetCustomAttribute(Type attributeType, bool inherit) + { + // Returns an Attribute of base class/inteface attributeType on the Assembly or null if none exists. + // throws an AmbiguousMatchException if there are more than one defined. + Attribute[] attrib = Attribute.GetCustomAttributes(this, attributeType, inherit); + + if (attrib == null || attrib.Length == 0) + return null; + + if (attrib.Length == 1) + return attrib[0]; + + throw new AmbiguousMatchException(SR.RFLCT_AmbigCust); + } } } diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs index 4458fc4e6af2a0..720e47d2ace2a6 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Buffers; +using System.Buffers.Binary; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; @@ -8,6 +10,8 @@ using System.Runtime.InteropServices; using System.Text; +using Internal.Runtime.CompilerServices; + namespace System.Reflection { public class CustomAttributeData @@ -15,7 +19,7 @@ public class CustomAttributeData #region Public Static Members public static IList GetCustomAttributes(MemberInfo target) { - if (target == null) + if (target is null) throw new ArgumentNullException(nameof(target)); return target.GetCustomAttributesData(); @@ -23,7 +27,7 @@ public static IList GetCustomAttributes(MemberInfo target) public static IList GetCustomAttributes(Module target) { - if (target == null) + if (target is null) throw new ArgumentNullException(nameof(target)); return target.GetCustomAttributesData(); @@ -31,7 +35,7 @@ public static IList GetCustomAttributes(Module target) public static IList GetCustomAttributes(Assembly target) { - if (target == null) + if (target is null) throw new ArgumentNullException(nameof(target)); return target.GetCustomAttributesData(); @@ -39,7 +43,7 @@ public static IList GetCustomAttributes(Assembly target) public static IList GetCustomAttributes(ParameterInfo target) { - if (target == null) + if (target is null) throw new ArgumentNullException(nameof(target)); return target.GetCustomAttributesData(); @@ -49,55 +53,58 @@ public static IList GetCustomAttributes(ParameterInfo targe #region Internal Static Members internal static IList GetCustomAttributesInternal(RuntimeType target) { - Debug.Assert(target != null); + Debug.Assert(target is not null); IList cad = GetCustomAttributes(target.GetRuntimeModule(), target.MetadataToken); - PseudoCustomAttribute.GetCustomAttributes(target, (RuntimeType)typeof(object), out RuntimeType.ListBuilder pcas); - return GetCombinedList(cad, ref pcas); + RuntimeType.ListBuilder pcas = default; + PseudoCustomAttribute.GetCustomAttributes(target, (RuntimeType)typeof(object), ref pcas); + return pcas.Count > 0 ? GetCombinedList(cad, ref pcas) : cad; } internal static IList GetCustomAttributesInternal(RuntimeFieldInfo target) { - Debug.Assert(target != null); + Debug.Assert(target is not null); IList cad = GetCustomAttributes(target.GetRuntimeModule(), target.MetadataToken); - PseudoCustomAttribute.GetCustomAttributes(target, (RuntimeType)typeof(object), out RuntimeType.ListBuilder pcas); - return GetCombinedList(cad, ref pcas); + RuntimeType.ListBuilder pcas = default; + PseudoCustomAttribute.GetCustomAttributes(target, (RuntimeType)typeof(object), ref pcas); + return pcas.Count > 0 ? GetCombinedList(cad, ref pcas) : cad; } internal static IList GetCustomAttributesInternal(RuntimeMethodInfo target) { - Debug.Assert(target != null); + Debug.Assert(target is not null); IList cad = GetCustomAttributes(target.GetRuntimeModule(), target.MetadataToken); - PseudoCustomAttribute.GetCustomAttributes(target, (RuntimeType)typeof(object), out RuntimeType.ListBuilder pcas); - return GetCombinedList(cad, ref pcas); + RuntimeType.ListBuilder pcas = default; + PseudoCustomAttribute.GetCustomAttributes(target, (RuntimeType)typeof(object), ref pcas); + return pcas.Count > 0 ? GetCombinedList(cad, ref pcas) : cad; } internal static IList GetCustomAttributesInternal(RuntimeConstructorInfo target) { - Debug.Assert(target != null); + Debug.Assert(target is not null); return GetCustomAttributes(target.GetRuntimeModule(), target.MetadataToken); } internal static IList GetCustomAttributesInternal(RuntimeEventInfo target) { - Debug.Assert(target != null); + Debug.Assert(target is not null); return GetCustomAttributes(target.GetRuntimeModule(), target.MetadataToken); } internal static IList GetCustomAttributesInternal(RuntimePropertyInfo target) { - Debug.Assert(target != null); + Debug.Assert(target is not null); return GetCustomAttributes(target.GetRuntimeModule(), target.MetadataToken); } internal static IList GetCustomAttributesInternal(RuntimeModule target) { - Debug.Assert(target != null); + Debug.Assert(target is not null); if (target.IsResource()) return new List(); @@ -107,7 +114,7 @@ internal static IList GetCustomAttributesInternal(RuntimeMo internal static IList GetCustomAttributesInternal(RuntimeAssembly target) { - Debug.Assert(target != null); + Debug.Assert(target is not null); // No pseudo attributes for RuntimeAssembly @@ -116,11 +123,12 @@ internal static IList GetCustomAttributesInternal(RuntimeAs internal static IList GetCustomAttributesInternal(RuntimeParameterInfo target) { - Debug.Assert(target != null); + Debug.Assert(target is not null); + RuntimeType.ListBuilder pcas = default; IList cad = GetCustomAttributes(target.GetRuntimeModule()!, target.MetadataToken); - PseudoCustomAttribute.GetCustomAttributes(target, (RuntimeType)typeof(object), out RuntimeType.ListBuilder pcas); - return GetCombinedList(cad, ref pcas); + PseudoCustomAttribute.GetCustomAttributes(target, (RuntimeType)typeof(object), ref pcas); + return pcas.Count > 0 ? GetCombinedList(cad, ref pcas) : cad; } private static IList GetCombinedList(IList customAttributes, ref RuntimeType.ListBuilder pseudoAttributes) @@ -229,39 +237,39 @@ private static CustomAttributeType InitCustomAttributeType(RuntimeType parameter return new CustomAttributeType(encodedType, encodedArrayType, encodedEnumType, enumName); } + private static IList GetCustomAttributes(RuntimeModule module, int tkTarget) { - CustomAttributeRecord[] records = GetCustomAttributeRecords(module, tkTarget); + MetadataImport scope = module.MetadataImport; + scope.EnumCustomAttributes(tkTarget, out MetadataEnumResult attributeTokens); + if (attributeTokens.Length == 0) return Array.Empty(); + + Span records = attributeTokens.Length <= MetadataEnumResult.SmallSizeLength ? + stackalloc CustomAttributeRecord[attributeTokens.Length] : + new CustomAttributeRecord[attributeTokens.Length]; + + PopulateCustomAttributeRecords(in attributeTokens, scope, records); CustomAttributeData[] customAttributes = new CustomAttributeData[records.Length]; for (int i = 0; i < records.Length; i++) + { customAttributes[i] = new CustomAttributeData(module, records[i].tkCtor, in records[i].blob); + } return Array.AsReadOnly(customAttributes); } #endregion #region Internal Static Members - internal static CustomAttributeRecord[] GetCustomAttributeRecords(RuntimeModule module, int targetToken) + internal static void PopulateCustomAttributeRecords(in MetadataEnumResult attributeTokens, MetadataImport scope, Span records) { - MetadataImport scope = module.MetadataImport; - - scope.EnumCustomAttributes(targetToken, out MetadataEnumResult tkCustomAttributeTokens); - - if (tkCustomAttributeTokens.Length == 0) - { - return Array.Empty(); - } - - CustomAttributeRecord[] records = new CustomAttributeRecord[tkCustomAttributeTokens.Length]; + Debug.Assert(attributeTokens.Length > 0); for (int i = 0; i < records.Length; i++) { - scope.GetCustomAttributeProps(tkCustomAttributeTokens[i], + scope.GetCustomAttributeProps(attributeTokens[i], out records[i].tkCtor.Value, out records[i].blob); } - - return records; } internal static CustomAttributeTypedArgument Filter(IList attrs, Type? caType, int parameter) @@ -376,12 +384,12 @@ private void Init(MarshalAsAttribute marshalAs) }); int i = 3; // ArraySubType, SizeParamIndex, SizeConst - if (marshalAs.MarshalType != null) i++; - if (marshalAs.MarshalTypeRef != null) i++; - if (marshalAs.MarshalCookie != null) i++; + if (marshalAs.MarshalType is not null) i++; + if (marshalAs.MarshalTypeRef is not null) i++; + if (marshalAs.MarshalCookie is not null) i++; i++; // IidParameterIndex i++; // SafeArraySubType - if (marshalAs.SafeArrayUserDefinedSubType != null) i++; + if (marshalAs.SafeArrayUserDefinedSubType is not null) i++; CustomAttributeNamedArgument[] namedArgs = new CustomAttributeNamedArgument[i]; // For compatibility with previous runtimes, we always include the following 5 attributes, regardless @@ -392,13 +400,13 @@ private void Init(MarshalAsAttribute marshalAs) namedArgs[i++] = new CustomAttributeNamedArgument(type.GetField("SizeConst")!, marshalAs.SizeConst); namedArgs[i++] = new CustomAttributeNamedArgument(type.GetField("IidParameterIndex")!, marshalAs.IidParameterIndex); namedArgs[i++] = new CustomAttributeNamedArgument(type.GetField("SafeArraySubType")!, marshalAs.SafeArraySubType); - if (marshalAs.MarshalType != null) + if (marshalAs.MarshalType is not null) namedArgs[i++] = new CustomAttributeNamedArgument(type.GetField("MarshalType")!, marshalAs.MarshalType); - if (marshalAs.MarshalTypeRef != null) + if (marshalAs.MarshalTypeRef is not null) namedArgs[i++] = new CustomAttributeNamedArgument(type.GetField("MarshalTypeRef")!, marshalAs.MarshalTypeRef); - if (marshalAs.MarshalCookie != null) + if (marshalAs.MarshalCookie is not null) namedArgs[i++] = new CustomAttributeNamedArgument(type.GetField("MarshalCookie")!, marshalAs.MarshalCookie); - if (marshalAs.SafeArrayUserDefinedSubType != null) + if (marshalAs.SafeArrayUserDefinedSubType is not null) namedArgs[i++] = new CustomAttributeNamedArgument(type.GetField("SafeArrayUserDefinedSubType")!, marshalAs.SafeArrayUserDefinedSubType); m_namedArgs = Array.AsReadOnly(namedArgs); @@ -481,7 +489,7 @@ public virtual IList ConstructorArguments { get { - if (m_typedCtorArgs == null) + if (m_typedCtorArgs is null) { CustomAttributeTypedArgument[] typedCtorArgs = new CustomAttributeTypedArgument[m_ctorParams.Length]; @@ -503,9 +511,9 @@ public virtual IList NamedArguments { get { - if (m_namedArgs == null) + if (m_namedArgs is null) { - if (m_namedParams == null) + if (m_namedParams is null) return null!; int cNamedArgs = 0; @@ -609,7 +617,7 @@ private static RuntimeType ResolveType(RuntimeModule scope, string typeName) { RuntimeType type = RuntimeTypeHandle.GetTypeByNameUsingCARules(typeName, scope); - if (type == null) + if (type is null) throw new InvalidOperationException( SR.Format(SR.Arg_CATypeResolutionFailed, typeName)); @@ -619,7 +627,7 @@ private static RuntimeType ResolveType(RuntimeModule scope, string typeName) private static object CanonicalizeValue(object value) { - Debug.Assert(value != null); + Debug.Assert(value is not null); if (value.GetType().IsEnum) { @@ -651,7 +659,7 @@ internal CustomAttributeTypedArgument(RuntimeModule scope, CustomAttributeEncode m_value = null; - if (encodedArg.StringValue != null) + if (encodedArg.StringValue is not null) m_value = ResolveType(scope, encodedArg.StringValue); } else if (encodedType == CustomAttributeEncoding.Array) @@ -670,7 +678,7 @@ internal CustomAttributeTypedArgument(RuntimeModule scope, CustomAttributeEncode m_argumentType = elementType.MakeArrayType(); - if (encodedArg.ArrayValue == null) + if (encodedArg.ArrayValue is null) { m_value = null; } @@ -749,11 +757,11 @@ internal static void ParseAttributeArguments(ConstArray attributeBlob, ref CustomAttributeNamedParameter[] customAttributeNamedParameters, RuntimeModule customAttributeModule) { - if (customAttributeModule == null) + if (customAttributeModule is null) throw new ArgumentNullException(nameof(customAttributeModule)); - Debug.Assert(customAttributeCtorParameters != null); - Debug.Assert(customAttributeNamedParameters != null); + Debug.Assert(customAttributeCtorParameters is not null); + Debug.Assert(customAttributeNamedParameters is not null); if (customAttributeCtorParameters.Length != 0 || customAttributeNamedParameters.Length != 0) { @@ -783,7 +791,7 @@ internal readonly struct CustomAttributeNamedParameter public CustomAttributeNamedParameter(string argumentName, CustomAttributeEncoding fieldOrProperty, CustomAttributeType type) { - if (argumentName == null) + if (argumentName is null) throw new ArgumentNullException(nameof(argumentName)); m_argumentName = argumentName; @@ -845,9 +853,9 @@ internal static unsafe class CustomAttribute #region Internal Static Members internal static bool IsDefined(RuntimeType type, RuntimeType? caType, bool inherit) { - Debug.Assert(type != null); + Debug.Assert(type is not null); - if (type.GetElementType() != null) + if (type.GetElementType() is not null) return false; if (PseudoCustomAttribute.IsDefined(type, caType)) @@ -861,7 +869,7 @@ internal static bool IsDefined(RuntimeType type, RuntimeType? caType, bool inher type = (type.BaseType as RuntimeType)!; - while (type != null) + while (type is not null) { if (IsCustomAttributeDefined(type.GetRuntimeModule(), type.MetadataToken, caType, 0, inherit)) return true; @@ -874,8 +882,8 @@ internal static bool IsDefined(RuntimeType type, RuntimeType? caType, bool inher internal static bool IsDefined(RuntimeMethodInfo method, RuntimeType caType, bool inherit) { - Debug.Assert(method != null); - Debug.Assert(caType != null); + Debug.Assert(method is not null); + Debug.Assert(caType is not null); if (PseudoCustomAttribute.IsDefined(method, caType)) return true; @@ -888,7 +896,7 @@ internal static bool IsDefined(RuntimeMethodInfo method, RuntimeType caType, boo method = method.GetParentDefinition()!; - while (method != null) + while (method is not null) { if (IsCustomAttributeDefined(method.GetRuntimeModule(), method.MetadataToken, caType, 0, inherit)) return true; @@ -901,8 +909,8 @@ internal static bool IsDefined(RuntimeMethodInfo method, RuntimeType caType, boo internal static bool IsDefined(RuntimeConstructorInfo ctor, RuntimeType caType) { - Debug.Assert(ctor != null); - Debug.Assert(caType != null); + Debug.Assert(ctor is not null); + Debug.Assert(caType is not null); // No pseudo attributes for RuntimeConstructorInfo @@ -911,8 +919,8 @@ internal static bool IsDefined(RuntimeConstructorInfo ctor, RuntimeType caType) internal static bool IsDefined(RuntimePropertyInfo property, RuntimeType caType) { - Debug.Assert(property != null); - Debug.Assert(caType != null); + Debug.Assert(property is not null); + Debug.Assert(caType is not null); // No pseudo attributes for RuntimePropertyInfo @@ -921,8 +929,8 @@ internal static bool IsDefined(RuntimePropertyInfo property, RuntimeType caType) internal static bool IsDefined(RuntimeEventInfo e, RuntimeType caType) { - Debug.Assert(e != null); - Debug.Assert(caType != null); + Debug.Assert(e is not null); + Debug.Assert(caType is not null); // No pseudo attributes for RuntimeEventInfo @@ -931,8 +939,8 @@ internal static bool IsDefined(RuntimeEventInfo e, RuntimeType caType) internal static bool IsDefined(RuntimeFieldInfo field, RuntimeType caType) { - Debug.Assert(field != null); - Debug.Assert(caType != null); + Debug.Assert(field is not null); + Debug.Assert(caType is not null); if (PseudoCustomAttribute.IsDefined(field, caType)) return true; @@ -942,8 +950,8 @@ internal static bool IsDefined(RuntimeFieldInfo field, RuntimeType caType) internal static bool IsDefined(RuntimeParameterInfo parameter, RuntimeType caType) { - Debug.Assert(parameter != null); - Debug.Assert(caType != null); + Debug.Assert(parameter is not null); + Debug.Assert(caType is not null); if (PseudoCustomAttribute.IsDefined(parameter, caType)) return true; @@ -953,8 +961,8 @@ internal static bool IsDefined(RuntimeParameterInfo parameter, RuntimeType caTyp internal static bool IsDefined(RuntimeAssembly assembly, RuntimeType caType) { - Debug.Assert(assembly != null); - Debug.Assert(caType != null); + Debug.Assert(assembly is not null); + Debug.Assert(caType is not null); // No pseudo attributes for RuntimeAssembly return IsCustomAttributeDefined((assembly.ManifestModule as RuntimeModule)!, RuntimeAssembly.GetToken(assembly.GetNativeHandle()), caType); @@ -962,174 +970,565 @@ internal static bool IsDefined(RuntimeAssembly assembly, RuntimeType caType) internal static bool IsDefined(RuntimeModule module, RuntimeType caType) { - Debug.Assert(module != null); - Debug.Assert(caType != null); + Debug.Assert(module is not null); + Debug.Assert(caType is not null); // No pseudo attributes for RuntimeModule return IsCustomAttributeDefined(module, module.MetadataToken, caType); } - internal static object[] GetCustomAttributes(RuntimeType type, RuntimeType caType, bool inherit) + internal static Attribute? GetCustomAttribute(RuntimeType type, RuntimeType caType, bool inherit) { - Debug.Assert(type != null); - Debug.Assert(caType != null); + Debug.Assert(type is not null); + Debug.Assert(caType is not null); - if (type.GetElementType() != null) - return (caType.IsValueType) ? Array.Empty() : CreateAttributeArrayHelper(caType, 0); + if (type.GetElementType() is not null) + return null; if (type.IsGenericType && !type.IsGenericTypeDefinition) type = (type.GetGenericTypeDefinition() as RuntimeType)!; - PseudoCustomAttribute.GetCustomAttributes(type, caType, out RuntimeType.ListBuilder pcas); + RuntimeType.ListBuilder attributes = default; + AddCustomAttributes(ref attributes, type.GetRuntimeModule(), type.MetadataToken, caType); + PseudoCustomAttribute.GetCustomAttributes(type, caType, ref attributes); // if we are asked to go up the hierarchy chain we have to do it now and regardless of the // attribute usage for the specific attribute because a derived attribute may override the usage... // ... however if the attribute is sealed we can rely on the attribute usage - if (!inherit || (caType.IsSealed && !CustomAttribute.GetAttributeUsage(caType).Inherited)) + if (inherit && !(caType.IsSealed && !CustomAttribute.GetAttributeUsage(caType).Inherited)) { - object[] attributes = GetCustomAttributes(type.GetRuntimeModule(), type.MetadataToken, pcas.Count, caType); - if (pcas.Count > 0) pcas.CopyTo(attributes, attributes.Length - pcas.Count); - return attributes; + type = (type.BaseType as RuntimeType)!; + while (type != (RuntimeType)typeof(object) && type is not null) + { + AddCustomAttributes(ref attributes, type.GetRuntimeModule(), type.MetadataToken, caType, mustBeInheritable: true, attributes.Count); + type = (type.BaseType as RuntimeType)!; + } } - RuntimeType.ListBuilder result = default; - bool mustBeInheritable = false; - bool useObjectArray = (caType.IsValueType || caType.ContainsGenericParameters); - RuntimeType arrayType = useObjectArray ? (RuntimeType)typeof(object) : caType; + if (attributes.Count == 0) + return null; + + if (attributes.Count == 1) + return attributes[0]; + + throw new AmbiguousMatchException(SR.RFLCT_AmbigCust); + } + + internal static object[] GetCustomAttributes(RuntimeType type, RuntimeType caType, bool inherit) + { + Debug.Assert(type is not null); + Debug.Assert(caType is not null); + + if (type.GetElementType() is not null) + return (caType.IsValueType) ? Array.Empty() : CreateAttributeArrayHelper(caType, 0); + + if (type.IsGenericType && !type.IsGenericTypeDefinition) + type = (type.GetGenericTypeDefinition() as RuntimeType)!; - for (int i = 0; i < pcas.Count; i++) - result.Add(pcas[i]); + RuntimeType.ListBuilder attributes = default; + AddCustomAttributes(ref attributes, type.GetRuntimeModule(), type.MetadataToken, caType); + PseudoCustomAttribute.GetCustomAttributes(type, caType, ref attributes); - while (type != (RuntimeType)typeof(object) && type != null) + // if we are asked to go up the hierarchy chain we have to do it now and regardless of the + // attribute usage for the specific attribute because a derived attribute may override the usage... + // ... however if the attribute is sealed we can rely on the attribute usage + if (inherit && !(caType.IsSealed && !CustomAttribute.GetAttributeUsage(caType).Inherited)) { - AddCustomAttributes(ref result, type.GetRuntimeModule(), type.MetadataToken, caType, mustBeInheritable, result); - mustBeInheritable = true; type = (type.BaseType as RuntimeType)!; + while (type != (RuntimeType)typeof(object) && type is not null) + { + AddCustomAttributes(ref attributes, type.GetRuntimeModule(), type.MetadataToken, caType, mustBeInheritable: true, attributes.Count); + type = (type.BaseType as RuntimeType)!; + } } - object[] typedResult = CreateAttributeArrayHelper(arrayType, result.Count); - for (int i = 0; i < result.Count; i++) + return attributes.ToAttributeArray(caType); + } + + internal static Attribute? GetCustomAttribute(RuntimeMethodInfo method, RuntimeType caType, bool inherit) + { + Debug.Assert(method is not null); + Debug.Assert(caType is not null); + + if (method.IsGenericMethod && !method.IsGenericMethodDefinition) + method = (method.GetGenericMethodDefinition() as RuntimeMethodInfo)!; + + RuntimeType.ListBuilder attributes = default; + AddCustomAttributes(ref attributes, method.GetRuntimeModule(), method.MetadataToken, caType); + PseudoCustomAttribute.GetCustomAttributes(method, caType, ref attributes); + + // if we are asked to go up the hierarchy chain we have to do it now and regardless of the + // attribute usage for the specific attribute because a derived attribute may override the usage... + // ... however if the attribute is sealed we can rely on the attribute usage + if (inherit && !(caType.IsSealed && !CustomAttribute.GetAttributeUsage(caType).Inherited)) { - typedResult[i] = result[i]; + method = method.GetParentDefinition()!; + while (method is not null) + { + AddCustomAttributes(ref attributes, method.GetRuntimeModule(), method.MetadataToken, caType, mustBeInheritable: true, attributes.Count); + method = method.GetParentDefinition()!; + } } - return typedResult; + + if (attributes.Count == 0) + return null; + + if (attributes.Count == 1) + return attributes[0]; + + throw new AmbiguousMatchException(SR.RFLCT_AmbigCust); } internal static object[] GetCustomAttributes(RuntimeMethodInfo method, RuntimeType caType, bool inherit) { - Debug.Assert(method != null); - Debug.Assert(caType != null); + Debug.Assert(method is not null); + Debug.Assert(caType is not null); if (method.IsGenericMethod && !method.IsGenericMethodDefinition) method = (method.GetGenericMethodDefinition() as RuntimeMethodInfo)!; - PseudoCustomAttribute.GetCustomAttributes(method, caType, out RuntimeType.ListBuilder pcas); + RuntimeType.ListBuilder attributes = default; + AddCustomAttributes(ref attributes, method.GetRuntimeModule(), method.MetadataToken, caType); + PseudoCustomAttribute.GetCustomAttributes(method, caType, ref attributes); // if we are asked to go up the hierarchy chain we have to do it now and regardless of the // attribute usage for the specific attribute because a derived attribute may override the usage... // ... however if the attribute is sealed we can rely on the attribute usage - if (!inherit || (caType.IsSealed && !CustomAttribute.GetAttributeUsage(caType).Inherited)) + if (inherit && !(caType.IsSealed && !CustomAttribute.GetAttributeUsage(caType).Inherited)) { - object[] attributes = GetCustomAttributes(method.GetRuntimeModule(), method.MetadataToken, pcas.Count, caType); - if (pcas.Count > 0) pcas.CopyTo(attributes, attributes.Length - pcas.Count); - return attributes; + method = method.GetParentDefinition()!; + while (method is not null) + { + AddCustomAttributes(ref attributes, method.GetRuntimeModule(), method.MetadataToken, caType, mustBeInheritable: true, attributes.Count); + method = method.GetParentDefinition()!; + } } - RuntimeType.ListBuilder result = default; - bool mustBeInheritable = false; - bool useObjectArray = (caType.IsValueType || caType.ContainsGenericParameters); - RuntimeType arrayType = useObjectArray ? (RuntimeType)typeof(object) : caType; + return attributes.ToAttributeArray(caType); + } + + internal static Attribute? GetCustomAttribute(RuntimeConstructorInfo ctor, RuntimeType caType) + { + Debug.Assert(ctor is not null); + Debug.Assert(caType is not null); + + // No pseudo attributes for RuntimeConstructorInfo + + return GetCustomAttribute(ctor.GetRuntimeModule(), ctor.MetadataToken, caType); + } + + internal static object[] GetCustomAttributes(RuntimeConstructorInfo ctor, RuntimeType caType) + { + Debug.Assert(ctor is not null); + Debug.Assert(caType is not null); + + // No pseudo attributes for RuntimeConstructorInfo + + RuntimeType.ListBuilder attributes = default; + AddCustomAttributes(ref attributes, ctor.GetRuntimeModule(), ctor.MetadataToken, caType); + + return attributes.ToAttributeArray(caType); + } - for (int i = 0; i < pcas.Count; i++) - result.Add(pcas[i]); + internal static Attribute? GetCustomAttribute(RuntimePropertyInfo property, RuntimeType caType, bool inherit) + { + Debug.Assert(property is not null); + Debug.Assert(caType is not null); + + // No pseudo attributes for RuntimePropertyInfo - while (method != null) + Attribute? attribute = GetCustomAttribute(property.GetRuntimeModule(), property.MetadataToken, caType); + if (!inherit) { - AddCustomAttributes(ref result, method.GetRuntimeModule(), method.MetadataToken, caType, mustBeInheritable, result); - mustBeInheritable = true; - method = method.GetParentDefinition()!; + return attribute; } - object[] typedResult = CreateAttributeArrayHelper(arrayType, result.Count); - for (int i = 0; i < result.Count; i++) + // if this is an index we need to get the parameter types to help disambiguate + ParameterInfo[] parameters = property.GetIndexParametersNoCopy(); + Type[] paramTypes; + if (parameters.Length == 0) { - typedResult[i] = result[i]; + paramTypes = Type.EmptyTypes; } - return typedResult; + else + { + paramTypes = new Type[parameters.Length]; + for (int i = 0; i < parameters.Length; i++) + { + paramTypes[i] = parameters[i].ParameterType; + } + } + + RuntimePropertyInfo? baseProperty = GetParentDefinition(property, paramTypes) as RuntimePropertyInfo; + // We'll loop here rather than recurse so as not to need to recreate the paramTypes array more than once + while (baseProperty is not null) + { + Attribute? baseAttribute = GetCustomAttribute(baseProperty, caType, inherit: false); + + if (baseAttribute is not null) + { + AttributeUsageAttribute usage = GetAttributeUsage((baseAttribute.GetType() as RuntimeType)!); + if (usage.Inherited) + { + if (attribute is null) + { + attribute = baseAttribute; + } + else if (usage.AllowMultiple) + { + throw new AmbiguousMatchException(SR.RFLCT_AmbigCust); + } + } + } + + baseProperty = GetParentDefinition(baseProperty, paramTypes) as RuntimePropertyInfo; + } + + return attribute; + } - internal static object[] GetCustomAttributes(RuntimeConstructorInfo ctor, RuntimeType caType) + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2075:UnrecognizedReflectionPattern", + Justification = "Retrieving virtual/overrides PropertyInfo that this PropertyInfo overrides")] + internal static PropertyInfo? GetParentDefinition(PropertyInfo property, Type[] propertyParameters) { - Debug.Assert(ctor != null); - Debug.Assert(caType != null); + Debug.Assert(property != null); - // No pseudo attributes for RuntimeConstructorInfo + // For the current property get the base class of the getter and the setter, they might be different + // note that this only works for RuntimeMethodInfo + MethodInfo? propAccessor = property.GetGetMethod(true) ?? property.GetSetMethod(true); + RuntimeMethodInfo? rtPropAccessor = propAccessor as RuntimeMethodInfo; - return GetCustomAttributes(ctor.GetRuntimeModule(), ctor.MetadataToken, 0, caType); + rtPropAccessor = rtPropAccessor?.GetParentDefinition(); + if (rtPropAccessor != null) + { + return rtPropAccessor.DeclaringType?.GetProperty( + property.Name, + BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly, + null, + property.PropertyType, + propertyParameters, + null); + } + + return null; } - internal static object[] GetCustomAttributes(RuntimePropertyInfo property, RuntimeType caType) + internal static object[] GetCustomAttributes(RuntimePropertyInfo property, RuntimeType caType, bool inherit) { - Debug.Assert(property != null); - Debug.Assert(caType != null); + Debug.Assert(property is not null); + Debug.Assert(caType is not null); // No pseudo attributes for RuntimePropertyInfo - return GetCustomAttributes(property.GetRuntimeModule(), property.MetadataToken, 0, caType); + RuntimeType.ListBuilder attributes = default; + AddCustomAttributes(ref attributes, property.GetRuntimeModule(), property.MetadataToken, caType); + + if (inherit) + { + // if this is an index we need to get the parameter types to help disambiguate + ParameterInfo[] parameters = property.GetIndexParametersNoCopy(); + Type[] paramTypes; + if (parameters.Length == 0) + { + paramTypes = Type.EmptyTypes; + } + else + { + paramTypes = new Type[parameters.Length]; + for (int i = 0; i < parameters.Length; i++) + { + paramTypes[i] = parameters[i].ParameterType; + } + } + + RuntimePropertyInfo? baseProperty = GetParentDefinition(property, paramTypes) as RuntimePropertyInfo; + // We'll loop here rather than recurse so as not to need to recreate the paramTypes array more than once + while (baseProperty is not null) + { + AddCustomAttributes(ref attributes, baseProperty.GetRuntimeModule(), baseProperty.MetadataToken, caType, mustBeInheritable: true, attributes.Count); + baseProperty = GetParentDefinition(baseProperty, paramTypes) as RuntimePropertyInfo; + } + } + + return attributes.ToAttributeArray(caType); + } + + internal static Attribute? GetCustomAttribute(RuntimeEventInfo e, RuntimeType caType, bool inherit) + { + Debug.Assert(e is not null); + Debug.Assert(caType is not null); + + // No pseudo attributes for RuntimeEventInfo + + Attribute? attribute = GetCustomAttribute(e.GetRuntimeModule(), e.MetadataToken, caType); + if (!inherit) + { + return attribute; + } + + Type? disallowed = null; + if (attribute is not null) + { + Type objType = attribute.GetType(); + AttributeUsageAttribute attribUsage = GetAttributeUsage((objType as RuntimeType)!); + if (!attribUsage.AllowMultiple) + { + disallowed = objType; + } + } + + RuntimeEventInfo? baseEvent = GetParentDefinition(e) as RuntimeEventInfo; + + if (baseEvent is null) + { + return attribute; + } + + Attribute? baseAttribute = GetCustomAttribute(baseEvent, caType, inherit: true); + + if (attribute is null) + { + return baseAttribute; + } + + if (baseAttribute is not null && baseAttribute.GetType() != disallowed) + { + throw new AmbiguousMatchException(SR.RFLCT_AmbigCust); + } + + return attribute; + } + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2075:UnrecognizedReflectionPattern", + Justification = "Retrieving virtual/overrides EventInfo that this EventInfo overrides")] + internal static EventInfo? GetParentDefinition(EventInfo e) + { + Debug.Assert(e is not null); + + MethodInfo? add = e.GetAddMethod(true); + RuntimeMethodInfo? rtAdd = add as RuntimeMethodInfo; + rtAdd = rtAdd?.GetParentDefinition(); + + return rtAdd?.DeclaringType?.GetEvent(e.Name); } internal static object[] GetCustomAttributes(RuntimeEventInfo e, RuntimeType caType) { - Debug.Assert(e != null); - Debug.Assert(caType != null); + Debug.Assert(e is not null); + Debug.Assert(caType is not null); // No pseudo attributes for RuntimeEventInfo - return GetCustomAttributes(e.GetRuntimeModule(), e.MetadataToken, 0, caType); + RuntimeType.ListBuilder attributes = default; + AddCustomAttributes(ref attributes, e.GetRuntimeModule(), e.MetadataToken, caType); + + return attributes.ToAttributeArray(caType); + } + + internal static Attribute? GetCustomAttribute(RuntimeFieldInfo field, RuntimeType caType) + { + Debug.Assert(field is not null); + Debug.Assert(caType is not null); + + RuntimeType.ListBuilder pcas = default; + PseudoCustomAttribute.GetCustomAttributes(field, caType, ref pcas); + Attribute? attribute = GetCustomAttribute(field.GetRuntimeModule(), field.MetadataToken, caType); + + if (pcas.Count == 0) + { + return attribute; + } + if (attribute is not null || pcas.Count > 1) + { + throw new AmbiguousMatchException(SR.RFLCT_AmbigCust); + } + + return pcas[0]; } internal static object[] GetCustomAttributes(RuntimeFieldInfo field, RuntimeType caType) { - Debug.Assert(field != null); - Debug.Assert(caType != null); + Debug.Assert(field is not null); + Debug.Assert(caType is not null); - PseudoCustomAttribute.GetCustomAttributes(field, caType, out RuntimeType.ListBuilder pcas); - object[] attributes = GetCustomAttributes(field.GetRuntimeModule(), field.MetadataToken, pcas.Count, caType); - if (pcas.Count > 0) pcas.CopyTo(attributes, attributes.Length - pcas.Count); - return attributes; + RuntimeType.ListBuilder attributes = default; + AddCustomAttributes(ref attributes, field.GetRuntimeModule(), field.MetadataToken, caType); + PseudoCustomAttribute.GetCustomAttributes(field, caType, ref attributes); + + return attributes.ToAttributeArray(caType); } - internal static object[] GetCustomAttributes(RuntimeParameterInfo parameter, RuntimeType caType) + internal static Attribute? GetCustomAttribute(RuntimeParameterInfo parameter, RuntimeType caType, bool inherit) { - Debug.Assert(parameter != null); - Debug.Assert(caType != null); + Debug.Assert(parameter is not null); + Debug.Assert(caType is not null); + + RuntimeType.ListBuilder attributes = default; + PseudoCustomAttribute.GetCustomAttributes(parameter, caType, ref attributes); + Attribute? attribute = GetCustomAttribute(parameter.GetRuntimeModule()!, parameter.MetadataToken, caType); - PseudoCustomAttribute.GetCustomAttributes(parameter, caType, out RuntimeType.ListBuilder pcas); - object[] attributes = GetCustomAttributes(parameter.GetRuntimeModule()!, parameter.MetadataToken, pcas.Count, caType); - if (pcas.Count > 0) pcas.CopyTo(attributes, attributes.Length - pcas.Count); - return attributes; + if ((attribute is not null && attributes.Count > 0) || attributes.Count > 1) + { + throw new AmbiguousMatchException(SR.RFLCT_AmbigCust); + } + + if (!inherit || parameter.Member.MemberType != MemberTypes.Method) + { + return attributes.Count == 0 ? attribute : attributes[0]; + } + + if (attribute is null && attributes.Count == 1) + { + attribute = attributes[0]; + } + + // For ParameterInfo's we need to make sure that we chain through all the MethodInfo's in the inheritance chain that + // have this ParameterInfo defined. .We pick up all the CustomAttributes for the starting ParameterInfo. We need to pick up only attributes + // that are marked inherited from the remainder of the MethodInfo's in the inheritance chain. + // For MethodInfo's on an interface we do not do an inheritance walk so the default ParameterInfo attributes are returned. + // For MethodInfo's on a class we walk up the inheritance chain but do not look at the MethodInfo's on the interfaces that the + // class inherits from and return the respective ParameterInfo attributes + + Type? disallowed = null; + if (attribute is not null) + { + Type objType = attribute.GetType(); + AttributeUsageAttribute attribUsage = GetAttributeUsage((objType as RuntimeType)!); + if (!attribUsage.AllowMultiple) + { + disallowed = objType; + } + } + + if (parameter.Member.DeclaringType is null) // This is an interface so we are done. + { + return attribute; + } + + RuntimeParameterInfo? baseParam = GetParentDefinition(parameter) as RuntimeParameterInfo; + + if (baseParam is null) + { + return attribute; + } + + Attribute? baseAttribute = GetCustomAttribute(baseParam, caType, inherit: true); + + if (attribute is null) + { + return baseAttribute; + } + + if (baseAttribute is not null && baseAttribute.GetType() != disallowed) + { + throw new AmbiguousMatchException(SR.RFLCT_AmbigCust); + } + + return attribute; + } + + internal static ParameterInfo? GetParentDefinition(ParameterInfo param) + { + Debug.Assert(param != null); + + // note that this only works for RuntimeMethodInfo + RuntimeMethodInfo? rtMethod = param.Member as RuntimeMethodInfo; + + rtMethod = rtMethod?.GetParentDefinition(); + if (rtMethod == null) + { + return null; + } + + // Find the ParameterInfo on this method + int position = param.Position; + if (position == -1) + { + return rtMethod.ReturnParameter; + } + + ParameterInfo[] parameters = rtMethod.GetParametersNoCopy(); + return parameters[position]; // Point to the correct ParameterInfo of the method + } + + internal static object[] GetCustomAttributes(RuntimeParameterInfo parameter, RuntimeType caType, bool inherit) + { + Debug.Assert(parameter is not null); + Debug.Assert(caType is not null); + + RuntimeType.ListBuilder attributes = default; + AddCustomAttributes(ref attributes, parameter.GetRuntimeModule()!, parameter.MetadataToken, caType); + PseudoCustomAttribute.GetCustomAttributes(parameter, caType, ref attributes); + + if (inherit && parameter.Member.MemberType == MemberTypes.Method && parameter.Member.DeclaringType is not null) + { + // For ParameterInfo's we need to make sure that we chain through all the MethodInfo's in the inheritance chain that + // have this ParameterInfo defined. .We pick up all the CustomAttributes for the starting ParameterInfo. We need to pick up only attributes + // that are marked inherited from the remainder of the MethodInfo's in the inheritance chain. + // For MethodInfo's on an interface we do not do an inheritance walk so the default ParameterInfo attributes are returned. + // For MethodInfo's on a class we walk up the inheritance chain but do not look at the MethodInfo's on the interfaces that the + // class inherits from and return the respective ParameterInfo attributes + + RuntimeParameterInfo? parent = GetParentDefinition(parameter) as RuntimeParameterInfo; + while (parent is not null && parent.Member.DeclaringType is not null) + { + AddCustomAttributes(ref attributes, parent.GetRuntimeModule()!, parent.MetadataToken, caType, mustBeInheritable: true, attributes.Count); + + parent = GetParentDefinition(parent) as RuntimeParameterInfo; + } + } + + return attributes.ToAttributeArray(caType); + } + + internal static Attribute? GetCustomAttribute(RuntimeAssembly assembly, RuntimeType caType) + { + Debug.Assert(assembly is not null); + Debug.Assert(caType is not null); + + // No pseudo attributes for RuntimeAssembly + + int assemblyToken = RuntimeAssembly.GetToken(assembly.GetNativeHandle()); + return GetCustomAttribute((assembly.ManifestModule as RuntimeModule)!, assemblyToken, caType); } internal static object[] GetCustomAttributes(RuntimeAssembly assembly, RuntimeType caType) { - Debug.Assert(assembly != null); - Debug.Assert(caType != null); + Debug.Assert(assembly is not null); + Debug.Assert(caType is not null); // No pseudo attributes for RuntimeAssembly + RuntimeType.ListBuilder attributes = default; int assemblyToken = RuntimeAssembly.GetToken(assembly.GetNativeHandle()); - return GetCustomAttributes((assembly.ManifestModule as RuntimeModule)!, assemblyToken, 0, caType); + AddCustomAttributes(ref attributes, (assembly.ManifestModule as RuntimeModule)!, assemblyToken, caType); + + return attributes.ToAttributeArray(caType); + } + + internal static Attribute? GetCustomAttribute(RuntimeModule module, RuntimeType caType) + { + Debug.Assert(module is not null); + Debug.Assert(caType is not null); + + // No pseudo attributes for RuntimeModule + + return GetCustomAttribute(module, module.MetadataToken, caType); } internal static object[] GetCustomAttributes(RuntimeModule module, RuntimeType caType) { - Debug.Assert(module != null); - Debug.Assert(caType != null); + Debug.Assert(module is not null); + Debug.Assert(caType is not null); // No pseudo attributes for RuntimeModule - return GetCustomAttributes(module, module.MetadataToken, 0, caType); + RuntimeType.ListBuilder attributes = default; + AddCustomAttributes(ref attributes, module, module.MetadataToken, caType); + + return attributes.ToAttributeArray(caType); } internal static bool IsAttributeDefined(RuntimeModule decoratedModule, int decoratedMetadataToken, int attributeCtorToken) @@ -1156,11 +1555,11 @@ private static bool IsCustomAttributeDefined( } CustomAttributeRecord record = default; - if (attributeFilterType != null) + if (attributeFilterType is not null) { Debug.Assert(attributeCtorToken == 0); - RuntimeType.ListBuilder derivedAttributes = default; + RuntimeType.ListBuilder derivedAttributes = default; for (int i = 0; i < attributeTokens.Length; i++) { @@ -1168,7 +1567,7 @@ private static bool IsCustomAttributeDefined( out record.tkCtor.Value, out record.blob); if (FilterCustomAttributeRecord(record.tkCtor, in scope, - decoratedModule, decoratedMetadataToken, attributeFilterType, mustBeInheritable, ref derivedAttributes, + decoratedModule, decoratedMetadataToken, attributeFilterType, mustBeInheritable, in derivedAttributes, derivedAttributeCount: 0, out _, out _, out _)) { return true; @@ -1177,7 +1576,7 @@ private static bool IsCustomAttributeDefined( } else { - Debug.Assert(attributeFilterType == null); + Debug.Assert(attributeFilterType is null); Debug.Assert(!MetadataToken.IsNullToken(attributeCtorToken)); for (int i = 0; i < attributeTokens.Length; i++) @@ -1195,22 +1594,19 @@ private static bool IsCustomAttributeDefined( return false; } - private static object[] GetCustomAttributes( - RuntimeModule decoratedModule, int decoratedMetadataToken, int pcaCount, RuntimeType? attributeFilterType) + private static Attribute? GetCustomAttribute( + RuntimeModule decoratedModule, int decoratedMetadataToken, RuntimeType? attributeFilterType) { - RuntimeType.ListBuilder attributes = default; + RuntimeType.ListBuilder attributes = default; + AddCustomAttributes(ref attributes, decoratedModule, decoratedMetadataToken, attributeFilterType); - AddCustomAttributes(ref attributes, decoratedModule, decoratedMetadataToken, attributeFilterType, false, default); + if (attributes.Count == 0) + return null; - bool useObjectArray = attributeFilterType == null || attributeFilterType.IsValueType || attributeFilterType.ContainsGenericParameters; - RuntimeType arrayType = useObjectArray ? (RuntimeType)typeof(object) : attributeFilterType!; + if (attributes.Count == 1) + return attributes[0]; - object[] result = CreateAttributeArrayHelper(arrayType, attributes.Count + pcaCount); - for (int i = 0; i < attributes.Count; i++) - { - result[i] = attributes[i]; - } - return result; + throw new AmbiguousMatchException(SR.RFLCT_AmbigCust); } [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2065:UnrecognizedReflectionPattern", @@ -1218,30 +1614,36 @@ private static object[] GetCustomAttributes( "attribute instantiation which is present in the code linker has analyzed." + "As such the reflection usage in this method will never fail as those methods/fields will be present.")] private static void AddCustomAttributes( - ref RuntimeType.ListBuilder attributes, + ref RuntimeType.ListBuilder attributes, RuntimeModule decoratedModule, int decoratedMetadataToken, - RuntimeType? attributeFilterType, bool mustBeInheritable, - // The derivedAttributes list must be passed by value so that it is not modified with the discovered attributes - RuntimeType.ListBuilder derivedAttributes) + RuntimeType? attributeFilterType, bool mustBeInheritable = false, + int derivedAttributeCount = 0) { - CustomAttributeRecord[] car = CustomAttributeData.GetCustomAttributeRecords(decoratedModule, decoratedMetadataToken); - - if (attributeFilterType is null && car.Length == 0) + if (attributeFilterType is null) { return; } MetadataImport scope = decoratedModule.MetadataImport; - for (int i = 0; i < car.Length; i++) + scope.EnumCustomAttributes(decoratedMetadataToken, out MetadataEnumResult attributeTokens); + if (attributeTokens.Length == 0) return; + + Span records = attributeTokens.Length <= MetadataEnumResult.SmallSizeLength ? + stackalloc CustomAttributeRecord[attributeTokens.Length] : + new CustomAttributeRecord[attributeTokens.Length]; + + CustomAttributeData.PopulateCustomAttributeRecords(in attributeTokens, scope, records); + + for (int i = 0; i < records.Length; i++) { - ref CustomAttributeRecord caRecord = ref car[i]; + ref CustomAttributeRecord caRecord = ref records[i]; IntPtr blobStart = caRecord.blob.Signature; IntPtr blobEnd = (IntPtr)((byte*)blobStart + caRecord.blob.Length); if (!FilterCustomAttributeRecord(caRecord.tkCtor, in scope, decoratedModule, decoratedMetadataToken, attributeFilterType!, mustBeInheritable, - ref derivedAttributes, + in attributes, derivedAttributeCount, out RuntimeType attributeType, out IRuntimeMethodInfo? ctorWithParameters, out bool isVarArg)) { continue; @@ -1252,14 +1654,14 @@ private static void AddCustomAttributes( // Create custom attribute object int cNamedArgs; - object attribute; - if (ctorWithParameters != null) + Attribute attribute; + if (ctorWithParameters is not null) { - attribute = CreateCaObject(decoratedModule, attributeType, ctorWithParameters, ref blobStart, blobEnd, out cNamedArgs); + attribute = (Attribute)CreateCaObject(decoratedModule, attributeType, ctorWithParameters, ref blobStart, blobEnd, out cNamedArgs); } else { - attribute = attributeType.CreateInstanceDefaultCtor(publicOnly: false, wrapExceptions: false)!; + attribute = (Attribute)attributeType.CreateInstanceDefaultCtor(publicOnly: false, wrapExceptions: false)!; // It is allowed by the ECMA spec to have an empty signature blob int blobLen = (int)((byte*)blobEnd - (byte*)blobStart); @@ -1273,21 +1675,22 @@ private static void AddCustomAttributes( // big-endian platforms. #if BIGENDIAN const int CustomAttributeVersion = 0x0100; + int version = Unsafe.ReadUnaligned((void*)blobStart); + if (version != CustomAttributeVersion) + { + throw new CustomAttributeFormatException(); + } + cNamedArgs = BinaryPrimitives.ReverseEndianness(Unsafe.ReadUnaligned((byte*)blobStart + 2)); #else + int data = Unsafe.ReadUnaligned((void*)blobStart); const int CustomAttributeVersion = 0x0001; -#endif - if (Marshal.ReadInt16(blobStart) != CustomAttributeVersion) + if ((data & 0xffff) != CustomAttributeVersion) { throw new CustomAttributeFormatException(); } - - blobStart = (IntPtr)((byte*)blobStart + 2); // skip version prefix - - cNamedArgs = Marshal.ReadInt16(blobStart); - blobStart = (IntPtr)((byte*)blobStart + 2); // skip namedArgs count -#if BIGENDIAN - cNamedArgs = ((cNamedArgs & 0xff00) >> 8) | ((cNamedArgs & 0x00ff) << 8); + cNamedArgs = data >> 16; #endif + blobStart = (IntPtr)((byte*)blobStart + 4); // skip version and namedArgs count } } @@ -1295,11 +1698,12 @@ private static void AddCustomAttributes( { GetPropertyOrFieldData(decoratedModule, ref blobStart, blobEnd, out string name, out bool isProperty, out RuntimeType? type, out object? value); + Exception? ex = null; try { if (isProperty) { - if (type is null && value != null) + if (type is null && value is not null) { type = (RuntimeType)value.GetType(); if (type == Type_RuntimeType) @@ -1313,10 +1717,9 @@ private static void AddCustomAttributes( attributeType.GetProperty(name, type, Type.EmptyTypes); // Did we get a valid property reference? - if (property == null) + if (property is null) { - throw new CustomAttributeFormatException( - SR.Format(SR.RFLCT_InvalidPropFail, name)); + throw new CustomAttributeFormatException(SR.Format(SR.RFLCT_InvalidPropFail, name)); } MethodInfo setMethod = property.GetSetMethod(true)!; @@ -1336,9 +1739,14 @@ private static void AddCustomAttributes( } } catch (Exception e) + { + ex = e; + } + + if (ex is not null) { throw new CustomAttributeFormatException( - SR.Format(isProperty ? SR.RFLCT_InvalidPropFail : SR.RFLCT_InvalidFieldFail, name), e); + SR.Format(isProperty ? SR.RFLCT_InvalidPropFail : SR.RFLCT_InvalidFieldFail, name), ex); } } @@ -1363,7 +1771,8 @@ private static bool FilterCustomAttributeRecord( MetadataToken decoratedToken, RuntimeType attributeFilterType, bool mustBeInheritable, - ref RuntimeType.ListBuilder derivedAttributes, + in RuntimeType.ListBuilder derivedAttributes, + int derivedAttributeCount, out RuntimeType attributeType, out IRuntimeMethodInfo? ctorWithParameters, out bool isVarArg) @@ -1380,8 +1789,9 @@ private static bool FilterCustomAttributeRecord( // Ensure if attribute type must be inheritable that it is inheritable // Ensure that to consider a duplicate attribute type AllowMultiple is true - if (!AttributeUsageCheck(attributeType, mustBeInheritable, ref derivedAttributes)) - return false; + if (mustBeInheritable || derivedAttributeCount > 0) + if (!AttributeUsageCheck(attributeType, mustBeInheritable, in derivedAttributes, derivedAttributeCount)) + return false; // Windows Runtime attributes aren't real types - they exist to be read as metadata only, and as such // should be filtered out of the GetCustomAttributes path. @@ -1449,7 +1859,7 @@ private static bool FilterCustomAttributeRecord( RuntimeTypeHandle attributeTypeHandle = attributeType.TypeHandle; bool result = RuntimeMethodHandle.IsCAVisibleFromDecoratedType(new QCallTypeHandle(ref attributeTypeHandle), - ctorWithParameters != null ? ctorWithParameters.Value : RuntimeMethodHandleInternal.EmptyHandle, + ctorWithParameters is not null ? ctorWithParameters.Value : RuntimeMethodHandleInternal.EmptyHandle, new QCallTypeHandle(ref parentTypeHandle), new QCallModule(ref decoratedModule)) != Interop.BOOL.FALSE; @@ -1460,7 +1870,7 @@ private static bool FilterCustomAttributeRecord( #region Private Static Methods private static bool AttributeUsageCheck( - RuntimeType attributeType, bool mustBeInheritable, ref RuntimeType.ListBuilder derivedAttributes) + RuntimeType attributeType, bool mustBeInheritable, in RuntimeType.ListBuilder derivedAttributes, int derivedAttributeCount) { AttributeUsageAttribute? attributeUsageAttribute = null; @@ -1473,10 +1883,10 @@ private static bool AttributeUsageCheck( } // Legacy: AllowMultiple ignored for none inheritable attributes - if (derivedAttributes.Count == 0) + if (derivedAttributeCount == 0) return true; - for (int i = 0; i < derivedAttributes.Count; i++) + for (int i = 0; i < derivedAttributeCount; i++) { if (derivedAttributes[i].GetType() == attributeType) { @@ -1497,19 +1907,26 @@ internal static AttributeUsageAttribute GetAttributeUsage(RuntimeType decoratedA { RuntimeModule decoratedModule = decoratedAttribute.GetRuntimeModule(); MetadataImport scope = decoratedModule.MetadataImport; - CustomAttributeRecord[] car = CustomAttributeData.GetCustomAttributeRecords(decoratedModule, decoratedAttribute.MetadataToken); + scope.EnumCustomAttributes(decoratedAttribute.MetadataToken, out MetadataEnumResult attributeTokens); + if (attributeTokens.Length == 0) return AttributeUsageAttribute.Default; + + Span records = attributeTokens.Length <= MetadataEnumResult.SmallSizeLength ? + stackalloc CustomAttributeRecord[attributeTokens.Length] : + new CustomAttributeRecord[attributeTokens.Length]; + + CustomAttributeData.PopulateCustomAttributeRecords(in attributeTokens, scope, records); AttributeUsageAttribute? attributeUsageAttribute = null; - for (int i = 0; i < car.Length; i++) + for (int i = 0; i < records.Length; i++) { - ref CustomAttributeRecord caRecord = ref car[i]; + ref CustomAttributeRecord caRecord = ref records[i]; RuntimeType? attributeType = decoratedModule.ResolveType(scope.GetParentToken(caRecord.tkCtor), null, null) as RuntimeType; if (attributeType != (RuntimeType)typeof(AttributeUsageAttribute)) continue; - if (attributeUsageAttribute != null) + if (attributeUsageAttribute is not null) throw new FormatException(SR.Format(SR.Format_AttributeUsage, attributeType)); ParseAttributeUsageAttribute(caRecord.blob, out AttributeTargets targets, out bool inherited, out bool allowMultiple); @@ -1518,6 +1935,18 @@ internal static AttributeUsageAttribute GetAttributeUsage(RuntimeType decoratedA return attributeUsageAttribute ?? AttributeUsageAttribute.Default; } + + private static object[] ToAttributeArray(this ref RuntimeType.ListBuilder attributes, RuntimeType caType) + { + bool useObjectArray = (caType.IsValueType || caType.ContainsGenericParameters); + RuntimeType arrayType = useObjectArray ? (RuntimeType)typeof(object) : caType; + object[] typedResult = CreateAttributeArrayHelper(arrayType, attributes.Count); + for (int i = 0; i < typedResult.Length; i++) + { + typedResult[i] = attributes[i]; + } + return typedResult; + } #endregion #region Private Static FCalls @@ -1576,11 +2005,11 @@ internal static class PseudoCustomAttribute // the only method that adds values to the Dictionary. For more details on // Dictionary versus Hashtable thread safety: // See code:Dictionary#DictionaryVersusHashtableThreadSafety - private static readonly Dictionary s_pca = CreatePseudoCustomAttributeDictionary(); + private static readonly HashSet s_pca = CreatePseudoCustomAttributeHashSet(); #endregion #region Static Constructor - private static Dictionary CreatePseudoCustomAttributeDictionary() + private static HashSet CreatePseudoCustomAttributeHashSet() { Type[] pcas = new Type[] { @@ -1598,13 +2027,13 @@ private static Dictionary CreatePseudoCustomAttributeD typeof(TypeForwardedToAttribute), // assembly }; - Dictionary dict = new Dictionary(pcas.Length); + HashSet set = new HashSet(pcas.Length); foreach (RuntimeType runtimeType in pcas) { VerifyPseudoCustomAttribute(runtimeType); - dict[runtimeType] = runtimeType; + set.Add(runtimeType); } - return dict; + return set; } [Conditional("DEBUG")] @@ -1621,14 +2050,13 @@ private static void VerifyPseudoCustomAttribute(RuntimeType pca) #endregion #region Internal Static - internal static void GetCustomAttributes(RuntimeType type, RuntimeType caType, out RuntimeType.ListBuilder pcas) + internal static void GetCustomAttributes(RuntimeType type, RuntimeType caType, ref RuntimeType.ListBuilder pcas) { - Debug.Assert(type != null); - Debug.Assert(caType != null); - pcas = default; + Debug.Assert(type is not null); + Debug.Assert(caType is not null); bool all = caType == typeof(object) || caType == typeof(Attribute); - if (!all && !s_pca.ContainsKey(caType)) + if (!all && !s_pca.Contains(caType)) return; if (all || caType == typeof(SerializableAttribute)) @@ -1645,7 +2073,7 @@ internal static void GetCustomAttributes(RuntimeType type, RuntimeType caType, o internal static bool IsDefined(RuntimeType type, RuntimeType? caType) { bool all = caType == typeof(object) || caType == typeof(Attribute); - if (!all && !s_pca.ContainsKey(caType!)) + if (!all && !s_pca.Contains(caType!)) return false; if (all || caType == typeof(SerializableAttribute)) @@ -1662,20 +2090,19 @@ internal static bool IsDefined(RuntimeType type, RuntimeType? caType) return false; } - internal static void GetCustomAttributes(RuntimeMethodInfo method, RuntimeType caType, out RuntimeType.ListBuilder pcas) + internal static void GetCustomAttributes(RuntimeMethodInfo method, RuntimeType caType, ref RuntimeType.ListBuilder pcas) { - Debug.Assert(method != null); - Debug.Assert(caType != null); - pcas = default; + Debug.Assert(method is not null); + Debug.Assert(caType is not null); bool all = caType == typeof(object) || caType == typeof(Attribute); - if (!all && !s_pca.ContainsKey(caType)) + if (!all && !s_pca.Contains(caType)) return; if (all || caType == typeof(DllImportAttribute)) { Attribute? pca = GetDllImportCustomAttribute(method); - if (pca != null) pcas.Add(pca); + if (pca is not null) pcas.Add(pca); } if (all || caType == typeof(PreserveSigAttribute)) { @@ -1686,7 +2113,7 @@ internal static void GetCustomAttributes(RuntimeMethodInfo method, RuntimeType c internal static bool IsDefined(RuntimeMethodInfo method, RuntimeType? caType) { bool all = caType == typeof(object) || caType == typeof(Attribute); - if (!all && !s_pca.ContainsKey(caType!)) + if (!all && !s_pca.Contains(caType!)) return false; if (all || caType == typeof(DllImportAttribute)) @@ -1703,14 +2130,13 @@ internal static bool IsDefined(RuntimeMethodInfo method, RuntimeType? caType) return false; } - internal static void GetCustomAttributes(RuntimeParameterInfo parameter, RuntimeType caType, out RuntimeType.ListBuilder pcas) + internal static void GetCustomAttributes(RuntimeParameterInfo parameter, RuntimeType caType, ref RuntimeType.ListBuilder pcas) { - Debug.Assert(parameter != null); - Debug.Assert(caType != null); - pcas = default; + Debug.Assert(parameter is not null); + Debug.Assert(caType is not null); bool all = caType == typeof(object) || caType == typeof(Attribute); - if (!all && !s_pca.ContainsKey(caType)) + if (!all && !s_pca.Contains(caType)) return; if (all || caType == typeof(InAttribute)) @@ -1731,13 +2157,13 @@ internal static void GetCustomAttributes(RuntimeParameterInfo parameter, Runtime if (all || caType == typeof(MarshalAsAttribute)) { Attribute? pca = GetMarshalAsCustomAttribute(parameter); - if (pca != null) pcas.Add(pca); + if (pca is not null) pcas.Add(pca); } } internal static bool IsDefined(RuntimeParameterInfo parameter, RuntimeType? caType) { bool all = caType == typeof(object) || caType == typeof(Attribute); - if (!all && !s_pca.ContainsKey(caType!)) + if (!all && !s_pca.Contains(caType!)) return false; if (all || caType == typeof(InAttribute)) @@ -1754,21 +2180,19 @@ internal static bool IsDefined(RuntimeParameterInfo parameter, RuntimeType? caTy } if (all || caType == typeof(MarshalAsAttribute)) { - if (GetMarshalAsCustomAttribute(parameter) != null) return true; + if (GetMarshalAsCustomAttribute(parameter) is not null) return true; } return false; } - internal static void GetCustomAttributes(RuntimeFieldInfo field, RuntimeType caType, out RuntimeType.ListBuilder pcas) + internal static void GetCustomAttributes(RuntimeFieldInfo field, RuntimeType caType, ref RuntimeType.ListBuilder pcas) { - Debug.Assert(field != null); - Debug.Assert(caType != null); - - pcas = default; + Debug.Assert(field is not null); + Debug.Assert(caType is not null); bool all = caType == typeof(object) || caType == typeof(Attribute); - if (!all && !s_pca.ContainsKey(caType)) + if (!all && !s_pca.Contains(caType)) return; Attribute? pca; @@ -1776,12 +2200,12 @@ internal static void GetCustomAttributes(RuntimeFieldInfo field, RuntimeType caT if (all || caType == typeof(MarshalAsAttribute)) { pca = GetMarshalAsCustomAttribute(field); - if (pca != null) pcas.Add(pca); + if (pca is not null) pcas.Add(pca); } if (all || caType == typeof(FieldOffsetAttribute)) { pca = GetFieldOffsetCustomAttribute(field); - if (pca != null) pcas.Add(pca); + if (pca is not null) pcas.Add(pca); } if (all || caType == typeof(NonSerializedAttribute)) { @@ -1792,16 +2216,16 @@ internal static void GetCustomAttributes(RuntimeFieldInfo field, RuntimeType caT internal static bool IsDefined(RuntimeFieldInfo field, RuntimeType? caType) { bool all = caType == typeof(object) || caType == typeof(Attribute); - if (!all && !s_pca.ContainsKey(caType!)) + if (!all && !s_pca.Contains(caType!)) return false; if (all || caType == typeof(MarshalAsAttribute)) { - if (GetMarshalAsCustomAttribute(field) != null) return true; + if (GetMarshalAsCustomAttribute(field) is not null) return true; } if (all || caType == typeof(FieldOffsetAttribute)) { - if (GetFieldOffsetCustomAttribute(field) != null) return true; + if (GetFieldOffsetCustomAttribute(field) is not null) return true; } if (all || caType == typeof(NonSerializedAttribute)) { @@ -1891,13 +2315,13 @@ internal static bool IsDefined(RuntimeFieldInfo field, RuntimeType? caType) try { - marshalTypeRef = marshalTypeName == null ? null : RuntimeTypeHandle.GetTypeByNameUsingCARules(marshalTypeName, scope); + marshalTypeRef = marshalTypeName is null ? null : RuntimeTypeHandle.GetTypeByNameUsingCARules(marshalTypeName, scope); } catch (TypeLoadException) { // The user may have supplied a bad type name string causing this TypeLoadException // Regardless, we return the bad type name - Debug.Assert(marshalTypeName != null); + Debug.Assert(marshalTypeName is not null); } MarshalAsAttribute attribute = new MarshalAsAttribute(unmanagedType); @@ -1917,7 +2341,7 @@ internal static bool IsDefined(RuntimeFieldInfo field, RuntimeType? caType) private static FieldOffsetAttribute? GetFieldOffsetCustomAttribute(RuntimeFieldInfo field) { - if (field.DeclaringType != null && + if (field.DeclaringType is not null && field.GetRuntimeModule().MetadataImport.GetFieldOffset(field.DeclaringType.MetadataToken, field.MetadataToken, out int fieldOffset)) return new FieldOffsetAttribute(fieldOffset); diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/MdImport.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/MdImport.cs index 9368fc25d340b8..a5303720652a65 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/MdImport.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/MdImport.cs @@ -173,14 +173,15 @@ public static bool IsTokenOfType(int token, params MetadataTokenType[] types) internal unsafe struct MetadataEnumResult { + public const int SmallSizeLength = 16; // Keep the definition in sync with vm\ManagedMdImport.hpp private int[] largeResult; private int length; - private fixed int smallResult[16]; + private fixed int smallResult[SmallSizeLength]; - public int Length => length; + public readonly int Length => length; - public int this[int index] + public readonly int this[int index] { get { diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/MemberInfo.Internal.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/MemberInfo.Internal.cs index 7d5736ee88de11..4fb37da20c637d 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/MemberInfo.Internal.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/MemberInfo.Internal.cs @@ -24,5 +24,18 @@ internal bool HasSameMetadataDefinitionAsCore(MemberInfo other) where TO return true; } + + internal virtual Attribute? GetCustomAttribute(Type attributeType, bool inherit) + { + Attribute[]? attrib = GetCustomAttributes(attributeType, inherit) as Attribute[]; + + if (attrib == null || attrib.Length == 0) + return null; + + if (attrib.Length == 1) + return attrib[0]; + + throw new AmbiguousMatchException(SR.RFLCT_AmbigCust); + } } } diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs index 89fb20181a83e4..903102d97f3256 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs @@ -277,6 +277,19 @@ public override bool IsCollectible return null; } + internal override Attribute? GetCustomAttribute(Type attributeType, bool inherit) + { + if (attributeType == null) + throw new ArgumentNullException(nameof(attributeType)); + + RuntimeType? attributeRuntimeType = attributeType.UnderlyingSystemType as RuntimeType; + + if (attributeRuntimeType == null) + throw new ArgumentException(SR.Arg_MustBeType, nameof(attributeType)); + + return CustomAttribute.GetCustomAttribute(this, attributeRuntimeType); + } + // ISerializable implementation public override void GetObjectData(SerializationInfo info, StreamingContext context) { diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeConstructorInfo.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeConstructorInfo.cs index c991b642cfc5ec..3be729e9ee8790 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeConstructorInfo.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeConstructorInfo.cs @@ -183,6 +183,19 @@ public override object[] GetCustomAttributes(Type attributeType, bool inherit) return CustomAttribute.GetCustomAttributes(this, attributeRuntimeType); } + internal override Attribute? GetCustomAttribute(Type attributeType, bool inherit) + { + if (attributeType == null) + throw new ArgumentNullException(nameof(attributeType)); + + RuntimeType? attributeRuntimeType = attributeType.UnderlyingSystemType as RuntimeType; + + if (attributeRuntimeType == null) + throw new ArgumentException(SR.Arg_MustBeType, nameof(attributeType)); + + return CustomAttribute.GetCustomAttribute(this, attributeRuntimeType); + } + public override bool IsDefined(Type attributeType, bool inherit) { if (attributeType == null) @@ -253,7 +266,7 @@ public override MethodImplAttributes GetMethodImplementationFlags() internal static void CheckCanCreateInstance(Type declaringType, bool isVarArg) { - if (declaringType == null) + if (declaringType is null) throw new ArgumentNullException(nameof(declaringType)); // ctor is declared on interface class @@ -267,7 +280,7 @@ internal static void CheckCanCreateInstance(Type declaringType, bool isVarArg) SR.Format(SR.Acc_CreateAbstEx, declaringType)); // ctor is on a class that contains stack pointers - else if (declaringType.GetRootElementType() == typeof(ArgIterator)) + else if (ReferenceEquals(declaringType.GetRootElementType(), typeof(ArgIterator))) throw new NotSupportedException(); // ctor is vararg @@ -282,7 +295,7 @@ internal static void CheckCanCreateInstance(Type declaringType, bool isVarArg) } // ctor is declared on System.Void - else if (declaringType == typeof(void)) + else if (ReferenceEquals(declaringType, typeof(void))) throw new MemberAccessException(SR.Access_Void); } diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeEventInfo.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeEventInfo.cs index 17721025b51eaf..c350e7bfffc066 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeEventInfo.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeEventInfo.cs @@ -89,6 +89,19 @@ public override object[] GetCustomAttributes(Type attributeType, bool inherit) return CustomAttribute.GetCustomAttributes(this, attributeRuntimeType); } + internal override Attribute? GetCustomAttribute(Type attributeType, bool inherit) + { + if (attributeType == null) + throw new ArgumentNullException(nameof(attributeType)); + + RuntimeType? attributeRuntimeType = attributeType.UnderlyingSystemType as RuntimeType; + + if (attributeRuntimeType == null) + throw new ArgumentException(SR.Arg_MustBeType, nameof(attributeType)); + + return CustomAttribute.GetCustomAttribute(this, attributeRuntimeType, inherit); + } + public override bool IsDefined(Type attributeType, bool inherit) { if (attributeType == null) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeFieldInfo.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeFieldInfo.cs index 85625ac3150a98..ef12a9b1716d45 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeFieldInfo.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeFieldInfo.cs @@ -74,6 +74,19 @@ public override object[] GetCustomAttributes(Type attributeType, bool inherit) return CustomAttribute.GetCustomAttributes(this, attributeRuntimeType); } + internal override Attribute? GetCustomAttribute(Type attributeType, bool inherit) + { + if (attributeType == null) + throw new ArgumentNullException(nameof(attributeType)); + + RuntimeType? attributeRuntimeType = attributeType.UnderlyingSystemType as RuntimeType; + + if (attributeRuntimeType == null) + throw new ArgumentException(SR.Arg_MustBeType, nameof(attributeType)); + + return CustomAttribute.GetCustomAttribute(this, attributeRuntimeType); + } + public override bool IsDefined(Type attributeType, bool inherit) { if (attributeType == null) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.cs index 9d1023bda3733c..05a31dace60b18 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.cs @@ -256,6 +256,19 @@ public override object[] GetCustomAttributes(Type attributeType, bool inherit) return CustomAttribute.GetCustomAttributes(this, attributeRuntimeType, inherit); } + internal override Attribute? GetCustomAttribute(Type attributeType, bool inherit) + { + if (attributeType == null) + throw new ArgumentNullException(nameof(attributeType)); + + RuntimeType? attributeRuntimeType = attributeType.UnderlyingSystemType as RuntimeType; + + if (attributeRuntimeType == null) + throw new ArgumentException(SR.Arg_MustBeType, nameof(attributeType)); + + return CustomAttribute.GetCustomAttribute(this, attributeRuntimeType, inherit); + } + public override bool IsDefined(Type attributeType, bool inherit) { if (attributeType == null) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeModule.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeModule.cs index 0b31bf27759e29..58d790f1d903dc 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeModule.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeModule.cs @@ -388,6 +388,19 @@ internal bool IsTransientInternal() return RuntimeModule.nIsTransientInternal(new QCallModule(ref thisAsLocal)); } + internal override Attribute? GetCustomAttribute(Type attributeType, bool inherit) + { + if (attributeType == null) + throw new ArgumentNullException(nameof(attributeType)); + + RuntimeType? attributeRuntimeType = attributeType.UnderlyingSystemType as RuntimeType; + + if (attributeRuntimeType == null) + throw new ArgumentException(SR.Arg_MustBeType, nameof(attributeType)); + + return CustomAttribute.GetCustomAttribute(this, attributeRuntimeType); + } + internal MetadataImport MetadataImport => ModuleHandle.GetMetadataImport(this); #endregion diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeParameterInfo.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeParameterInfo.cs index 77b2d5e8f5a7a3..1bd3ab0564724a 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeParameterInfo.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeParameterInfo.cs @@ -143,6 +143,25 @@ internal void SetAttributes(ParameterAttributes attributes) { AttrsImpl = attributes; } + + internal override Attribute? GetCustomAttribute(Type attributeType, bool inherit) + { + if (attributeType == null) + throw new ArgumentNullException(nameof(attributeType)); + + if (MdToken.IsNullToken(m_tkParamDef)) + { + if (!inherit || Member.MemberType != MemberTypes.Method) + return null; + } + + RuntimeType? attributeRuntimeType = attributeType.UnderlyingSystemType as RuntimeType; + + if (attributeRuntimeType == null) + throw new ArgumentException(SR.Arg_MustBeType, nameof(attributeType)); + + return CustomAttribute.GetCustomAttribute(this, attributeRuntimeType, inherit); + } #endregion #region Constructor @@ -502,9 +521,12 @@ public override Type[] GetOptionalCustomModifiers() public override object[] GetCustomAttributes(bool inherit) { if (MdToken.IsNullToken(m_tkParamDef)) - return Array.Empty(); + { + if (!inherit || Member.MemberType != MemberTypes.Method) + return Array.Empty(); + } - return CustomAttribute.GetCustomAttributes(this, (typeof(object) as RuntimeType)!); + return CustomAttribute.GetCustomAttributes(this, (typeof(object) as RuntimeType)!, inherit); } public override object[] GetCustomAttributes(Type attributeType, bool inherit) @@ -513,14 +535,17 @@ public override object[] GetCustomAttributes(Type attributeType, bool inherit) throw new ArgumentNullException(nameof(attributeType)); if (MdToken.IsNullToken(m_tkParamDef)) - return Array.Empty(); + { + if (!inherit || Member.MemberType != MemberTypes.Method) + return Array.Empty(); + } RuntimeType? attributeRuntimeType = attributeType.UnderlyingSystemType as RuntimeType; if (attributeRuntimeType == null) throw new ArgumentException(SR.Arg_MustBeType, nameof(attributeType)); - return CustomAttribute.GetCustomAttributes(this, attributeRuntimeType); + return CustomAttribute.GetCustomAttributes(this, attributeRuntimeType, inherit); } public override bool IsDefined(Type attributeType, bool inherit) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimePropertyInfo.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimePropertyInfo.cs index b7e6a58f1fe4b4..4031ba09b02ced 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimePropertyInfo.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimePropertyInfo.cs @@ -134,7 +134,7 @@ public override string ToString() #region ICustomAttributeProvider public override object[] GetCustomAttributes(bool inherit) { - return CustomAttribute.GetCustomAttributes(this, (typeof(object) as RuntimeType)!); + return CustomAttribute.GetCustomAttributes(this, (typeof(object) as RuntimeType)!, inherit); } public override object[] GetCustomAttributes(Type attributeType, bool inherit) @@ -147,7 +147,20 @@ public override object[] GetCustomAttributes(Type attributeType, bool inherit) if (attributeRuntimeType == null) throw new ArgumentException(SR.Arg_MustBeType, nameof(attributeType)); - return CustomAttribute.GetCustomAttributes(this, attributeRuntimeType); + return CustomAttribute.GetCustomAttributes(this, attributeRuntimeType, inherit); + } + + internal override Attribute? GetCustomAttribute(Type attributeType, bool inherit) + { + if (attributeType == null) + throw new ArgumentNullException(nameof(attributeType)); + + RuntimeType? attributeRuntimeType = attributeType.UnderlyingSystemType as RuntimeType; + + if (attributeRuntimeType == null) + throw new ArgumentException(SR.Arg_MustBeType, nameof(attributeType)); + + return CustomAttribute.GetCustomAttribute(this, attributeRuntimeType, inherit); } public override bool IsDefined(Type attributeType, bool inherit) diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs index d01d077b473689..2611295c3f7416 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs @@ -69,7 +69,7 @@ public ListBuilder(int capacity) _capacity = capacity; } - public T this[int index] + public readonly T this[int index] { get { @@ -104,7 +104,7 @@ public void CopyTo(object[] array, int index) Array.Copy(_items!, 0, array, index, _count); } - public int Count => _count; + public readonly int Count => _count; public void Add(T item) { @@ -1870,6 +1870,32 @@ internal FieldInfo GetField(RuntimeFieldHandleInternal field) return retval; } + internal override Attribute? GetCustomAttribute(Type attributeType, bool inherit) + { + if (attributeType == null) + throw new ArgumentNullException(nameof(attributeType)); + + RuntimeType? attributeRuntimeType = attributeType.UnderlyingSystemType as RuntimeType; + + if (attributeRuntimeType == null) + throw new ArgumentException(SR.Arg_MustBeType, nameof(attributeType)); + + return CustomAttribute.GetCustomAttribute(this, attributeRuntimeType, inherit); + } + + internal override AttributeUsageAttribute GetAttributeUsageAttribute() + { + if (!this.IsSubclassOf(typeof(Attribute))) + throw new ArgumentException(SR.Argument_MustHaveAttributeBaseClass); + + RuntimeType? attributeRuntimeType = typeof(AttributeUsageAttribute).UnderlyingSystemType as RuntimeType; + + if (attributeRuntimeType == null) + throw new ArgumentException(SR.Arg_MustBeType, nameof(Attribute)); + + return CustomAttribute.GetAttributeUsage(this); + } + internal object? GenericCache { get => CacheIfExists?.GenericCache; diff --git a/src/coreclr/System.Private.CoreLib/src/System/Type.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Type.CoreCLR.cs index 7a39e6e5e8bd38..440b436887fb59 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Type.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Type.CoreCLR.cs @@ -105,5 +105,21 @@ internal virtual RuntimeTypeHandle GetTypeHandleInternal() // Exists to faciliate code sharing between CoreCLR and CoreRT. [MethodImpl(MethodImplOptions.AggressiveInlining)] internal bool IsRuntimeImplemented() => this is RuntimeType; + + internal virtual AttributeUsageAttribute GetAttributeUsageAttribute() + { + if (!this.IsSubclassOf(typeof(Attribute))) + throw new ArgumentException(SR.Argument_MustHaveAttributeBaseClass); + + Attribute[]? attrib = GetCustomAttributes(typeof(AttributeUsageAttribute), false) as Attribute[]; + + if (attrib == null || attrib.Length == 0) + return AttributeUsageAttribute.Default; + + if (attrib.Length == 1) + return (AttributeUsageAttribute)attrib[0]; + + throw new FormatException(SR.Format(SR.Format_AttributeUsage, this)); + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Module.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Module.cs index 4fe3b9ad74747a..366cc9497d37b8 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Module.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Module.cs @@ -189,5 +189,20 @@ private static bool FilterTypeNameImpl(Type cls, object filterCriteria, StringCo return cls.Name.Equals(str, comparison); } + + internal virtual Attribute? GetCustomAttribute(Type attributeType, bool inherit) + { + // Returns an Attribute of base class/interface attributeType on the Module or null if none exists. + // throws an AmbiguousMatchException if there are more than one defined. + Attribute[] attrib = Attribute.GetCustomAttributes(this, attributeType, inherit); + + if (attrib == null || attrib.Length == 0) + return null; + + if (attrib.Length == 1) + return attrib[0]; + + throw new AmbiguousMatchException(SR.RFLCT_AmbigCust); + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/ParameterInfo.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/ParameterInfo.cs index bae3bdea9ff37c..a87582d3f51847 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/ParameterInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/ParameterInfo.cs @@ -37,6 +37,21 @@ public virtual bool IsDefined(Type attributeType, bool inherit) public virtual IEnumerable CustomAttributes => GetCustomAttributesData(); public virtual IList GetCustomAttributesData() { throw NotImplemented.ByDesign; } + internal virtual Attribute? GetCustomAttribute(Type attributeType, bool inherit) + { + // Returns an Attribute of base class/inteface attributeType on the ParameterInfo or null if none exists. + // throws an AmbiguousMatchException if there are more than one defined. + Attribute[] attrib = Attribute.GetCustomAttributes(this, attributeType, inherit); + + if (attrib == null || attrib.Length == 0) + return null; + + if (attrib.Length == 1) + return attrib[0]; + + throw new AmbiguousMatchException(SR.RFLCT_AmbigCust); + } + public virtual object[] GetCustomAttributes(bool inherit) => Array.Empty(); public virtual object[] GetCustomAttributes(Type attributeType, bool inherit) {