Make TypeComponentsCache trimmable (#80726)
authorMichal Strehovský <MichalStrehovsky@users.noreply.github.com>
Thu, 19 Jan 2023 00:20:54 +0000 (09:20 +0900)
committerGitHub <noreply@github.com>
Thu, 19 Jan 2023 00:20:54 +0000 (09:20 +0900)
Contributes to #80165.

Dispensing of reflection member infos is done through a member policies class. This is a generic class that has specialized implementations for each kind of member info.

It used a clever trick to get to the specific implementations. Just access `MemberPolicies<EventInfo>.Default` to get one for events or `MemberPolicies<PropertyInfo>.Default` to get one for properties. It was also absolutely not trimming friendly.

This change removes the clever trick and replaces it with good old fashioned parameter passing.

15 files changed:
src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/BindingFlagSupport/ConstructorPolicies.cs
src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/BindingFlagSupport/EventPolicies.cs
src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/BindingFlagSupport/FieldPolicies.cs
src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/BindingFlagSupport/MemberPolicies.cs
src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/BindingFlagSupport/MethodPolicies.cs
src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/BindingFlagSupport/NestedTypePolicies.cs
src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/BindingFlagSupport/PropertyPolicies.cs
src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/BindingFlagSupport/QueriedMemberList.cs
src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/BindingFlagSupport/QueryResult.cs
src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/BindingFlagSupport/Shared.cs
src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/ReflectionCoreCallbacksImplementation.cs
src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/MethodInfos/RuntimeMethodInfo.cs
src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.BindingFlags.cs
src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.GetMember.cs
src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.TypeComponentsCache.cs

index ee55cf1..3bfe90d 100644 (file)
@@ -13,6 +13,10 @@ namespace System.Reflection.Runtime.BindingFlagSupport
     //==========================================================================================================================
     internal sealed class ConstructorPolicies : MemberPolicies<ConstructorInfo>
     {
+        public static readonly ConstructorPolicies Instance = new ConstructorPolicies();
+
+        public ConstructorPolicies() : base(MemberTypeIndex.Constructor) { }
+
         [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern",
             Justification = "Reflection implementation")]
         public sealed override IEnumerable<ConstructorInfo> GetDeclaredMembers(TypeInfo typeInfo)
index e8ba0e2..6c5bd52 100644 (file)
@@ -13,6 +13,10 @@ namespace System.Reflection.Runtime.BindingFlagSupport
     //==========================================================================================================================
     internal sealed class EventPolicies : MemberPolicies<EventInfo>
     {
+        public static readonly EventPolicies Instance = new EventPolicies();
+
+        public EventPolicies() : base(MemberTypeIndex.Event) { }
+
         [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern",
             Justification = "Reflection implementation")]
         public sealed override IEnumerable<EventInfo> GetDeclaredMembers(TypeInfo typeInfo)
@@ -67,7 +71,7 @@ namespace System.Reflection.Runtime.BindingFlagSupport
         {
             MethodInfo? baseAccessor = GetAccessorMethod(baseMember!);
             MethodInfo? derivedAccessor = GetAccessorMethod(derivedMember!);
-            return MemberPolicies<MethodInfo>.Default.ImplicitlyOverrides(baseAccessor, derivedAccessor);
+            return MethodPolicies.Instance.ImplicitlyOverrides(baseAccessor, derivedAccessor);
         }
 
         public sealed override bool OkToIgnoreAmbiguity(EventInfo m1, EventInfo m2)
index a1fe8f0..6447680 100644 (file)
@@ -13,6 +13,10 @@ namespace System.Reflection.Runtime.BindingFlagSupport
     //==========================================================================================================================
     internal sealed class FieldPolicies : MemberPolicies<FieldInfo>
     {
+        public static readonly FieldPolicies Instance = new FieldPolicies();
+
+        public FieldPolicies() : base(MemberTypeIndex.Field) { }
+
         [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern",
             Justification = "Reflection implementation")]
         public sealed override IEnumerable<FieldInfo> GetDeclaredMembers(TypeInfo typeInfo)
index 50a665e..e8c5ca8 100644 (file)
@@ -15,6 +15,11 @@ namespace System.Reflection.Runtime.BindingFlagSupport
     //=================================================================================================================
     internal abstract class MemberPolicies<M> where M : MemberInfo
     {
+        public MemberPolicies(int index)
+        {
+            Index = index;
+        }
+
         //=================================================================================================================
         // Subclasses for specific MemberInfo types must override these:
         //=================================================================================================================
@@ -184,55 +189,11 @@ namespace System.Reflection.Runtime.BindingFlagSupport
             return false;
         }
 
-        static MemberPolicies()
-        {
-            Type t = typeof(M);
-            if (t.Equals(typeof(FieldInfo)))
-            {
-                MemberTypeIndex = BindingFlagSupport.MemberTypeIndex.Field;
-                Default = (MemberPolicies<M>)(object)(new FieldPolicies());
-            }
-            else if (t.Equals(typeof(MethodInfo)))
-            {
-                MemberTypeIndex = BindingFlagSupport.MemberTypeIndex.Method;
-                Default = (MemberPolicies<M>)(object)(new MethodPolicies());
-            }
-            else if (t.Equals(typeof(ConstructorInfo)))
-            {
-                MemberTypeIndex = BindingFlagSupport.MemberTypeIndex.Constructor;
-                Default = (MemberPolicies<M>)(object)(new ConstructorPolicies());
-            }
-            else if (t.Equals(typeof(PropertyInfo)))
-            {
-                MemberTypeIndex = BindingFlagSupport.MemberTypeIndex.Property; ;
-                Default = (MemberPolicies<M>)(object)(new PropertyPolicies());
-            }
-            else if (t.Equals(typeof(EventInfo)))
-            {
-                MemberTypeIndex = BindingFlagSupport.MemberTypeIndex.Event;
-                Default = (MemberPolicies<M>)(object)(new EventPolicies());
-            }
-            else if (t.Equals(typeof(Type)))
-            {
-                MemberTypeIndex = BindingFlagSupport.MemberTypeIndex.NestedType;
-                Default = (MemberPolicies<M>)(object)(new NestedTypePolicies());
-            }
-            else
-            {
-                Debug.Fail("Unknown MemberInfo type.");
-            }
-        }
-
-        //
-        // This is a singleton class one for each MemberInfo category: Return the appropriate one.
-        //
-        public static readonly MemberPolicies<M> Default;
-
         //
         // This returns a fixed value from 0 to MemberIndex.Count-1 with each possible type of M
         // being assigned a unique index (see the MemberTypeIndex for possible values). This is useful
         // for converting a type reference to M to an array index or switch case label.
         //
-        public static readonly int MemberTypeIndex;
+        public int Index { get; }
     }
 }
