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