/* ****************************************************************************
*
* Copyright (c) Microsoft Corporation.
*
* This source code is subject to terms and conditions of the Microsoft Public License. A
* copy of the license can be found in the License.html file at the root of this distribution. If
* you cannot locate the Microsoft Public License, please send an email to
* dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
* by the terms of the Microsoft Public License.
*
* You must not remove this notice, or any other, from this software.
*
*
* ***************************************************************************/
#if !CLR2
using System.Linq.Expressions;
#else
using Microsoft.Scripting.Ast;
#endif
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
namespace Microsoft.Scripting.ComInterop{
internal static class TypeUtils {
private const BindingFlags AnyStatic = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
internal const MethodAttributes PublicStatic = MethodAttributes.Public | MethodAttributes.Static;
//CONFORMING
internal static Type GetNonNullableType(Type type) {
if (IsNullableType(type)) {
return type.GetGenericArguments()[0];
}
return type;
}
//CONFORMING
internal static bool IsNullableType(this Type type) {
return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
}
//CONFORMING
internal static bool AreReferenceAssignable(Type dest, Type src) {
// WARNING: This actually implements "Is this identity assignable and/or reference assignable?"
if (dest == src) {
return true;
}
if (!dest.IsValueType && !src.IsValueType && AreAssignable(dest, src)) {
return true;
}
return false;
}
//CONFORMING
internal static bool AreAssignable(Type dest, Type src) {
if (dest == src) {
return true;
}
if (dest.IsAssignableFrom(src)) {
return true;
}
if (dest.IsArray && src.IsArray && dest.GetArrayRank() == src.GetArrayRank() && AreReferenceAssignable(dest.GetElementType(), src.GetElementType())) {
return true;
}
if (src.IsArray && dest.IsGenericType &&
(dest.GetGenericTypeDefinition() == typeof(System.Collections.Generic.IEnumerable<>)
|| dest.GetGenericTypeDefinition() == typeof(System.Collections.Generic.IList<>)
|| dest.GetGenericTypeDefinition() == typeof(System.Collections.Generic.ICollection<>))
&& dest.GetGenericArguments()[0] == src.GetElementType()) {
return true;
}
return false;
}
//CONFORMING
internal static bool IsImplicitlyConvertible(Type source, Type destination) {
return IsIdentityConversion(source, destination) ||
IsImplicitNumericConversion(source, destination) ||
IsImplicitReferenceConversion(source, destination) ||
IsImplicitBoxingConversion(source, destination);
}
internal static bool IsImplicitlyConvertible(Type source, Type destination, bool considerUserDefined) {
return IsImplicitlyConvertible(source, destination) ||
(considerUserDefined && GetUserDefinedCoercionMethod(source, destination, true) != null);
}
//CONFORMING
internal static MethodInfo GetUserDefinedCoercionMethod(Type convertFrom, Type convertToType, bool implicitOnly) {
// check for implicit coercions first
Type nnExprType = TypeUtils.GetNonNullableType(convertFrom);
Type nnConvType = TypeUtils.GetNonNullableType(convertToType);
// try exact match on types
MethodInfo[] eMethods = nnExprType.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
MethodInfo method = FindConversionOperator(eMethods, convertFrom, convertToType, implicitOnly);
if (method != null) {
return method;
}
MethodInfo[] cMethods = nnConvType.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
method = FindConversionOperator(cMethods, convertFrom, convertToType, implicitOnly);
if (method != null) {
return method;
}
// try lifted conversion
if (nnExprType != convertFrom || nnConvType != convertToType) {
method = FindConversionOperator(eMethods, nnExprType, nnConvType, implicitOnly);
if (method == null) {
method = FindConversionOperator(cMethods, nnExprType, nnConvType, implicitOnly);
}
if (method != null) {
return method;
}
}
return null;
}
//CONFORMING
internal static MethodInfo FindConversionOperator(MethodInfo[] methods, Type typeFrom, Type typeTo, bool implicitOnly) {
foreach (MethodInfo mi in methods) {
if (mi.Name != "op_Implicit" && (implicitOnly || mi.Name != "op_Explicit"))
continue;
if (mi.ReturnType != typeTo)
continue;
ParameterInfo[] pis = mi.GetParameters();
if (pis[0].ParameterType != typeFrom)
continue;
return mi;
}
return null;
}
//CONFORMING
private static bool IsIdentityConversion(Type source, Type destination) {
return source == destination;
}
//CONFORMING
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
private static bool IsImplicitNumericConversion(Type source, Type destination) {
TypeCode tcSource = Type.GetTypeCode(source);
TypeCode tcDest = Type.GetTypeCode(destination);
switch (tcSource) {
case TypeCode.SByte:
switch (tcDest) {
case TypeCode.Int16:
case TypeCode.Int32:
case TypeCode.Int64:
case TypeCode.Single:
case TypeCode.Double:
case TypeCode.Decimal:
return true;
}
return false;
case TypeCode.Byte:
switch (tcDest) {
case TypeCode.Int16:
case TypeCode.UInt16:
case TypeCode.Int32:
case TypeCode.UInt32:
case TypeCode.Int64:
case TypeCode.UInt64:
case TypeCode.Single:
case TypeCode.Double:
case TypeCode.Decimal:
return true;
}
return false;
case TypeCode.Int16:
switch (tcDest) {
case TypeCode.Int32:
case TypeCode.Int64:
case TypeCode.Single:
case TypeCode.Double:
case TypeCode.Decimal:
return true;
}
return false;
case TypeCode.UInt16:
switch (tcDest) {
case TypeCode.Int32:
case TypeCode.UInt32:
case TypeCode.Int64:
case TypeCode.UInt64:
case TypeCode.Single:
case TypeCode.Double:
case TypeCode.Decimal:
return true;
}
return false;
case TypeCode.Int32:
switch (tcDest) {
case TypeCode.Int64:
case TypeCode.Single:
case TypeCode.Double:
case TypeCode.Decimal:
return true;
}
return false;
case TypeCode.UInt32:
switch (tcDest) {
case TypeCode.UInt32:
case TypeCode.UInt64:
case TypeCode.Single:
case TypeCode.Double:
case TypeCode.Decimal:
return true;
}
return false;
case TypeCode.Int64:
case TypeCode.UInt64:
switch (tcDest) {
case TypeCode.Single:
case TypeCode.Double:
case TypeCode.Decimal:
return true;
}
return false;
case TypeCode.Char:
switch (tcDest) {
case TypeCode.UInt16:
case TypeCode.Int32:
case TypeCode.UInt32:
case TypeCode.Int64:
case TypeCode.UInt64:
case TypeCode.Single:
case TypeCode.Double:
case TypeCode.Decimal:
return true;
}
return false;
case TypeCode.Single:
return (tcDest == TypeCode.Double);
}
return false;
}
//CONFORMING
private static bool IsImplicitReferenceConversion(Type source, Type destination) {
return AreAssignable(destination, source);
}
//CONFORMING
private static bool IsImplicitBoxingConversion(Type source, Type destination) {
if (source.IsValueType && (destination == typeof(object) || destination == typeof(System.ValueType)))
return true;
if (source.IsEnum && destination == typeof(System.Enum))
return true;
return false;
}
}
}
|