index 040034c..b56f748 100644 (file)
@@ -13,6 +13,10 @@ namespace System.Reflection.Runtime.BindingFlagSupport
     //==========================================================================================================================
     internal sealed class MethodPolicies : MemberPolicies<MethodInfo>
     {
+        public static readonly MethodPolicies Instance = new MethodPolicies();
+
+        public MethodPolicies() : base(MemberTypeIndex.Method) { }
+
         [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern",
             Justification = "Reflection implementation")]
         public sealed override IEnumerable<MethodInfo> GetDeclaredMembers(TypeInfo typeInfo)
index 9fd791d..495ad6a 100644 (file)
@@ -23,6 +23,10 @@ namespace System.Reflection.Runtime.BindingFlagSupport
     //==========================================================================================================================
     internal sealed class NestedTypePolicies : MemberPolicies<Type>
     {
+        public static readonly NestedTypePolicies Instance = new NestedTypePolicies();
+
+        public NestedTypePolicies() : base(MemberTypeIndex.NestedType) { }
+
         [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern",
             Justification = "Reflection implementation")]
         public sealed override IEnumerable<Type> GetDeclaredMembers(TypeInfo typeInfo)
index c775204..c7eac9b 100644 (file)
@@ -13,6 +13,10 @@ namespace System.Reflection.Runtime.BindingFlagSupport
     //==========================================================================================================================
     internal sealed class PropertyPolicies : MemberPolicies<PropertyInfo>
     {
+        public static readonly PropertyPolicies Instance = new PropertyPolicies();
+
+        public PropertyPolicies() : base(MemberTypeIndex.Property) { }
+
         [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern",
             Justification = "Reflection implementation")]
         public sealed override IEnumerable<PropertyInfo> GetDeclaredMembers(TypeInfo typeInfo)
