1 /* ****************************************************************************
3 * Copyright (c) Microsoft Corporation.
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.
11 * You must not remove this notice, or any other, from this software.
14 * ***************************************************************************/
15 using System; using Microsoft;
18 using System.Collections.Generic;
19 using System.Diagnostics;
21 using System.Linq.Expressions;
23 using Microsoft.Linq.Expressions;
25 using System.Reflection;
32 namespace System.Dynamic.Utils {
34 namespace Microsoft.Scripting.Utils {
37 internal static class TypeUtils {
38 private const BindingFlags AnyStatic = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
39 internal const MethodAttributes PublicStatic = MethodAttributes.Public | MethodAttributes.Static;
41 internal static Type GetNonNullableType(this Type type) {
42 if (IsNullableType(type)) {
43 return type.GetGenericArguments()[0];
48 internal static Type GetNullableType(Type type) {
49 Debug.Assert(type != null, "type cannot be null");
50 if (type.IsValueType && !IsNullableType(type)) {
51 return typeof(Nullable<>).MakeGenericType(type);
56 internal static bool IsNullableType(this Type type) {
57 return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
60 internal static bool IsBool(Type type) {
61 return GetNonNullableType(type) == typeof(bool);
64 internal static bool IsNumeric(Type type) {
65 type = GetNonNullableType(type);
67 switch (Type.GetTypeCode(type)) {
85 internal static bool IsInteger(Type type) {
86 type = GetNonNullableType(type);
90 switch (Type.GetTypeCode(type)) {
106 internal static bool IsArithmetic(Type type) {
107 type = GetNonNullableType(type);
109 switch (Type.GetTypeCode(type)) {
113 case TypeCode.Double:
114 case TypeCode.Single:
115 case TypeCode.UInt16:
116 case TypeCode.UInt32:
117 case TypeCode.UInt64:
124 internal static bool IsUnsignedInt(Type type) {
125 type = GetNonNullableType(type);
127 switch (Type.GetTypeCode(type)) {
128 case TypeCode.UInt16:
129 case TypeCode.UInt32:
130 case TypeCode.UInt64:
137 internal static bool IsIntegerOrBool(Type type) {
138 type = GetNonNullableType(type);
140 switch (Type.GetTypeCode(type)) {
144 case TypeCode.UInt64:
145 case TypeCode.UInt32:
146 case TypeCode.UInt16:
147 case TypeCode.Boolean:
156 internal static bool AreEquivalent(Type t1, Type t2)
158 #if MICROSOFT_SCRIPTING_CORE || SILVERLIGHT
161 return t1 == t2 || t1.IsEquivalentTo(t2);
165 internal static bool AreReferenceAssignable(Type dest, Type src) {
166 // WARNING: This actually implements "Is this identity assignable and/or reference assignable?"
167 if (AreEquivalent(dest, src)) {
170 if (!dest.IsValueType && !src.IsValueType && dest.IsAssignableFrom(src)) {
176 // Checks if the type is a valid target for an instance call
177 internal static bool IsValidInstanceType(MemberInfo member, Type instanceType) {
178 Type targetType = member.DeclaringType;
179 if (AreReferenceAssignable(targetType, instanceType)) {
182 if (instanceType.IsValueType) {
183 if (AreReferenceAssignable(targetType, typeof(System.Object))) {
186 if (AreReferenceAssignable(targetType, typeof(System.ValueType))) {
189 if (instanceType.IsEnum && AreReferenceAssignable(targetType, typeof(System.Enum))) {
192 // A call to an interface implemented by a struct is legal whether the struct has
193 // been boxed or not.
194 if (targetType.IsInterface) {
195 foreach (Type interfaceType in instanceType.GetInterfaces()) {
196 if (AreReferenceAssignable(targetType, interfaceType)) {
205 internal static bool HasIdentityPrimitiveOrNullableConversion(Type source, Type dest) {
206 Debug.Assert(source != null && dest != null);
208 // Identity conversion
209 if (AreEquivalent(source, dest)) {
213 // Nullable conversions
214 if (IsNullableType(source) && AreEquivalent(dest, GetNonNullableType(source))) {
217 if (IsNullableType(dest) && AreEquivalent(source, GetNonNullableType(dest))) {
220 // Primitive runtime conversions
221 // All conversions amongst enum, bool, char, integer and float types
222 // (and their corresponding nullable types) are legal except for
223 // nonbool==>bool and nonbool==>bool?
224 // Since we have already covered bool==>bool, bool==>bool?, etc, above,
225 // we can just disallow having a bool or bool? destination type here.
226 if (IsConvertible(source) && IsConvertible(dest) && GetNonNullableType(dest) != typeof(bool)) {
232 internal static bool HasReferenceConversion(Type source, Type dest) {
233 Debug.Assert(source != null && dest != null);
235 // void -> void conversion is handled elsewhere
236 // (it's an identity conversion)
237 // All other void conversions are disallowed.
238 if (source == typeof(void) || dest == typeof(void)) {
242 Type nnSourceType = TypeUtils.GetNonNullableType(source);
243 Type nnDestType = TypeUtils.GetNonNullableType(dest);
246 if (nnSourceType.IsAssignableFrom(nnDestType)) {
250 if (nnDestType.IsAssignableFrom(nnSourceType)) {
253 // Interface conversion
254 if (source.IsInterface || dest.IsInterface) {
258 if (source == typeof(object) || dest == typeof(object)) {
264 internal static bool IsConvertible(Type type) {
265 type = GetNonNullableType(type);
269 switch (Type.GetTypeCode(type)) {
270 case TypeCode.Boolean:
276 case TypeCode.UInt16:
277 case TypeCode.UInt32:
278 case TypeCode.UInt64:
279 case TypeCode.Single:
280 case TypeCode.Double:
288 internal static bool HasReferenceEquality(Type left, Type right) {
289 if (left.IsValueType || right.IsValueType) {
293 // If we have an interface and a reference type then we can do
294 // reference equality.
296 // If we have two reference types and one is assignable to the
297 // other then we can do reference equality.
299 return left.IsInterface || right.IsInterface ||
300 AreReferenceAssignable(left, right) ||
301 AreReferenceAssignable(right, left);
304 internal static bool HasBuiltInEqualityOperator(Type left, Type right) {
305 // If we have an interface and a reference type then we can do
306 // reference equality.
307 if (left.IsInterface && !right.IsValueType) {
310 if (right.IsInterface && !left.IsValueType) {
313 // If we have two reference types and one is assignable to the
314 // other then we can do reference equality.
315 if (!left.IsValueType && !right.IsValueType) {
316 if (AreReferenceAssignable(left, right) || AreReferenceAssignable(right, left)) {
320 // Otherwise, if the types are not the same then we definitely
321 // do not have a built-in equality operator.
322 if (!AreEquivalent(left, right)) {
325 // We have two identical value types, modulo nullability. (If they were both the
326 // same reference type then we would have returned true earlier.)
327 Debug.Assert(left.IsValueType);
328 // Equality between struct types is only defined for numerics, bools, enums,
329 // and their nullable equivalents.
330 Type nnType = GetNonNullableType(left);
331 if (nnType == typeof(bool) || IsNumeric(nnType) || nnType.IsEnum) {
337 internal static bool IsImplicitlyConvertible(Type source, Type destination) {
338 return AreEquivalent(source, destination) || // identity conversion
339 IsImplicitNumericConversion(source, destination) ||
340 IsImplicitReferenceConversion(source, destination) ||
341 IsImplicitBoxingConversion(source, destination) ||
342 IsImplicitNullableConversion(source, destination);
346 internal static MethodInfo GetUserDefinedCoercionMethod(Type convertFrom, Type convertToType, bool implicitOnly) {
347 // check for implicit coercions first
348 Type nnExprType = TypeUtils.GetNonNullableType(convertFrom);
349 Type nnConvType = TypeUtils.GetNonNullableType(convertToType);
350 // try exact match on types
351 MethodInfo[] eMethods = nnExprType.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
352 MethodInfo method = FindConversionOperator(eMethods, convertFrom, convertToType, implicitOnly);
353 if (method != null) {
356 MethodInfo[] cMethods = nnConvType.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
357 method = FindConversionOperator(cMethods, convertFrom, convertToType, implicitOnly);
358 if (method != null) {
361 // try lifted conversion
362 if (!TypeUtils.AreEquivalent(nnExprType, convertFrom) ||
363 !TypeUtils.AreEquivalent(nnConvType, convertToType)) {
364 method = FindConversionOperator(eMethods, nnExprType, nnConvType, implicitOnly);
365 if (method == null) {
366 method = FindConversionOperator(cMethods, nnExprType, nnConvType, implicitOnly);
368 if (method != null) {
375 internal static MethodInfo FindConversionOperator(MethodInfo[] methods, Type typeFrom, Type typeTo, bool implicitOnly) {
376 foreach (MethodInfo mi in methods) {
377 if (mi.Name != "op_Implicit" && (implicitOnly || mi.Name != "op_Explicit")) {
380 if (!TypeUtils.AreEquivalent(mi.ReturnType, typeTo)) {
383 ParameterInfo[] pis = mi.GetParametersCached();
384 if (!TypeUtils.AreEquivalent(pis[0].ParameterType, typeFrom)) {
392 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
393 private static bool IsImplicitNumericConversion(Type source, Type destination) {
394 TypeCode tcSource = Type.GetTypeCode(source);
395 TypeCode tcDest = Type.GetTypeCode(destination);
403 case TypeCode.Single:
404 case TypeCode.Double:
405 case TypeCode.Decimal:
412 case TypeCode.UInt16:
414 case TypeCode.UInt32:
416 case TypeCode.UInt64:
417 case TypeCode.Single:
418 case TypeCode.Double:
419 case TypeCode.Decimal:
427 case TypeCode.Single:
428 case TypeCode.Double:
429 case TypeCode.Decimal:
433 case TypeCode.UInt16:
436 case TypeCode.UInt32:
438 case TypeCode.UInt64:
439 case TypeCode.Single:
440 case TypeCode.Double:
441 case TypeCode.Decimal:
448 case TypeCode.Single:
449 case TypeCode.Double:
450 case TypeCode.Decimal:
454 case TypeCode.UInt32:
456 case TypeCode.UInt32:
457 case TypeCode.UInt64:
458 case TypeCode.Single:
459 case TypeCode.Double:
460 case TypeCode.Decimal:
465 case TypeCode.UInt64:
467 case TypeCode.Single:
468 case TypeCode.Double:
469 case TypeCode.Decimal:
475 case TypeCode.UInt16:
477 case TypeCode.UInt32:
479 case TypeCode.UInt64:
480 case TypeCode.Single:
481 case TypeCode.Double:
482 case TypeCode.Decimal:
486 case TypeCode.Single:
487 return (tcDest == TypeCode.Double);
492 private static bool IsImplicitReferenceConversion(Type source, Type destination) {
493 return destination.IsAssignableFrom(source);
496 private static bool IsImplicitBoxingConversion(Type source, Type destination) {
497 if (source.IsValueType && (destination == typeof(object) || destination == typeof(System.ValueType)))
499 if (source.IsEnum && destination == typeof(System.Enum))
504 private static bool IsImplicitNullableConversion(Type source, Type destination) {
505 if (IsNullableType(destination))
506 return IsImplicitlyConvertible(GetNonNullableType(source), GetNonNullableType(destination));
510 internal static bool IsSameOrSubclass(Type type, Type subType) {
511 return AreEquivalent(type, subType) || subType.IsSubclassOf(type);
514 internal static void ValidateType(Type type) {
515 if (type.IsGenericTypeDefinition) {
516 throw Error.TypeIsGeneric(type);
518 if (type.ContainsGenericParameters) {
519 throw Error.TypeContainsGenericParameters(type);
524 internal static Type FindGenericType(Type definition, Type type) {
525 while (type != null && type != typeof(object)) {
526 if (type.IsGenericType && AreEquivalent(type.GetGenericTypeDefinition(), definition)) {
529 if (definition.IsInterface) {
530 foreach (Type itype in type.GetInterfaces()) {
531 Type found = FindGenericType(definition, itype);
536 type = type.BaseType;
541 internal static bool IsUnsigned(Type type) {
542 type = GetNonNullableType(type);
543 switch (Type.GetTypeCode(type)) {
545 case TypeCode.UInt16:
547 case TypeCode.UInt32:
548 case TypeCode.UInt64:
555 internal static bool IsFloatingPoint(Type type) {
556 type = GetNonNullableType(type);
557 switch (Type.GetTypeCode(type)) {
558 case TypeCode.Single:
559 case TypeCode.Double:
567 /// Searches for an operator method on the type. The method must have
568 /// the specified signature, no generic arguments, and have the
569 /// SpecialName bit set. Also searches inherited operator methods.
571 /// NOTE: This was designed to satisfy the needs of op_True and
572 /// op_False, because we have to do runtime lookup for those. It may
573 /// not work right for unary operators in general.
575 internal static MethodInfo GetBooleanOperator(Type type, string name) {
577 MethodInfo result = type.GetMethodValidated(name, AnyStatic, null, new Type[] { type }, null);
578 if (result != null && result.IsSpecialName && !result.ContainsGenericParameters) {
581 type = type.BaseType;
582 } while (type != null);
586 internal static Type GetNonRefType(this Type type) {
587 return type.IsByRef ? type.GetElementType() : type;
590 private static readonly Assembly _mscorlib = typeof(object).Assembly;
591 private static readonly Assembly _systemCore = typeof(Expression).Assembly;
594 /// We can cache references to types, as long as they aren't in
595 /// collectable assemblies. Unfortunately, we can't really distinguish
596 /// between different flavors of assemblies. But, we can at least
597 /// create a whitelist for types in mscorlib (so we get the primitives)
598 /// and System.Core (so we find Func/Action overloads, etc).
600 internal static bool CanCache(this Type t) {
601 // Note: we don't have to scan base or declaring types here.
602 // There's no way for a type in mscorlib to derive from or be
603 // contained in a type from another assembly. The only thing we
604 // need to look at is the generic arguments, which are the thing
605 // that allows mscorlib types to be specialized by types in other
608 var asm = t.Assembly;
609 if (asm != _mscorlib && asm != _systemCore) {
610 // Not in mscorlib or our assembly
614 if (t.IsGenericType) {
615 foreach (Type g in t.GetGenericArguments()) {