File: src\ExpressionEvaluator\Core\Source\ResultProvider\Helpers\TypeHelpers.cs
Web Access
Project: src\src\ExpressionEvaluator\Core\Test\ResultProvider\Microsoft.CodeAnalysis.ResultProvider.Utilities.csproj (Microsoft.CodeAnalysis.ExpressionEvaluator.ResultProvider.Utilities)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
#nullable disable
 
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Microsoft.VisualStudio.Debugger.Clr;
using Microsoft.VisualStudio.Debugger.Evaluation;
using Microsoft.VisualStudio.Debugger.Evaluation.ClrCompilation;
using Microsoft.VisualStudio.Debugger.Metadata;
using Roslyn.Utilities;
using MethodAttributes = System.Reflection.MethodAttributes;
using Type = Microsoft.VisualStudio.Debugger.Metadata.Type;
using TypeCode = Microsoft.VisualStudio.Debugger.Metadata.TypeCode;
 
namespace Microsoft.CodeAnalysis.ExpressionEvaluator
{
    internal static class TypeHelpers
    {
        internal const BindingFlags MemberBindingFlags = BindingFlags.Public |
                                                         BindingFlags.NonPublic |
                                                         BindingFlags.Instance |
                                                         BindingFlags.Static |
                                                         BindingFlags.DeclaredOnly;
 