@@ -54,7 +58,7 @@ namespace System.Reflection.Runtime.BindingFlagSupport
         {
             MethodInfo? baseAccessor = GetAccessorMethod(baseMember!);
             MethodInfo? derivedAccessor = GetAccessorMethod(derivedMember!);
-            return MemberPolicies<MethodInfo>.Default.ImplicitlyOverrides(baseAccessor, derivedAccessor);
+            return MethodPolicies.Instance.ImplicitlyOverrides(baseAccessor, derivedAccessor);
         }
 
         //
index ed5cabd..ff09d8b 100644 (file)
@@ -96,12 +96,10 @@ namespace System.Reflection.Runtime.BindingFlagSupport
         //
         // Filter by name and visibility from the ReflectedType.
         //
-        public static QueriedMemberList<M> Create(RuntimeTypeInfo type, string optionalNameFilter, bool ignoreCase)
+        public static QueriedMemberList<M> Create(MemberPolicies<M> policies, RuntimeTypeInfo type, string optionalNameFilter, bool ignoreCase)
         {
             RuntimeTypeInfo reflectedType = type;
 
-            MemberPolicies<M> policies = MemberPolicies<M>.Default;
-
             NameFilter? nameFilter;
             if (optionalNameFilter == null)
                 nameFilter = null;
index 0a47c45..301fe25 100644 (file)
@@ -14,8 +14,9 @@ namespace System.Reflection.Runtime.BindingFlagSupport
     //
     internal partial struct QueryResult<M> where M : MemberInfo
     {
-        public QueryResult(BindingFlags bindingAttr, QueriedMemberList<M> queriedMembers)
+        public QueryResult(MemberPolicies<M> policies, BindingFlags bindingAttr, QueriedMemberList<M> queriedMembers)
         {
+            _policies = policies;
             _lazyCount = 0;
             _bindingAttr = bindingAttr;
             _queriedMembers = queriedMembers;
@@ -114,8 +115,7 @@ namespace System.Reflection.Runtime.BindingFlagSupport
                         if (match.DeclaringType.Equals(challenger.DeclaringType))
                             throw new AmbiguousMatchException();
 
-                        MemberPolicies<M> policies = MemberPolicies<M>.Default;
-                        if (!policies.OkToIgnoreAmbiguity(match, challenger))
+                        if (!_policies.OkToIgnoreAmbiguity(match, challenger))
                             throw new AmbiguousMatchException();
                     }
                     else
@@ -129,6 +129,7 @@ namespace System.Reflection.Runtime.BindingFlagSupport
 
         private int UnfilteredCount => ((_bindingAttr & BindingFlags.DeclaredOnly) != 0) ? _queriedMembers.DeclaredOnlyCount : _queriedMembers.TotalCount;
 
+        private readonly MemberPolicies<M> _policies;
         private readonly BindingFlags _bindingAttr;
         private int _lazyCount; // Intentionally not marking as volatile. QueryResult is for short-term use within a single method call - no aspiration to be thread-safe.
         private QueriedMemberList<M> _queriedMembers;
index 8d68023..27ad6e2 100644 (file)
@@ -146,9 +146,8 @@ namespace System.Reflection.Runtime.BindingFlagSupport
         // - MethodImpls ignored. (I didn't say it made sense, this is just how the desktop api we're porting behaves.)
         // - Implemented interfaces ignores. (I didn't say it made sense, this is just how the desktop api we're porting behaves.)
         //
-        public static M GetImplicitlyOverriddenBaseClassMember<M>(this M member) where M : MemberInfo
+        public static M GetImplicitlyOverriddenBaseClassMember<M>(this M member, MemberPolicies<M> policies) where M : MemberInfo
         {
-            MemberPolicies<M> policies = MemberPolicies<M>.Default;
             bool isVirtual;
             bool isNewSlot;
             policies.GetMemberAttributes(member, out _, out _, out isVirtual, out isNewSlot);
index eebda9c..fb97779 100644 (file)
@@ -146,17 +146,17 @@ namespace System.Reflection.Runtime.General
 
         public sealed override EventInfo GetImplicitlyOverriddenBaseClassEvent(EventInfo e)
         {
-            return e.GetImplicitlyOverriddenBaseClassMember();
+            return e.GetImplicitlyOverriddenBaseClassMember(EventPolicies.Instance);
         }
 
         public sealed override MethodInfo GetImplicitlyOverriddenBaseClassMethod(MethodInfo m)
         {
-            return m.GetImplicitlyOverriddenBaseClassMember();
+            return m.GetImplicitlyOverriddenBaseClassMember(MethodPolicies.Instance);
         }
 
         public sealed override PropertyInfo GetImplicitlyOverriddenBaseClassProperty(PropertyInfo p)
         {
-            return p.GetImplicitlyOverriddenBaseClassMember();
+            return p.GetImplicitlyOverriddenBaseClassMember(PropertyPolicies.Instance);
         }
 
         private static FieldInfo GetFieldInfo(RuntimeTypeHandle declaringTypeHandle, FieldHandle fieldHandle)
index f9abaea..1bacedb 100644 (file)
@@ -118,7 +118,7 @@ namespace System.Reflection.Runtime.MethodInfos
 
             while (true)
             {
-                MethodInfo next = method.GetImplicitlyOverriddenBaseClassMember();
+                MethodInfo next = method.GetImplicitlyOverriddenBaseClassMember(MethodPolicies.Instance);
                 if (next == null)
                     return ((RuntimeMethodInfo)method).WithReflectedTypeSetToDeclaringType;
 
index 562d2b3..dcb0ed3 100644 (file)
@@ -11,14 +11,14 @@ namespace System.Reflection.Runtime.TypeInfos
     internal abstract partial class RuntimeTypeInfo
     {
         [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)]
-        public sealed override ConstructorInfo[] GetConstructors(BindingFlags bindingAttr) => Query<ConstructorInfo>(bindingAttr).ToArray();
+        public sealed override ConstructorInfo[] GetConstructors(BindingFlags bindingAttr) => Query<ConstructorInfo>(ConstructorPolicies.Instance, bindingAttr).ToArray();
 
         [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)]
         protected sealed override ConstructorInfo GetConstructorImpl(BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers)
         {
             Debug.Assert(types != null);
 
-            QueryResult<ConstructorInfo> queryResult = Query<ConstructorInfo>(bindingAttr);
+            QueryResult<ConstructorInfo> queryResult = Query<ConstructorInfo>(ConstructorPolicies.Instance, bindingAttr);
             ListBuilder<ConstructorInfo> candidates = new ListBuilder<ConstructorInfo>();
             foreach (ConstructorInfo candidate in queryResult)
             {
@@ -47,19 +47,19 @@ namespace System.Reflection.Runtime.TypeInfos
         }
 
         [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents)]
-        public sealed override EventInfo[] GetEvents(BindingFlags bindingAttr) => Query<EventInfo>(bindingAttr).ToArray();
+        public sealed override EventInfo[] GetEvents(BindingFlags bindingAttr) => Query<EventInfo>(EventPolicies.Instance, bindingAttr).ToArray();
 
         [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents)]
-        public sealed override EventInfo GetEvent(string name, BindingFlags bindingAttr) => Query<EventInfo>(name, bindingAttr).Disambiguate();
+        public sealed override EventInfo GetEvent(string name, BindingFlags bindingAttr) => Query<EventInfo>(EventPolicies.Instance, name, bindingAttr).Disambiguate();
 
         [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)]
-        public sealed override FieldInfo[] GetFields(BindingFlags bindingAttr) => Query<FieldInfo>(bindingAttr).ToArray();
+        public sealed override FieldInfo[] GetFields(BindingFlags bindingAttr) => Query<FieldInfo>(FieldPolicies.Instance, bindingAttr).ToArray();
 
         [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)]
