[reflection] Coop handles icalls in System.Reflection and System.RuntimeTypeHandle...
[mono.git] / mcs / class / referencesource / System.Activities.Presentation / System.Activities.Presentation / System / Activities / Presentation / Base / Core / Internal / Metadata / AttributeDataCache.cs
1 //----------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //----------------------------------------------------------------
4 namespace System.Activities.Presentation.Internal.Metadata 
5 {
6     using System;
7     using System.Collections.Generic;
8     using System.Text;
9     using System.Activities.Presentation.Metadata;
10     using System.Reflection;
11     using System.Diagnostics;
12     using System.Collections;
13     using System.Globalization;
14     using System.Diagnostics.CodeAnalysis;
15     using System.Runtime;
16
17     // <summary>
18     // Helper class that knows how to look up the base implementation of a given MemberInfo,
19     // as well as custom attributes from the MetadataStore or the CLR.  However, it does not
20     // actually cache those attributes.  We can add this functionality in the future if needed.
21     // On the other hand, this class does cache the map between attribute types and our internal
22     // AttributeData data structures that contain AttributeUsageAttributes so we don't have to keep
23     // looking them up via reflection.
24     // </summary>
25     internal static class AttributeDataCache 
26     {
27
28         // BindingFlags used for all GetInfo() types of calls
29         private static readonly BindingFlags _getInfoBindingFlags =
30             BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.NonPublic |
31             BindingFlags.Public | BindingFlags.Static;
32
33         // Note: we use Hashtables instead of Dictionaries because they are thread safe for
34         // read operations without the need for explicit locking.
35
36         // Hashtable of MemberInfos to their base MemberInfos, or null if there is no base MemberInfo
37         private static Hashtable _baseMemberMap = new Hashtable();
38
39         // Hashtable of attribute Types to their corresponding AttributeData classes
40         private static Hashtable _attributeDataCache = new Hashtable();
41
42         // Indicator for no MemberInfo in _baseMemberMap:  Null value means the base MemberInfo wasn't
43         // looked up yet, _noMemberInfo value means it was looked up but it doesn't exist.
44         private static object _noMemberInfo = new object();
45
46         // Used for thread safety
47         private static object _syncObject = new object();
48
49         // This table gets populated once at initialization, so there is no need for a Hashtable here
50         private static Dictionary<MemberTypes, GetBaseMemberCallback> _baseMemberFinders;
51
52         // Static Ctor to populate the lookup table for helper methods that know how
53         // to look up the base MemberInfo of a particular MemberType (ctor, method, event, ...)
54         [SuppressMessage(FxCop.Category.Performance, FxCop.Rule.InitializeReferenceTypeStaticFieldsInline)]
55         static AttributeDataCache() 
56         {
57             _baseMemberFinders = new Dictionary<MemberTypes, GetBaseMemberCallback>();
58             _baseMemberFinders[MemberTypes.Constructor] = new GetBaseMemberCallback(GetBaseConstructorInfo);
59             _baseMemberFinders[MemberTypes.Method] = new GetBaseMemberCallback(GetBaseMethodInfo);
60             _baseMemberFinders[MemberTypes.Property] = new GetBaseMemberCallback(GetBasePropertyInfo);
61             _baseMemberFinders[MemberTypes.Event] = new GetBaseMemberCallback(GetBaseEventInfo);
62         }
63
64         // <summary>
65         // Gets the base MemberInfo for the specified MemberInfo.  For types,
66         // the method returns the base type, if any.  For methods, events, and properties
67         // the method returns the base method, event, or property, if they exists, null
68         // otherwise.
69         // </summary>
70         // <param name="member">MemberInfo to look up in the base class</param>
71         // <returns>Specified MemberInfo in the base class if it exists, null otherwise.</returns>
72         internal static MemberInfo GetBaseMemberInfo(MemberInfo member) 
73         {
74             object baseMember = _baseMemberMap[member];
75             if (baseMember == _noMemberInfo)
76             {
77                 return null;
78             }
79
80             if (baseMember == null) 
81             {
82                 baseMember = CalculateBaseMemberInfo(member);
83
84                 // With Hashtable we only need to lock on writes
85                 lock (_syncObject) 
86                 {
87                     _baseMemberMap[member] = baseMember ?? _noMemberInfo;
88                 }
89             }
90
91             return (MemberInfo)baseMember;
92         }
93
94         // <summary>
95         // Looks up the specified MemberInfo in the custom MetadataStore AttributeTables
96         // and returns any attributes associated with it as an enumeration.  This method
97         // does not return any inherited attributes.
98         // </summary>
99         // <param name="type">Type to look up</param>
100         // <param name="memberName">Member name to look up.  If null, attributes associated
101         // with the type itself will be returned.</param>
102         // <param name="tables">AttributeTables to look in</param>
103         // <returns>Attributes in the AttributeTables associated with the specified
104         // Type and member name.</returns>
105         internal static IEnumerable<object> GetMetadataStoreAttributes(Type type, string memberName, AttributeTable[] tables) {
106             if (tables == null || tables.Length == 0)
107             {
108                 yield break;
109             }
110
111             foreach (AttributeTable table in tables) 
112             {
113                 if (table.ContainsAttributes(type)) 
114                 {
115                     IEnumerable attrEnum;
116                     if (memberName == null)
117                     {
118                         attrEnum = table.GetCustomAttributes(type);
119                     }
120                     else
121                     {
122                         attrEnum = table.GetCustomAttributes(type, memberName);
123                     }
124
125                     foreach (object attr in attrEnum) 
126                     {
127                         yield return attr;
128                     }
129                 }
130             }
131         }
132
133         // <summary>
134         // Looks up custom attributes for the specified MemberInfo in CLR via reflection
135         // and returns them as an enumeration.  This method does not return any
136         // inherited attributes.
137         // </summary>
138         // <param name="member">MemberInfo to look up</param>
139         // <returns>Custom Attributes associated with the specified
140         // MemberInfo in the CLR.</returns>
141         internal static IEnumerable<object> GetClrAttributes(MemberInfo member) 
142         {
143             object[] attrs = member.GetCustomAttributes(false);
144             Fx.Assert(attrs != null, "It looks like GetCustomAttributes() CAN return null.  Protect for it.");
145             return attrs;
146         }
147
148         // <summary>
149         // Gets an existing instance of AttributeData associated with the
150         // specified attribute Type, or creates a new one and caches it for
151         // later.  AttributeData is used as a cache for AttributeUsageAttributes
152         // so don't have to keep using reflection to get them.
153         // </summary>
154         // <param name="attributeType">Attribute type to look up</param>
155         // <returns>Instance of AttributeData associated with the specified
156         // attribute type.</returns>
157         internal static AttributeData GetAttributeData(Type attributeType) 
158         {
159             AttributeData attrData = _attributeDataCache[attributeType] as AttributeData;
160
161             if (attrData == null) 
162             {
163                 attrData = new AttributeData(attributeType);
164
165                 // With Hashtable we only need to lock on writes
166                 lock (_syncObject) 
167                 {
168                     _attributeDataCache[attributeType] = attrData;
169                 }
170             }
171
172             return attrData;
173         }
174
175         //
176         // Tries to get the base MemberInfo associated with the specified
177         // member info, if any.
178         //
179         private static MemberInfo CalculateBaseMemberInfo(MemberInfo member) 
180         {
181             Fx.Assert(member != null, "member parameter should not be null");
182
183             // Type is a special case that covers the majority of cases
184             Type type = member as Type;
185             if (type != null)
186             {
187                 return type.BaseType;
188             }
189
190             Type targetType = member.DeclaringType.BaseType;
191
192             Fx.Assert(
193                 _baseMemberFinders.ContainsKey(member.MemberType),
194                 string.Format(
195                 CultureInfo.CurrentCulture,
196                 "Didn't know how to look up the base MemberInfo for member type {0}. " +
197                 "Please update the list of known GetBaseInfoCallbacks in AttributeDataCache.",
198                 member.MemberType));
199
200             MemberInfo baseMemberInfo = null;
201
202             while (targetType != null && baseMemberInfo == null) 
203             {
204                 baseMemberInfo = _baseMemberFinders[member.MemberType](member, targetType);
205                 targetType = targetType.BaseType;
206             }
207
208             return baseMemberInfo;
209         }
210
211         //
212         // Helper method that knows how to look up the base constructor of a class.  However,
213         // since constructors can't derive from one another, this method always returns
214         // null.
215         // 
216         private static MemberInfo GetBaseConstructorInfo(MemberInfo info, Type targetType) 
217         {
218             return null;
219         }
220
221         //
222         // Helper method that knows how to look up the base implementation of a virtual method.
223         //
224         private static MemberInfo GetBaseMethodInfo(MemberInfo info, Type targetType) 
225         {
226             MethodInfo methodInfo = info as MethodInfo;
227             Fx.Assert(methodInfo != null, "It looks like MemberType did not match the type of MemberInfo: " + info.GetType().Name);
228             return targetType.GetMethod(methodInfo.Name, _getInfoBindingFlags, null, ToTypeArray(methodInfo.GetParameters()), null);
229         }
230
231         //
232         // Helper method that knows how to look up the base implementation of a virtual property.
233         //
234         private static MemberInfo GetBasePropertyInfo(MemberInfo info, Type targetType) 
235         {
236             PropertyInfo propInfo = info as PropertyInfo;
237             Fx.Assert(propInfo != null, "It looks like MemberType did not match the type of MemberInfo: " + info.GetType().Name);
238             return targetType.GetProperty(propInfo.Name, _getInfoBindingFlags, null, propInfo.PropertyType, ToTypeArray(propInfo.GetIndexParameters()), null);
239         }
240
241         //
242         // Helper method that knows how to look up the base implementation of a virtual event.
243         //
244         private static MemberInfo GetBaseEventInfo(MemberInfo info, Type targetType) 
245         {
246             EventInfo eventInfo = info as EventInfo;
247             Fx.Assert(eventInfo != null, "It looks like MemberType did not match the type of MemberInfo: " + info.GetType().Name);
248             return targetType.GetEvent(eventInfo.Name, _getInfoBindingFlags);
249         }
250
251         //
252         // Helper that converts ParamenterInfo[] into Type[]
253         //
254         private static Type[] ToTypeArray(ParameterInfo[] parameterInfo) {
255             if (parameterInfo == null)
256             {
257                 return null;
258             }
259
260             Type[] parameterTypes = new Type[parameterInfo.Length];
261             for (int i = 0; i < parameterInfo.Length; i++)
262             {
263                 parameterTypes[i] = parameterInfo[i].ParameterType;
264             }
265
266             return parameterTypes;
267         }
268
269         // Delegate used to call specific methods to get a base MemberInfo from the given MemberInfo
270         private delegate MemberInfo GetBaseMemberCallback(MemberInfo member, Type targetType);
271     }
272 }