        internal static void AppendTypeMembers(
            this Type type,
            ArrayBuilder<MemberAndDeclarationInfo> includedMembers,
            Predicate<MemberInfo> predicate,
            ResultProvider resultProvider,
            Type declaredType,
            DkmClrAppDomain appDomain,
            bool includeInherited,
            bool hideNonPublic,
            bool isProxyType,
            bool raw,
            bool supportsFavorites,
            DkmClrObjectFavoritesInfo favoritesInfo)
        {
            Debug.Assert(!type.IsInterface);
 
            var memberLocation = DeclarationInfo.FromSubTypeOfDeclaredType;
            var previousDeclarationMap = includeInherited ? new Dictionary<string, DeclarationInfo>() : null;
 
            int inheritanceLevel = 0;
            while (!type.IsObject())
            {
                if (type.Equals(declaredType))
                {
                    Debug.Assert(memberLocation == DeclarationInfo.FromSubTypeOfDeclaredType);
                    memberLocation = DeclarationInfo.FromDeclaredTypeOrBase;
                }
 
                // Get the state from DebuggerBrowsableAttributes for the members of the current type.
                var browsableState = DkmClrType.Create(appDomain, type).GetDebuggerBrowsableAttributeState();
 
                // Disable favorites if any of the members have a browsable state of RootHidden
                if (supportsFavorites && browsableState != null)
                {
                    foreach (var browsableStateValue in browsableState.Values)
                    {
                        if (browsableStateValue == DkmClrDebuggerBrowsableAttributeState.RootHidden)
                        {
                            supportsFavorites = false;
                            break;
                        }
                    }
                }
 
                // Get the favorites information if it is supported.
                // NOTE: Using a Dictionary since Hashset is not available in .net 2.0
                Dictionary<string, object> favoritesMemberNames = null;
                if (supportsFavorites && favoritesInfo?.Favorites != null)
                {
                    favoritesMemberNames = new Dictionary<string, object>(favoritesInfo.Favorites.Count);
 
                    foreach (var favorite in favoritesInfo.Favorites)
                    {
                        favoritesMemberNames.Add(favorite, null);
                    }
                }
 
                // Hide non-public members if hideNonPublic is specified (intended to reflect the
                // DkmInspectionContext's DkmEvaluationFlags), and the type is from an assembly
                // with no symbols.
                var hideNonPublicBehavior = DeclarationInfo.None;
                if (hideNonPublic)
                {
                    var moduleInstance = appDomain.FindClrModuleInstance(type.Module.ModuleVersionId);
                    if (moduleInstance == null || moduleInstance.Module == null)
                    {
                        // Synthetic module or no symbols loaded.
                        hideNonPublicBehavior = DeclarationInfo.HideNonPublic;
                    }
                }
 
                foreach (var member in type.GetMembers(MemberBindingFlags))
                {
                    var memberName = member.Name;
                    var isGenerated = memberName.IsCompilerGenerated();
 
                    if (isGenerated)
                    {
                        // Some generated names are displayed under their original (unmangled) name,
                        // others are only shown when displaying raw object.
                        if (resultProvider.TryGetGeneratedMemberDisplay(memberName, out var unmangledMemberName))
                        {
                            memberName = unmangledMemberName;
                        }
                        else if (!raw)
                        {
                            continue;
                        }
                    }
 
                    // The native EE shows proxy members regardless of accessibility if they have a
                    // DebuggerBrowsable attribute of any value. Match that behaviour here.
                    if (!isProxyType || browsableState == null || !browsableState.ContainsKey(memberName))
                    {
                        if (!predicate(member))
                        {
                            continue;
                        }
                    }
 
                    // This represents information about the immediately preceding (more derived)
                    // declaration with the same name as the current member.
                    var previousDeclaration = DeclarationInfo.None;
                    var memberNameAlreadySeen = false;
                    if (includeInherited)
                    {
                        memberNameAlreadySeen = previousDeclarationMap.TryGetValue(memberName, out previousDeclaration);
                        if (memberNameAlreadySeen)
                        {
                            // There was a name conflict, so we'll need to include the declaring
                            // type of the member to disambiguate.
                            previousDeclaration |= DeclarationInfo.IncludeTypeInMemberName;
                        }
 
                        // Update previous member with name hiding (casting) and declared location information for next time.
                        previousDeclarationMap[memberName] =
                            (previousDeclaration & ~(DeclarationInfo.RequiresExplicitCast |
                                DeclarationInfo.FromSubTypeOfDeclaredType)) |
                            member.AccessingBaseMemberWithSameNameRequiresExplicitCast() |
                            memberLocation;
                    }
 
                    Debug.Assert(memberNameAlreadySeen != (previousDeclaration == DeclarationInfo.None));
 
                    // Decide whether to include this member in the list of members to display.
                    if (!memberNameAlreadySeen || previousDeclaration.IsSet(DeclarationInfo.RequiresExplicitCast))
                    {
                        DkmClrDebuggerBrowsableAttributeState? browsableStateValue = null;
                        if (browsableState != null)
                        {
                            DkmClrDebuggerBrowsableAttributeState value;
                            if (browsableState.TryGetValue(memberName, out value))
                            {
                                browsableStateValue = value;
                            }
                        }
 
                        if (memberLocation.IsSet(DeclarationInfo.FromSubTypeOfDeclaredType))
                        {
                            // If the current type is a sub-type of the declared type, then
                            // we always need to insert a cast to access the member
                            previousDeclaration |= DeclarationInfo.RequiresExplicitCast;
                        }
                        else if (previousDeclaration.IsSet(DeclarationInfo.FromSubTypeOfDeclaredType))
                        {
                            // If the immediately preceding member (less derived) was
                            // declared on a sub-type of the declared type, then we'll
                            // ignore the casting bit.  Accessing a member through the
                            // declared type is the same as casting to that type, so
                            // the cast would be redundant.
                            previousDeclaration &= ~DeclarationInfo.RequiresExplicitCast;
                        }
 
                        previousDeclaration |= hideNonPublicBehavior;
 
                        includedMembers.Add(
                            new MemberAndDeclarationInfo(
                                member,
                                memberName,
                                browsableStateValue,
                                previousDeclaration,
                                inheritanceLevel,
                                isGenerated: isGenerated,
                                canFavorite: supportsFavorites && !previousDeclaration.HasFlag(DeclarationInfo.IncludeTypeInMemberName),
                                isFavorite: favoritesMemberNames?.ContainsKey(memberName) == true && !previousDeclaration.HasFlag(DeclarationInfo.IncludeTypeInMemberName)));
                    }
                }
 
                if (!includeInherited)
                {
                    break;
                }
 
                type = type.BaseType;
                inheritanceLevel++;
            }
 
            includedMembers.Sort(MemberAndDeclarationInfo.Comparer);
        }
 