-        public sealed override FieldInfo GetField(string name, BindingFlags bindingAttr) => Query<FieldInfo>(name, bindingAttr).Disambiguate();
+        public sealed override FieldInfo GetField(string name, BindingFlags bindingAttr) => Query<FieldInfo>(FieldPolicies.Instance, name, bindingAttr).Disambiguate();
 
         [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)]
-        public sealed override MethodInfo[] GetMethods(BindingFlags bindingAttr) => Query<MethodInfo>(bindingAttr).ToArray();
+        public sealed override MethodInfo[] GetMethods(BindingFlags bindingAttr) => Query<MethodInfo>(MethodPolicies.Instance, bindingAttr).ToArray();
 
         [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)]
         protected sealed override MethodInfo GetMethodImpl(string name, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers)
@@ -85,12 +85,12 @@ namespace System.Reflection.Runtime.TypeInfos
                 Debug.Assert(binder == null);
                 Debug.Assert(callConvention == CallingConventions.Any);
                 Debug.Assert(modifiers == null);
-                return Query<MethodInfo>(name, bindingAttr).Disambiguate();
+                return Query<MethodInfo>(MethodPolicies.Instance, name, bindingAttr).Disambiguate();
             }
             else
             {
                 // Group #2: This group of api takes a set of parameter types and an optional binder.
-                QueryResult<MethodInfo> queryResult = Query<MethodInfo>(name, bindingAttr);
+                QueryResult<MethodInfo> queryResult = Query<MethodInfo>(MethodPolicies.Instance, name, bindingAttr);
                 ListBuilder<MethodInfo> candidates = new ListBuilder<MethodInfo>();
                 foreach (MethodInfo candidate in queryResult)
                 {
@@ -114,13 +114,13 @@ namespace System.Reflection.Runtime.TypeInfos
         }
 
         [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes)]
