Welcome to mirror list, hosted at ThFree Co, Russian Federation.

QueriedMemberList.cs « BindingFlagSupport « Runtime « Reflection « System « src « System.Private.Reflection.Core « src - github.com/mono/corert.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 2da7ffe58cfc6fab6cb32df2c483f2f3aefc50e8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Reflection.Runtime.General;
using System.Reflection.Runtime.TypeInfos;

using Internal.Reflection.Core.Execution;

namespace System.Reflection.Runtime.BindingFlagSupport
{
    //
    // Stores the result of a member filtering that's filtered by name and visibility from base class (as defined by the Type.Get*() family of apis).
    //
    // The results are as if you'd passed in a bindingFlags value of "Public | NonPublic | Instance | Static | FlattenHierarchy"
    // In addition, if "ignoreCase" was passed to Create(), BindingFlags.IgnoreCase is also in effect.
    //
    // Results are sorted by declaring type. The members declared by the most derived type appear first, then those declared by his base class, and so on.
    // The Disambiguation logic takes advantage of this.
    //
    // This object is a good candidate for long term caching.
    //
    internal sealed class QueriedMemberList<M> where M : MemberInfo
    {
        private QueriedMemberList()
        {
            _members = new M[Grow];
            _allFlagsThatMustMatch = new BindingFlags[Grow];
        }

        private QueriedMemberList(int totalCount, int declaredOnlyCount, M[] members, BindingFlags[] allFlagsThatMustMatch, RuntimeTypeInfo typeThatBlockedBrowsing)
        {
            _totalCount = totalCount;
            _declaredOnlyCount = declaredOnlyCount;
            _members = members;
            _allFlagsThatMustMatch = allFlagsThatMustMatch;
            _typeThatBlockedBrowsing = typeThatBlockedBrowsing;
        }

        /// <summary>
        /// Returns the # of candidates for a non-DeclaredOnly search. Caution: Can throw MissingMetadataException. Use DeclaredOnlyCount if you don't want to search base classes.
        /// </summary>
        public int TotalCount
        {
            get
            {
                if (_typeThatBlockedBrowsing != null)
                    throw ReflectionCoreExecution.ExecutionDomain.CreateMissingMetadataException(_typeThatBlockedBrowsing);
                return _totalCount;
            }
        }

        /// <summary>
        /// Returns the # of candidates for a DeclaredOnly search
        /// </summary>
        public int DeclaredOnlyCount => _declaredOnlyCount;