        private static DeclarationInfo AccessingBaseMemberWithSameNameRequiresExplicitCast(this MemberInfo member)
        {
            switch (member.MemberType)
            {
                case MemberTypes.Field:
                    return DeclarationInfo.RequiresExplicitCast;
                case MemberTypes.Property:
                    var getMethod = GetNonIndexerGetMethod((PropertyInfo)member);
                    if ((getMethod != null) &&
                        (!getMethod.IsVirtual || ((getMethod.Attributes & MethodAttributes.VtableLayoutMask) == MethodAttributes.NewSlot)))
                    {
                        return DeclarationInfo.RequiresExplicitCast;
                    }
                    return DeclarationInfo.None;
                default:
                    throw ExceptionUtilities.UnexpectedValue(member.MemberType);
            }
        }
 
        internal static bool IsVisibleMember(MemberInfo member)
        {
            switch (member.MemberType)
            {
                case MemberTypes.Field:
                    return true;
                case MemberTypes.Property:
                    return GetNonIndexerGetMethod((PropertyInfo)member) != null;
            }
            return false;
        }
 
        /// <summary>
        /// Returns true if the member is public or protected.
        /// </summary>
        internal static bool IsPublic(this MemberInfo member)
        {
            // Matches native EE which includes protected members.
            switch (member.MemberType)
            {
                case MemberTypes.Field:
                    {
                        var field = (FieldInfo)member;
                        var attributes = field.Attributes;
                        return ((attributes & System.Reflection.FieldAttributes.Public) == System.Reflection.FieldAttributes.Public) ||
                            ((attributes & System.Reflection.FieldAttributes.Family) == System.Reflection.FieldAttributes.Family);
                    }
                case MemberTypes.Property:
                    {
                        // Native EE uses the accessibility of the property rather than getter
                        // so "public object P { private get; set; }" is treated as public.
                        // Instead, we drop properties if the getter is inaccessible.
                        var getMethod = GetNonIndexerGetMethod((PropertyInfo)member);
                        if (getMethod == null)
                        {
                            return false;
                        }
                        var attributes = getMethod.Attributes;
                        return ((attributes & System.Reflection.MethodAttributes.Public) == System.Reflection.MethodAttributes.Public) ||
                            ((attributes & System.Reflection.MethodAttributes.Family) == System.Reflection.MethodAttributes.Family);
                    }
                default:
                    return false;
            }
        }
 
        private static MethodInfo GetNonIndexerGetMethod(PropertyInfo property)
        {
            return (property.GetIndexParameters().Length == 0)
                ? property.GetGetMethod(nonPublic: true)
                : null;
        }
 
        internal static bool IsBoolean(this Type type)
        {
            return Type.GetTypeCode(type) == TypeCode.Boolean;
        }
 
        internal static bool IsCharacter(this Type type)
        {
            return Type.GetTypeCode(type) == TypeCode.Char;
        }
 
        internal static bool IsDecimal(this Type type)
        {
            return Type.GetTypeCode(type) == TypeCode.Decimal;
        }
 
        internal static bool IsDateTime(this Type type)
        {
            return Type.GetTypeCode(type) == TypeCode.DateTime;
        }
 
        internal static bool IsObject(this Type type)
        {
            bool result = type.IsClass && (type.BaseType == null) && !type.IsPointer;
            Debug.Assert(result == type.IsMscorlibType("System", "Object"));
            return result;
        }
 
        internal static bool IsValueType(this Type type)
        {
            return type.IsMscorlibType("System", "ValueType");
        }
 
        internal static bool IsString(this Type type)
        {
            return Type.GetTypeCode(type) == TypeCode.String;
        }
 
        internal static bool IsVoid(this Type type)
        {
            return type.IsMscorlibType("System", "Void") && !type.IsGenericType;
        }
 