-        public sealed override Type[] GetNestedTypes(BindingFlags bindingAttr) => Query<Type>(bindingAttr).ToArray();
+        public sealed override Type[] GetNestedTypes(BindingFlags bindingAttr) => Query<Type>(NestedTypePolicies.Instance, bindingAttr).ToArray();
 
         [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes)]
-        public sealed override Type GetNestedType(string name, BindingFlags bindingAttr) => Query<Type>(name, bindingAttr).Disambiguate();
+        public sealed override Type GetNestedType(string name, BindingFlags bindingAttr) => Query<Type>(NestedTypePolicies.Instance, name, bindingAttr).Disambiguate();
 
         [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)]
-        public sealed override PropertyInfo[] GetProperties(BindingFlags bindingAttr) => Query<PropertyInfo>(bindingAttr).ToArray();
+        public sealed override PropertyInfo[] GetProperties(BindingFlags bindingAttr) => Query<PropertyInfo>(PropertyPolicies.Instance, bindingAttr).ToArray();
 
         [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)]
         protected sealed override PropertyInfo GetPropertyImpl(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers)
@@ -133,12 +133,12 @@ namespace System.Reflection.Runtime.TypeInfos
                 // Group #1: This group of api accept only a name and BindingFlags. The other parameters are hard-wired by the non-virtual api entrypoints.
                 Debug.Assert(binder == null);
                 Debug.Assert(modifiers == null);
-                return Query<PropertyInfo>(name, bindingAttr).Disambiguate();
+                return Query<PropertyInfo>(PropertyPolicies.Instance, name, bindingAttr).Disambiguate();
             }
             else
             {
                 // Group #2: This group of api takes a set of parameter types, a return type (both cannot be null) and an optional binder.
-                QueryResult<PropertyInfo> queryResult = Query<PropertyInfo>(name, bindingAttr);
+                QueryResult<PropertyInfo> queryResult = Query<PropertyInfo>(PropertyPolicies.Instance, name, bindingAttr);
                 ListBuilder<PropertyInfo> candidates = new ListBuilder<PropertyInfo>();
                 foreach (PropertyInfo candidate in queryResult)
                 {
@@ -179,33 +179,32 @@ namespace System.Reflection.Runtime.TypeInfos
             }
         }
 
