summaryrefslogtreecommitdiffstats
path: root/tools/Sandcastle/Source/Reflection/Reflection.cs
blob: e646cc6d094e5fb8ae6462672b6551371615d615 (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
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
// Copyright (c) Microsoft Corporation.  All rights reserved.
//

using System;
using System.Collections.Generic;

using System.Compiler;

namespace Microsoft.Ddue.Tools.Reflection {

    public static class ReflectionUtilities {

        public static Event[] GetImplementedEvents(Event trigger) {
            List < Event > list = new List < Event >();

            // get the adder
            Method adder = trigger.HandlerAdder;

            // get interface methods corresponding to this adder
            Method[] implementedAdders = GetImplementedMethods(adder);

            // get the events corresponding to the implemented adders
            foreach (Method implementedAdder in implementedAdders) {
                Event implementedTrigger = GetEventFromAdder(implementedAdder);
                if (implementedTrigger != null) list.Add(implementedTrigger);
            }

            return (list.ToArray());

        }

        public static Method[] GetImplementedMethods(Method method) {
            List < Method > list = new List < Method >();

            // Explicit implementations
            MethodList explicitImplementations = method.ImplementedInterfaceMethods;
            if (explicitImplementations != null) {
                for (int i = 0; i < explicitImplementations.Count; i++) {
                    Method explicitImplementation = explicitImplementations[i];
                    list.Add(explicitImplementation);
                }
            }

            // Implicit implementations
            MethodList implicitImplementations = method.ImplicitlyImplementedInterfaceMethods;
            if (implicitImplementations != null) {
                for (int i = 0; i < implicitImplementations.Count; i++) {
                    Method implicitImplementation = implicitImplementations[i];
                    list.Add(implicitImplementation);
                }
            }

            return (list.ToArray());
        }

        public static Property[] GetImplementedProperties(Property property) {
            List < Property > list = new List < Property >();

            // get an accessor
            Method accessor = property.Getter;
            if (accessor == null) accessor = property.Setter;
            if (accessor == null) return (new Property[0]);

            // get the interface methods corresponding to this accessor
            Method[] methods = GetImplementedMethods(accessor);

            // look for properties corresponding to these methods
            for (int i = 0; i < methods.Length; i++) {
                Method method = methods[i];
                Property entry = GetPropertyFromAccessor(method);
                if (entry != null) list.Add(entry);
            }

            return (list.ToArray());
        }

        public static Namespace GetNamespace(TypeNode type) {
            if (type.DeclaringType != null) {
                return (GetNamespace(type.DeclaringType));
            } else {
                return (new Namespace(type.Namespace));
            }
        }

        public static Member GetTemplateMember(Member member) {

            if (member == null) throw new ArgumentNullException("member");

            // if the containing type isn't generic, the member is the template member
            TypeNode type = member.DeclaringType;
            if (!type.IsGeneric) return (member);

            // if the containing type isn't specialized, the member is the template member
            if (!IsSpecialized(type)) return (member);

            // get the template type, and look for members with the same name
            TypeNode template = ReflectionUtilities.GetTemplateType(member.DeclaringType);
            Identifier name = member.Name;
            MemberList candidates = template.GetMembersNamed(name);

            // if no candidates, say so (this shouldn't happen)
            if (candidates.Count == 0) throw new InvalidOperationException("No members in the template had the name found in the specialization. This is not possible, but apparently it happened.");

            // if only one candidate, return it
            if (candidates.Count == 1) return (candidates[0]);

            // multiple candidates, so now we need to compare parameters
            ParameterList parameters = GetParameters(member);

            for (int i = 0; i < candidates.Count; i++) {
                Member candidate = candidates[i];

                // candidate must be same kind of node
                if (candidate.NodeType != member.NodeType) continue;

                // if parameters match, this is the one
                if (ParametersMatch(parameters, GetParameters(candidate))) return (candidate);

            }

            Console.WriteLine(member.DeclaringType.FullName);
            Console.WriteLine(member.FullName);
            throw new InvalidOperationException("No members in the template matched the parameters of the specialization. This is not possible.");
        }

        public static TypeNode GetTemplateType(TypeNode type) {

            if (type == null) throw new ArgumentNullException("type");
            // Console.WriteLine(type.FullName);

            // only generic types have templates
            if (!type.IsGeneric) return (type);

            if (type.DeclaringType == null) {
                // if the type is not nested, life is simpler

                // if the type is not specified, the type is the template
                if (type.TemplateArguments == null) return (type);

                // otherwise, construct the template type identifier and use it to fetch the template type
                Module templateModule = type.DeclaringModule;
                Identifier name = new Identifier(String.Format("{0}`{1}", type.GetUnmangledNameWithoutTypeParameters(), type.TemplateArguments.Count));
                Identifier space = type.Namespace;
                TypeNode template = templateModule.GetType(space, name);
                return (template);
            } else {
                // if the type is nested, life is harder; we have to walk up the chain, constructing
                // un-specialized identifiers as we go, then walk back down the chain, fetching
                // template types as we go

                // create a stack to keep track of identifiers
                Stack < Identifier > identifiers = new Stack < Identifier >();

                // populate the stack with the identifiers of all the types up to the outermost type
                TypeNode current = type;
                while (true) {
                    int count = 0;
                    if ((current.TemplateArguments != null) && (current.TemplateArguments.Count > count)) count = current.TemplateArguments.Count;
                    if ((current.TemplateParameters != null) && (current.TemplateParameters.Count > count)) count = current.TemplateParameters.Count;
                    TypeNodeList arguments = current.TemplateParameters;
                    if (count == 0) {
                        identifiers.Push(new Identifier(current.GetUnmangledNameWithoutTypeParameters()));
                    } else {
                        identifiers.Push(new Identifier(String.Format("{0}`{1}", current.GetUnmangledNameWithoutTypeParameters(), count)));
                    }
                    // Console.WriteLine("U {0} {1}", identifiers.Peek(), CountArguments(current));
                    if (current.DeclaringType == null) break;
                    current = current.DeclaringType;
                }

                // fetch a TypeNode representing that outermost type
                Module module = current.DeclaringModule;
                Identifier space = current.Namespace;
                current = module.GetType(space, identifiers.Pop());

                // move down the stack to the inner type we want
                while (identifiers.Count > 0) {
                    current = (TypeNode)current.GetMembersNamed(identifiers.Pop())[0];
                    // Console.WriteLine("D {0} {1}", current.GetFullUnmangledNameWithTypeParameters(), CountArguments(current));
                }

                // whew, finally we've got it
                return (current);
            }

        }

        public static bool IsDefaultMember(Member member) {

            if (member == null) throw new ArgumentNullException("member");
            TypeNode type = member.DeclaringType;

            MemberList defaultMembers = type.DefaultMembers;
            for (int i = 0; i < defaultMembers.Count; i++) {
                Member defaultMember = defaultMembers[i];
                if (member == defaultMember) return (true);
            }
            return (false);

        }

        private static Event GetEventFromAdder(Method adder) {
            if (adder == null) throw new ArgumentNullException("adder");
            TypeNode type = adder.DeclaringType;
            MemberList members = type.Members;
            foreach (Member member in members) {
                if (member.NodeType != NodeType.Event) continue;
                Event trigger = member as Event;
                if (trigger.HandlerAdder == adder) return (trigger);
            }
            return (null);
        }

        private static ParameterList GetParameters(Member member) {
            Method method = member as Method;
            if (method != null) return (method.Parameters);

            Property property = member as Property;
            if (property != null) return (property.Parameters);

            return (new ParameterList());
        }

        private static Property GetPropertyFromAccessor(Method accessor) {
            if (accessor == null) throw new ArgumentNullException("accessor");
            TypeNode type = accessor.DeclaringType;
            MemberList members = type.Members;
            foreach (Member member in members) {
                if (member.NodeType != NodeType.Property) continue;
                Property property = member as Property;
                if (property.Getter == accessor) return (property);
                if (property.Setter == accessor) return (property);
            }
            return (null);
        }

        private static bool IsSpecialized(TypeNode type) {
            for (TypeNode t = type; t != null; t = t.DeclaringType) {
                TypeNodeList templates = t.TemplateArguments;
                if ((templates != null) && (templates.Count > 0)) return (true);
            }
            return (false);
        }

        // parameters1 should be fully specialized; parameters2 

        private static bool ParametersMatch(ParameterList parameters1, ParameterList parameters2) {

            if (parameters1.Count != parameters2.Count) return (false);

            for (int i = 0; i < parameters1.Count; i++) {
                TypeNode type1 = parameters1[i].Type;
                TypeNode type2 = parameters2[i].Type;

                // we can't determine the equivilence of template parameters; this is probably not good
                if (type1.IsTemplateParameter || type2.IsTemplateParameter) continue;

                // the node type must be the same; this is probably a fast check
                if (type1.NodeType != type2.NodeType) return (false);

                // if they are "normal" types, we will compare them
                // comparing arrays, pointers, etc. is dangerous, because the types they contian may be template parameters
                if ((type1.NodeType == NodeType.Class) || (type1.NodeType == NodeType.Struct) || (type1.NodeType == NodeType.Interface) ||
                    (type1.NodeType == NodeType.EnumNode) || (type1.NodeType == NodeType.DelegateNode)) {
                    type1 = GetTemplateType(type1);
                    type2 = GetTemplateType(type2);
                    if (!type2.IsStructurallyEquivalentTo(type1)) {
                        // Console.WriteLine("{0} !~ {1}", type1.FullName, type2.FullName);
                        return (false);
                    } else {
                        // Console.WriteLine("{0} ~ {1}", type1.FullName, type2.FullName);
                    }
                }

            }

            return (true);
        }

        private static bool TypeMatch(TypeNode type1, TypeNode type2) {

            // the two types must be of the same kind
            if (type1.NodeType != type2.NodeType) return (false);

            if (type1.NodeType == NodeType.ArrayType) {
                // they are arrays, so check elements
                ArrayType array1 = (ArrayType)type1;
                ArrayType array2 = (ArrayType)type2;
                return (TypeMatch(array1.ElementType, array2.ElementType));
            } else {
                // they are normal types
                return (type1.IsStructurallyEquivalentTo(type2));
            }
        }

    }

}