        internal static bool IsIEnumerable(this Type type)
        {
            return type.IsMscorlibType("System.Collections", "IEnumerable");
        }
 
        internal static bool IsIntPtr(this Type type)
            => type.IsMscorlibType("System", "IntPtr");
 
        internal static bool IsUIntPtr(this Type type)
            => type.IsMscorlibType("System", "UIntPtr");
 
        internal static bool IsIEnumerableOfT(this Type type)
        {
            return type.IsMscorlibType("System.Collections.Generic", "IEnumerable`1");
        }
 
        internal static bool IsTypeVariables(this Type type)
        {
            return type.IsType(null, "<>c__TypeVariables");
        }
 
        internal static bool IsComObject(this Type type)
        {
            return type.IsType("System", "__ComObject");
        }
 
        internal static bool IsDynamicProperty(this Type type)
        {
            return type.IsType("Microsoft.CSharp.RuntimeBinder", "DynamicProperty");
        }
 
        internal static bool IsDynamicDebugViewEmptyException(this Type type)
        {
            return type.IsType("Microsoft.CSharp.RuntimeBinder", "DynamicDebugViewEmptyException");
        }
 
        internal static bool IsIDynamicMetaObjectProvider(this Type type)
        {
            foreach (var @interface in type.GetInterfaces())
            {
                if (@interface.IsType("System.Dynamic", "IDynamicMetaObjectProvider"))
                {
                    return true;
                }
            }
            return false;
        }
 
        /// <summary>
        /// Returns type argument if the type is
        /// Nullable&lt;T&gt;, otherwise null.
        /// </summary>
        internal static Type GetNullableTypeArgument(this Type type)
        {
            if (type.IsMscorlibType("System", "Nullable`1"))
            {
                var typeArgs = type.GetGenericArguments();
                if (typeArgs.Length == 1)
                {
                    return typeArgs[0];
                }
            }
            return null;
        }
 
        internal static bool IsNullable(this Type type)
        {
            return type.GetNullableTypeArgument() != null;
        }
 
        internal static DkmClrValue GetFieldValue(this DkmClrValue value, string name, DkmInspectionContext inspectionContext)
        {
            return value.GetMemberValue(name, (int)MemberTypes.Field, ParentTypeName: null, InspectionContext: inspectionContext);
        }
 
        internal static DkmClrValue GetPropertyValue(this DkmClrValue value, string name, DkmInspectionContext inspectionContext)
        {
            return value.GetMemberValue(name, (int)MemberTypes.Property, ParentTypeName: null, InspectionContext: inspectionContext);
        }
 
        internal static DkmClrValue GetNullableValue(this DkmClrValue value, Type nullableTypeArg, DkmInspectionContext inspectionContext)
        {
            var valueType = value.Type.GetLmrType();
            if (valueType.Equals(nullableTypeArg))
            {
                return value;
            }
            return value.GetNullableValue(inspectionContext);
        }
 
        internal static DkmClrValue GetNullableValue(this DkmClrValue value, DkmInspectionContext inspectionContext)
        {
            Debug.Assert(value.Type.GetLmrType().IsNullable());
 
            var hasValue = value.GetFieldValue(InternalWellKnownMemberNames.NullableHasValue, inspectionContext);
            if (object.Equals(hasValue.HostObjectValue, false))
            {
                return null;
            }
 
            return value.GetFieldValue(InternalWellKnownMemberNames.NullableValue, inspectionContext);
        }
 
        internal const int TupleFieldRestPosition = 8;
        private const string TupleTypeNamePrefix = "ValueTuple`";
        private const string TupleFieldItemNamePrefix = "Item";
        internal const string TupleFieldRestName = "Rest";
 