-        private QueryResult<M> Query<M>(BindingFlags bindingAttr) where M : MemberInfo
+        private QueryResult<M> Query<M>(MemberPolicies<M> policies, BindingFlags bindingAttr) where M : MemberInfo
         {
-            return Query<M>(null, bindingAttr, null);
+            return Query<M>(policies, null, bindingAttr, null);
         }
 
-        private QueryResult<M> Query<M>(string name, BindingFlags bindingAttr) where M : MemberInfo
+        private QueryResult<M> Query<M>(MemberPolicies<M> policies, string name, BindingFlags bindingAttr) where M : MemberInfo
         {
             ArgumentNullException.ThrowIfNull(name);
-            return Query<M>(name, bindingAttr, null);
+            return Query<M>(policies, name, bindingAttr, null);
         }
 
-        private QueryResult<M> Query<M>(string optionalName, BindingFlags bindingAttr, Func<M, bool> optionalPredicate) where M : MemberInfo
+        private QueryResult<M> Query<M>(MemberPolicies<M> policies, string optionalName, BindingFlags bindingAttr, Func<M, bool> optionalPredicate) where M : MemberInfo
         {
-            MemberPolicies<M> policies = MemberPolicies<M>.Default;
             bindingAttr = policies.ModifyBindingFlags(bindingAttr);
             bool ignoreCase = (bindingAttr & BindingFlags.IgnoreCase) != 0;
 
             TypeComponentsCache cache = Cache;
             QueriedMemberList<M> queriedMembers;
             if (optionalName == null)
-                queriedMembers = cache.GetQueriedMembers<M>();
+                queriedMembers = cache.GetQueriedMembers(policies);
             else
-                queriedMembers = cache.GetQueriedMembers<M>(optionalName, ignoreCase: ignoreCase);
+                queriedMembers = cache.GetQueriedMembers<M>(policies, optionalName, ignoreCase: ignoreCase);
 
             if (optionalPredicate != null)
                 queriedMembers = queriedMembers.Filter(optionalPredicate);
-            return new QueryResult<M>(bindingAttr, queriedMembers);
+            return new QueryResult<M>(policies, bindingAttr, queriedMembers);
         }
 
         private TypeComponentsCache Cache => _lazyCache ??= new TypeComponentsCache(this);
index d992e97..d0c8292 100644 (file)
@@ -51,21 +51,21 @@ namespace System.Reflection.Runtime.TypeInfos
 
             MemberInfo[] results;
 
-            if ((results = QuerySpecificMemberTypeIfRequested(type, optionalName, bindingAttr, predicate, MemberTypes.Method, out methods)) != null)
+            if ((results = QuerySpecificMemberTypeIfRequested(MethodPolicies.Instance, type, optionalName, bindingAttr, predicate, MemberTypes.Method, out methods)) != null)
                 return results;
-            if ((results = QuerySpecificMemberTypeIfRequested(type, optionalName, bindingAttr, predicate, MemberTypes.Constructor, out constructors)) != null)
+            if ((results = QuerySpecificMemberTypeIfRequested(ConstructorPolicies.Instance, type, optionalName, bindingAttr, predicate, MemberTypes.Constructor, out constructors)) != null)
                 return results;
-            if ((results = QuerySpecificMemberTypeIfRequested(type, optionalName, bindingAttr, predicate, MemberTypes.Property, out properties)) != null)
+            if ((results = QuerySpecificMemberTypeIfRequested(PropertyPolicies.Instance, type, optionalName, bindingAttr, predicate, MemberTypes.Property, out properties)) != null)
                 return results;
-            if ((results = QuerySpecificMemberTypeIfRequested(type, optionalName, bindingAttr, predicate, MemberTypes.Event, out events)) != null)
+            if ((results = QuerySpecificMemberTypeIfRequested(EventPolicies.Instance, type, optionalName, bindingAttr, predicate, MemberTypes.Event, out events)) != null)
                 return results;
