//---------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. //---------------------------------------------------------------- namespace System.Activities.Presentation.Internal.Metadata { using System; using System.Collections.Generic; using System.Text; using System.Activities.Presentation.Metadata; using System.Reflection; using System.Diagnostics; using System.Collections; using System.Globalization; using System.Diagnostics.CodeAnalysis; using System.Runtime; // // Helper class that knows how to look up the base implementation of a given MemberInfo, // as well as custom attributes from the MetadataStore or the CLR. However, it does not // actually cache those attributes. We can add this functionality in the future if needed. // On the other hand, this class does cache the map between attribute types and our internal // AttributeData data structures that contain AttributeUsageAttributes so we don't have to keep // looking them up via reflection. // internal static class AttributeDataCache { // BindingFlags used for all GetInfo() types of calls private static readonly BindingFlags _getInfoBindingFlags = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static; // Note: we use Hashtables instead of Dictionaries because they are thread safe for // read operations without the need for explicit locking. // Hashtable of MemberInfos to their base MemberInfos, or null if there is no base MemberInfo private static Hashtable _baseMemberMap = new Hashtable(); // Hashtable of attribute Types to their corresponding AttributeData classes private static Hashtable _attributeDataCache = new Hashtable(); // Indicator for no MemberInfo in _baseMemberMap: Null value means the base MemberInfo wasn't // looked up yet, _noMemberInfo value means it was looked up but it doesn't exist. private static object _noMemberInfo = new object(); // Used for thread safety private static object _syncObject = new object(); // This table gets populated once at initialization, so there is no need for a Hashtable here private static Dictionary _baseMemberFinders; // Static Ctor to populate the lookup table for helper methods that know how // to look up the base MemberInfo of a particular MemberType (ctor, method, event, ...) [SuppressMessage(FxCop.Category.Performance, FxCop.Rule.InitializeReferenceTypeStaticFieldsInline)] static AttributeDataCache() { _baseMemberFinders = new Dictionary(); _baseMemberFinders[MemberTypes.Constructor] = new GetBaseMemberCallback(GetBaseConstructorInfo); _baseMemberFinders[MemberTypes.Method] = new GetBaseMemberCallback(GetBaseMethodInfo); _baseMemberFinders[MemberTypes.Property] = new GetBaseMemberCallback(GetBasePropertyInfo); _baseMemberFinders[MemberTypes.Event] = new GetBaseMemberCallback(GetBaseEventInfo); } // // Gets the base MemberInfo for the specified MemberInfo. For types, // the method returns the base type, if any. For methods, events, and properties // the method returns the base method, event, or property, if they exists, null // otherwise. // // MemberInfo to look up in the base class // Specified MemberInfo in the base class if it exists, null otherwise. internal static MemberInfo GetBaseMemberInfo(MemberInfo member) { object baseMember = _baseMemberMap[member]; if (baseMember == _noMemberInfo) { return null; } if (baseMember == null) { baseMember = CalculateBaseMemberInfo(member); // With Hashtable we only need to lock on writes lock (_syncObject) { _baseMemberMap[member] = baseMember ?? _noMemberInfo; } } return (MemberInfo)baseMember; } // // Looks up the specified MemberInfo in the custom MetadataStore AttributeTables // and returns any attributes associated with it as an enumeration. This method // does not return any inherited attributes. // // Type to look up // Member name to look up. If null, attributes associated // with the type itself will be returned. // AttributeTables to look in // Attributes in the AttributeTables associated with the specified // Type and member name. internal static IEnumerable GetMetadataStoreAttributes(Type type, string memberName, AttributeTable[] tables) { if (tables == null || tables.Length == 0) { yield break; } foreach (AttributeTable table in tables) { if (table.ContainsAttributes(type)) { IEnumerable attrEnum; if (memberName == null) { attrEnum = table.GetCustomAttributes(type); } else { attrEnum = table.GetCustomAttributes(type, memberName); } foreach (object attr in attrEnum) { yield return attr; } } } } // // Looks up custom attributes for the specified MemberInfo in CLR via reflection // and returns them as an enumeration. This method does not return any // inherited attributes. // // MemberInfo to look up // Custom Attributes associated with the specified // MemberInfo in the CLR. internal static IEnumerable GetClrAttributes(MemberInfo member) { object[] attrs = member.GetCustomAttributes(false); Fx.Assert(attrs != null, "It looks like GetCustomAttributes() CAN return null. Protect for it."); return attrs; } // // Gets an existing instance of AttributeData associated with the // specified attribute Type, or creates a new one and caches it for // later. AttributeData is used as a cache for AttributeUsageAttributes // so don't have to keep using reflection to get them. // // Attribute type to look up // Instance of AttributeData associated with the specified // attribute type. internal static AttributeData GetAttributeData(Type attributeType) { AttributeData attrData = _attributeDataCache[attributeType] as AttributeData; if (attrData == null) { attrData = new AttributeData(attributeType); // With Hashtable we only need to lock on writes lock (_syncObject) { _attributeDataCache[attributeType] = attrData; } } return attrData; } // // Tries to get the base MemberInfo associated with the specified // member info, if any. // private static MemberInfo CalculateBaseMemberInfo(MemberInfo member) { Fx.Assert(member != null, "member parameter should not be null"); // Type is a special case that covers the majority of cases Type type = member as Type; if (type != null) { return type.BaseType; } Type targetType = member.DeclaringType.BaseType; Fx.Assert( _baseMemberFinders.ContainsKey(member.MemberType), string.Format( CultureInfo.CurrentCulture, "Didn't know how to look up the base MemberInfo for member type {0}. " + "Please update the list of known GetBaseInfoCallbacks in AttributeDataCache.", member.MemberType)); MemberInfo baseMemberInfo = null; while (targetType != null && baseMemberInfo == null) { baseMemberInfo = _baseMemberFinders[member.MemberType](member, targetType); targetType = targetType.BaseType; } return baseMemberInfo; } // // Helper method that knows how to look up the base constructor of a class. However, // since constructors can't derive from one another, this method always returns // null. // private static MemberInfo GetBaseConstructorInfo(MemberInfo info, Type targetType) { return null; } // // Helper method that knows how to look up the base implementation of a virtual method. // private static MemberInfo GetBaseMethodInfo(MemberInfo info, Type targetType) { MethodInfo methodInfo = info as MethodInfo; Fx.Assert(methodInfo != null, "It looks like MemberType did not match the type of MemberInfo: " + info.GetType().Name); return targetType.GetMethod(methodInfo.Name, _getInfoBindingFlags, null, ToTypeArray(methodInfo.GetParameters()), null); } // // Helper method that knows how to look up the base implementation of a virtual property. // private static MemberInfo GetBasePropertyInfo(MemberInfo info, Type targetType) { PropertyInfo propInfo = info as PropertyInfo; Fx.Assert(propInfo != null, "It looks like MemberType did not match the type of MemberInfo: " + info.GetType().Name); return targetType.GetProperty(propInfo.Name, _getInfoBindingFlags, null, propInfo.PropertyType, ToTypeArray(propInfo.GetIndexParameters()), null); } // // Helper method that knows how to look up the base implementation of a virtual event. // private static MemberInfo GetBaseEventInfo(MemberInfo info, Type targetType) { EventInfo eventInfo = info as EventInfo; Fx.Assert(eventInfo != null, "It looks like MemberType did not match the type of MemberInfo: " + info.GetType().Name); return targetType.GetEvent(eventInfo.Name, _getInfoBindingFlags); } // // Helper that converts ParamenterInfo[] into Type[] // private static Type[] ToTypeArray(ParameterInfo[] parameterInfo) { if (parameterInfo == null) { return null; } Type[] parameterTypes = new Type[parameterInfo.Length]; for (int i = 0; i < parameterInfo.Length; i++) { parameterTypes[i] = parameterInfo[i].ParameterType; } return parameterTypes; } // Delegate used to call specific methods to get a base MemberInfo from the given MemberInfo private delegate MemberInfo GetBaseMemberCallback(MemberInfo member, Type targetType); } }