|
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<string, EventInfo> _events;
private ConcurrentDictionary<Tuple<string, int>, FieldInfo> _fields;
private ConcurrentDictionary<Tuple<string, int>, PropertyInfo> _properties;
internal MemberCache() {
}
internal ConcurrentDictionary<string, EventInfo> Events {
get {
if (_events == null) {
_events = new ConcurrentDictionary<string, EventInfo>();
}
return _events;
}
}
internal ConcurrentDictionary<Tuple<string, int>, FieldInfo> Fields {
get {
if (_fields == null) {
_fields = new ConcurrentDictionary<Tuple<string, int>, FieldInfo>();
}
return _fields;
}
}
internal ConcurrentDictionary<Tuple<string, int>, PropertyInfo> Properties {
get {
if (_properties == null) {
_properties = new ConcurrentDictionary<Tuple<string, int>, PropertyInfo>();
}
return _properties;
}
}
}
private static ConcurrentDictionary<Type, MemberCache> s_memberCache = new ConcurrentDictionary<Type, MemberCache>();
private static ClientBuildManagerTypeDescriptionProviderBridge s_cbmTdpBridge;
private static ConcurrentDictionary<Type, PropertyDescriptorCollection> s_typePropertyDescriptorCollectionDict =
new ConcurrentDictionary<Type, PropertyDescriptorCollection>();
private static ConcurrentDictionary<object, PropertyDescriptorCollection> s_objectPropertyDescriptorCollectionDict =
new ConcurrentDictionary<object, PropertyDescriptorCollection>();
private static ConcurrentDictionary<Type, EventDescriptorCollection> s_eventDescriptorCollectionDict =
new ConcurrentDictionary<Type, EventDescriptorCollection>();
private static ConcurrentDictionary<Type, bool> s_isFrameworkType = new ConcurrentDictionary<Type, bool>();
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<string, int> MakeTuple(string name, BindingFlags bindingAttr) {
return new Tuple<string, int>(name, (int)bindingAttr);
}
private static TypeDescriptionProviderService TypeDescriptionProviderService {
get {
if (DesignerHost == null) {
return null;
}
TypeDescriptionProviderService tdpService = DesignerHost.GetService(typeof(TypeDescriptionProviderService)) as TypeDescriptionProviderService;
return tdpService;
}
}
/// <summary>
/// The DesignerHost is only available within the context of DesignTimeTemplateParser.ParseControl, which is called
/// from the design view.
/// </summary>
internal static IDesignerHost DesignerHost { get; set; }
/// <summary>
/// The CBMTypeDescriptionProviderBridge is only available when building using the ClientBuildManager in VS.
/// </summary>
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;
}
/// <summary>
/// Returns the target type if it is available, otherwise returns back the original type
/// </summary>
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<string, int> 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<string, int> 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);
}
}
/// <summary>
/// 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.
/// </summary>
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);
}
}
/// <summary>
/// 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.
/// </summary>
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);
}
/// <summary>
/// Function to return an assembly qualified type name based on the type in the target framework
/// </summary>
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);
}
}
}
|