-            if ((results = QuerySpecificMemberTypeIfRequested(type, optionalName, bindingAttr, predicate, MemberTypes.Field, out fields)) != null)
+            if ((results = QuerySpecificMemberTypeIfRequested(FieldPolicies.Instance, type, optionalName, bindingAttr, predicate, MemberTypes.Field, out fields)) != null)
                 return results;
-            if ((results = QuerySpecificMemberTypeIfRequested(type, optionalName, bindingAttr, predicate, MemberTypes.NestedType, out nestedTypes)) != null)
+            if ((results = QuerySpecificMemberTypeIfRequested(NestedTypePolicies.Instance, type, optionalName, bindingAttr, predicate, MemberTypes.NestedType, out nestedTypes)) != null)
                 return results;
             if ((type & (MemberTypes.NestedType | MemberTypes.TypeInfo)) == MemberTypes.TypeInfo)
             {
-                if ((results = QuerySpecificMemberTypeIfRequested(type, optionalName, bindingAttr, predicate, MemberTypes.TypeInfo, out nestedTypes)) != null)
+                if ((results = QuerySpecificMemberTypeIfRequested(NestedTypePolicies.Instance, type, optionalName, bindingAttr, predicate, MemberTypes.TypeInfo, out nestedTypes)) != null)
                     return results;
             }
 
@@ -96,7 +96,7 @@ namespace System.Reflection.Runtime.TypeInfos
             return results;
         }
 
