1 /* **************************************************************************** 2 * 3 * Copyright (c) Microsoft Corporation. 4 * 5 * This source code is subject to terms and conditions of the Microsoft Public License. A 6 * copy of the license can be found in the License.html file at the root of this distribution. If 7 * you cannot locate the Microsoft Public License, please send an email to 8 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 9 * by the terms of the Microsoft Public License. 10 * 11 * You must not remove this notice, or any other, from this software. 12 * 13 * 14 * ***************************************************************************/ 15 using System; using Microsoft; 16 17 18 using System.Collections.Generic; 19 using System.Diagnostics; 20 #if CODEPLEX_40 21 using System.Linq.Expressions; 22 #else 23 using Microsoft.Linq.Expressions; 24 #endif 25 using System.Reflection; 26 27 #if CODEPLEX_40 28 namespace System.Dynamic { 29 #else 30 namespace Microsoft.Scripting { 31 #endif 32 33 internal static class TypeUtils { 34 private const BindingFlags AnyStatic = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; 35 internal const MethodAttributes PublicStatic = MethodAttributes.Public | MethodAttributes.Static; 36 37 //CONFORMING GetNonNullableType(Type type)38 internal static Type GetNonNullableType(Type type) { 39 if (IsNullableType(type)) { 40 return type.GetGenericArguments()[0]; 41 } 42 return type; 43 } 44 45 //CONFORMING IsNullableType(this Type type)46 internal static bool IsNullableType(this Type type) { 47 return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); 48 } 49 50 //CONFORMING AreReferenceAssignable(Type dest, Type src)51 internal static bool AreReferenceAssignable(Type dest, Type src) { 52 // WARNING: This actually implements "Is this identity assignable and/or reference assignable?" 53 if (dest == src) { 54 return true; 55 } 56 if (!dest.IsValueType && !src.IsValueType && AreAssignable(dest, src)) { 57 return true; 58 } 59 return false; 60 } 61 //CONFORMING AreAssignable(Type dest, Type src)62 internal static bool AreAssignable(Type dest, Type src) { 63 if (dest == src) { 64 return true; 65 } 66 if (dest.IsAssignableFrom(src)) { 67 return true; 68 } 69 if (dest.IsArray && src.IsArray && dest.GetArrayRank() == src.GetArrayRank() && AreReferenceAssignable(dest.GetElementType(), src.GetElementType())) { 70 return true; 71 } 72 if (src.IsArray && dest.IsGenericType && 73 (dest.GetGenericTypeDefinition() == typeof(System.Collections.Generic.IEnumerable<>) 74 || dest.GetGenericTypeDefinition() == typeof(System.Collections.Generic.IList<>) 75 || dest.GetGenericTypeDefinition() == typeof(System.Collections.Generic.ICollection<>)) 76 && dest.GetGenericArguments()[0] == src.GetElementType()) { 77 return true; 78 } 79 return false; 80 } 81 82 //CONFORMING IsImplicitlyConvertible(Type source, Type destination)83 internal static bool IsImplicitlyConvertible(Type source, Type destination) { 84 return IsIdentityConversion(source, destination) || 85 IsImplicitNumericConversion(source, destination) || 86 IsImplicitReferenceConversion(source, destination) || 87 IsImplicitBoxingConversion(source, destination); 88 } 89 IsImplicitlyConvertible(Type source, Type destination, bool considerUserDefined)90 internal static bool IsImplicitlyConvertible(Type source, Type destination, bool considerUserDefined) { 91 return IsImplicitlyConvertible(source, destination) || 92 (considerUserDefined && GetUserDefinedCoercionMethod(source, destination, true) != null); 93 } 94 95 //CONFORMING GetUserDefinedCoercionMethod(Type convertFrom, Type convertToType, bool implicitOnly)96 internal static MethodInfo GetUserDefinedCoercionMethod(Type convertFrom, Type convertToType, bool implicitOnly) { 97 // check for implicit coercions first 98 Type nnExprType = TypeUtils.GetNonNullableType(convertFrom); 99 Type nnConvType = TypeUtils.GetNonNullableType(convertToType); 100 // try exact match on types 101 MethodInfo[] eMethods = nnExprType.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); 102 MethodInfo method = FindConversionOperator(eMethods, convertFrom, convertToType, implicitOnly); 103 if (method != null) { 104 return method; 105 } 106 MethodInfo[] cMethods = nnConvType.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); 107 method = FindConversionOperator(cMethods, convertFrom, convertToType, implicitOnly); 108 if (method != null) { 109 return method; 110 } 111 // try lifted conversion 112 if (nnExprType != convertFrom || nnConvType != convertToType) { 113 method = FindConversionOperator(eMethods, nnExprType, nnConvType, implicitOnly); 114 if (method == null) { 115 method = FindConversionOperator(cMethods, nnExprType, nnConvType, implicitOnly); 116 } 117 if (method != null) { 118 return method; 119 } 120 } 121 return null; 122 } 123 124 //CONFORMING FindConversionOperator(MethodInfo[] methods, Type typeFrom, Type typeTo, bool implicitOnly)125 internal static MethodInfo FindConversionOperator(MethodInfo[] methods, Type typeFrom, Type typeTo, bool implicitOnly) { 126 foreach (MethodInfo mi in methods) { 127 if (mi.Name != "op_Implicit" && (implicitOnly || mi.Name != "op_Explicit")) 128 continue; 129 if (mi.ReturnType != typeTo) 130 continue; 131 ParameterInfo[] pis = mi.GetParameters(); 132 if (pis[0].ParameterType != typeFrom) 133 continue; 134 return mi; 135 } 136 return null; 137 } 138 139 140 //CONFORMING IsIdentityConversion(Type source, Type destination)141 private static bool IsIdentityConversion(Type source, Type destination) { 142 return source == destination; 143 } 144 145 //CONFORMING 146 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] IsImplicitNumericConversion(Type source, Type destination)147 private static bool IsImplicitNumericConversion(Type source, Type destination) { 148 TypeCode tcSource = Type.GetTypeCode(source); 149 TypeCode tcDest = Type.GetTypeCode(destination); 150 151 switch (tcSource) { 152 case TypeCode.SByte: 153 switch (tcDest) { 154 case TypeCode.Int16: 155 case TypeCode.Int32: 156 case TypeCode.Int64: 157 case TypeCode.Single: 158 case TypeCode.Double: 159 case TypeCode.Decimal: 160 return true; 161 } 162 return false; 163 case TypeCode.Byte: 164 switch (tcDest) { 165 case TypeCode.Int16: 166 case TypeCode.UInt16: 167 case TypeCode.Int32: 168 case TypeCode.UInt32: 169 case TypeCode.Int64: 170 case TypeCode.UInt64: 171 case TypeCode.Single: 172 case TypeCode.Double: 173 case TypeCode.Decimal: 174 return true; 175 } 176 return false; 177 case TypeCode.Int16: 178 switch (tcDest) { 179 case TypeCode.Int32: 180 case TypeCode.Int64: 181 case TypeCode.Single: 182 case TypeCode.Double: 183 case TypeCode.Decimal: 184 return true; 185 } 186 return false; 187 case TypeCode.UInt16: 188 switch (tcDest) { 189 case TypeCode.Int32: 190 case TypeCode.UInt32: 191 case TypeCode.Int64: 192 case TypeCode.UInt64: 193 case TypeCode.Single: 194 case TypeCode.Double: 195 case TypeCode.Decimal: 196 return true; 197 } 198 return false; 199 case TypeCode.Int32: 200 switch (tcDest) { 201 case TypeCode.Int64: 202 case TypeCode.Single: 203 case TypeCode.Double: 204 case TypeCode.Decimal: 205 return true; 206 } 207 return false; 208 case TypeCode.UInt32: 209 switch (tcDest) { 210 case TypeCode.UInt32: 211 case TypeCode.UInt64: 212 case TypeCode.Single: 213 case TypeCode.Double: 214 case TypeCode.Decimal: 215 return true; 216 } 217 return false; 218 case TypeCode.Int64: 219 case TypeCode.UInt64: 220 switch (tcDest) { 221 case TypeCode.Single: 222 case TypeCode.Double: 223 case TypeCode.Decimal: 224 return true; 225 } 226 return false; 227 case TypeCode.Char: 228 switch (tcDest) { 229 case TypeCode.UInt16: 230 case TypeCode.Int32: 231 case TypeCode.UInt32: 232 case TypeCode.Int64: 233 case TypeCode.UInt64: 234 case TypeCode.Single: 235 case TypeCode.Double: 236 case TypeCode.Decimal: 237 return true; 238 } 239 return false; 240 case TypeCode.Single: 241 return (tcDest == TypeCode.Double); 242 } 243 return false; 244 } 245 246 //CONFORMING IsImplicitReferenceConversion(Type source, Type destination)247 private static bool IsImplicitReferenceConversion(Type source, Type destination) { 248 return AreAssignable(destination, source); 249 } 250 251 //CONFORMING IsImplicitBoxingConversion(Type source, Type destination)252 private static bool IsImplicitBoxingConversion(Type source, Type destination) { 253 if (source.IsValueType && (destination == typeof(object) || destination == typeof(System.ValueType))) 254 return true; 255 if (source.IsEnum && destination == typeof(System.Enum)) 256 return true; 257 return false; 258 } 259 } 260 } 261