using System; using System.Collections.Generic; using System.Collections.Concurrent; using System.ComponentModel; using System.ComponentModel.Design; using System.Linq; using System.Web; using System.Web.Compilation; using System.Web.Util; using System.Reflection; namespace System.Web.UI { // Helper class to retrieve filtered members from the target framework type using TargetFrameworkProvider. // We need to be careful not to expose faux/LMR types or memberInfo as they don't work properly when mixed // with their runtime counter parts. internal static class TargetFrameworkUtil { private class MemberCache { private ConcurrentDictionary _events; private ConcurrentDictionary, FieldInfo> _fields; private ConcurrentDictionary, PropertyInfo> _properties; internal MemberCache() { } internal ConcurrentDictionary Events { get { if (_events == null) { _events = new ConcurrentDictionary(); } return _events; } } internal ConcurrentDictionary, FieldInfo> Fields { get { if (_fields == null) { _fields = new ConcurrentDictionary, FieldInfo>(); } return _fields; } } internal ConcurrentDictionary, PropertyInfo> Properties { get { if (_properties == null) { _properties = new ConcurrentDictionary, PropertyInfo>(); } return _properties; } } } private static ConcurrentDictionary s_memberCache = new ConcurrentDictionary(); private static ClientBuildManagerTypeDescriptionProviderBridge s_cbmTdpBridge; private static ConcurrentDictionary s_typePropertyDescriptorCollectionDict = new ConcurrentDictionary(); private static ConcurrentDictionary s_objectPropertyDescriptorCollectionDict = new ConcurrentDictionary(); private static ConcurrentDictionary s_eventDescriptorCollectionDict = new ConcurrentDictionary(); private static ConcurrentDictionary s_isFrameworkType = new ConcurrentDictionary(); private static MemberCache GetMemberCache(Type type){ MemberCache memberCache = null; if (!s_memberCache.TryGetValue(type, out memberCache)) { memberCache = new MemberCache(); s_memberCache.TryAdd(type, memberCache); } return memberCache; } private static Tuple MakeTuple(string name, BindingFlags bindingAttr) { return new Tuple(name, (int)bindingAttr); } private static TypeDescriptionProviderService TypeDescriptionProviderService { get { if (DesignerHost == null) { return null; } TypeDescriptionProviderService tdpService = DesignerHost.GetService(typeof(TypeDescriptionProviderService)) as TypeDescriptionProviderService; return tdpService; } } /// /// The DesignerHost is only available within the context of DesignTimeTemplateParser.ParseControl, which is called /// from the design view. /// internal static IDesignerHost DesignerHost { get; set; } /// /// The CBMTypeDescriptionProviderBridge is only available when building using the ClientBuildManager in VS. /// internal static ClientBuildManagerTypeDescriptionProviderBridge CBMTypeDescriptionProviderBridge { set { s_cbmTdpBridge = value; } } // The provider needs not be cached because the TFP service // returns light-weight providers that delegate to the same // underlying TFP instance. (Dev10 bug 795001) private static TypeDescriptionProvider GetTargetFrameworkProvider(object obj) { TypeDescriptionProviderService service = TargetFrameworkUtil.TypeDescriptionProviderService; if (service != null) { return service.GetProvider(obj); } return null; } private static TypeDescriptionProvider GetTargetFrameworkProvider(Type type) { TypeDescriptionProviderService service = TargetFrameworkUtil.TypeDescriptionProviderService; if (service != null) { return service.GetProvider(type); } return null; } private static ICustomTypeDescriptor GetTypeDescriptor(Type type) { TypeDescriptionProvider tdp = GetTargetFrameworkProvider(type); if (tdp != null) { ICustomTypeDescriptor descriptor = tdp.GetTypeDescriptor(type); if (descriptor != null) { return descriptor; } } return null; } private static ICustomTypeDescriptor GetTypeDescriptor(object obj) { TypeDescriptionProvider tdp = GetTargetFrameworkProvider(obj); if (tdp != null) { ICustomTypeDescriptor descriptor = tdp.GetTypeDescriptor(obj); if (descriptor != null) { return descriptor; } } return null; } /// /// Returns the target type if it is available, otherwise returns back the original type /// private static Type GetReflectionType(Type type) { if (type == null) { return null; } TypeDescriptionProvider provider = GetTargetFrameworkProvider(type); if (provider != null) { return provider.GetReflectionType(type); } return type; } private static Type[] GetReflectionTypes(Type[] types) { if (types == null) { return null; } var reflectionTypes = from t in types select GetReflectionType(t); return reflectionTypes.ToArray(); } internal static PropertyInfo GetProperty(Type type, string name, BindingFlags bindingAttr, Type returnType = null, Type[] types = null, bool throwAmbiguousMatchException = false) { if (types == null) { types = Type.EmptyTypes; } if (SkipCache || returnType != null || types != Type.EmptyTypes) { // Don't cache if any of the values are non-default return GetPropertyHelper(type, name, bindingAttr, returnType, types, throwAmbiguousMatchException); } PropertyInfo result = null; MemberCache memberCache = GetMemberCache(type); Tuple key = MakeTuple(name, bindingAttr); if (!memberCache.Properties.TryGetValue(key, out result)) { result = GetPropertyHelper(type, name, bindingAttr, returnType, types, throwAmbiguousMatchException); memberCache.Properties.TryAdd(key, result); } return result; } private static PropertyInfo GetPropertyHelper(Type type, string name, BindingFlags bindingAttr, Type returnType, Type[] types, bool throwAmbiguousMatchException) { try { bool hasProperty = false; if (s_cbmTdpBridge != null && IsFrameworkType(type)) { Type typeToUse = GetTypeToUseForCBMBridge(type); hasProperty = s_cbmTdpBridge.HasProperty(typeToUse, name, bindingAttr, returnType, types); } else { Type reflectionType = GetReflectionType(type); Type reflectionReturnType = GetReflectionType(returnType); Type[] reflectionTypes = GetReflectionTypes(types); PropertyInfo propInfo = reflectionType.GetProperty(name, bindingAttr, null /* binder */, reflectionReturnType, reflectionTypes, null /* modifiers */); hasProperty = propInfo != null; } // Return the actual runtime PropertyInfo only if it was found in the target type. if (hasProperty) { return type.GetProperty(name, bindingAttr, null /* binder */, returnType, types, null /* modifiers */); } } catch (AmbiguousMatchException) { if (throwAmbiguousMatchException) { throw; } return GetMostSpecificProperty(type, name, bindingAttr, returnType, types); } return null; } internal static FieldInfo GetField(Type type, string name, BindingFlags bindingAttr) { if (SkipCache) { return GetFieldInfo(type, name, bindingAttr); } FieldInfo result = null; MemberCache memberCache = GetMemberCache(type); Tuple key = MakeTuple(name, bindingAttr); if (!memberCache.Fields.TryGetValue(key, out result)) { result = GetFieldInfo(type, name, bindingAttr); memberCache.Fields.TryAdd(key, result); } return result; } private static FieldInfo GetFieldInfo(Type type, string name, BindingFlags bindingAttr) { if (s_cbmTdpBridge != null && IsFrameworkType(type)) { Type typeToUse = GetTypeToUseForCBMBridge(type); bool hasField = s_cbmTdpBridge.HasField(typeToUse, name, bindingAttr); if (hasField) { return type.GetField(name, bindingAttr); } return null; } Type targetFrameworkType = GetReflectionType(type); FieldInfo fieldInfo = targetFrameworkType.GetField(name, bindingAttr); // Return the actual runtime FieldInfo only if it was found in the target type. if (fieldInfo != null) { return type.GetField(name, bindingAttr); } return null; } internal static EventInfo GetEvent(Type type, string name) { if (SkipCache) { return GetEventInfo(type, name); } EventInfo result = null; MemberCache memberCache = GetMemberCache(type); if (!memberCache.Events.TryGetValue(name, out result)) { result = GetEventInfo(type, name); memberCache.Events.TryAdd(name, result); } return result; } private static EventInfo GetEventInfo(Type type, string name) { if (s_cbmTdpBridge != null && IsFrameworkType(type)) { Type typeToUse = GetTypeToUseForCBMBridge(type); bool hasEvent = s_cbmTdpBridge.HasEvent(typeToUse, name); if (hasEvent) { return type.GetEvent(name); } return null; } Type targetFrameworkType = GetReflectionType(type); EventInfo eventInfo = targetFrameworkType.GetEvent(name); // Return the actual runtime EventInfo only if it was found in the target type. if (eventInfo != null) { return type.GetEvent(name); } return null; } internal static PropertyDescriptorCollection GetProperties(Type type) { if (SkipCache) { return GetPropertyDescriptorCollection(type); } PropertyDescriptorCollection result = null; if (!s_typePropertyDescriptorCollectionDict.TryGetValue(type, out result)) { result = GetPropertyDescriptorCollection(type); s_typePropertyDescriptorCollectionDict.TryAdd(type, result); } return result; } private static PropertyDescriptorCollection GetPropertyDescriptorCollection(Type type) { if (s_cbmTdpBridge != null && IsFrameworkType(type)) { return GetFilteredPropertyDescriptorCollection(type, null); } ICustomTypeDescriptor descriptor = GetTypeDescriptor(type); if (descriptor != null) { return descriptor.GetProperties(); } else { return TypeDescriptor.GetProperties(type); } } internal static PropertyDescriptorCollection GetProperties(object obj) { if (SkipCache) { return GetPropertyDescriptorCollection(obj); } PropertyDescriptorCollection result = null; if (!s_objectPropertyDescriptorCollectionDict.TryGetValue(obj, out result)) { result = GetPropertyDescriptorCollection(obj); s_objectPropertyDescriptorCollectionDict.TryAdd(obj, result); } return result; } private static PropertyDescriptorCollection GetPropertyDescriptorCollection(object obj) { Type type = obj.GetType(); if (s_cbmTdpBridge != null && IsFrameworkType(type)) { return GetFilteredPropertyDescriptorCollection(type, obj); } ICustomTypeDescriptor descriptor = GetTypeDescriptor(obj); if (descriptor != null) { return descriptor.GetProperties(); } else { return TypeDescriptor.GetProperties(obj); } } /// /// This method does filtering based on propertyInfo, and should only be used when the TargetFrameworkProvider /// is not directly available, for example in the CBM case where it is in another appdomain. /// private static PropertyDescriptorCollection GetFilteredPropertyDescriptorCollection(Type objectType, object instance) { Debug.Assert(s_cbmTdpBridge != null, "s_cbmTdpBridge should not be null"); PropertyDescriptorCollection propertyDescriptors = null; if (instance != null) { propertyDescriptors = TypeDescriptor.GetProperties(instance); } else if (objectType != null) { propertyDescriptors = TypeDescriptor.GetProperties(objectType); } else { throw new ArgumentException("At least one argument should be non-null"); } BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Instance; Type typeToUse = GetTypeToUseForCBMBridge(objectType); string[] propertyNames = s_cbmTdpBridge.GetFilteredProperties(typeToUse, bindingFlags); var filteredPropertyDescriptors = from p in propertyNames let d = propertyDescriptors[p] where d != null select d; return new PropertyDescriptorCollection(filteredPropertyDescriptors.ToArray()); } internal static EventDescriptorCollection GetEvents(Type type) { if (SkipCache) { return GetEventDescriptorCollection(type); } EventDescriptorCollection result = null; if (!s_eventDescriptorCollectionDict.TryGetValue(type, out result)) { result = GetEventDescriptorCollection(type); s_eventDescriptorCollectionDict.TryAdd(type, result); } return result; } private static EventDescriptorCollection GetEventDescriptorCollection(Type type) { if (s_cbmTdpBridge != null && IsFrameworkType(type)) { return GetFilteredEventDescriptorCollection(type, null); } ICustomTypeDescriptor descriptor = GetTypeDescriptor(type); if (descriptor != null) { return descriptor.GetEvents(); } else { return TypeDescriptor.GetEvents(type); } } /// /// This method does filtering based on eventInfo, and should only be used when the TargetFrameworkProvider /// is not directly available, for example in the CBM case where it is in another appdomain. /// private static EventDescriptorCollection GetFilteredEventDescriptorCollection(Type objectType, object instance) { Debug.Assert(s_cbmTdpBridge != null, "s_cbmTdpBridge should not be null"); EventDescriptorCollection eventDescriptors = null; if (instance != null) { eventDescriptors = TypeDescriptor.GetEvents(instance); } else if (objectType != null) { eventDescriptors = TypeDescriptor.GetEvents(objectType); } else { throw new ArgumentException("At least one argument should be non-null"); } BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Instance; Type typeToUse = GetTypeToUseForCBMBridge(objectType); string[] eventNames = s_cbmTdpBridge.GetFilteredEvents(typeToUse, bindingFlags); var filteredEventDescriptors = from e in eventNames let d = eventDescriptors[e] where d != null select d; return new EventDescriptorCollection(filteredEventDescriptors.ToArray()); } internal static System.ComponentModel.AttributeCollection GetAttributes(Type type) { ICustomTypeDescriptor descriptor = GetTypeDescriptor(type); if (descriptor != null) { return descriptor.GetAttributes(); } else { return TypeDescriptor.GetAttributes(type); } } internal static object[] GetCustomAttributes(Type type, Type attributeType, bool inherit) { Type targetType = GetReflectionType(type); return targetType.GetCustomAttributes(attributeType, inherit); } /// /// Function to return an assembly qualified type name based on the type in the target framework /// internal static string TypeNameConverter(Type type) { string typeName = null; if (type != null) { Type targetFrameworkType = GetReflectionType(type); if (targetFrameworkType != null) { typeName = targetFrameworkType.AssemblyQualifiedName; } } return typeName; } private static bool IsFrameworkType(Type type) { // We need to make sure a type is a framework type, before we try to use s_cbmTdpBridge on it. // Also, isFrameworkType should only be called in this specific scenario, when we are trying // to use s_cbmTdpBridge. Debug.Assert(s_cbmTdpBridge != null, "s_cbmTdpBridge should not be null in IsFrameworkType"); bool result; if (!s_isFrameworkType.TryGetValue(type, out result)) { Assembly a = type.Assembly; string path; ReferenceAssemblyType referenceAssemblyType = AssemblyResolver.GetPathToReferenceAssembly(a, out path); result = (referenceAssemblyType != ReferenceAssemblyType.NonFrameworkAssembly); s_isFrameworkType.TryAdd(type, result); } return result; } private static PropertyInfo GetMostSpecificProperty(Type type, string name, BindingFlags additionalFlags, Type returnType, Type[] types) { BindingFlags flags = BindingFlags.DeclaredOnly; flags |= additionalFlags; PropertyInfo propInfo; Type currentType = type; while (currentType != null) { // DevDiv #425681: Even with BindingFlags.DeclaredOnly, Type.GetProperty may still throw an // AmbiguousMatchException, such as if there exist two properties that differ only by case. // If this happens, we need to let that exception propagate up, otherwise GetPropertyHelper // will call GetMostSpecificProperty, eventually resulting in stack overflow. propInfo = GetProperty(currentType, name, flags, returnType, types, throwAmbiguousMatchException: true); if (propInfo != null) { return propInfo; } else { currentType = currentType.BaseType; } } return null; } // If the type is a generic type, use the generic type definition instead, // in case it has non-framework type arguments. private static Type GetTypeToUseForCBMBridge(Type type) { return type.IsGenericType ? type.GetGenericTypeDefinition() : type; } internal static bool HasMethod(Type type, string name, BindingFlags bindingAttr) { bool hasMethod = false; if (s_cbmTdpBridge != null && IsFrameworkType(type)) { Type typeToUse = GetTypeToUseForCBMBridge(type); hasMethod = s_cbmTdpBridge.HasMethod(typeToUse, name, bindingAttr); } else { Type reflectionType = GetReflectionType(type); MethodInfo methodInfo = reflectionType.GetMethod(name, bindingAttr); hasMethod = methodInfo != null; } return hasMethod; } private static bool SkipCache { get { // We should not be statically caching items in the VS primary appdomain. // - If the cbm bridge is available, caching is ok since we are in a separate appdomain. // (The appdomain gets reset when the project unloads or when a reference assembly is // updated). // - Otherwise, we are either already using standard reflection, or we are using the // TFP in the primary appdomain, and should not be caching statically. // Dev10 bug 805134 return s_cbmTdpBridge == null; } } internal static bool IsSupportedType(Type type) { TypeDescriptionProvider provider = GetTargetFrameworkProvider(type); if (provider == null) { provider = TypeDescriptor.GetProvider(type); } return provider.IsSupportedType(type); } } }