-        private M[] QuerySpecificMemberTypeIfRequested<M>(MemberTypes memberType, string optionalName, BindingFlags bindingAttr, Func<MemberInfo, bool> optionalPredicate, MemberTypes targetMemberType, out QueryResult<M> queryResult) where M : MemberInfo
+        private M[] QuerySpecificMemberTypeIfRequested<M>(MemberPolicies<M> policies, MemberTypes memberType, string optionalName, BindingFlags bindingAttr, Func<MemberInfo, bool> optionalPredicate, MemberTypes targetMemberType, out QueryResult<M> queryResult) where M : MemberInfo
         {
             if ((memberType & targetMemberType) == 0)
             {
@@ -105,7 +105,7 @@ namespace System.Reflection.Runtime.TypeInfos
                 return null;
             }
 
-            queryResult = Query<M>(optionalName, bindingAttr, optionalPredicate);
+            queryResult = Query<M>(policies, optionalName, bindingAttr, optionalPredicate);
 
             // Desktop compat: If exactly one type of member was requested, the returned array has to be of that specific type (M[], not MemberInfo[]). Create it now and return it
             // to cause GetMember() to short-cut the search.
@@ -137,19 +137,19 @@ namespace System.Reflection.Runtime.TypeInfos
         {
             return member.MemberType switch
             {
-                MemberTypes.Method => QueryMemberWithSameMetadataDefinitionAs<MethodInfo>(member),
-                MemberTypes.Constructor => QueryMemberWithSameMetadataDefinitionAs<ConstructorInfo>(member),
-                MemberTypes.Property => QueryMemberWithSameMetadataDefinitionAs<PropertyInfo>(member),
-                MemberTypes.Field => QueryMemberWithSameMetadataDefinitionAs<FieldInfo>(member),
-                MemberTypes.Event => QueryMemberWithSameMetadataDefinitionAs<EventInfo>(member),
-                MemberTypes.NestedType => QueryMemberWithSameMetadataDefinitionAs<Type>(member),
+                MemberTypes.Method => QueryMemberWithSameMetadataDefinitionAs(MethodPolicies.Instance, member),
+                MemberTypes.Constructor => QueryMemberWithSameMetadataDefinitionAs(ConstructorPolicies.Instance, member),
+                MemberTypes.Property => QueryMemberWithSameMetadataDefinitionAs(PropertyPolicies.Instance, member),
+                MemberTypes.Field => QueryMemberWithSameMetadataDefinitionAs(FieldPolicies.Instance, member),
+                MemberTypes.Event => QueryMemberWithSameMetadataDefinitionAs(EventPolicies.Instance, member),
+                MemberTypes.NestedType => QueryMemberWithSameMetadataDefinitionAs(NestedTypePolicies.Instance, member),
                 _ => null,
             };
         }
 
-        private M QueryMemberWithSameMetadataDefinitionAs<M>(MemberInfo member) where M : MemberInfo
+        private M QueryMemberWithSameMetadataDefinitionAs<M>(MemberPolicies<M> policies, MemberInfo member) where M : MemberInfo
         {
-            QueryResult<M> members = Query<M>(member.Name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
+            QueryResult<M> members = Query<M>(policies, member.Name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
             foreach (M candidate in members)
             {
                 if (candidate.HasSameMetadataDefinitionAs(member))
index eb38835..3e70a4a 100644 (file)
@@ -3,6 +3,7 @@
 
 using System.Threading;
 using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
 using System.Collections.Generic;
 using System.Collections.Concurrent;
 using System.Reflection;
@@ -43,12 +44,16 @@ namespace System.Reflection.Runtime.TypeInfos
             //
             //  BindingFlags == Public | NonPublic | Instance | Static | FlattenHierarchy
             //
-            public QueriedMemberList<M> GetQueriedMembers<M>(string name, bool ignoreCase) where M : MemberInfo
+            public QueriedMemberList<M> GetQueriedMembers<M>(MemberPolicies<M> policies, string name, bool ignoreCase) where M : MemberInfo
             {
-                int index = MemberPolicies<M>.MemberTypeIndex;
+                int index = policies.Index;
                 object obj = ignoreCase ? _perNameQueryCaches_CaseInsensitive[index] : _perNameQueryCaches_CaseSensitive[index];
                 Debug.Assert(obj is PerNameQueryCache<M>);
                 PerNameQueryCache<M> unifier = Unsafe.As<PerNameQueryCache<M>>(obj);
+
+                // Set the policies if they're not set yet. See the comment on SetPolicies on why we do this for details.
+                unifier.SetPolicies(policies);
+
                 QueriedMemberList<M> result = unifier.GetOrAdd(name);
                 return result;
             }
@@ -58,13 +63,13 @@ namespace System.Reflection.Runtime.TypeInfos
             //
             //  BindingFlags == Public | NonPublic | Instance | Static | FlattenHierarchy
             //
-            public QueriedMemberList<M> GetQueriedMembers<M>() where M : MemberInfo
+            public QueriedMemberList<M> GetQueriedMembers<M>(MemberPolicies<M> policies) where M : MemberInfo
             {
-                int index = MemberPolicies<M>.MemberTypeIndex;
+                int index = policies.Index;
                 object result = Volatile.Read(ref _nameAgnosticQueryCaches[index]);
                 if (result == null)
                 {
-                    QueriedMemberList<M> newResult = QueriedMemberList<M>.Create(_type, optionalNameFilter: null, ignoreCase: false);
+                    QueriedMemberList<M> newResult = QueriedMemberList<M>.Create(policies, _type, optionalNameFilter: null, ignoreCase: false);
                     newResult.Compact();
                     result = newResult;
                     Volatile.Write(ref _nameAgnosticQueryCaches[index], result);
@@ -118,13 +123,23 @@ namespace System.Reflection.Runtime.TypeInfos
                     _ignoreCase = ignoreCase;
                 }
 
+                // This looks like something that should have been a parameter to the constructor, but we do this on
+                // purpose - the PerNameQueryCache instances are created eagerly, but not all apps might require
+                // MemberPolicies for all members. This allows us to delay creating the MemberPolicies instance
+                // until the need arises.
+                public void SetPolicies(MemberPolicies<M> policies)
+                {
+                    _policies = policies;
+                }
+
                 protected sealed override QueriedMemberList<M> Factory(string key)
                 {
-                    QueriedMemberList<M> result = QueriedMemberList<M>.Create(_type, key, ignoreCase: _ignoreCase);
+                    QueriedMemberList<M> result = QueriedMemberList<M>.Create(_policies, _type, key, ignoreCase: _ignoreCase);
                     result.Compact();
                     return result;
                 }
 
+                private MemberPolicies<M> _policies;
                 private readonly RuntimeTypeInfo _type;
                 private readonly bool _ignoreCase;
             }