        // See NamedTypeSymbol.IsTupleCompatible.
        internal static bool IsTupleCompatible(this Type type, out int cardinality)
        {
            if (type.IsGenericType &&
                AreNamesEqual(type.Namespace, "System") &&
                type.Name.StartsWith(TupleTypeNamePrefix, StringComparison.Ordinal))
            {
                var typeArguments = type.GetGenericArguments();
                int n = typeArguments.Length;
                if ((n > 0) && (n <= TupleFieldRestPosition))
                {
                    if (!AreNamesEqual(type.Name, TupleTypeNamePrefix + n))
                    {
                        cardinality = 0;
                        return false;
                    }
 
                    if (n < TupleFieldRestPosition)
                    {
                        cardinality = n;
                        return true;
                    }
 
                    var restType = typeArguments[n - 1];
                    int restCardinality;
                    if (restType.IsTupleCompatible(out restCardinality))
                    {
                        cardinality = n - 1 + restCardinality;
                        return true;
                    }
                }
            }
 
            cardinality = 0;
            return false;
        }
 
        // Returns cardinality if tuple type, otherwise 0.
        internal static int GetTupleCardinalityIfAny(this Type type)
        {
            int cardinality;
            type.IsTupleCompatible(out cardinality);
            return cardinality;
        }
 