        public M this[int index]
        {
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            get
            {
                Debug.Assert(index >= 0 && index < _totalCount);
                return _members[index];
            }
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool Matches(int index, BindingFlags bindingAttr)
        {
            Debug.Assert(index >= 0 && index < _totalCount);
            BindingFlags allFlagsThatMustMatch = _allFlagsThatMustMatch[index];
            return ((bindingAttr & allFlagsThatMustMatch) == allFlagsThatMustMatch);
        }

        public QueriedMemberList<M> Filter(Func<M, bool> predicate)
        {
            BindingFlags[] newAllFlagsThatMustMatch = new BindingFlags[_totalCount];
            M[] newMembers = new M[_totalCount];
            int newDeclaredOnlyCount = 0;
            int newTotalCount = 0;
            for (int i = 0; i < _totalCount; i++)
            {
                M member = _members[i];
                if (predicate(member))
                {
                    newMembers[newTotalCount] = member;
                    newAllFlagsThatMustMatch[newTotalCount] = _allFlagsThatMustMatch[i];
                    newTotalCount++;
                    if (i < _declaredOnlyCount)
                        newDeclaredOnlyCount++;
                }
            }

            return new QueriedMemberList<M>(newTotalCount, newDeclaredOnlyCount, newMembers, newAllFlagsThatMustMatch, _typeThatBlockedBrowsing);
        }

        //
        // Filter by name and visibility from the ReflectedType.
        //
        public static QueriedMemberList<M> Create(RuntimeTypeInfo type, string optionalNameFilter, bool ignoreCase)
        {
            RuntimeTypeInfo reflectedType = type;

            MemberPolicies<M> policies = MemberPolicies<M>.Default;

            NameFilter nameFilter;
            if (optionalNameFilter == null)
                nameFilter = null;
            else if (ignoreCase)
                nameFilter = new NameFilterCaseInsensitive(optionalNameFilter);
            else
                nameFilter = new NameFilterCaseSensitive(optionalNameFilter);

            bool inBaseClass = false;
            QueriedMemberList<M> queriedMembers = new QueriedMemberList<M>();
            while (type != null)
            {
                int numCandidatesInDerivedTypes = queriedMembers._totalCount;

                foreach (M member in policies.CoreGetDeclaredMembers(type, nameFilter, reflectedType))
                {
                    MethodAttributes visibility;
                    bool isStatic;
                    bool isVirtual;
                    bool isNewSlot;
                    policies.GetMemberAttributes(member, out visibility, out isStatic, out isVirtual, out isNewSlot);

                    if (inBaseClass && visibility == MethodAttributes.Private)
                        continue;

                    if (numCandidatesInDerivedTypes != 0 && policies.IsSuppressedByMoreDerivedMember(member, queriedMembers._members, startIndex: 0, endIndex: numCandidatesInDerivedTypes))
                        continue;

                    BindingFlags allFlagsThatMustMatch = default(BindingFlags);
                    allFlagsThatMustMatch |= (isStatic ? BindingFlags.Static : BindingFlags.Instance);
                    if (isStatic && inBaseClass)
                        allFlagsThatMustMatch |= BindingFlags.FlattenHierarchy;
                    allFlagsThatMustMatch |= ((visibility == MethodAttributes.Public) ? BindingFlags.Public : BindingFlags.NonPublic);

                    queriedMembers.Add(member, allFlagsThatMustMatch);
                }

                if (!inBaseClass)
                {
                    queriedMembers._declaredOnlyCount = queriedMembers._totalCount;
                    if (policies.AlwaysTreatAsDeclaredOnly)
                        break;
                    inBaseClass = true;
                }

                type = type.BaseType.CastToRuntimeTypeInfo();

                if (type != null && !type.CanBrowseWithoutMissingMetadataExceptions)
                {
                    // If we got here, one of the base classes is missing metadata. We don't want to throw a MissingMetadataException now because we may be 
                    // building a cached result for a caller who passed BindingFlags.DeclaredOnly. So we'll mark the results in a way that 
                    // it will throw a MissingMetadataException if a caller attempts to iterate past the declared-only subset.
                    queriedMembers._typeThatBlockedBrowsing = type;
                    queriedMembers._totalCount = queriedMembers._declaredOnlyCount;
                    break;
                }
            }

            return queriedMembers;
        }

        public void Compact()
        {
            Array.Resize(ref _members, _totalCount);
            Array.Resize(ref _allFlagsThatMustMatch, _totalCount);
        }

        private void Add(M member, BindingFlags allFlagsThatMustMatch)
        {
            const BindingFlags validBits = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy;
            Debug.Assert((allFlagsThatMustMatch & ~validBits) == 0);
            Debug.Assert(((allFlagsThatMustMatch & BindingFlags.Public) == 0) != ((allFlagsThatMustMatch & BindingFlags.NonPublic) == 0));
            Debug.Assert(((allFlagsThatMustMatch & BindingFlags.Instance) == 0) != ((allFlagsThatMustMatch & BindingFlags.Static) == 0));
            Debug.Assert((allFlagsThatMustMatch & BindingFlags.FlattenHierarchy) == 0 || (allFlagsThatMustMatch & BindingFlags.Static) != 0);

            int count = _totalCount;
            if (count == _members.Length)
            {
                Array.Resize(ref _members, count + Grow);
                Array.Resize(ref _allFlagsThatMustMatch, count + Grow);
            }

            _members[count] = member;
            _allFlagsThatMustMatch[count] = allFlagsThatMustMatch;
            _totalCount++;
        }

        private int _totalCount; // # of entries including members in base classes.
        private int _declaredOnlyCount; // # of entries for members only in the most derived class.
        private M[] _members;  // Length is equal to or greater than _totalCount. Entries beyond _totalCount contain null or garbage and should be read.
        private BindingFlags[] _allFlagsThatMustMatch; // Length will be equal to _members.Length
        private RuntimeTypeInfo _typeThatBlockedBrowsing; // If non-null, one of the base classes was missing metadata.

        private const int Grow = 64;
    }
}