From 622b74ebb7c3f3dbcd052cab7fef1c4c668ba81d Mon Sep 17 00:00:00 2001 From: Nick Craver Date: Fri, 2 Nov 2018 21:42:51 -0400 Subject: [PATCH 1/7] Optimization: avoid 2 array allocations in inherited attribute misses This is a follow-up to many allocations recognized in #20448. A lot of methods ultimately call through CustomAttributes.GetCustomAttributes(). In the inherited search path (default for most searches above), the inheritance path of a class is traversed, resulting in an array allocation per crawled type, a copy to the overall List and then after that - in current code - an array allocation from that list and a typed array allocation it's copied to. However, in the common miss case as simple as: typeof(T).GetCustomAttributes(typeof(TAttr), true); ...and many other overloads, all the same path underneath... We can avoid the last 2 arrays in the miss case. We have the List to go off of. If that's a zero-entry list, we can return an Array.Empty(). That's effectively what this change does. While converting the entire attribute pipeline to generics is problematic and has issue since some object[] return abstracts aren't sealed, we can at least somewhat trivially cache an array per attribute type (only one static, ultimately from Array.Empty underneath) and return that for the miss case. There are far more wins to be had here, but they require more changes. --- .../src/System/Reflection/CustomAttribute.cs | 5 +++++ src/System.Private.CoreLib/src/System/RtType.cs | 17 +++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs b/src/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs index c1c6e4f033d1..ad9feb0ef22a 100644 --- a/src/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs +++ b/src/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs @@ -1294,6 +1294,11 @@ internal static object[] GetCustomAttributes(RuntimeType type, RuntimeType caTyp type = type.BaseType as RuntimeType; } + if (result.Count == 0) + { + return useObjectArray ? Array.Empty() : caType.GetEmptyArray(); + } + object[] typedResult = CreateAttributeArrayHelper(arrayType, result.Count); Array.Copy(result.ToArray(), 0, typedResult, 0, result.Count); return typedResult; diff --git a/src/System.Private.CoreLib/src/System/RtType.cs b/src/System.Private.CoreLib/src/System/RtType.cs index 743e2291e5b2..61fe2df1a5fe 100644 --- a/src/System.Private.CoreLib/src/System/RtType.cs +++ b/src/System.Private.CoreLib/src/System/RtType.cs @@ -1462,6 +1462,7 @@ internal RuntimeType ReflectedType private static object s_methodInstantiationsLock; private string m_defaultMemberName; private object m_genericCache; // Generic cache for rare scenario specific data. It is used to cache Enum names and values. + private object[] _emptyArray; // Object array cache for Attribute.GetCustomAttributes() pathological no-result case. #endregion #region Constructor @@ -1636,6 +1637,17 @@ internal string GetDefaultMemberName() return m_defaultMemberName; } + + internal object[] GetEmptyArray() + { + if (_emptyArray == null) + { + MethodInfo method = typeof(Array).GetMethod(nameof(Array.Empty)); + MethodInfo genericMethod = method.MakeGenericMethod(m_runtimeType); + _emptyArray = (object[])genericMethod.Invoke(null, null); + } + return _emptyArray; + } #endregion #region Caches Accessors @@ -3549,6 +3561,11 @@ public override Type GetElementType() { return RuntimeTypeHandle.GetElementType(this); } + + internal object[] GetEmptyArray() + { + return Cache.GetEmptyArray(); + } #endregion #region Enums From cb2305f7337a430640b941ebaa44ad0014221914 Mon Sep 17 00:00:00 2001 From: Nick Craver Date: Sat, 3 Nov 2018 09:07:53 -0400 Subject: [PATCH 2/7] Move RuntimeType empty array cache generation to Attribute.CreateAttributeArrayHelper This exposes Attribute.CreateAttributeArrayHelper to internal and uses it directly on the RuntimeType caching for empty arrays. Though this allocated 1 additional array overall, it's simpler, faster to init, and still is an infinite win over the per-call allocations before this overall changesets. --- src/System.Private.CoreLib/src/System/Attribute.cs | 2 +- src/System.Private.CoreLib/src/System/RtType.cs | 8 +------- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/System.Private.CoreLib/src/System/Attribute.cs b/src/System.Private.CoreLib/src/System/Attribute.cs index fd84dd33b08c..c6c301cad549 100644 --- a/src/System.Private.CoreLib/src/System/Attribute.cs +++ b/src/System.Private.CoreLib/src/System/Attribute.cs @@ -434,7 +434,7 @@ private static AttributeUsageAttribute InternalGetAttributeUsage(Type type) SR.Format(SR.Format_AttributeUsage, type)); } - private static Attribute[] CreateAttributeArrayHelper(Type elementType, int elementCount) + internal static Attribute[] CreateAttributeArrayHelper(Type elementType, int elementCount) { return (Attribute[])Array.UnsafeCreateInstance(elementType, elementCount); } diff --git a/src/System.Private.CoreLib/src/System/RtType.cs b/src/System.Private.CoreLib/src/System/RtType.cs index 61fe2df1a5fe..d2a8887c59d2 100644 --- a/src/System.Private.CoreLib/src/System/RtType.cs +++ b/src/System.Private.CoreLib/src/System/RtType.cs @@ -1640,13 +1640,7 @@ internal string GetDefaultMemberName() internal object[] GetEmptyArray() { - if (_emptyArray == null) - { - MethodInfo method = typeof(Array).GetMethod(nameof(Array.Empty)); - MethodInfo genericMethod = method.MakeGenericMethod(m_runtimeType); - _emptyArray = (object[])genericMethod.Invoke(null, null); - } - return _emptyArray; + return _emptyArray ?? (_emptyArray = Attribute.CreateAttributeArrayHelper(m_runtimeType, 0)); } #endregion From 1b459b9a6f3dc267eb802a1e0dead6b03631736f Mon Sep 17 00:00:00 2001 From: Nick Craver Date: Sat, 3 Nov 2018 09:16:13 -0400 Subject: [PATCH 3/7] CustomAttributes: remove needless array copy in the inherited hit case This removes a .ToArray() for the sake of Array.Copy() where a simple for loop suffices and removes the allocation. Reversing the "empty" result checks is also just a bit cleaner here. This also expands the same fix to the MemberInfo path. Note: should DRY these up too (longstanding issue) - but let's do that in a separate commit for clarity. --- .../src/System/Reflection/CustomAttribute.cs | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs b/src/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs index ad9feb0ef22a..6af71baa803d 100644 --- a/src/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs +++ b/src/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs @@ -1294,14 +1294,17 @@ internal static object[] GetCustomAttributes(RuntimeType type, RuntimeType caTyp type = type.BaseType as RuntimeType; } - if (result.Count == 0) + if (result.Count > 0) { - return useObjectArray ? Array.Empty() : caType.GetEmptyArray(); + object[] typedResult = CreateAttributeArrayHelper(arrayType, result.Count); + for (var i = 0; i < result.Count; i++) + { + typedResult[i] = result[i]; + } + return typedResult; } - - object[] typedResult = CreateAttributeArrayHelper(arrayType, result.Count); - Array.Copy(result.ToArray(), 0, typedResult, 0, result.Count); - return typedResult; + // Return a cached, empty array + return useObjectArray ? Array.Empty() : caType.GetEmptyArray(); } internal static object[] GetCustomAttributes(RuntimeMethodInfo method, RuntimeType caType, bool inherit) @@ -1342,9 +1345,17 @@ internal static object[] GetCustomAttributes(RuntimeMethodInfo method, RuntimeTy method = method.GetParentDefinition(); } - object[] typedResult = CreateAttributeArrayHelper(arrayType, result.Count); - Array.Copy(result.ToArray(), 0, typedResult, 0, result.Count); - return typedResult; + if (result.Count > 0) + { + object[] typedResult = CreateAttributeArrayHelper(arrayType, result.Count); + for (var i = 0; i < result.Count; i++) + { + typedResult[i] = result[i]; + } + return typedResult; + } + // Return a cached, empty array + return useObjectArray ? Array.Empty() : caType.GetEmptyArray(); } internal static object[] GetCustomAttributes(RuntimeConstructorInfo ctor, RuntimeType caType) From d6004aea181d3494621c4638f3c4f3d78fa73a95 Mon Sep 17 00:00:00 2001 From: Nick Craver Date: Sat, 3 Nov 2018 09:41:35 -0400 Subject: [PATCH 4/7] GetCusomAttributes: use ListBuilder for inheritance crawls This exposes RuntimeType.ListBuilder for internal usage and replaces the List allocation in GetCustomAttributes() paths to reduce allocations and increase performance in the inherited crawl paths (which is the default for many optional-parameter methods in layers above). Note: there is a subtle behavior depending on previous-null (not possible with a struct now) in AttributeUsageCheck() that I believe still behaves correctly, but could use another set of eyes and a full test suite run to confirm. object[] attributes was removed there simply because it wasn't used before - only cleaning up. --- .../src/System/Reflection/CustomAttribute.cs | 19 +++++++++---------- .../src/System/RtType.cs | 2 +- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs b/src/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs index 6af71baa803d..0a0e207d9e30 100644 --- a/src/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs +++ b/src/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs @@ -1276,7 +1276,7 @@ internal static object[] GetCustomAttributes(RuntimeType type, RuntimeType caTyp return attributes; } - List result = new List(); + RuntimeType.ListBuilder result = new RuntimeType.ListBuilder(); bool mustBeInheritable = false; bool useObjectArray = (caType == null || caType.IsValueType || caType.ContainsGenericParameters); Type arrayType = useObjectArray ? typeof(object) : caType; @@ -1327,7 +1327,7 @@ internal static object[] GetCustomAttributes(RuntimeMethodInfo method, RuntimeTy return attributes; } - List result = new List(); + RuntimeType.ListBuilder result = new RuntimeType.ListBuilder(); bool mustBeInheritable = false; bool useObjectArray = (caType == null || caType.IsValueType || caType.ContainsGenericParameters); Type arrayType = useObjectArray ? typeof(object) : caType; @@ -1471,7 +1471,7 @@ private static bool IsCustomAttributeDefined( CustomAttributeRecord caRecord = car[i]; if (FilterCustomAttributeRecord(caRecord, scope, ref lastAptcaOkAssembly, - decoratedModule, decoratedMetadataToken, attributeFilterType, mustBeInheritable, null, null, + decoratedModule, decoratedMetadataToken, attributeFilterType, mustBeInheritable, null, default, out attributeType, out ctor, out ctorHasParameters, out isVarArg)) return true; } @@ -1496,12 +1496,12 @@ private static bool IsCustomAttributeDefined( private static unsafe object[] GetCustomAttributes( RuntimeModule decoratedModule, int decoratedMetadataToken, int pcaCount, RuntimeType attributeFilterType) { - return GetCustomAttributes(decoratedModule, decoratedMetadataToken, pcaCount, attributeFilterType, false, null); + return GetCustomAttributes(decoratedModule, decoratedMetadataToken, pcaCount, attributeFilterType, false, default); } private static unsafe object[] GetCustomAttributes( RuntimeModule decoratedModule, int decoratedMetadataToken, int pcaCount, - RuntimeType attributeFilterType, bool mustBeInheritable, IList derivedAttributes) + RuntimeType attributeFilterType, bool mustBeInheritable, RuntimeType.ListBuilder derivedAttributes) { MetadataImport scope = decoratedModule.MetadataImport; CustomAttributeRecord[] car = CustomAttributeData.GetCustomAttributeRecords(decoratedModule, decoratedMetadataToken); @@ -1663,7 +1663,7 @@ private static unsafe bool FilterCustomAttributeRecord( RuntimeType attributeFilterType, bool mustBeInheritable, object[] attributes, - IList derivedAttributes, + RuntimeType.ListBuilder derivedAttributes, out RuntimeType attributeType, out IRuntimeMethodInfo ctor, out bool ctorHasParameters, @@ -1687,7 +1687,7 @@ private static unsafe bool FilterCustomAttributeRecord( // Ensure if attribute type must be inheritable that it is inhertiable // Ensure that to consider a duplicate attribute type AllowMultiple is true - if (!AttributeUsageCheck(attributeType, mustBeInheritable, attributes, derivedAttributes)) + if (!AttributeUsageCheck(attributeType, mustBeInheritable, derivedAttributes)) return false; // Windows Runtime attributes aren't real types - they exist to be read as metadata only, and as such @@ -1759,7 +1759,7 @@ private static unsafe bool FilterCustomAttributeRecord( #region Private Static Methods private static bool AttributeUsageCheck( - RuntimeType attributeType, bool mustBeInheritable, object[] attributes, IList derivedAttributes) + RuntimeType attributeType, bool mustBeInheritable, RuntimeType.ListBuilder derivedAttributes) { AttributeUsageAttribute attributeUsageAttribute = null; @@ -1772,8 +1772,7 @@ private static bool AttributeUsageCheck( } // Legacy: AllowMultiple ignored for none inheritable attributes - - if (derivedAttributes == null) + if (derivedAttributes.Count == 0) return true; for (int i = 0; i < derivedAttributes.Count; i++) diff --git a/src/System.Private.CoreLib/src/System/RtType.cs b/src/System.Private.CoreLib/src/System/RtType.cs index d2a8887c59d2..3791acc75cd7 100644 --- a/src/System.Private.CoreLib/src/System/RtType.cs +++ b/src/System.Private.CoreLib/src/System/RtType.cs @@ -83,7 +83,7 @@ internal enum MemberListType } // Helper to build lists of MemberInfos. Special cased to avoid allocations for lists of one element. - private struct ListBuilder where T : class + internal struct ListBuilder where T : class { private T[] _items; private T _item; From 496a760efe9a2d2e53e27bca7942b6dda0d8983b Mon Sep 17 00:00:00 2001 From: Nick Craver Date: Sat, 3 Nov 2018 13:16:51 -0400 Subject: [PATCH 5/7] Attribute caching: use Array.CreateInstance() directly on RuntimeType This also reverts the CreateAttributeArrayHelper => internal change, since it's no longer needed. --- src/System.Private.CoreLib/src/System/Attribute.cs | 2 +- src/System.Private.CoreLib/src/System/RtType.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/System.Private.CoreLib/src/System/Attribute.cs b/src/System.Private.CoreLib/src/System/Attribute.cs index c6c301cad549..fd84dd33b08c 100644 --- a/src/System.Private.CoreLib/src/System/Attribute.cs +++ b/src/System.Private.CoreLib/src/System/Attribute.cs @@ -434,7 +434,7 @@ private static AttributeUsageAttribute InternalGetAttributeUsage(Type type) SR.Format(SR.Format_AttributeUsage, type)); } - internal static Attribute[] CreateAttributeArrayHelper(Type elementType, int elementCount) + private static Attribute[] CreateAttributeArrayHelper(Type elementType, int elementCount) { return (Attribute[])Array.UnsafeCreateInstance(elementType, elementCount); } diff --git a/src/System.Private.CoreLib/src/System/RtType.cs b/src/System.Private.CoreLib/src/System/RtType.cs index 3791acc75cd7..37225ca835fe 100644 --- a/src/System.Private.CoreLib/src/System/RtType.cs +++ b/src/System.Private.CoreLib/src/System/RtType.cs @@ -1640,7 +1640,7 @@ internal string GetDefaultMemberName() internal object[] GetEmptyArray() { - return _emptyArray ?? (_emptyArray = Attribute.CreateAttributeArrayHelper(m_runtimeType, 0)); + return _emptyArray ?? (_emptyArray = (object[])Array.CreateInstance(m_runtimeType, 0)); } #endregion From 70dc6799ac1a3d6cc6b72140f99d7f44fa45168b Mon Sep 17 00:00:00 2001 From: Nick Craver Date: Sat, 3 Nov 2018 13:49:35 -0400 Subject: [PATCH 6/7] Ref passing for RuntimeType.ListBuilder & CustomAttribute simplification This fixes the struct passing duplication and tweaks how we're creating arrays a bit, centralizing the zero-element checks to cover all cases as well as simplify the per-method code to rely on the fact this is happening underneath. --- .../src/System/Reflection/CustomAttribute.cs | 60 +++++++++---------- 1 file changed, 29 insertions(+), 31 deletions(-) diff --git a/src/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs b/src/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs index 0a0e207d9e30..7e286562293c 100644 --- a/src/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs +++ b/src/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs @@ -1279,14 +1279,14 @@ internal static object[] GetCustomAttributes(RuntimeType type, RuntimeType caTyp RuntimeType.ListBuilder result = new RuntimeType.ListBuilder(); bool mustBeInheritable = false; bool useObjectArray = (caType == null || caType.IsValueType || caType.ContainsGenericParameters); - Type arrayType = useObjectArray ? typeof(object) : caType; + RuntimeType arrayType = useObjectArray ? typeof(object) as RuntimeType : caType; while (pcaCount > 0) result.Add(pca[--pcaCount]); while (type != (RuntimeType)typeof(object) && type != null) { - object[] attributes = GetCustomAttributes(type.GetRuntimeModule(), type.MetadataToken, 0, caType, mustBeInheritable, result); + object[] attributes = GetCustomAttributes(type.GetRuntimeModule(), type.MetadataToken, 0, caType, mustBeInheritable, ref result); mustBeInheritable = true; for (int i = 0; i < attributes.Length; i++) result.Add(attributes[i]); @@ -1294,17 +1294,12 @@ internal static object[] GetCustomAttributes(RuntimeType type, RuntimeType caTyp type = type.BaseType as RuntimeType; } - if (result.Count > 0) + object[] typedResult = CreateAttributeArrayHelper(arrayType, result.Count); + for (var i = 0; i < result.Count; i++) { - object[] typedResult = CreateAttributeArrayHelper(arrayType, result.Count); - for (var i = 0; i < result.Count; i++) - { - typedResult[i] = result[i]; - } - return typedResult; + typedResult[i] = result[i]; } - // Return a cached, empty array - return useObjectArray ? Array.Empty() : caType.GetEmptyArray(); + return typedResult; } internal static object[] GetCustomAttributes(RuntimeMethodInfo method, RuntimeType caType, bool inherit) @@ -1330,14 +1325,14 @@ internal static object[] GetCustomAttributes(RuntimeMethodInfo method, RuntimeTy RuntimeType.ListBuilder result = new RuntimeType.ListBuilder(); bool mustBeInheritable = false; bool useObjectArray = (caType == null || caType.IsValueType || caType.ContainsGenericParameters); - Type arrayType = useObjectArray ? typeof(object) : caType; + RuntimeType arrayType = useObjectArray ? typeof(object) as RuntimeType : caType; while (pcaCount > 0) result.Add(pca[--pcaCount]); while (method != null) { - object[] attributes = GetCustomAttributes(method.GetRuntimeModule(), method.MetadataToken, 0, caType, mustBeInheritable, result); + object[] attributes = GetCustomAttributes(method.GetRuntimeModule(), method.MetadataToken, 0, caType, mustBeInheritable, ref result); mustBeInheritable = true; for (int i = 0; i < attributes.Length; i++) result.Add(attributes[i]); @@ -1345,17 +1340,12 @@ internal static object[] GetCustomAttributes(RuntimeMethodInfo method, RuntimeTy method = method.GetParentDefinition(); } - if (result.Count > 0) + object[] typedResult = CreateAttributeArrayHelper(arrayType, result.Count); + for (var i = 0; i < result.Count; i++) { - object[] typedResult = CreateAttributeArrayHelper(arrayType, result.Count); - for (var i = 0; i < result.Count; i++) - { - typedResult[i] = result[i]; - } - return typedResult; + typedResult[i] = result[i]; } - // Return a cached, empty array - return useObjectArray ? Array.Empty() : caType.GetEmptyArray(); + return typedResult; } internal static object[] GetCustomAttributes(RuntimeConstructorInfo ctor, RuntimeType caType) @@ -1461,6 +1451,7 @@ private static bool IsCustomAttributeDefined( RuntimeType attributeType; IRuntimeMethodInfo ctor; bool ctorHasParameters, isVarArg; + RuntimeType.ListBuilder derivedAttributes = default; // Optimization for the case where attributes decorate entities in the same assembly in which case // we can cache the successful APTCA check between the decorated and the declared assembly. @@ -1471,7 +1462,7 @@ private static bool IsCustomAttributeDefined( CustomAttributeRecord caRecord = car[i]; if (FilterCustomAttributeRecord(caRecord, scope, ref lastAptcaOkAssembly, - decoratedModule, decoratedMetadataToken, attributeFilterType, mustBeInheritable, null, default, + decoratedModule, decoratedMetadataToken, attributeFilterType, mustBeInheritable, null, ref derivedAttributes, out attributeType, out ctor, out ctorHasParameters, out isVarArg)) return true; } @@ -1496,18 +1487,19 @@ private static bool IsCustomAttributeDefined( private static unsafe object[] GetCustomAttributes( RuntimeModule decoratedModule, int decoratedMetadataToken, int pcaCount, RuntimeType attributeFilterType) { - return GetCustomAttributes(decoratedModule, decoratedMetadataToken, pcaCount, attributeFilterType, false, default); + RuntimeType.ListBuilder _ = default; + return GetCustomAttributes(decoratedModule, decoratedMetadataToken, pcaCount, attributeFilterType, false, ref _); } private static unsafe object[] GetCustomAttributes( RuntimeModule decoratedModule, int decoratedMetadataToken, int pcaCount, - RuntimeType attributeFilterType, bool mustBeInheritable, RuntimeType.ListBuilder derivedAttributes) + RuntimeType attributeFilterType, bool mustBeInheritable, ref RuntimeType.ListBuilder derivedAttributes) { MetadataImport scope = decoratedModule.MetadataImport; CustomAttributeRecord[] car = CustomAttributeData.GetCustomAttributeRecords(decoratedModule, decoratedMetadataToken); bool useObjectArray = (attributeFilterType == null || attributeFilterType.IsValueType || attributeFilterType.ContainsGenericParameters); - Type arrayType = useObjectArray ? typeof(object) : attributeFilterType; + RuntimeType arrayType = useObjectArray ? typeof(object) as RuntimeType : attributeFilterType; if (attributeFilterType == null && car.Length == 0) return CreateAttributeArrayHelper(arrayType, 0); @@ -1535,7 +1527,7 @@ private static unsafe object[] GetCustomAttributes( if (!FilterCustomAttributeRecord(caRecord, scope, ref lastAptcaOkAssembly, decoratedModule, decoratedMetadataToken, attributeFilterType, mustBeInheritable, - attributes, derivedAttributes, + attributes, ref derivedAttributes, out attributeType, out ctor, out ctorHasParameters, out isVarArg)) continue; @@ -1663,7 +1655,7 @@ private static unsafe bool FilterCustomAttributeRecord( RuntimeType attributeFilterType, bool mustBeInheritable, object[] attributes, - RuntimeType.ListBuilder derivedAttributes, + ref RuntimeType.ListBuilder derivedAttributes, out RuntimeType attributeType, out IRuntimeMethodInfo ctor, out bool ctorHasParameters, @@ -1687,7 +1679,7 @@ private static unsafe bool FilterCustomAttributeRecord( // Ensure if attribute type must be inheritable that it is inhertiable // Ensure that to consider a duplicate attribute type AllowMultiple is true - if (!AttributeUsageCheck(attributeType, mustBeInheritable, derivedAttributes)) + if (!AttributeUsageCheck(attributeType, mustBeInheritable, ref derivedAttributes)) return false; // Windows Runtime attributes aren't real types - they exist to be read as metadata only, and as such @@ -1759,7 +1751,7 @@ private static unsafe bool FilterCustomAttributeRecord( #region Private Static Methods private static bool AttributeUsageCheck( - RuntimeType attributeType, bool mustBeInheritable, RuntimeType.ListBuilder derivedAttributes) + RuntimeType attributeType, bool mustBeInheritable, ref RuntimeType.ListBuilder derivedAttributes) { AttributeUsageAttribute attributeUsageAttribute = null; @@ -1859,8 +1851,14 @@ private static unsafe void GetPropertyOrFieldData( blobStart = (IntPtr)pBlobStart; } - private static object[] CreateAttributeArrayHelper(Type elementType, int elementCount) + private static object[] CreateAttributeArrayHelper(RuntimeType elementType, int elementCount) { + // If we have 0 elements, don't allocate a new array + if (elementCount == 0) + { + return elementType.GetEmptyArray(); + } + return (object[])Array.UnsafeCreateInstance(elementType, elementCount); } #endregion From b2ac5c6cf541f2c1e96a6c25294d6ca9efdad635 Mon Sep 17 00:00:00 2001 From: Nick Craver Date: Sat, 3 Nov 2018 17:01:28 -0400 Subject: [PATCH 7/7] Tidy up RuntimeType casts from object --- .../src/System/Reflection/CustomAttribute.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs b/src/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs index dfdbd329f1db..604425614cec 100644 --- a/src/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs +++ b/src/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs @@ -62,7 +62,7 @@ internal static IList GetCustomAttributesInternal(RuntimeTy IList cad = GetCustomAttributes(target.GetRuntimeModule(), target.MetadataToken); - Attribute[] a = PseudoCustomAttribute.GetCustomAttributes((RuntimeType)target, typeof(object) as RuntimeType, out int pcaCount); + Attribute[] a = PseudoCustomAttribute.GetCustomAttributes((RuntimeType)target, (RuntimeType)typeof(object), out int pcaCount); if (pcaCount == 0) return cad; @@ -83,7 +83,7 @@ internal static IList GetCustomAttributesInternal(RuntimeFi IList cad = GetCustomAttributes(target.GetRuntimeModule(), target.MetadataToken); - Attribute[] a = PseudoCustomAttribute.GetCustomAttributes((RuntimeFieldInfo)target, typeof(object) as RuntimeType, out int pcaCount); + Attribute[] a = PseudoCustomAttribute.GetCustomAttributes((RuntimeFieldInfo)target, (RuntimeType)typeof(object), out int pcaCount); if (pcaCount == 0) return cad; @@ -104,7 +104,7 @@ internal static IList GetCustomAttributesInternal(RuntimeMe IList cad = GetCustomAttributes(target.GetRuntimeModule(), target.MetadataToken); - Attribute[] a = PseudoCustomAttribute.GetCustomAttributes((RuntimeMethodInfo)target, typeof(object) as RuntimeType, out int pcaCount); + Attribute[] a = PseudoCustomAttribute.GetCustomAttributes((RuntimeMethodInfo)target, (RuntimeType)typeof(object), out int pcaCount); if (pcaCount == 0) return cad; @@ -156,7 +156,7 @@ internal static IList GetCustomAttributesInternal(RuntimeAs IList cad = GetCustomAttributes((RuntimeModule)target.ManifestModule, RuntimeAssembly.GetToken(target.GetNativeHandle())); - Attribute[] a = PseudoCustomAttribute.GetCustomAttributes(target, typeof(object) as RuntimeType, out int pcaCount); + Attribute[] a = PseudoCustomAttribute.GetCustomAttributes(target, (RuntimeType)typeof(object), out int pcaCount); if (pcaCount == 0) return cad; @@ -177,7 +177,7 @@ internal static IList GetCustomAttributesInternal(RuntimePa IList cad = GetCustomAttributes(target.GetRuntimeModule(), target.MetadataToken); - Attribute[] a = PseudoCustomAttribute.GetCustomAttributes(target, typeof(object) as RuntimeType, out int pcaCount); + Attribute[] a = PseudoCustomAttribute.GetCustomAttributes(target, (RuntimeType)typeof(object), out int pcaCount); if (pcaCount == 0) return cad; @@ -1279,7 +1279,7 @@ internal static object[] GetCustomAttributes(RuntimeType type, RuntimeType caTyp RuntimeType.ListBuilder result = new RuntimeType.ListBuilder(); bool mustBeInheritable = false; bool useObjectArray = (caType == null || caType.IsValueType || caType.ContainsGenericParameters); - RuntimeType arrayType = useObjectArray ? typeof(object) as RuntimeType : caType; + RuntimeType arrayType = useObjectArray ? (RuntimeType)typeof(object) : caType; while (pcaCount > 0) result.Add(pca[--pcaCount]); @@ -1325,7 +1325,7 @@ internal static object[] GetCustomAttributes(RuntimeMethodInfo method, RuntimeTy RuntimeType.ListBuilder result = new RuntimeType.ListBuilder(); bool mustBeInheritable = false; bool useObjectArray = (caType == null || caType.IsValueType || caType.ContainsGenericParameters); - RuntimeType arrayType = useObjectArray ? typeof(object) as RuntimeType : caType; + RuntimeType arrayType = useObjectArray ? (RuntimeType)typeof(object) : caType; while (pcaCount > 0) result.Add(pca[--pcaCount]); @@ -1499,7 +1499,7 @@ private static unsafe object[] GetCustomAttributes( CustomAttributeRecord[] car = CustomAttributeData.GetCustomAttributeRecords(decoratedModule, decoratedMetadataToken); bool useObjectArray = (attributeFilterType == null || attributeFilterType.IsValueType || attributeFilterType.ContainsGenericParameters); - RuntimeType arrayType = useObjectArray ? typeof(object) as RuntimeType : attributeFilterType; + RuntimeType arrayType = useObjectArray ? (RuntimeType)typeof(object) : attributeFilterType; if (attributeFilterType == null && car.Length == 0) return CreateAttributeArrayHelper(arrayType, 0);