        internal static FieldInfo GetTupleField(this Type type, string name)
        {
            return type.GetField(name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
        }
 
        internal static string GetTupleFieldName(int index)
        {
            Debug.Assert(index >= 0);
            return TupleFieldItemNamePrefix + (index + 1);
        }
 
        internal static bool TryGetTupleFieldValues(this DkmClrValue tuple, int cardinality, ArrayBuilder<string> values, DkmInspectionContext inspectionContext)
        {
            while (true)
            {
                var type = tuple.Type.GetLmrType();
                int n = Math.Min(cardinality, TupleFieldRestPosition - 1);
                for (int index = 0; index < n; index++)
                {
                    var fieldName = GetTupleFieldName(index);
                    var fieldInfo = type.GetTupleField(fieldName);
                    if (fieldInfo == null)
                    {
                        return false;
                    }
                    var value = tuple.GetFieldValue(fieldName, inspectionContext);
                    var str = value.GetValueString(inspectionContext, Formatter.NoFormatSpecifiers);
                    values.Add(str);
                }
                cardinality -= n;
                if (cardinality == 0)
                {
                    return true;
                }
                var restInfo = type.GetTupleField(TypeHelpers.TupleFieldRestName);
                if (restInfo == null)
                {
                    return false;
                }
                tuple = tuple.GetFieldValue(TupleFieldRestName, inspectionContext);
            }
        }
 
        internal static Type GetBaseTypeOrNull(this Type underlyingType, DkmClrAppDomain appDomain, out DkmClrType type)
        {
            Debug.Assert((underlyingType.BaseType != null) || underlyingType.IsPointer || underlyingType.IsArray, "BaseType should only return null if the underlyingType is a pointer or array.");
 
            underlyingType = underlyingType.BaseType;
            type = (underlyingType != null) ? DkmClrType.Create(appDomain, underlyingType) : null;
 
            return underlyingType;
        }
 
        /// <summary>
        /// Get the first attribute from <see cref="DkmClrType.GetEvalAttributes()"/> (including inherited attributes)
        /// that is of type T, as well as the type that it targeted.
        /// </summary>
        internal static bool TryGetEvalAttribute<T>(this DkmClrType type, out DkmClrType attributeTarget, out T evalAttribute)
            where T : DkmClrEvalAttribute
        {
            attributeTarget = null;
            evalAttribute = null;
 
            var appDomain = type.AppDomain;
            var underlyingType = type.GetLmrType();
            while ((underlyingType != null) && !underlyingType.IsObject())
            {
                foreach (var attribute in type.GetEvalAttributes())
                {
                    evalAttribute = attribute as T;
                    if (evalAttribute != null)
                    {
                        attributeTarget = type;
                        return true;
                    }
                }
 
                underlyingType = underlyingType.GetBaseTypeOrNull(appDomain, out type);
            }
 
            return false;
        }
 
        /// <summary>
        /// Returns the set of DebuggerBrowsableAttribute state for the
        /// members of the type, indexed by member name, or null if there
        /// are no DebuggerBrowsableAttributes on members of the type.
        /// </summary>
        private static Dictionary<string, DkmClrDebuggerBrowsableAttributeState> GetDebuggerBrowsableAttributeState(this DkmClrType type)
        {
            Dictionary<string, DkmClrDebuggerBrowsableAttributeState> result = null;
            foreach (var attribute in type.GetEvalAttributes())
            {
                var browsableAttribute = attribute as DkmClrDebuggerBrowsableAttribute;
                if (browsableAttribute == null)
                {
                    continue;
                }
                result ??= new Dictionary<string, DkmClrDebuggerBrowsableAttributeState>();
 
                // There can be multiple same attributes for derived classes.
                // Debugger provides attributes starting from derived classes and then up to base ones.
                // We should use derived attributes if there is more than one instance.
                if (!result.ContainsKey(browsableAttribute.TargetMember))
                {
                    result.Add(browsableAttribute.TargetMember, browsableAttribute.State);
                }
            }
            return result;
        }
 
        /// <summary>
        /// Extracts information from the first <see cref="DebuggerDisplayAttribute"/> on the runtime type of <paramref name="value"/>, if there is one.
        /// </summary>
        internal static bool TryGetDebuggerDisplayInfo(this DkmClrValue value, out DebuggerDisplayInfo displayInfo)
        {
            displayInfo = null;
 
            // The native EE does not consider DebuggerDisplayAttribute
            // on null or error instances.
            if (value.IsError() || value.IsNull)
            {
                return false;
            }
 
            var clrType = value.Type;
            displayInfo = new DebuggerDisplayInfo(clrType);
 
            DkmClrObjectFavoritesInfo favoritesInfo = clrType.GetFavorites();
            if (favoritesInfo != null)
            {
                displayInfo = displayInfo.WithFavoritesInfo(favoritesInfo);
            }
 
            DkmClrType attributeTarget;
            DkmClrDebuggerDisplayAttribute attribute;
            if (clrType.TryGetEvalAttribute(out attributeTarget, out attribute)) // First, as in dev12.
            {
                displayInfo = displayInfo.WithDebuggerDisplayAttribute(attribute, attributeTarget);
            }
 
            return displayInfo.HasValues;
        }
 
        /// <summary>
        /// Returns the array of <see cref="DkmCustomUIVisualizerInfo"/> objects of the type from its <see cref="DkmClrDebuggerVisualizerAttribute"/> attributes,
        /// or null if the type has no [DebuggerVisualizer] attributes associated with it.
        /// </summary>
        internal static DkmCustomUIVisualizerInfo[] GetDebuggerCustomUIVisualizerInfo(this DkmClrType type)
        {
            var builder = ArrayBuilder<DkmCustomUIVisualizerInfo>.GetInstance();
 
            var appDomain = type.AppDomain;
            var underlyingType = type.GetLmrType();
            while ((underlyingType != null) && !underlyingType.IsObject())
            {
                foreach (var attribute in type.GetEvalAttributes())
                {
                    var visualizerAttribute = attribute as DkmClrDebuggerVisualizerAttribute;
                    if (visualizerAttribute == null)
                    {
                        continue;
                    }
 
                    builder.Add(DkmCustomUIVisualizerInfo.Create((uint)builder.Count,
                        visualizerAttribute.VisualizerDescription,
                        visualizerAttribute.VisualizerDescription,
                        // ClrCustomVisualizerVSHost is a registry entry that specifies the CLSID of the
                        // IDebugCustomViewer class that will be instantiated to display the custom visualizer.
                        "ClrCustomVisualizerVSHost",
                        visualizerAttribute.UISideVisualizerTypeName,
                        visualizerAttribute.UISideVisualizerAssemblyName,
                        visualizerAttribute.UISideVisualizerAssemblyLocation,
                        visualizerAttribute.DebuggeeSideVisualizerTypeName,
                        visualizerAttribute.DebuggeeSideVisualizerAssemblyName,
                        visualizerAttribute.ExtensionPartId));
                }
 
                underlyingType = underlyingType.GetBaseTypeOrNull(appDomain, out type);
            }
 
            var result = (builder.Count > 0) ? builder.ToArray() : null;
            builder.Free();
            return result;
        }
 
        internal static DkmClrType GetProxyType(this DkmClrType type)
        {
            // CONSIDER: If needed, we could probably compute a new DynamicAttribute for
            // the proxy type based on the DynamicAttribute of the argument.
            DkmClrType attributeTarget;
            DkmClrDebuggerTypeProxyAttribute attribute;
            if (type.TryGetEvalAttribute(out attributeTarget, out attribute))
            {
                var targetedType = attributeTarget.GetLmrType();
                var proxyType = attribute.ProxyType;
                var underlyingProxy = proxyType.GetLmrType();
                if (underlyingProxy.IsGenericType && targetedType.IsGenericType)
                {
                    var typeArgs = targetedType.GetGenericArguments();
 
                    // Drop the proxy type if the arity does not match.
                    if (typeArgs.Length != underlyingProxy.GetGenericArguments().Length)
                    {
                        return null;
                    }
 
                    // Substitute target type arguments for proxy type arguments.
                    var constructedProxy = underlyingProxy.Substitute(underlyingProxy, typeArgs);
                    proxyType = DkmClrType.Create(type.AppDomain, constructedProxy);
                }
 
                return proxyType;
            }
 
            return null;
        }
 
        /// <summary>
        /// Substitute references to type parameters from 'typeDef'
        /// with type arguments from 'typeArgs' in type 'type'.
        /// </summary>
        internal static Type Substitute(this Type type, Type typeDef, Type[] typeArgs)
        {
            Debug.Assert(typeDef.IsGenericTypeDefinition);
            Debug.Assert(typeDef.GetGenericArguments().Length == typeArgs.Length);
 
            if (type.IsGenericType)
            {
                var builder = ArrayBuilder<Type>.GetInstance();
                foreach (var t in type.GetGenericArguments())
                {
                    builder.Add(t.Substitute(typeDef, typeArgs));
                }
                var typeDefinition = type.GetGenericTypeDefinition();
                return typeDefinition.MakeGenericType(builder.ToArrayAndFree());
            }
            else if (type.IsArray)
            {
                var elementType = type.GetElementType();
                elementType = elementType.Substitute(typeDef, typeArgs);
                var n = type.GetArrayRank();
                return (n == 1) ? elementType.MakeArrayType() : elementType.MakeArrayType(n);
            }
            else if (type.IsPointer)
            {
                var elementType = type.GetElementType();
                elementType = elementType.Substitute(typeDef, typeArgs);
                return elementType.MakePointerType();
            }
            else if (type.IsGenericParameter)
            {
                if (type.DeclaringType.Equals(typeDef))
                {
                    var ordinal = type.GenericParameterPosition;
                    return typeArgs[ordinal];
                }
            }
 
            return type;
        }
 
        // Returns the IEnumerable interface implemented by the given type,
        // preferring System.Collections.Generic.IEnumerable<T> over
        // System.Collections.IEnumerable. If there are multiple implementations
        // of IEnumerable<T> on base and derived types, the implementation on
        // the most derived type is returned. If there are multiple implementations
        // of IEnumerable<T> on the same type, it is undefined which is returned.
        internal static Type GetIEnumerableImplementationIfAny(this Type type)
        {
            var t = type;
            do
            {
                foreach (var @interface in t.GetInterfacesOnType())
                {
                    if (@interface.IsIEnumerableOfT())
                    {
                        // Return the first implementation of IEnumerable<T>.
                        return @interface;
                    }
                }
                t = t.BaseType;
            } while (t != null);
 
            foreach (var @interface in type.GetInterfaces())
            {
                if (@interface.IsIEnumerable())
                {
                    return @interface;
                }
            }
 
            return null;
        }
 
        internal static bool IsEmptyResultsViewException(this Type type)
        {
            return type.IsType("System.Linq", "SystemCore_EnumerableDebugViewEmptyException");
        }
 
        internal static bool IsOrInheritsFrom(this Type type, Type baseType)
        {
            Debug.Assert(type != null);
            Debug.Assert(baseType != null);
            Debug.Assert(!baseType.IsInterface);
 
            if (type.IsInterface)
            {
                return false;
            }
 
            do
            {
                if (type.Equals(baseType))
                {
                    return true;
                }
                type = type.BaseType;
            }
            while (type != null);
 
            return false;
        }
 
        private static bool IsMscorlibType(this Type type, string @namespace, string name)
        {
            // Ignore IsMscorlib for now since type.Assembly returns
            // System.Runtime.dll for some types in mscorlib.dll.
            // TODO: Re-enable commented out check.
            return type.IsType(@namespace, name) /*&& type.Assembly.IsMscorlib()*/;
        }
 
        internal static bool IsOrInheritsFrom(this Type type, string @namespace, string name)
        {
            do
            {
                if (type.IsType(@namespace, name))
                {
                    return true;
                }
                type = type.BaseType;
            }
            while (type != null);
            return false;
        }
 
        internal static bool IsType(this Type type, string @namespace, string name)
        {
            Debug.Assert((@namespace == null) || (@namespace.Length > 0)); // Type.Namespace is null not empty.
            Debug.Assert(!string.IsNullOrEmpty(name));
            return AreNamesEqual(type.Namespace, @namespace) &&
                AreNamesEqual(type.Name, name);
        }
 
        private static bool AreNamesEqual(string nameA, string nameB)
        {
            return string.Equals(nameA, nameB, StringComparison.Ordinal);
        }
 
        internal static MemberInfo GetOriginalDefinition(this MemberInfo member)
        {
            var declaringType = member.DeclaringType;
            if (!declaringType.IsGenericType)
            {
                return member;
            }
 
            var declaringTypeOriginalDefinition = declaringType.GetGenericTypeDefinition();
            if (declaringType.Equals(declaringTypeOriginalDefinition))
            {
                return member;
            }
 
            foreach (var candidate in declaringTypeOriginalDefinition.GetMember(member.Name, MemberBindingFlags))
            {
                var memberType = candidate.MemberType;
                if (memberType != member.MemberType)
                    continue;
 
                switch (memberType)
                {
                    case MemberTypes.Field:
                        return candidate;
                    case MemberTypes.Property:
                        Debug.Assert(((PropertyInfo)member).GetIndexParameters().Length == 0);
                        if (((PropertyInfo)candidate).GetIndexParameters().Length == 0)
                        {
                            return candidate;
                        }
                        break;
                    default:
                        throw ExceptionUtilities.UnexpectedValue(memberType);
                }
            }
 
            throw ExceptionUtilities.Unreachable();
        }
 
        internal static Type GetInterfaceListEntry(this Type interfaceType, Type declaration)
        {
            Debug.Assert(interfaceType.IsInterface);
 
            if (!interfaceType.IsGenericType || !declaration.IsGenericType)
            {
                return interfaceType;
            }
 
            var index = Array.IndexOf(declaration.GetInterfacesOnType(), interfaceType);
            Debug.Assert(index >= 0);
 
            var result = declaration.GetGenericTypeDefinition().GetInterfacesOnType()[index];
            Debug.Assert(interfaceType.GetGenericTypeDefinition().Equals(result.GetGenericTypeDefinition()));
            return result;
        }
 
        internal static MemberAndDeclarationInfo GetMemberByName(this DkmClrType type, string name)
        {
            var member = type.GetLmrType().GetMember(name, MemberBindingFlags).Single();
            return new MemberAndDeclarationInfo(member, displayName: member.Name, browsableState: null, info: DeclarationInfo.None, inheritanceLevel: 0, isGenerated: false, canFavorite: false, isFavorite: false);
        }
    }
}