1 //----------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //----------------------------------------------------------------
4 namespace System.Activities.Presentation.Internal.Metadata
7 using System.Collections.Generic;
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;
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.
25 internal static class AttributeDataCache
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;
33 // Note: we use Hashtables instead of Dictionaries because they are thread safe for
34 // read operations without the need for explicit locking.
36 // Hashtable of MemberInfos to their base MemberInfos, or null if there is no base MemberInfo
37 private static Hashtable _baseMemberMap = new Hashtable();
39 // Hashtable of attribute Types to their corresponding AttributeData classes
40 private static Hashtable _attributeDataCache = new Hashtable();
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();
46 // Used for thread safety
47 private static object _syncObject = new object();
49 // This table gets populated once at initialization, so there is no need for a Hashtable here
50 private static Dictionary<MemberTypes, GetBaseMemberCallback> _baseMemberFinders;
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()
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);
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
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)
74 object baseMember = _baseMemberMap[member];
75 if (baseMember == _noMemberInfo)
80 if (baseMember == null)
82 baseMember = CalculateBaseMemberInfo(member);
84 // With Hashtable we only need to lock on writes
87 _baseMemberMap[member] = baseMember ?? _noMemberInfo;
91 return (MemberInfo)baseMember;
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.
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)
111 foreach (AttributeTable table in tables)
113 if (table.ContainsAttributes(type))
115 IEnumerable attrEnum;
116 if (memberName == null)
118 attrEnum = table.GetCustomAttributes(type);
122 attrEnum = table.GetCustomAttributes(type, memberName);
125 foreach (object attr in attrEnum)
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.
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)
143 object[] attrs = member.GetCustomAttributes(false);
144 Fx.Assert(attrs != null, "It looks like GetCustomAttributes() CAN return null. Protect for it.");
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.
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)
159 AttributeData attrData = _attributeDataCache[attributeType] as AttributeData;
161 if (attrData == null)
163 attrData = new AttributeData(attributeType);
165 // With Hashtable we only need to lock on writes
168 _attributeDataCache[attributeType] = attrData;
176 // Tries to get the base MemberInfo associated with the specified
177 // member info, if any.
179 private static MemberInfo CalculateBaseMemberInfo(MemberInfo member)
181 Fx.Assert(member != null, "member parameter should not be null");
183 // Type is a special case that covers the majority of cases
184 Type type = member as Type;
187 return type.BaseType;
190 Type targetType = member.DeclaringType.BaseType;
193 _baseMemberFinders.ContainsKey(member.MemberType),
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.",
200 MemberInfo baseMemberInfo = null;
202 while (targetType != null && baseMemberInfo == null)
204 baseMemberInfo = _baseMemberFinders[member.MemberType](member, targetType);
205 targetType = targetType.BaseType;
208 return baseMemberInfo;
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
216 private static MemberInfo GetBaseConstructorInfo(MemberInfo info, Type targetType)
222 // Helper method that knows how to look up the base implementation of a virtual method.
224 private static MemberInfo GetBaseMethodInfo(MemberInfo info, Type targetType)
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);
232 // Helper method that knows how to look up the base implementation of a virtual property.
234 private static MemberInfo GetBasePropertyInfo(MemberInfo info, Type targetType)
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);
242 // Helper method that knows how to look up the base implementation of a virtual event.
244 private static MemberInfo GetBaseEventInfo(MemberInfo info, Type targetType)
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);
252 // Helper that converts ParamenterInfo[] into Type[]
254 private static Type[] ToTypeArray(ParameterInfo[] parameterInfo) {
255 if (parameterInfo == null)
260 Type[] parameterTypes = new Type[parameterInfo.Length];
261 for (int i = 0; i < parameterInfo.Length; i++)
263 parameterTypes[i] = parameterInfo[i].ParameterType;
266 return parameterTypes;
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);