File: MetadataReader\MetadataDecoder.cs
Web Access
Project: src\src\Compilers\Core\Portable\Microsoft.CodeAnalysis.csproj (Microsoft.CodeAnalysis)
// 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.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection.Metadata;
using System.Runtime.InteropServices;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Symbols;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis
{
    [StructLayout(LayoutKind.Auto)]
    internal readonly struct ModifierInfo<TypeSymbol>
        where TypeSymbol : class
    {
        internal readonly bool IsOptional;
        internal readonly TypeSymbol Modifier;
 
        public ModifierInfo(bool isOptional, TypeSymbol modifier)
        {
            IsOptional = isOptional;
            Modifier = modifier;
        }
    }
 
    internal static class ModifierInfoExtensions
    {
        internal static bool AnyRequired<TypeSymbol>(this ImmutableArray<ModifierInfo<TypeSymbol>> modifiers) where TypeSymbol : class
        {
            return !modifiers.IsDefaultOrEmpty && modifiers.Any(static m => !m.IsOptional);
        }
    }
 
    internal readonly struct FieldInfo<TypeSymbol>
        where TypeSymbol : class
    {
        internal readonly bool IsByRef;
        internal readonly ImmutableArray<ModifierInfo<TypeSymbol>> RefCustomModifiers;
        internal readonly TypeSymbol Type;
        internal readonly ImmutableArray<ModifierInfo<TypeSymbol>> CustomModifiers;
 
        internal FieldInfo(bool isByRef, ImmutableArray<ModifierInfo<TypeSymbol>> refCustomModifiers, TypeSymbol type, ImmutableArray<ModifierInfo<TypeSymbol>> customModifiers)
        {
            IsByRef = isByRef;
            RefCustomModifiers = refCustomModifiers;
            Type = type;
            CustomModifiers = customModifiers;
        }
 
        internal FieldInfo(TypeSymbol type) : this(isByRef: false, refCustomModifiers: default, type, customModifiers: default)
        {
        }
    }
 
    [StructLayout(LayoutKind.Auto)]
    internal struct ParamInfo<TypeSymbol>
        where TypeSymbol : class
    {
        internal bool IsByRef;
        internal TypeSymbol Type;
        internal ParameterHandle Handle; // may be nil
        internal ImmutableArray<ModifierInfo<TypeSymbol>> RefCustomModifiers;
        internal ImmutableArray<ModifierInfo<TypeSymbol>> CustomModifiers;
    }
 
    [StructLayout(LayoutKind.Auto)]
    internal readonly struct LocalInfo<TypeSymbol>
        where TypeSymbol : class
    {
        internal readonly byte[] SignatureOpt;
        internal readonly TypeSymbol Type;
        internal readonly ImmutableArray<ModifierInfo<TypeSymbol>> CustomModifiers;
        internal readonly LocalSlotConstraints Constraints;
 
        internal LocalInfo(TypeSymbol type, ImmutableArray<ModifierInfo<TypeSymbol>> customModifiers, LocalSlotConstraints constraints, byte[] signatureOpt)
        {
            Debug.Assert(type != null);
 
            this.Type = type;
            this.CustomModifiers = customModifiers;
            this.Constraints = constraints;
            this.SignatureOpt = signatureOpt;
        }
 
        internal LocalInfo<TypeSymbol> WithSignature(byte[] signature)
        {
            return new LocalInfo<TypeSymbol>(this.Type, this.CustomModifiers, this.Constraints, signature);
        }
 
        public bool IsByRef => (Constraints & LocalSlotConstraints.ByRef) != 0;
 
        public bool IsPinned => (Constraints & LocalSlotConstraints.Pinned) != 0;
    }
 
#nullable enable
    internal interface IAttributeNamedArgumentDecoder
    {
        (KeyValuePair<string, TypedConstant> nameValuePair, bool isProperty, SerializationTypeCode typeCode, SerializationTypeCode elementTypeCode) DecodeCustomAttributeNamedArgumentOrThrow(ref BlobReader argReader);
    }
 
    internal abstract class MetadataDecoder<ModuleSymbol, TypeSymbol, MethodSymbol, FieldSymbol, Symbol> :
        TypeNameDecoder<ModuleSymbol, TypeSymbol>,
        IAttributeNamedArgumentDecoder
        where ModuleSymbol : class, IModuleSymbolInternal
        where TypeSymbol : class, Symbol, ITypeSymbolInternal
        where MethodSymbol : class, Symbol, IMethodSymbolInternal
        where FieldSymbol : class, Symbol, IFieldSymbolInternal
        where Symbol : class, ISymbolInternal
#nullable disable
    {
        public readonly PEModule Module;
 
        // Identity of an assembly containing the module, or null if the module is a standalone module
        private readonly AssemblyIdentity _containingAssemblyIdentity;
 
        internal MetadataDecoder(PEModule module, AssemblyIdentity containingAssemblyIdentity, SymbolFactory<ModuleSymbol, TypeSymbol> factory, ModuleSymbol moduleSymbol) :
            base(factory, moduleSymbol)
        {
            Debug.Assert(module != null);
            this.Module = module;
            _containingAssemblyIdentity = containingAssemblyIdentity;
        }
 
        internal TypeSymbol GetTypeOfToken(EntityHandle token)
        {
            bool isNoPiaLocalType;
            return GetTypeOfToken(token, out isNoPiaLocalType);
        }
 
        internal TypeSymbol GetTypeOfToken(EntityHandle token, out bool isNoPiaLocalType)
        {
            Debug.Assert(!token.IsNil);
 
            TypeSymbol type;
            HandleKind tokenType = token.Kind;
 
            switch (tokenType)
            {
                case HandleKind.TypeDefinition:
                    type = GetTypeOfTypeDef((TypeDefinitionHandle)token, out isNoPiaLocalType, isContainingType: false);
                    break;
 
                case HandleKind.TypeSpecification:
                    isNoPiaLocalType = false;
                    type = GetTypeOfTypeSpec((TypeSpecificationHandle)token);
                    break;
 
                case HandleKind.TypeReference:
                    type = GetTypeOfTypeRef((TypeReferenceHandle)token, out isNoPiaLocalType);
                    break;
 
                default:
                    isNoPiaLocalType = false;
                    type = GetUnsupportedMetadataTypeSymbol();
                    break;
            }
 
            Debug.Assert(type != null);
            return type;
        }
 
        private TypeSymbol GetTypeOfTypeSpec(TypeSpecificationHandle typeSpec)
        {
            TypeSymbol ptype;
 
            try
            {
                BlobReader memoryReader = this.Module.GetTypeSpecificationSignatureReaderOrThrow(typeSpec);
 
                bool refersToNoPiaLocalType;
                ptype = DecodeTypeOrThrow(ref memoryReader, out refersToNoPiaLocalType);
            }
            catch (BadImageFormatException mrEx)
            {
                ptype = GetUnsupportedMetadataTypeSymbol(mrEx); // an exception from metadata reader.
            }
            catch (UnsupportedSignatureContent)
            {
                ptype = GetUnsupportedMetadataTypeSymbol(); // unsupported signature content
            }
 
            return ptype;
        }
 
        /// <exception cref="UnsupportedSignatureContent">If the encoded type is invalid.</exception>
        /// <exception cref="BadImageFormatException">An exception from metadata reader.</exception>
        private TypeSymbol DecodeTypeOrThrow(ref BlobReader ppSig, out bool refersToNoPiaLocalType)
        {
            SignatureTypeCode typeCode = ppSig.ReadSignatureTypeCode();
            return DecodeTypeOrThrow(ref ppSig, typeCode, out refersToNoPiaLocalType);
        }
 
        /// <exception cref="UnsupportedSignatureContent">If the encoded type is invalid.</exception>
        /// <exception cref="BadImageFormatException">An exception from metadata reader.</exception>
        private TypeSymbol DecodeTypeOrThrow(ref BlobReader ppSig, SignatureTypeCode typeCode, out bool refersToNoPiaLocalType)
        {
            TypeSymbol typeSymbol;
            int paramPosition;
            ImmutableArray<ModifierInfo<TypeSymbol>> modifiers;
 
            refersToNoPiaLocalType = false;
 
            // Switch on the type.
            switch (typeCode)
            {
                case SignatureTypeCode.Void:
                case SignatureTypeCode.Boolean:
                case SignatureTypeCode.SByte:
                case SignatureTypeCode.Byte:
                case SignatureTypeCode.Int16:
                case SignatureTypeCode.UInt16:
                case SignatureTypeCode.Int32:
                case SignatureTypeCode.UInt32:
                case SignatureTypeCode.Int64:
                case SignatureTypeCode.UInt64:
                case SignatureTypeCode.Single:
                case SignatureTypeCode.Double:
                case SignatureTypeCode.Char:
                case SignatureTypeCode.String:
                case SignatureTypeCode.IntPtr:
                case SignatureTypeCode.UIntPtr:
                case SignatureTypeCode.Object:
                case SignatureTypeCode.TypedReference:
                    typeSymbol = GetSpecialType(typeCode.ToSpecialType());
                    break;
 
                case SignatureTypeCode.TypeHandle:
                    // Spec (6th edition): In II.23.2.12 and II.23.2.14, it is implied that the token in (CLASS | VALUETYPE) TypeDefOrRefOrSpecEncoded 
                    // can be a TypeSpec, when in fact it must be a TypeDef or TypeRef.
                    // See https://github.com/dotnet/roslyn/issues/7970
                    typeSymbol = GetSymbolForTypeHandleOrThrow(ppSig.ReadTypeHandle(), out refersToNoPiaLocalType, allowTypeSpec: false, requireShortForm: true);
                    break;
 
                case SignatureTypeCode.Array:
                    int countOfDimensions;
                    int countOfSizes;
                    int countOfLowerBounds;
 
                    modifiers = DecodeModifiersOrThrow(ref ppSig, out typeCode);
                    typeSymbol = DecodeTypeOrThrow(ref ppSig, typeCode, out refersToNoPiaLocalType);
                    if (!ppSig.TryReadCompressedInteger(out countOfDimensions) ||
                        !ppSig.TryReadCompressedInteger(out countOfSizes))
                    {
                        throw new UnsupportedSignatureContent();
                    }
 
                    // The most common case is when countOfSizes is 0.
                    ImmutableArray<int> sizes;
 
                    if (countOfSizes == 0)
                    {
                        sizes = ImmutableArray<int>.Empty;
                    }
                    else
                    {
                        var builder = ArrayBuilder<int>.GetInstance(countOfSizes);
 
                        for (int i = 0; i < countOfSizes; i++)
                        {
                            int size;
                            if (ppSig.TryReadCompressedInteger(out size))
                            {
                                builder.Add(size);
                            }
                            else
                            {
                                throw new UnsupportedSignatureContent();
                            }
                        }
 
                        sizes = builder.ToImmutableAndFree();
                    }
 
                    if (!ppSig.TryReadCompressedInteger(out countOfLowerBounds))
                    {
                        throw new UnsupportedSignatureContent();
                    }
 
                    // The most common case is when countOfLowerBounds == countOfDimensions and they are all 0.
                    // This is what Default will stand for.
                    ImmutableArray<int> lowerBounds = default(ImmutableArray<int>);
 
                    if (countOfLowerBounds == 0)
                    {
                        lowerBounds = ImmutableArray<int>.Empty;
                    }
                    else
                    {
                        ArrayBuilder<int> builder = countOfLowerBounds != countOfDimensions ? ArrayBuilder<int>.GetInstance(countOfLowerBounds, 0) : null;
 
                        for (int i = 0; i < countOfLowerBounds; i++)
                        {
                            int lowerBound;
                            if (ppSig.TryReadCompressedSignedInteger(out lowerBound))
                            {
                                if (lowerBound != 0)
                                {
                                    if (builder == null)
                                    {
                                        builder = ArrayBuilder<int>.GetInstance(countOfLowerBounds, 0);
                                    }
 
                                    builder[i] = lowerBound;
                                }
                            }
                            else
                            {
                                throw new UnsupportedSignatureContent();
                            }
                        }
 
                        if (builder != null)
                        {
                            lowerBounds = builder.ToImmutableAndFree();
                        }
                    }
 
                    typeSymbol = GetMDArrayTypeSymbol(countOfDimensions, typeSymbol, modifiers, sizes, lowerBounds);
                    break;
 
                case SignatureTypeCode.SZArray:
                    modifiers = DecodeModifiersOrThrow(ref ppSig, out typeCode);
                    typeSymbol = DecodeTypeOrThrow(ref ppSig, typeCode, out refersToNoPiaLocalType);
                    typeSymbol = GetSZArrayTypeSymbol(typeSymbol, modifiers);
                    break;
 
                case SignatureTypeCode.Pointer:
                    modifiers = DecodeModifiersOrThrow(ref ppSig, out typeCode);
                    typeSymbol = DecodeTypeOrThrow(ref ppSig, typeCode, out refersToNoPiaLocalType);
                    typeSymbol = MakePointerTypeSymbol(typeSymbol, modifiers);
                    break;
 
                case SignatureTypeCode.GenericTypeParameter:
                    if (!ppSig.TryReadCompressedInteger(out paramPosition))
                    {
                        throw new UnsupportedSignatureContent();
                    }
 
                    typeSymbol = GetGenericTypeParamSymbol(paramPosition);
                    break;
 
                case SignatureTypeCode.GenericMethodParameter:
                    if (!ppSig.TryReadCompressedInteger(out paramPosition))
                    {
                        throw new UnsupportedSignatureContent();
                    }
 
                    typeSymbol = GetGenericMethodTypeParamSymbol(paramPosition);
                    break;
 
                case SignatureTypeCode.GenericTypeInstance:
                    typeSymbol = DecodeGenericTypeInstanceOrThrow(ref ppSig, out refersToNoPiaLocalType);
                    break;
 
                case SignatureTypeCode.FunctionPointer:
                    var signatureHeader = ppSig.ReadSignatureHeader();
                    var parameters = DecodeSignatureParametersOrThrow(ref ppSig, signatureHeader, typeParameterCount: out int typeParamCount, shouldProcessAllBytes: false, isFunctionPointerSignature: true);
 
                    if (typeParamCount != 0)
                    {
                        throw new UnsupportedSignatureContent();
                    }
 
                    typeSymbol = MakeFunctionPointerTypeSymbol(Cci.CallingConventionUtils.FromSignatureConvention(signatureHeader.CallingConvention), ImmutableArray.Create(parameters));
                    break;
 
                default:
                    throw new UnsupportedSignatureContent();
            }
 
            return typeSymbol;
        }
 
        private TypeSymbol DecodeGenericTypeInstanceOrThrow(ref BlobReader ppSig, out bool refersToNoPiaLocalType)
        {
            SignatureTypeCode elementTypeCode = ppSig.ReadSignatureTypeCode();
            if (elementTypeCode != SignatureTypeCode.TypeHandle)
            {
                throw new UnsupportedSignatureContent();
            }
 
            EntityHandle tokenGeneric = ppSig.ReadTypeHandle();
            int argumentCount;
            if (!ppSig.TryReadCompressedInteger(out argumentCount))
            {
                throw new UnsupportedSignatureContent();
            }
 
            TypeSymbol generic = GetTypeOfToken(tokenGeneric, out refersToNoPiaLocalType);
            Debug.Assert(!refersToNoPiaLocalType || generic.TypeKind == TypeKind.Error);
 
            var argumentsBuilder = ArrayBuilder<KeyValuePair<TypeSymbol, ImmutableArray<ModifierInfo<TypeSymbol>>>>.GetInstance(argumentCount);
            var argumentRefersToNoPiaLocalTypeBuilder = ArrayBuilder<bool>.GetInstance(argumentCount);
 
            for (int argumentIndex = 0; argumentIndex < argumentCount; argumentIndex++)
            {
                bool argumentRefersToNoPia;
                SignatureTypeCode typeCode;
                ImmutableArray<ModifierInfo<TypeSymbol>> modifiers = DecodeModifiersOrThrow(ref ppSig, out typeCode);
                argumentsBuilder.Add(KeyValuePairUtil.Create(DecodeTypeOrThrow(ref ppSig, typeCode, out argumentRefersToNoPia), modifiers));
                argumentRefersToNoPiaLocalTypeBuilder.Add(argumentRefersToNoPia);
            }
 
            // The instantiated type might have a generic parent, in which case some or all of the type
            // arguments might actually be for the parent.
 
            var arguments = argumentsBuilder.ToImmutableAndFree();
            var argumentRefersToNoPiaLocalType = argumentRefersToNoPiaLocalTypeBuilder.ToImmutableAndFree();
            TypeSymbol typeSymbol = SubstituteTypeParameters(generic, arguments, argumentRefersToNoPiaLocalType);
 
            foreach (bool flag in argumentRefersToNoPiaLocalType)
            {
                if (flag)
                {
                    refersToNoPiaLocalType = true;
                    break;
                }
            }
 
            return typeSymbol;
        }
 
        /// <exception cref="UnsupportedSignatureContent">If the encoded type is invalid.</exception>
        /// <exception cref="BadImageFormatException">An exception from metadata reader.</exception>
        internal TypeSymbol GetSymbolForTypeHandleOrThrow(EntityHandle handle, out bool isNoPiaLocalType, bool allowTypeSpec, bool requireShortForm)
        {
            if (handle.IsNil)
            {
                throw new UnsupportedSignatureContent();
            }
 
            TypeSymbol typeSymbol;
            switch (handle.Kind)
            {
                case HandleKind.TypeDefinition:
                    typeSymbol = GetTypeOfTypeDef((TypeDefinitionHandle)handle, out isNoPiaLocalType, isContainingType: false);
                    break;
 
                case HandleKind.TypeReference:
                    typeSymbol = GetTypeOfTypeRef((TypeReferenceHandle)handle, out isNoPiaLocalType);
                    break;
 
                case HandleKind.TypeSpecification:
                    if (!allowTypeSpec)
                    {
                        throw new UnsupportedSignatureContent();
                    }
 
                    isNoPiaLocalType = false;
                    typeSymbol = GetTypeOfTypeSpec((TypeSpecificationHandle)handle);
                    break;
 
                default:
                    throw ExceptionUtilities.UnexpectedValue(handle.Kind);
            }
 
            // tomat: Breaking change
            // Metadata spec II.23.2.16 (Short form signatures) requires primitive types to be encoded using a short form:
            // 
            //  "The general specification for signatures leaves some leeway in how to encode certain items. For
            //   example, it appears valid to encode a String as either 
            //     long-form: (ELEMENT_TYPE_CLASS, TypeRef-to-System.String )
            //     short-form: ELEMENT_TYPE_STRING
            //   Only the short form is valid."
            // 
            // Native compilers accept long form signatures (actually IMetadataImport does).
            // When a MemberRef is emitted the signature blob is copied from the metadata reference to the resulting assembly. 
            // Such assembly doesn't PEVerify but the CLR type loader matches the MemberRef with the original signature 
            // (since they are identical copies).
            // 
            // Roslyn doesn't copy signature blobs to the resulting assembly, it encodes the MemberRef using the 
            // correct short type codes. If we allowed long forms in a signature we would produce IL that PEVerifies but
            // the type loader isn't able to load it since the MemberRef signature wouldn't match the original signature.
            // 
            // Rather then producing broken code we report an error at compile time.
 
            if (requireShortForm && typeSymbol.SpecialType.HasShortFormSignatureEncoding())
            {
                throw new UnsupportedSignatureContent();
            }
 
            return typeSymbol;
        }
 
        // MetaImport::GetTypeOfTypeRef
        private TypeSymbol GetTypeOfTypeRef(TypeReferenceHandle typeRef, out bool isNoPiaLocalType)
        {
            // This is a cache similar to one used by native compiler in MetaImport::GetTypeOfTypeRef.
            // TypeRef tokens are unique only within a Module
            ConcurrentDictionary<TypeReferenceHandle, TypeSymbol> cache = GetTypeRefHandleToTypeMap();
            TypeSymbol result;
 
            if (cache != null && cache.TryGetValue(typeRef, out result))
            {
                isNoPiaLocalType = false; // We do not cache otherwise.
                return result;
            }
 
            try
            {
                string name, @namespace;
                EntityHandle resolutionScope;
                Module.GetTypeRefPropsOrThrow(typeRef, out name, out @namespace, out resolutionScope);
                Debug.Assert(MetadataHelpers.IsValidMetadataIdentifier(name));
 
                MetadataTypeName mdName = @namespace.Length > 0
                    ? MetadataTypeName.FromNamespaceAndTypeName(@namespace, name)
                    : MetadataTypeName.FromTypeName(name);
 
                result = GetTypeByNameOrThrow(ref mdName, resolutionScope, out isNoPiaLocalType);
            }
            catch (BadImageFormatException mrEx)
            {
                result = GetUnsupportedMetadataTypeSymbol(mrEx); // an exception from metadata reader.
                isNoPiaLocalType = false;
            }
 
            Debug.Assert(result != null);
 
            // Cache the result, but only if it is not a local type because the cache doesn't retain this information.
            if (cache != null && !isNoPiaLocalType)
            {
                TypeSymbol result1 = cache.GetOrAdd(typeRef, result);
                Debug.Assert(result1.Equals(result, TypeCompareKind.ConsiderEverything));
            }
 
            return result;
        }
 
        // MetaImport::GetTypeByName
        /// <exception cref="BadImageFormatException">An exception from metadata reader.</exception>
        private TypeSymbol GetTypeByNameOrThrow(
            ref MetadataTypeName fullName,
            EntityHandle tokenResolutionScope,
            out bool isNoPiaLocalType)
        {
            HandleKind tokenType = tokenResolutionScope.Kind;
 
            // TODO: I believe refs can be parented by a def tokens too, not common, but.
            //       Should also do NoPia related checks.
 
            // The resolution scope should be either a type ref, an assembly or a module.
            if (tokenType == HandleKind.TypeReference)
            {
                if (tokenResolutionScope.IsNil)
                {
                    throw new BadImageFormatException();
                }
                TypeSymbol psymContainer = GetTypeOfToken(tokenResolutionScope);
                Debug.Assert(fullName.NamespaceName.Length == 0);
                isNoPiaLocalType = false;
                return LookupNestedTypeDefSymbol(psymContainer, ref fullName);
            }
 
            if (tokenType == HandleKind.AssemblyReference)
            {
                isNoPiaLocalType = false;
                var assemblyRef = (AssemblyReferenceHandle)tokenResolutionScope;
                if (assemblyRef.IsNil)
                {
                    throw new BadImageFormatException();
                }
                return LookupTopLevelTypeDefSymbol(Module.GetAssemblyReferenceIndexOrThrow(assemblyRef), ref fullName);
            }
 
            if (tokenType == HandleKind.ModuleReference)
            {
                var moduleRef = (ModuleReferenceHandle)tokenResolutionScope;
                if (moduleRef.IsNil)
                {
                    throw new BadImageFormatException();
                }
                return LookupTopLevelTypeDefSymbol(
                    Module.GetModuleRefNameOrThrow(moduleRef),
                    ref fullName,
                    out isNoPiaLocalType);
            }
 
            if (tokenResolutionScope == EntityHandle.ModuleDefinition)
            {
                // The last case is a little bit strange.  Here, the TypeRef's TypeDef
                // lives in the same module as the TypeRef itself.  This is represented
                // as a resolution scope of 0x00000001.
                return LookupTopLevelTypeDefSymbol(ref fullName, out isNoPiaLocalType);
            }
 
            isNoPiaLocalType = false;
            return GetUnsupportedMetadataTypeSymbol();
        }
 
        private TypeSymbol GetTypeOfTypeDef(TypeDefinitionHandle typeDef)
        {
            bool isNoPiaLocalType;
            return GetTypeOfTypeDef(typeDef, out isNoPiaLocalType, isContainingType: false);
        }
 
        private TypeSymbol GetTypeOfTypeDef(TypeDefinitionHandle typeDef, out bool isNoPiaLocalType, bool isContainingType)
        {
            try
            {
                // This is a cache similar to one used in MetaImport::GetTypeOfToken by native compiler.
                // TypeDef tokens are unique within Module.
                // This cache makes lookup of top level types about twice as fast, about three times as fast if 
                // EmittedNameToTypeMap in LookupTopLevelType doesn't contain the name. 
                // It is likely that gain for nested types will be bigger because we don't cache names of nested types.
 
                ConcurrentDictionary<TypeDefinitionHandle, TypeSymbol> cache = GetTypeHandleToTypeMap();
 
                TypeSymbol result;
 
                if (cache != null && cache.TryGetValue(typeDef, out result))
                {
                    if (!Module.IsNestedTypeDefOrThrow(typeDef) && Module.IsNoPiaLocalType(typeDef))
                    {
                        isNoPiaLocalType = true;
                    }
                    else
                    {
                        isNoPiaLocalType = false;
                    }
 
                    return result;
                }
 
                MetadataTypeName mdName;
                string name = Module.GetTypeDefNameOrThrow(typeDef);
                Debug.Assert(MetadataHelpers.IsValidMetadataIdentifier(name));
 
                if (Module.IsNestedTypeDefOrThrow(typeDef))
                {
                    // first resolve nesting type 
                    TypeDefinitionHandle containerTypeDef = Module.GetContainingTypeOrThrow(typeDef);
 
                    // invalid metadata?
                    if (containerTypeDef.IsNil)
                    {
                        isNoPiaLocalType = false;
                        return GetUnsupportedMetadataTypeSymbol();
                    }
 
                    TypeSymbol container = GetTypeOfTypeDef(containerTypeDef, out isNoPiaLocalType, isContainingType: true);
 
                    if (isNoPiaLocalType)
                    {
                        // Types nested into local types are not supported.
                        if (!isContainingType)
                        {
                            isNoPiaLocalType = false;
                        }
 
                        return GetUnsupportedMetadataTypeSymbol();
                    }
 
                    mdName = MetadataTypeName.FromTypeName(name);
                    return LookupNestedTypeDefSymbol(container, ref mdName);
                }
 
                string namespaceName = Module.GetTypeDefNamespaceOrThrow(typeDef);
 
                mdName = namespaceName.Length > 0
                    ? MetadataTypeName.FromNamespaceAndTypeName(namespaceName, name)
                    : MetadataTypeName.FromTypeName(name);
                // It is extremely difficult to hit the last branch because it is executed 
                // only for types in the Global namespace and they are getting loaded 
                // as soon as we start traversing Symbol Table, therefore, their TypeDef
                // handle is getting cached and lookup in the cache succeeds. 
                // Probably we can hit it if the first thing we do is to interrogate 
                // Module/Assembly level attributes, which refer to a TypeDef in the 
                // Global namespace.
 
                // Check if this is NoPia local type which should be substituted 
                // with corresponding canonical type
                string interfaceGuid;
                string scope;
                string identifier;
                if (Module.IsNoPiaLocalType(
                    typeDef,
                    out interfaceGuid,
                    out scope,
                    out identifier))
                {
                    isNoPiaLocalType = true;
 
                    if (!Module.HasGenericParametersOrThrow(typeDef))
                    {
                        MetadataTypeName localTypeName = MetadataTypeName.FromNamespaceAndTypeName(mdName.NamespaceName, mdName.TypeName, forcedArity: 0);
                        result = SubstituteNoPiaLocalType(typeDef,
                            ref localTypeName,
                            interfaceGuid,
                            scope,
                            identifier);
                        Debug.Assert((object)result != null);
                        return result;
                    }
 
                    // Unification of generic local types is not supported 
                    result = GetUnsupportedMetadataTypeSymbol();
 
                    if (cache != null)
                    {
                        result = cache.GetOrAdd(typeDef, result);
                    }
 
                    return result;
                }
 
                isNoPiaLocalType = false;
                result = LookupTopLevelTypeDefSymbol(ref mdName, out isNoPiaLocalType);
                Debug.Assert(!isNoPiaLocalType);
                return result;
            }
            catch (BadImageFormatException mrEx)
            {
                isNoPiaLocalType = false;
                return GetUnsupportedMetadataTypeSymbol(mrEx); // an exception from metadata reader.
            }
        }
 
        /// <exception cref="UnsupportedSignatureContent">If the encoded type is invalid.</exception>
        /// <exception cref="BadImageFormatException">An exception from metadata reader.</exception>
        private ImmutableArray<ModifierInfo<TypeSymbol>> DecodeModifiersOrThrow(
            ref BlobReader signatureReader,
            out SignatureTypeCode typeCode)
        {
            ArrayBuilder<ModifierInfo<TypeSymbol>> modifiers = null;
 
            for (; ; )
            {
                typeCode = signatureReader.ReadSignatureTypeCode();
                bool isOptional;
 
                if (typeCode == SignatureTypeCode.RequiredModifier)
                {
                    isOptional = false;
                }
                else if (typeCode == SignatureTypeCode.OptionalModifier)
                {
                    isOptional = true;
                }
                else
                {
                    break;
                }
 
                TypeSymbol type = DecodeModifierTypeOrThrow(ref signatureReader);
                ModifierInfo<TypeSymbol> modifier = new ModifierInfo<TypeSymbol>(isOptional, type);
 
                if (modifiers == null)
                {
                    modifiers = ArrayBuilder<ModifierInfo<TypeSymbol>>.GetInstance();
                }
 
                modifiers.Add(modifier);
            }
 
            return modifiers?.ToImmutableAndFree() ?? default;
        }
 
        private TypeSymbol DecodeModifierTypeOrThrow(ref BlobReader signatureReader)
        {
            EntityHandle token = signatureReader.ReadTypeHandle();
            TypeSymbol type;
            bool isNoPiaLocalType;
 
// According to ECMA spec:
//  The CMOD_OPT or CMOD_REQD is followed by a metadata token that
//  indexes a row in the TypeDef table or the TypeRef table.
tryAgain:
            switch (token.Kind)
            {
                case HandleKind.TypeDefinition:
                    type = GetTypeOfTypeDef((TypeDefinitionHandle)token, out isNoPiaLocalType, isContainingType: false);
                    // it is valid for a modifier to refer to an unconstructed type, we need to preserve this fact
                    type = SubstituteWithUnboundIfGeneric(type);
                    break;
 
                case HandleKind.TypeReference:
                    type = GetTypeOfTypeRef((TypeReferenceHandle)token, out isNoPiaLocalType);
                    // it is valid for a modifier to refer to an unconstructed type, we need to preserve this fact
                    type = SubstituteWithUnboundIfGeneric(type);
                    break;
 
                case HandleKind.TypeSpecification:
                    // Section 23.2.7 of the CLI spec specifically says that this is not allowed (see comment on method),
                    // but, apparently, ilasm turns modopt(int32) into a TypeSpec.
                    // In addition, managed C++ compiler can use constructed generic types as modifiers, for example Nullable<bool>, etc.
                    // We will support only cases like these even though it looks like CLR allows any types that can be encoded through a TypeSpec.
 
                    BlobReader memoryReader = this.Module.GetTypeSpecificationSignatureReaderOrThrow((TypeSpecificationHandle)token);
 
                    SignatureTypeCode typeCode = memoryReader.ReadSignatureTypeCode();
                    bool refersToNoPiaLocalType;
 
                    switch (typeCode)
                    {
                        case SignatureTypeCode.Void:
                        case SignatureTypeCode.Boolean:
                        case SignatureTypeCode.SByte:
                        case SignatureTypeCode.Byte:
                        case SignatureTypeCode.Int16:
                        case SignatureTypeCode.UInt16:
                        case SignatureTypeCode.Int32:
                        case SignatureTypeCode.UInt32:
                        case SignatureTypeCode.Int64:
                        case SignatureTypeCode.UInt64:
                        case SignatureTypeCode.Single:
                        case SignatureTypeCode.Double:
                        case SignatureTypeCode.Char:
                        case SignatureTypeCode.String:
                        case SignatureTypeCode.IntPtr:
                        case SignatureTypeCode.UIntPtr:
                        case SignatureTypeCode.Object:
                        case SignatureTypeCode.TypedReference:
                            type = GetSpecialType(typeCode.ToSpecialType());
                            break;
 
                        case SignatureTypeCode.TypeHandle:
 
                            token = memoryReader.ReadTypeHandle();
                            goto tryAgain;
 
                        case SignatureTypeCode.GenericTypeInstance:
                            type = DecodeGenericTypeInstanceOrThrow(ref memoryReader, out refersToNoPiaLocalType);
                            break;
 
                        default:
                            throw new UnsupportedSignatureContent();
                    }
                    break;
 
                default:
                    throw new UnsupportedSignatureContent();
            }
 
            return type;
        }
 
        /// <exception cref="UnsupportedSignatureContent">If the encoded local variable type is invalid.</exception>
        /// <exception cref="BadImageFormatException">An exception from metadata reader.</exception>
        internal ImmutableArray<LocalInfo<TypeSymbol>> DecodeLocalSignatureOrThrow(ref BlobReader signatureReader)
        {
            SignatureHeader signatureHeader = signatureReader.ReadSignatureHeader();
 
            if (signatureHeader.Kind != SignatureKind.LocalVariables)
            {
                throw new UnsupportedSignatureContent();
            }
 
            int localCount;
            int typeParameterCount;
            GetSignatureCountsOrThrow(ref signatureReader, signatureHeader, out localCount, out typeParameterCount);
            Debug.Assert(typeParameterCount == 0);
 
            var locals = ArrayBuilder<LocalInfo<TypeSymbol>>.GetInstance(localCount);
            var offsets = ArrayBuilder<int>.GetInstance(localCount);
            try
            {
                for (int i = 0; i < localCount; i++)
                {
                    offsets.Add(signatureReader.Offset);
                    locals.Add(DecodeLocalVariableOrThrow(ref signatureReader));
                }
 
                if (signatureReader.RemainingBytes > 0)
                {
                    throw new UnsupportedSignatureContent();
                }
 
                // Include signatures with each local.
                signatureReader.Reset();
                for (int i = 0; i < localCount; i++)
                {
                    int start = offsets[i];
                    Debug.Assert(signatureReader.Offset <= start);
                    while (signatureReader.Offset < start)
                    {
                        signatureReader.ReadByte();
                    }
 
                    int n = (i < localCount - 1) ? (offsets[i + 1] - start) : signatureReader.RemainingBytes;
                    var signature = signatureReader.ReadBytes(n);
 
                    locals[i] = locals[i].WithSignature(signature);
                }
 
                return locals.ToImmutable();
            }
            finally
            {
                offsets.Free();
                locals.Free();
            }
        }
 
        internal TypeSymbol DecodeGenericParameterConstraint(EntityHandle token, out ImmutableArray<ModifierInfo<TypeSymbol>> modifiers)
        {
            modifiers = ImmutableArray<ModifierInfo<TypeSymbol>>.Empty;
 
            switch (token.Kind)
            {
                case HandleKind.TypeSpecification:
                    {
                        try
                        {
                            var memoryReader = this.Module.GetTypeSpecificationSignatureReaderOrThrow((TypeSpecificationHandle)token);
                            modifiers = DecodeModifiersOrThrow(ref memoryReader, out var typeCode);
                            var type = DecodeTypeOrThrow(ref memoryReader, typeCode, out _);
                            return type;
                        }
                        catch (BadImageFormatException mrEx)
                        {
                            return GetUnsupportedMetadataTypeSymbol(mrEx);
                        }
                        catch (UnsupportedSignatureContent)
                        {
                            return GetUnsupportedMetadataTypeSymbol();
                        }
                    }
                case HandleKind.TypeReference:
                    return GetTypeOfTypeRef((TypeReferenceHandle)token, out _);
                case HandleKind.TypeDefinition:
                    return GetTypeOfTypeDef((TypeDefinitionHandle)token);
                default:
                    return GetUnsupportedMetadataTypeSymbol();
            }
        }
 
        /// <exception cref="UnsupportedSignatureContent">If the encoded local variable type is invalid.</exception>
        /// <exception cref="BadImageFormatException">An exception from metadata reader.</exception>
        internal LocalInfo<TypeSymbol> DecodeLocalVariableOrThrow(ref BlobReader signatureReader)
        {
            SignatureTypeCode typeCode;
 
            var customModifiers = DecodeModifiersOrThrow(ref signatureReader, out typeCode);
 
            if (customModifiers.AnyRequired())
            {
                throw new UnsupportedSignatureContent();
            }
 
            var constraints = LocalSlotConstraints.None;
            TypeSymbol typeSymbol;
 
            if (typeCode == SignatureTypeCode.Pinned)
            {
                constraints |= LocalSlotConstraints.Pinned;
                typeCode = signatureReader.ReadSignatureTypeCode();
            }
 
            if (typeCode == SignatureTypeCode.ByReference)
            {
                constraints |= LocalSlotConstraints.ByRef;
                typeCode = signatureReader.ReadSignatureTypeCode();
            }
 
            // TypedReference can't be by-ref or pinned:
            if (typeCode == SignatureTypeCode.TypedReference && constraints != LocalSlotConstraints.None)
            {
                typeSymbol = GetUnsupportedMetadataTypeSymbol();
            }
            else
            {
                try
                {
                    bool refersToNoPiaLocalType;
                    typeSymbol = DecodeTypeOrThrow(ref signatureReader, typeCode, out refersToNoPiaLocalType);
                }
                catch (UnsupportedSignatureContent)
                {
                    typeSymbol = GetUnsupportedMetadataTypeSymbol();
                }
            }
 
            return new LocalInfo<TypeSymbol>(typeSymbol, customModifiers, constraints, signatureOpt: null);
        }
 
        internal void DecodeLocalConstantBlobOrThrow(ref BlobReader sigReader, out TypeSymbol type, out ConstantValue value)
        {
            SignatureTypeCode typeCode;
 
            var customModifiers = DecodeModifiersOrThrow(ref sigReader, out typeCode);
 
            if (customModifiers.AnyRequired())
            {
                throw new UnsupportedSignatureContent();
            }
 
            if (typeCode == SignatureTypeCode.TypeHandle)
            {
                // TypeDefOrRefOrSpec encoded
                // From the PortablePDB spec: https://github.com/dotnet/runtime/blob/main/src/libraries/System.Reflection.Metadata/specs/PortablePdb-Metadata.md#localconstant-table-0x34
                // The encoding of the GeneralValue is determined based upon the type expressed by TypeDefOrRefOrSpecEncoded
                // specified in GeneralConstant. GeneralValue for special types listed in the table below has to be present
                // and is encoded as specified.
                // If the GeneralValue is not present the value of the constant is the default value of the type. If the type 
                // is a reference type the value is a null reference, if the type is a pointer type the value is a null pointer, etc.
                bool refersToNoPiaLocalType;
                type = GetSymbolForTypeHandleOrThrow(sigReader.ReadTypeHandle(), out refersToNoPiaLocalType, allowTypeSpec: true, requireShortForm: true);
 
                if (type.SpecialType == SpecialType.System_Decimal)
                {
                    value = ConstantValue.Create(sigReader.ReadDecimal());
                }
                else if (type.SpecialType == SpecialType.System_DateTime)
                {
                    value = ConstantValue.Create(sigReader.ReadDateTime());
                }
                else if (sigReader.RemainingBytes == 0)
                {
                    // Note: even though the PortablePDB spec permits constants of pointer types, C# does not, so those
                    // should only ever be seen if the PDB being decoded is a custom-assembled PDB for non-legal C#.
                    value = (type.IsReferenceType || type.TypeKind == TypeKind.Pointer || type.TypeKind == TypeKind.FunctionPointer) ? ConstantValue.Null : ConstantValue.Bad;
                }
                else
                {
                    value = ConstantValue.Bad;
                }
            }
            else
            {
                bool isEnumTypeCode;
                value = DecodePrimitiveConstantValue(ref sigReader, typeCode, out isEnumTypeCode);
                var specialType = typeCode.ToSpecialType();
 
                if (isEnumTypeCode && sigReader.RemainingBytes > 0)
                {
                    bool refersToNoPiaLocalType;
                    type = GetSymbolForTypeHandleOrThrow(sigReader.ReadTypeHandle(), out refersToNoPiaLocalType, allowTypeSpec: true, requireShortForm: true);
 
                    if (GetEnumUnderlyingType(type)?.SpecialType != specialType)
                    {
                        throw new UnsupportedSignatureContent();
                    }
                }
                else
                {
                    type = GetSpecialType(specialType);
                }
 
                if (sigReader.RemainingBytes > 0)
                {
                    throw new UnsupportedSignatureContent();
                }
            }
        }
 
        private static ConstantValue DecodePrimitiveConstantValue(ref BlobReader sigReader, SignatureTypeCode typeCode, out bool isEnumTypeCode)
        {
            switch (typeCode)
            {
                case SignatureTypeCode.Boolean:
                    isEnumTypeCode = true;
                    return ConstantValue.Create(sigReader.ReadBoolean());
 
                case SignatureTypeCode.Char:
                    isEnumTypeCode = true;
                    return ConstantValue.Create(sigReader.ReadChar());
 
                case SignatureTypeCode.SByte:
                    isEnumTypeCode = true;
                    return ConstantValue.Create(sigReader.ReadSByte());
 
                case SignatureTypeCode.Byte:
                    isEnumTypeCode = true;
                    return ConstantValue.Create(sigReader.ReadByte());
 
                case SignatureTypeCode.Int16:
                    isEnumTypeCode = true;
                    return ConstantValue.Create(sigReader.ReadInt16());
 
                case SignatureTypeCode.UInt16:
                    isEnumTypeCode = true;
                    return ConstantValue.Create(sigReader.ReadUInt16());
 
                case SignatureTypeCode.Int32:
                    isEnumTypeCode = true;
                    return ConstantValue.Create(sigReader.ReadInt32());
 
                case SignatureTypeCode.UInt32:
                    isEnumTypeCode = true;
                    return ConstantValue.Create(sigReader.ReadUInt32());
 
                case SignatureTypeCode.Int64:
                    isEnumTypeCode = true;
                    return ConstantValue.Create(sigReader.ReadInt64());
 
                case SignatureTypeCode.UInt64:
                    isEnumTypeCode = true;
                    return ConstantValue.Create(sigReader.ReadUInt64());
 
                case SignatureTypeCode.Single:
                    isEnumTypeCode = false;
                    return ConstantValue.Create(sigReader.ReadSingle());
 
                case SignatureTypeCode.Double:
                    isEnumTypeCode = false;
                    return ConstantValue.Create(sigReader.ReadDouble());
 
                case SignatureTypeCode.String:
                    isEnumTypeCode = false;
 
                    if (sigReader.RemainingBytes == 1)
                    {
                        if (sigReader.ReadByte() != 0xff)
                        {
                            return ConstantValue.Bad;
                        }
 
                        return ConstantValue.Null;
                    }
 
                    if (sigReader.RemainingBytes % 2 != 0)
                    {
                        return ConstantValue.Bad;
                    }
 
                    return ConstantValue.Create(sigReader.ReadUTF16(sigReader.RemainingBytes));
 
                case SignatureTypeCode.Object:
                    // null reference
                    isEnumTypeCode = false;
                    return ConstantValue.Null;
 
                default:
                    throw new UnsupportedSignatureContent();
            }
        }
 
        internal ImmutableArray<LocalInfo<TypeSymbol>> GetLocalsOrThrow(StandaloneSignatureHandle handle)
        {
            var signatureHandle = Module.MetadataReader.GetStandaloneSignature(handle).Signature;
            var signatureReader = Module.MetadataReader.GetBlobReader(signatureHandle);
            return DecodeLocalSignatureOrThrow(ref signatureReader);
        }
 
        /// <summary>
        /// Used to decode signatures of local constants returned by SymReader.
        /// </summary>
        internal unsafe TypeSymbol DecodeLocalVariableTypeOrThrow(ImmutableArray<byte> signature)
        {
            if (signature.IsDefaultOrEmpty)
            {
                throw new UnsupportedSignatureContent();
            }
 
            fixed (byte* ptr = signature.AsSpan())
            {
                var blobReader = new BlobReader(ptr, signature.Length);
                var info = DecodeLocalVariableOrThrow(ref blobReader);
 
                if (info.IsByRef || info.IsPinned)
                {
                    throw new UnsupportedSignatureContent();
                }
 
                return info.Type;
            }
        }
 
        /// <summary>
        /// Returns the local info for all locals indexed by slot.
        /// </summary>
        internal ImmutableArray<LocalInfo<TypeSymbol>> GetLocalInfo(StandaloneSignatureHandle localSignatureHandle)
        {
            if (localSignatureHandle.IsNil)
            {
                return ImmutableArray<LocalInfo<TypeSymbol>>.Empty;
            }
 
            var reader = Module.MetadataReader;
            var signature = reader.GetStandaloneSignature(localSignatureHandle).Signature;
            var blobReader = reader.GetBlobReader(signature);
            return DecodeLocalSignatureOrThrow(ref blobReader);
        }
 
        /// <exception cref="UnsupportedSignatureContent">If the encoded parameter type is invalid.</exception>
        private void DecodeParameterOrThrow(ref BlobReader signatureReader, /*out*/ ref ParamInfo<TypeSymbol> info)
        {
            info.CustomModifiers = DecodeModifiersOrThrow(
                ref signatureReader,
                out SignatureTypeCode typeCode);
 
            if (typeCode == SignatureTypeCode.ByReference)
            {
                info.IsByRef = true;
 
                info.RefCustomModifiers = info.CustomModifiers;
                info.CustomModifiers = DecodeModifiersOrThrow(ref signatureReader, out typeCode);
            }
 
            info.Type = DecodeTypeOrThrow(ref signatureReader, typeCode, out _);
        }
 
        // MetaImport::DecodeMethodSignature
        internal ParamInfo<TypeSymbol>[] GetSignatureForMethod(MethodDefinitionHandle methodDef, out SignatureHeader signatureHeader, out BadImageFormatException metadataException, bool setParamHandles = true)
        {
            ParamInfo<TypeSymbol>[] paramInfo = null;
            signatureHeader = default(SignatureHeader);
 
            try
            {
                BlobHandle signature = Module.GetMethodSignatureOrThrow(methodDef);
                BlobReader signatureReader = DecodeSignatureHeaderOrThrow(signature, out signatureHeader);
 
                int typeParameterCount; //CONSIDER: expose to caller?
                paramInfo = DecodeSignatureParametersOrThrow(ref signatureReader, signatureHeader, out typeParameterCount);
 
                if (setParamHandles)
                {
                    int paramInfoLength = paramInfo.Length;
 
                    // For each parameter, get corresponding row id from Param table. 
                    foreach (var param in Module.GetParametersOfMethodOrThrow(methodDef))
                    {
                        int sequenceNumber = Module.GetParameterSequenceNumberOrThrow(param);
 
                        // Ignore possible errors in parameter table.
                        if (sequenceNumber >= 0 && sequenceNumber < paramInfoLength && paramInfo[sequenceNumber].Handle.IsNil)
                        {
                            paramInfo[sequenceNumber].Handle = param;
                        }
                    }
                }
 
                metadataException = null;
            }
            catch (BadImageFormatException mrEx)
            {
                metadataException = mrEx;
 
                // An exception from metadata reader.
                if (paramInfo == null)
                {
                    // Pretend there are no parameters and capture error information in the return type.
                    paramInfo = new ParamInfo<TypeSymbol>[1];
                    paramInfo[0].Type = GetUnsupportedMetadataTypeSymbol(mrEx);
                }
            }
 
            return paramInfo;
        }
 
        internal void DecodeMethodSignatureParameterCountsOrThrow(MethodDefinitionHandle methodDef, out int parameterCount, out int typeParameterCount)
            => GetSignatureCountsOrThrow(Module, methodDef, out parameterCount, out typeParameterCount);
 
        /// <exception cref="BadImageFormatException">An exception from metadata reader.</exception>
        internal static void GetSignatureCountsOrThrow(PEModule module, MethodDefinitionHandle methodDef, out int parameterCount, out int typeParameterCount)
        {
            BlobHandle signature = module.GetMethodSignatureOrThrow(methodDef);
            SignatureHeader signatureHeader;
            BlobReader signatureReader = DecodeSignatureHeaderOrThrow(module, signature, out signatureHeader);
 
            GetSignatureCountsOrThrow(ref signatureReader, signatureHeader, out parameterCount, out typeParameterCount);
        }
 
        internal ParamInfo<TypeSymbol>[] GetSignatureForProperty(PropertyDefinitionHandle handle, out SignatureHeader signatureHeader, out BadImageFormatException BadImageFormatException)
        {
            ParamInfo<TypeSymbol>[] paramInfo = null;
            signatureHeader = default(SignatureHeader);
 
            try
            {
                var signature = Module.GetPropertySignatureOrThrow(handle);
                BlobReader signatureReader = DecodeSignatureHeaderOrThrow(signature, out signatureHeader);
 
                int typeParameterCount; //CONSIDER: expose to caller?
                paramInfo = DecodeSignatureParametersOrThrow(ref signatureReader, signatureHeader, out typeParameterCount);
                BadImageFormatException = null;
            }
            catch (BadImageFormatException mrEx)
            {
                BadImageFormatException = mrEx;
 
                // An exception from metadata reader.
                if (paramInfo == null)
                {
                    // Pretend there are no parameters and capture error information in the return type as well.
                    paramInfo = new ParamInfo<TypeSymbol>[1];
                    paramInfo[0].Type = GetUnsupportedMetadataTypeSymbol(mrEx);
                }
            }
 
            return paramInfo;
        }
 
        internal SignatureHeader GetSignatureHeaderForProperty(PropertyDefinitionHandle handle)
        {
            try
            {
                var signature = Module.GetPropertySignatureOrThrow(handle);
                SignatureHeader signatureHeader;
                DecodeSignatureHeaderOrThrow(signature, out signatureHeader);
                return signatureHeader;
            }
            catch (BadImageFormatException)
            {
                return default(SignatureHeader);
            }
        }
 
        #region Custom Attributes
 
        /// <summary>
        /// Decodes attribute argument type from attribute blob (called FieldOrPropType in the spec).
        /// </summary>
        /// <exception cref="UnsupportedSignatureContent">If the encoded argument type is invalid.</exception>
        /// <exception cref="BadImageFormatException">An exception from metadata reader.</exception>
        private void DecodeCustomAttributeFieldOrPropTypeOrThrow(ref BlobReader argReader, out SerializationTypeCode typeCode, out TypeSymbol type, out SerializationTypeCode elementTypeCode, out TypeSymbol elementType, bool isElementType)
        {
            typeCode = argReader.ReadSerializationTypeCode();
 
            // Spec:
            // The FieldOrPropType (typeCode) shall be exactly one of: ELEMENT_TYPE_BOOLEAN,
            // ELEMENT_TYPE_CHAR, ELEMENT_TYPE_I1, ELEMENT_TYPE_U1, ELEMENT_TYPE_I2,
            // ELEMENT_TYPE_U2, ELEMENT_TYPE_I4, ELEMENT_TYPE_U4, ELEMENT_TYPE_I8,
            // ELEMENT_TYPE_U8, ELEMENT_TYPE_R4, ELEMENT_TYPE_R8, ELEMENT_TYPE_STRING.
            // 
            // A single-dimensional, zero-based array is specified as a single byte 0x1D followed
            // by the FieldOrPropType of the element type. (See §II.23.1.16) An enum is
            // specified as a single byte 0x55 followed by a SerString.
            // 
            // tomat: The spec is missing ELEMENT_TYPE_TYPE.
 
            if (typeCode == SerializationTypeCode.SZArray)
            {
                if (isElementType)
                {
                    // nested array not allowed:
                    throw new UnsupportedSignatureContent();
                }
 
                SerializationTypeCode unusedElementTypeCode;
                TypeSymbol unusedElementType;
                DecodeCustomAttributeFieldOrPropTypeOrThrow(ref argReader, out elementTypeCode, out elementType, out unusedElementTypeCode, out unusedElementType, isElementType: true);
                type = GetSZArrayTypeSymbol(elementType, customModifiers: default(ImmutableArray<ModifierInfo<TypeSymbol>>));
                return;
            }
 
            elementTypeCode = SerializationTypeCode.Invalid;
            elementType = null;
 
            switch (typeCode)
            {
                case SerializationTypeCode.TaggedObject:
                    type = GetSpecialType(SpecialType.System_Object);
                    return;
 
                case SerializationTypeCode.Enum:
                    string enumTypeName;
                    if (!PEModule.CrackStringInAttributeValue(out enumTypeName, ref argReader))
                    {
                        throw new UnsupportedSignatureContent();
                    }
 
                    type = GetTypeSymbolForSerializedType(enumTypeName);
                    var underlyingType = GetEnumUnderlyingType(type);
                    if ((object)underlyingType == null)
                    {
                        throw new UnsupportedSignatureContent();
                    }
 
                    // GetEnumUnderlyingType always returns a valid enum underlying type
                    typeCode = underlyingType.SpecialType.ToSerializationType();
                    return;
 
                case SerializationTypeCode.Type:
                    type = SystemTypeSymbol;
                    return;
 
                case SerializationTypeCode.String:
                case SerializationTypeCode.Boolean:
                case SerializationTypeCode.Char:
                case SerializationTypeCode.SByte:
                case SerializationTypeCode.Byte:
                case SerializationTypeCode.Int16:
                case SerializationTypeCode.UInt16:
                case SerializationTypeCode.Int32:
                case SerializationTypeCode.UInt32:
                case SerializationTypeCode.Int64:
                case SerializationTypeCode.UInt64:
                case SerializationTypeCode.Single:
                case SerializationTypeCode.Double:
                    type = GetSpecialType(((SignatureTypeCode)typeCode).ToSpecialType());
                    return;
            }
 
            throw new UnsupportedSignatureContent();
        }
 
        /// <exception cref="UnsupportedSignatureContent">If the encoded attribute argument is invalid.</exception>
        /// <exception cref="BadImageFormatException">An exception from metadata reader.</exception>
        private TypedConstant DecodeCustomAttributeFixedArgumentOrThrow(ITypeSymbolInternal type, ref BlobReader argReader)
        {
            // arrays are allowed only on top-level:
            if (type is IArrayTypeSymbolInternal { IsSZArray: true, ElementType: { } elementType })
            {
                return DecodeCustomAttributeElementArrayOrThrow(ref argReader, getTypeCode(elementType), (TypeSymbol)elementType, (TypeSymbol)type);
            }
 
            return DecodeCustomAttributeElementOrThrow(ref argReader, getTypeCode(type), (TypeSymbol)type);
 
            SerializationTypeCode getTypeCode(ITypeSymbolInternal type)
            {
                if (ReferenceEquals(type, SystemTypeSymbol))
                {
                    return SerializationTypeCode.Type;
                }
 
                if (type is INamedTypeSymbolInternal { EnumUnderlyingType: { } underlyingType })
                {
                    type = underlyingType;
                }
 
                var result = type.SpecialType.ToSerializationTypeOrInvalid();
 
                if (result == SerializationTypeCode.Invalid)
                {
                    throw new UnsupportedSignatureContent();
                }
 
                return result;
            }
        }
 
        /// <exception cref="UnsupportedSignatureContent">If the encoded attribute argument is invalid.</exception>
        /// <exception cref="BadImageFormatException">An exception from metadata reader.</exception>
        private TypedConstant DecodeCustomAttributeElementOrThrow(ref BlobReader argReader, SerializationTypeCode typeCode, TypeSymbol type)
        {
            if (typeCode == SerializationTypeCode.TaggedObject)
            {
                // Spec: If the parameter kind is System.Object, the value stored represents the "boxed" instance of that value-type.
                SerializationTypeCode elementTypeCode;
                TypeSymbol elementType;
                DecodeCustomAttributeFieldOrPropTypeOrThrow(ref argReader, out typeCode, out type, out elementTypeCode, out elementType, isElementType: false);
 
                if (typeCode == SerializationTypeCode.SZArray)
                {
                    return DecodeCustomAttributeElementArrayOrThrow(ref argReader, elementTypeCode, elementType, type);
                }
            }
 
            return DecodeCustomAttributePrimitiveElementOrThrow(ref argReader, typeCode, type);
        }
 
        /// <exception cref="UnsupportedSignatureContent">If the encoded attribute argument is invalid.</exception>
        /// <exception cref="BadImageFormatException">An exception from metadata reader.</exception>
        private TypedConstant DecodeCustomAttributeElementArrayOrThrow(ref BlobReader argReader, SerializationTypeCode elementTypeCode, TypeSymbol elementType, TypeSymbol arrayType)
        {
            int count = argReader.ReadInt32();
            TypedConstant[] values;
 
            if (count == -1)
            {
                values = null;
            }
            else if (count == 0)
            {
                values = Array.Empty<TypedConstant>();
            }
            else
            {
                values = new TypedConstant[count];
                for (int i = 0; i < count; i++)
                {
                    values[i] = DecodeCustomAttributeElementOrThrow(ref argReader, elementTypeCode, elementType);
                }
            }
 
            return CreateArrayTypedConstant(arrayType, values.AsImmutableOrNull());
        }
 
        /// <exception cref="UnsupportedSignatureContent">If the given <paramref name="typeCode"/> is invalid.</exception>
        /// <exception cref="BadImageFormatException">An exception from metadata reader.</exception>
        private TypedConstant DecodeCustomAttributePrimitiveElementOrThrow(ref BlobReader argReader, SerializationTypeCode typeCode, TypeSymbol type)
        {
            Debug.Assert(type != null);
 
            switch (typeCode)
            {
                case SerializationTypeCode.Boolean:
                    return CreateTypedConstant(type, GetPrimitiveOrEnumTypedConstantKind(type), argReader.ReadSByte() != 0);
 
                case SerializationTypeCode.SByte:
                    return CreateTypedConstant(type, GetPrimitiveOrEnumTypedConstantKind(type), argReader.ReadSByte());
 
                case SerializationTypeCode.Byte:
                    return CreateTypedConstant(type, GetPrimitiveOrEnumTypedConstantKind(type), argReader.ReadByte());
 
                case SerializationTypeCode.Int16:
                    return CreateTypedConstant(type, GetPrimitiveOrEnumTypedConstantKind(type), argReader.ReadInt16());
 
                case SerializationTypeCode.UInt16:
                    return CreateTypedConstant(type, GetPrimitiveOrEnumTypedConstantKind(type), argReader.ReadUInt16());
 
                case SerializationTypeCode.Int32:
                    return CreateTypedConstant(type, GetPrimitiveOrEnumTypedConstantKind(type), argReader.ReadInt32());
 
                case SerializationTypeCode.UInt32:
                    return CreateTypedConstant(type, GetPrimitiveOrEnumTypedConstantKind(type), argReader.ReadUInt32());
 
                case SerializationTypeCode.Int64:
                    return CreateTypedConstant(type, GetPrimitiveOrEnumTypedConstantKind(type), argReader.ReadInt64());
 
                case SerializationTypeCode.UInt64:
                    return CreateTypedConstant(type, GetPrimitiveOrEnumTypedConstantKind(type), argReader.ReadUInt64());
 
                case SerializationTypeCode.Single:
                    return CreateTypedConstant(type, GetPrimitiveOrEnumTypedConstantKind(type), argReader.ReadSingle());
 
                case SerializationTypeCode.Double:
                    return CreateTypedConstant(type, GetPrimitiveOrEnumTypedConstantKind(type), argReader.ReadDouble());
 
                case SerializationTypeCode.Char:
                    return CreateTypedConstant(type, GetPrimitiveOrEnumTypedConstantKind(type), argReader.ReadChar());
 
                case SerializationTypeCode.String:
                    string s;
                    TypedConstantKind kind = PEModule.CrackStringInAttributeValue(out s, ref argReader) ?
                        TypedConstantKind.Primitive :
                        TypedConstantKind.Error;
 
                    return CreateTypedConstant(type, kind, s);
 
                case SerializationTypeCode.Type:
                    string typeName;
                    TypeSymbol serializedType = PEModule.CrackStringInAttributeValue(out typeName, ref argReader) ?
                        (typeName != null ? GetTypeSymbolForSerializedType(typeName) : null) :
                        GetUnsupportedMetadataTypeSymbol();
 
                    return CreateTypedConstant(type, TypedConstantKind.Type, serializedType);
 
                default:
                    throw new UnsupportedSignatureContent();
            }
        }
 
        private static TypedConstantKind GetPrimitiveOrEnumTypedConstantKind(TypeSymbol type)
        {
            return (type.TypeKind == TypeKind.Enum) ? TypedConstantKind.Enum : TypedConstantKind.Primitive;
        }
 
        /// <exception cref="UnsupportedSignatureContent">If the encoded named argument is invalid.</exception>
        /// <exception cref="BadImageFormatException">An exception from metadata reader.</exception>
        public (KeyValuePair<string, TypedConstant> nameValuePair, bool isProperty, SerializationTypeCode typeCode, SerializationTypeCode elementTypeCode) DecodeCustomAttributeNamedArgumentOrThrow(ref BlobReader argReader)
        {
            // Ecma-335 23.3 - A NamedArg is simply a FixedArg preceded by information to identify which field or
            // property it represents. [Note: Recall that the CLI allows fields and properties to have the same name; so
            // we require a means to disambiguate such situations. end note] FIELD is the single byte 0x53. PROPERTY is
            // the single byte 0x54.
 
            var kind = (CustomAttributeNamedArgumentKind)argReader.ReadCompressedInteger();
            if (kind != CustomAttributeNamedArgumentKind.Field && kind != CustomAttributeNamedArgumentKind.Property)
            {
                throw new UnsupportedSignatureContent();
            }
 
            SerializationTypeCode typeCode, elementTypeCode;
            TypeSymbol type, elementType;
            DecodeCustomAttributeFieldOrPropTypeOrThrow(ref argReader, out typeCode, out type, out elementTypeCode, out elementType, isElementType: false);
 
            string name;
            if (!PEModule.CrackStringInAttributeValue(out name, ref argReader))
            {
                throw new UnsupportedSignatureContent();
            }
 
            TypedConstant value = typeCode == SerializationTypeCode.SZArray
                ? DecodeCustomAttributeElementArrayOrThrow(ref argReader, elementTypeCode, elementType, type)
                : DecodeCustomAttributeElementOrThrow(ref argReader, typeCode, type);
 
            return (new KeyValuePair<string, TypedConstant>(name, value), kind == CustomAttributeNamedArgumentKind.Property, typeCode, elementTypeCode);
        }
 
        internal bool IsTargetAttribute(
            CustomAttributeHandle customAttribute,
            string namespaceName,
            string typeName,
            bool ignoreCase = false)
        {
            try
            {
                EntityHandle ctor;
 
                return Module.IsTargetAttribute(
                    customAttribute,
                    namespaceName,
                    typeName,
                    out ctor,
                    ignoreCase);
            }
            catch (BadImageFormatException)
            {
                return false;
            }
        }
 
        internal int GetTargetAttributeSignatureIndex(CustomAttributeHandle customAttribute, AttributeDescription description)
        {
            try
            {
                return Module.GetTargetAttributeSignatureIndex(customAttribute, description);
            }
            catch (BadImageFormatException)
            {
                return -1;
            }
        }
 
        internal bool GetCustomAttribute(
            CustomAttributeHandle handle,
            IMethodSymbolInternal attributeConstructor,
            out TypedConstant[] positionalArgs,
            out KeyValuePair<string, TypedConstant>[] namedArgs)
        {
            try
            {
                positionalArgs = Array.Empty<TypedConstant>();
                namedArgs = Array.Empty<KeyValuePair<string, TypedConstant>>();
 
                if (attributeConstructor is null ||
                    attributeConstructor.IsGenericMethod ||
                    !attributeConstructor.ReturnsVoid)
                {
                    return false;
                }
 
                BlobReader argsReader = Module.GetMemoryReaderOrThrow(Module.GetCustomAttributeValueOrThrow(handle));
 
                uint prolog = argsReader.ReadUInt16();
                if (prolog != 1)
                {
                    return false;
                }
 
                int paramCount = attributeConstructor.ParameterCount;
 
                if (paramCount > 0)
                {
                    positionalArgs = new TypedConstant[paramCount];
 
                    for (int i = 0; i < positionalArgs.Length; i++)
                    {
                        var parameterType = attributeConstructor.Parameters[i].Type;
                        positionalArgs[i] = DecodeCustomAttributeFixedArgumentOrThrow(parameterType, ref argsReader);
                    }
                }
 
                short namedParamCount = argsReader.ReadInt16();
 
                if (namedParamCount > 0)
                {
                    namedArgs = new KeyValuePair<string, TypedConstant>[namedParamCount];
 
                    for (int i = 0; i < namedArgs.Length; i++)
                    {
                        (namedArgs[i], _, _, _) = DecodeCustomAttributeNamedArgumentOrThrow(ref argsReader);
                    }
                }
 
                return true;
            }
            catch (Exception e) when (e is UnsupportedSignatureContent || e is BadImageFormatException)
            {
                positionalArgs = Array.Empty<TypedConstant>();
                namedArgs = Array.Empty<KeyValuePair<String, TypedConstant>>();
            }
 
            return false;
        }
 
#nullable enable
        internal bool GetCustomAttribute(CustomAttributeHandle handle, [NotNullWhen(true)] out TypeSymbol? attributeClass, [NotNullWhen(true)] out MethodSymbol? attributeCtor)
        {
            EntityHandle attributeType;
            EntityHandle ctor;
 
            try
            {
                if (!Module.GetTypeAndConstructor(handle, out attributeType, out ctor))
                {
                    attributeClass = null;
                    attributeCtor = null;
                    return false;
                }
            }
            catch (BadImageFormatException)
            {
                attributeClass = null;
                attributeCtor = null;
                return false;
            }
 
            attributeClass = GetTypeOfToken(attributeType);
            attributeCtor = GetMethodSymbolForMethodDefOrMemberRef(ctor, attributeClass);
            return true;
        }
#nullable disable
 
        internal bool GetCustomAttributeWellKnownType(CustomAttributeHandle handle, out WellKnownType wellKnownAttribute)
        {
            wellKnownAttribute = WellKnownType.Unknown;
 
            try
            {
                EntityHandle attributeType;
                EntityHandle ctor;
 
                if (!Module.GetTypeAndConstructor(handle, out attributeType, out ctor))
                {
                    return false;
                }
 
                StringHandle namespaceHandle;
                StringHandle nameHandle;
                if (!Module.GetAttributeNamespaceAndName(attributeType, out namespaceHandle, out nameHandle))
                {
                    return false;
                }
 
                string fullName = Module.GetFullNameOrThrow(namespaceHandle, nameHandle);
                wellKnownAttribute = WellKnownTypes.GetTypeFromMetadataName(fullName);
                return true;
            }
            catch (BadImageFormatException)
            {
                return false;
            }
        }
 
        #endregion
 
        /// <exception cref="BadImageFormatException">An exception from metadata reader.</exception>
        private TypeSymbol[] DecodeMethodSpecTypeArgumentsOrThrow(BlobHandle signature)
        {
            SignatureHeader signatureHeader;
            var signatureReader = DecodeSignatureHeaderOrThrow(signature, out signatureHeader);
            if (signatureHeader.Kind != SignatureKind.MethodSpecification)
            {
                throw new BadImageFormatException();
            }
 
            int argumentCount = signatureReader.ReadCompressedInteger();
            if (argumentCount == 0)
            {
                throw new BadImageFormatException();
            }
 
            var result = new TypeSymbol[argumentCount];
            for (int i = 0; i < result.Length; i++)
            {
                bool refersToNoPiaLocalType;
                result[i] = DecodeTypeOrThrow(ref signatureReader, out refersToNoPiaLocalType);
            }
 
            return result;
        }
 
        /// <exception cref="BadImageFormatException">An exception from metadata reader.</exception>
        internal BlobReader DecodeSignatureHeaderOrThrow(BlobHandle signature, out SignatureHeader signatureHeader)
        {
            return DecodeSignatureHeaderOrThrow(Module, signature, out signatureHeader);
        }
 
        /// <exception cref="BadImageFormatException">An exception from metadata reader.</exception>
        internal static BlobReader DecodeSignatureHeaderOrThrow(PEModule module, BlobHandle signature, out SignatureHeader signatureHeader)
        {
            BlobReader reader = module.GetMemoryReaderOrThrow(signature);
            signatureHeader = reader.ReadSignatureHeader();
            return reader;
        }
 
        /// <exception cref="BadImageFormatException">An exception from metadata reader.</exception>
        protected ParamInfo<TypeSymbol>[] DecodeSignatureParametersOrThrow(ref BlobReader signatureReader, SignatureHeader signatureHeader, out int typeParameterCount, bool shouldProcessAllBytes = true, bool isFunctionPointerSignature = false)
        {
            int paramCount;
            GetSignatureCountsOrThrow(ref signatureReader, signatureHeader, out paramCount, out typeParameterCount);
 
            ParamInfo<TypeSymbol>[] paramInfo = new ParamInfo<TypeSymbol>[paramCount + 1];
 
            uint paramIndex = 0;
 
            try
            {
                // get the return type
                DecodeParameterOrThrow(ref signatureReader, ref paramInfo[0]);
 
                // Get all of the parameters.
                for (paramIndex = 1; paramIndex <= paramCount; paramIndex++)
                {
                    // Figure out the type.
                    DecodeParameterOrThrow(ref signatureReader, ref paramInfo[paramIndex]);
                }
 
                if (shouldProcessAllBytes && signatureReader.RemainingBytes > 0)
                {
                    throw new UnsupportedSignatureContent();
                }
            }
            catch (Exception e) when ((e is UnsupportedSignatureContent || e is BadImageFormatException) && !isFunctionPointerSignature)
            {
                for (; paramIndex <= paramCount; paramIndex++)
                {
                    paramInfo[paramIndex].Type = GetUnsupportedMetadataTypeSymbol(e as BadImageFormatException);
                }
            }
 
            return paramInfo;
        }
 
        /// <exception cref="BadImageFormatException">An exception from metadata reader.</exception>
        private static void GetSignatureCountsOrThrow(ref BlobReader signatureReader, SignatureHeader signatureHeader, out int parameterCount, out int typeParameterCount)
        {
            // Get the type parameter count.
            typeParameterCount = signatureHeader.IsGeneric ? signatureReader.ReadCompressedInteger() : 0;
 
            // Get the parameter count
            parameterCount = signatureReader.ReadCompressedInteger();
        }
 
        internal FieldInfo<TypeSymbol> DecodeFieldSignature(FieldDefinitionHandle fieldHandle)
        {
            try
            {
                BlobHandle signature = Module.GetFieldSignatureOrThrow(fieldHandle);
 
                SignatureHeader signatureHeader;
                BlobReader signatureReader = DecodeSignatureHeaderOrThrow(signature, out signatureHeader);
 
                if (signatureHeader.Kind != SignatureKind.Field)
                {
                    return new FieldInfo<TypeSymbol>(GetUnsupportedMetadataTypeSymbol()); // unsupported signature content
                }
                else
                {
                    return DecodeFieldSignature(ref signatureReader);
                }
            }
            catch (BadImageFormatException mrEx)
            {
                return new FieldInfo<TypeSymbol>(GetUnsupportedMetadataTypeSymbol(mrEx));
            }
        }
 
        // MetaImport::DecodeFieldSignature
        protected FieldInfo<TypeSymbol> DecodeFieldSignature(ref BlobReader signatureReader)
        {
            try
            {
                SignatureTypeCode typeCode;
                bool isByRef = false;
                ImmutableArray<ModifierInfo<TypeSymbol>> refCustomModifiers = default;
                ImmutableArray<ModifierInfo<TypeSymbol>> customModifiers = DecodeModifiersOrThrow(
                    ref signatureReader,
                    out typeCode);
 
                if (typeCode == SignatureTypeCode.ByReference)
                {
                    isByRef = true;
                    refCustomModifiers = customModifiers;
                    customModifiers = DecodeModifiersOrThrow(
                        ref signatureReader,
                        out typeCode);
                }
 
                var type = DecodeTypeOrThrow(ref signatureReader, typeCode, out _);
                return new FieldInfo<TypeSymbol>(isByRef, refCustomModifiers, type, customModifiers);
            }
            catch (UnsupportedSignatureContent)
            {
                return new FieldInfo<TypeSymbol>(GetUnsupportedMetadataTypeSymbol()); // unsupported signature content
            }
            catch (BadImageFormatException mrEx)
            {
                return new FieldInfo<TypeSymbol>(GetUnsupportedMetadataTypeSymbol(mrEx));
            }
        }
 
        /// <summary>
        /// Find the methods that a given method explicitly overrides.
        /// </summary>
        /// <remarks>
        /// Methods may be on class or interfaces.
        /// Containing classes/interfaces will be supertypes of the implementing type.
        /// </remarks>
        /// <param name="implementingTypeDef">TypeDef handle of the implementing type.</param>
        /// <param name="implementingMethodDef">MethodDef handle of the implementing method.</param>
        /// <param name="implementingTypeSymbol">The type symbol for the implementing type.</param>
        /// <returns>Array of implemented methods.</returns>
        internal ImmutableArray<MethodSymbol> GetExplicitlyOverriddenMethods(TypeDefinitionHandle implementingTypeDef, MethodDefinitionHandle implementingMethodDef, TypeSymbol implementingTypeSymbol)
        {
            ArrayBuilder<MethodSymbol> resultBuilder = ArrayBuilder<MethodSymbol>.GetInstance();
 
            try
            {
                foreach (var methodImpl in Module.GetMethodImplementationsOrThrow(implementingTypeDef))
                {
                    EntityHandle methodDebugHandle;
                    EntityHandle implementedMethodHandle;
                    Module.GetMethodImplPropsOrThrow(methodImpl, out methodDebugHandle, out implementedMethodHandle);
 
                    // Though it is rare in practice, the spec allows the MethodImpl table to represent
                    // methods defined in the current module as MemberRefs rather than MethodDefs.
                    if (methodDebugHandle.Kind == HandleKind.MemberReference)
                    {
                        MethodSymbol methodBodySymbol = GetMethodSymbolForMemberRef((MemberReferenceHandle)methodDebugHandle, implementingTypeSymbol);
                        if (methodBodySymbol != null)
                        {
                            // Note: this might have a nil row ID, but that won't cause a problem
                            // since it will simply fail to be equal to the implementingMethodToken.
                            methodDebugHandle = GetMethodHandle(methodBodySymbol);
                        }
                    }
 
                    if (methodDebugHandle == implementingMethodDef)
                    {
                        if (!implementedMethodHandle.IsNil)
                        {
                            HandleKind implementedMethodTokenType = implementedMethodHandle.Kind;
 
                            MethodSymbol methodSymbol = null;
 
                            if (implementedMethodTokenType == HandleKind.MethodDefinition)
                            {
                                methodSymbol = FindMethodSymbolInSuperType(implementingTypeDef, (MethodDefinitionHandle)implementedMethodHandle);
                            }
                            else if (implementedMethodTokenType == HandleKind.MemberReference)
                            {
                                methodSymbol = GetMethodSymbolForMemberRef((MemberReferenceHandle)implementedMethodHandle, implementingTypeSymbol);
                            }
 
                            if (methodSymbol != null)
                            {
                                resultBuilder.Add(methodSymbol);
                            }
                        }
                    }
                }
            }
            catch (BadImageFormatException)
            { }
 
            return resultBuilder.ToImmutableAndFree();
        }
 
        /// <summary>
        /// Search for the <typeparamref name="MethodSymbol"/> corresponding to the given MethodDef token. Search amongst
        /// the supertypes (classes and interfaces) of a designated type.
        /// </summary>
        /// <remarks>
        /// Generally, the type will be a type that explicitly implements an interface and the method will be the
        /// implemented method (i.e. on the interface).
        /// </remarks>
        /// <param name="searchTypeDef">TypeDef token of the type from which the search should begin.</param>
        /// <param name="targetMethodDef">MethodDef token of the target method.</param>
        /// <returns>Corresponding <typeparamref name="MethodSymbol"/> or null, if none is found.</returns>
        private MethodSymbol FindMethodSymbolInSuperType(TypeDefinitionHandle searchTypeDef, MethodDefinitionHandle targetMethodDef)
        {
            try
            {
                // We're using queues (i.e. BFS), rather than stacks (i.e. DFS), because we expect the common case
                // to be implementing a method on an immediate supertype, rather than a remote ancestor.
                // We're using more than one queue for two reasons: 1) some of our TypeDef tokens come directly from the
                // metadata tables and we'd prefer not to manipulate the corresponding symbol objects; 2) we bump TypeDefs
                // to the front of the search order (i.e. ahead of symbols) because a MethodDef can correspond to a TypeDef
                // but not to a type ref (i.e. symbol).
                Queue<TypeDefinitionHandle> typeDefsToSearch = new Queue<TypeDefinitionHandle>();
                Queue<TypeSymbol> typeSymbolsToSearch = new Queue<TypeSymbol>();
 
                // A method def represents a method defined in this module, so we can
                // just search the method defs of this module.
                EnqueueTypeDefInterfacesAndBaseTypeOrThrow(typeDefsToSearch, typeSymbolsToSearch, searchTypeDef);
 
                //catch both cycles and duplicate interfaces
                HashSet<TypeDefinitionHandle> visitedTypeDefTokens = new HashSet<TypeDefinitionHandle>();
                HashSet<TypeSymbol> visitedTypeSymbols = new HashSet<TypeSymbol>();
 
                bool hasMoreTypeDefs;
                while ((hasMoreTypeDefs = (typeDefsToSearch.Count > 0)) || typeSymbolsToSearch.Count > 0)
                {
                    if (hasMoreTypeDefs)
                    {
                        TypeDefinitionHandle typeDef = typeDefsToSearch.Dequeue();
                        Debug.Assert(!typeDef.IsNil);
 
                        if (visitedTypeDefTokens.Add(typeDef))
                        {
                            foreach (MethodDefinitionHandle methodDef in Module.GetMethodsOfTypeOrThrow(typeDef))
                            {
                                if (methodDef == targetMethodDef)
                                {
                                    TypeSymbol typeSymbol = this.GetTypeOfToken(typeDef);
                                    return FindMethodSymbolInType(typeSymbol, targetMethodDef);
                                }
                            }
 
                            EnqueueTypeDefInterfacesAndBaseTypeOrThrow(typeDefsToSearch, typeSymbolsToSearch, typeDef);
                        }
                    }
                    else //has more type symbols
                    {
                        TypeSymbol typeSymbol = typeSymbolsToSearch.Dequeue();
                        Debug.Assert(typeSymbol != null);
 
                        if (visitedTypeSymbols.Add(typeSymbol))
                        {
                            //we're looking for a method def but we're currently on a type *ref*, so just enqueue supertypes
                            EnqueueTypeSymbolInterfacesAndBaseTypes(typeDefsToSearch, typeSymbolsToSearch, typeSymbol);
                        }
                    }
                }
            }
            catch (BadImageFormatException)
            { }
 
            return null;
        }
 
        /// <summary>
        /// Enqueue the interfaces implemented and the type extended by a given TypeDef.
        /// </summary>
        /// <param name="typeDefsToSearch">Queue of TypeDefs to search.</param>
        /// <param name="typeSymbolsToSearch">Queue of TypeSymbols (representing typeRefs to search).</param>
        /// <param name="searchTypeDef">Handle of the TypeDef for which we want to enqueue supertypes.</param>
        /// <exception cref="BadImageFormatException">An exception from metadata reader.</exception>
        private void EnqueueTypeDefInterfacesAndBaseTypeOrThrow(Queue<TypeDefinitionHandle> typeDefsToSearch, Queue<TypeSymbol> typeSymbolsToSearch, TypeDefinitionHandle searchTypeDef)
        {
            foreach (var interfaceImplHandle in Module.GetInterfaceImplementationsOrThrow(searchTypeDef))
            {
                var interfaceImpl = Module.MetadataReader.GetInterfaceImplementation(interfaceImplHandle);
                EnqueueTypeToken(typeDefsToSearch, typeSymbolsToSearch, interfaceImpl.Interface);
            }
 
            EnqueueTypeToken(typeDefsToSearch, typeSymbolsToSearch, Module.GetBaseTypeOfTypeOrThrow(searchTypeDef));
        }
 
        /// <summary>
        /// Helper method for enqueuing a type token in the right queue.
        /// Def -> typeDefsToSearch
        /// Ref -> typeSymbolsToSearch
        /// null -> neither
        /// </summary>
        private void EnqueueTypeToken(Queue<TypeDefinitionHandle> typeDefsToSearch, Queue<TypeSymbol> typeSymbolsToSearch, EntityHandle typeToken)
        {
            if (!typeToken.IsNil)
            {
                if (typeToken.Kind == HandleKind.TypeDefinition)
                {
                    typeDefsToSearch.Enqueue((TypeDefinitionHandle)typeToken);
                }
                else
                {
                    EnqueueTypeSymbol(typeDefsToSearch, typeSymbolsToSearch, this.GetTypeOfToken(typeToken));
                }
            }
        }
 
        /// <summary>
        /// Enqueue the interfaces implemented and the type extended by a given TypeDef.
        /// </summary>
        /// <param name="typeDefsToSearch">Queue of TypeDefs to search.</param>
        /// <param name="typeSymbolsToSearch">Queue of TypeSymbols (representing typeRefs to search).</param>
        /// <param name="typeSymbol">Symbol for which we want to enqueue supertypes.</param>
        protected abstract void EnqueueTypeSymbolInterfacesAndBaseTypes(Queue<TypeDefinitionHandle> typeDefsToSearch, Queue<TypeSymbol> typeSymbolsToSearch, TypeSymbol typeSymbol);
 
        /// <summary>
        /// Enqueue the given type as either a def or a ref.
        /// </summary>
        /// <param name="typeDefsToSearch">Queue of TypeDefs to search.</param>
        /// <param name="typeSymbolsToSearch">Queue of TypeSymbols (representing typeRefs to search).</param>
        /// <param name="typeSymbol">Symbol to enqueue.</param>
        protected abstract void EnqueueTypeSymbol(Queue<TypeDefinitionHandle> typeDefsToSearch, Queue<TypeSymbol> typeSymbolsToSearch, TypeSymbol typeSymbol);
 
        /// <summary>
        /// Search the members of a TypeSymbol to find the one that matches a given MethodDef token.
        /// </summary>
        /// <param name="type">Type to search for method.</param>
        /// <param name="methodDef">MethodDef handle of the method to find.</param>
        /// <returns>The corresponding MethodSymbol or null.</returns>
        protected abstract MethodSymbol FindMethodSymbolInType(TypeSymbol type, MethodDefinitionHandle methodDef);
 
        /// <summary>
        /// Search the members of a TypeSymbol to find the one that matches a given FieldDef token.
        /// </summary>
        /// <param name="type">Type to search for field.</param>
        /// <param name="fieldDef">FieldDef handle of the field to find.</param>
        /// <returns>The corresponding FieldSymbol or null.</returns>
        protected abstract FieldSymbol FindFieldSymbolInType(TypeSymbol type, FieldDefinitionHandle fieldDef);
 
        /// <summary>
        /// Given a MemberRef token for a method, we can find a corresponding MethodSymbol by
        /// searching for the name and signature.
        /// </summary>
        /// <param name="memberRef">A MemberRef token for a method.</param>
        /// <param name="implementingTypeSymbol">Scope the search to supertypes of the implementing type.</param>
        /// <param name="methodsOnly">True to only return method symbols, null if the token resolves to a field.</param>
        /// <returns>The corresponding MethodSymbol or null.</returns>
        internal abstract Symbol GetSymbolForMemberRef(MemberReferenceHandle memberRef, TypeSymbol implementingTypeSymbol = null, bool methodsOnly = false);
 
        internal MethodSymbol GetMethodSymbolForMemberRef(MemberReferenceHandle methodRef, TypeSymbol implementingTypeSymbol)
        {
            return (MethodSymbol)GetSymbolForMemberRef(methodRef, implementingTypeSymbol, methodsOnly: true);
        }
 
        internal FieldSymbol GetFieldSymbolForMemberRef(MemberReferenceHandle methodRef, TypeSymbol implementingTypeSymbol)
        {
            return (FieldSymbol)GetSymbolForMemberRef(methodRef, implementingTypeSymbol, methodsOnly: true);
        }
 
        protected override bool IsContainingAssembly(AssemblyIdentity identity)
        {
            return _containingAssemblyIdentity != null && _containingAssemblyIdentity.Equals(identity);
        }
 
        /// <summary>
        /// Given a method symbol, return the MethodDef token, if it is defined in
        /// this module, or a nil token, otherwise.
        /// </summary>
        /// <param name="method">The method symbol for which to return a MethodDef token.</param>
        /// <returns>A MethodDef token or nil.</returns>
        protected abstract MethodDefinitionHandle GetMethodHandle(MethodSymbol method);
 
        protected abstract ConcurrentDictionary<TypeDefinitionHandle, TypeSymbol> GetTypeHandleToTypeMap();
        protected abstract ConcurrentDictionary<TypeReferenceHandle, TypeSymbol> GetTypeRefHandleToTypeMap();
 
        protected abstract TypeSymbol SubstituteNoPiaLocalType(TypeDefinitionHandle typeDef, ref MetadataTypeName name, string interfaceGuid, string scope, string identifier);
 
        protected abstract TypeSymbol LookupTopLevelTypeDefSymbol(string moduleName, ref MetadataTypeName emittedName, out bool isNoPiaLocalType);
 
        protected abstract TypeSymbol GetGenericTypeParamSymbol(int position);
        protected abstract TypeSymbol GetGenericMethodTypeParamSymbol(int position);
 
        private static TypedConstant CreateArrayTypedConstant(TypeSymbol type, ImmutableArray<TypedConstant> array)
        {
            if (type.TypeKind == TypeKind.Error)
            {
                return new TypedConstant(type, TypedConstantKind.Error, null);
            }
 
            Debug.Assert(type.TypeKind == TypeKind.Array);
            return new TypedConstant(type, array);
        }
 
        private static TypedConstant CreateTypedConstant(TypeSymbol type, TypedConstantKind kind, object value)
        {
            if (type.TypeKind == TypeKind.Error)
            {
                return new TypedConstant(type, TypedConstantKind.Error, null);
            }
 
            return new TypedConstant(type, kind, value);
        }
 
        private static TypedConstant CreateTypedConstant(TypeSymbol type, TypedConstantKind kind, bool value)
        {
            return CreateTypedConstant(type, kind, Boxes.Box(value));
        }
 
        /// <summary>
        /// Returns a symbol that given token resolves to or null of the token represents an entity that isn't represented by a symbol,
        /// such as vararg MemberRef.
        /// </summary>
        internal Symbol GetSymbolForILToken(EntityHandle token)
        {
            try
            {
                switch (token.Kind)
                {
                    case HandleKind.TypeDefinition:
                    case HandleKind.TypeSpecification:
                    case HandleKind.TypeReference:
                        return GetTypeOfToken(token);
 
                    case HandleKind.MethodDefinition:
                        {
                            TypeDefinitionHandle typeDef = Module.FindContainingTypeOrThrow((MethodDefinitionHandle)token);
 
                            if (typeDef.IsNil)
                            {
                                // error
                                return null;
                            }
 
                            TypeSymbol type = GetTypeOfTypeDef(typeDef);
                            if (type == null)
                            {
                                // error
                                return null;
                            }
 
                            return GetMethodSymbolForMethodDefOrMemberRef(token, type);
                        }
 
                    case HandleKind.FieldDefinition:
                        {
                            TypeDefinitionHandle typeDef = Module.FindContainingTypeOrThrow((FieldDefinitionHandle)token);
                            if (typeDef.IsNil)
                            {
                                // error
                                return null;
                            }
 
                            TypeSymbol type = GetTypeOfToken(typeDef);
                            if (type == null)
                            {
                                // error
                                return null;
                            }
 
                            return GetFieldSymbolForFieldDefOrMemberRef(token, type);
                        }
 
                    case HandleKind.MethodSpecification:
                        EntityHandle method;
                        BlobHandle instantiation;
                        this.Module.GetMethodSpecificationOrThrow((MethodSpecificationHandle)token, out method, out instantiation);
 
                        var genericDefinition = (MethodSymbol)GetSymbolForILToken(method);
                        if (genericDefinition == null)
                        {
                            // error
                            return null;
                        }
 
                        var genericArguments = DecodeMethodSpecTypeArgumentsOrThrow(instantiation);
                        return (MethodSymbol)genericDefinition.Construct(genericArguments);
 
                    case HandleKind.MemberReference:
                        return GetSymbolForMemberRef((MemberReferenceHandle)token);
                }
            }
            catch (BadImageFormatException)
            { }
 
            // error: unexpected token in IL
            return null;
        }
 
        /// <summary>
        /// Given a MemberRef token, return the TypeSymbol for its Class field.
        /// </summary>
        internal TypeSymbol GetMemberRefTypeSymbol(MemberReferenceHandle memberRef)
        {
            try
            {
                EntityHandle container = Module.GetContainingTypeOrThrow(memberRef);
 
                HandleKind containerType = container.Kind;
                Debug.Assert(
                    containerType == HandleKind.MethodDefinition ||
                    containerType == HandleKind.ModuleReference ||
                    containerType == HandleKind.TypeDefinition ||
                    containerType == HandleKind.TypeReference ||
                    containerType == HandleKind.TypeSpecification);
 
                if (containerType != HandleKind.TypeDefinition &&
                    containerType != HandleKind.TypeReference &&
                    containerType != HandleKind.TypeSpecification)
                {
                    // C# symbols don't support global methods
                    return null;
                }
 
                return this.GetTypeOfToken(container);
            }
            catch (BadImageFormatException)
            {
                return null;
            }
        }
 
        internal MethodSymbol GetMethodSymbolForMethodDefOrMemberRef(EntityHandle memberToken, TypeSymbol container)
        {
            HandleKind type = memberToken.Kind;
            Debug.Assert(type == HandleKind.MethodDefinition || type == HandleKind.MemberReference);
 
            return type == HandleKind.MethodDefinition
                ? FindMethodSymbolInType(container, (MethodDefinitionHandle)memberToken)
                : GetMethodSymbolForMemberRef((MemberReferenceHandle)memberToken, container);
        }
 
        internal FieldSymbol GetFieldSymbolForFieldDefOrMemberRef(EntityHandle memberToken, TypeSymbol container)
        {
            HandleKind type = memberToken.Kind;
            Debug.Assert(type == HandleKind.FieldDefinition ||
                            type == HandleKind.MemberReference);
 
            return type == HandleKind.FieldDefinition
                ? FindFieldSymbolInType(container, (FieldDefinitionHandle)memberToken)
                : GetFieldSymbolForMemberRef((MemberReferenceHandle)memberToken, container);
        }
 
        /// <summary>
        /// Checks whether signatures match where the signatures are either from a property
        /// and an accessor or two accessors. When comparing a property or getter to setter, the
        /// setter signature must be the second argument and 'comparingToSetter' must be true.
        /// </summary>
        /// <param name="signature1">
        /// Signature of the property containing the accessor, or the getter (type, then parameters).
        /// </param>
        /// <param name="signature2">
        /// Signature of the accessor when comparing property and accessor,
        /// or the setter when comparing getter and setter (return type and then parameters).
        /// </param>
        /// <param name="comparingToSetter">
        /// True when comparing a property or getter to a setter, false otherwise.
        /// </param>
        /// <param name="compareParamByRef">
        /// True if differences in IsByRef for parameters should be treated as significant.
        /// </param>
        /// <param name="compareReturnType">
        /// True if differences in return type (or value parameter for setter) should be treated as significant.
        /// </param>
        /// <returns>True if the accessor signature is appropriate for the containing property.</returns>
        internal bool DoPropertySignaturesMatch(ParamInfo<TypeSymbol>[] signature1, ParamInfo<TypeSymbol>[] signature2, bool comparingToSetter, bool compareParamByRef, bool compareReturnType)
        {
            int additionalParamCount = (comparingToSetter ? 1 : 0);
 
            // Check the number of parameters.
            if ((signature2.Length - additionalParamCount) != signature1.Length)
            {
                return false;
            }
 
            // Check the setter has a void type.
            if (comparingToSetter &&
                (GetPrimitiveTypeCode(signature2[0].Type) != Cci.PrimitiveTypeCode.Void))
            {
                return false;
            }
 
            // Check the type of each parameter.
            for (int paramIndex1 = compareReturnType ? 0 : 1; paramIndex1 < signature1.Length; paramIndex1++)
            {
                int paramIndex2 =
                    ((paramIndex1 == 0) && comparingToSetter) ?
                    signature1.Length :
                    paramIndex1;
                var param1 = signature1[paramIndex1];
                var param2 = signature2[paramIndex2];
                if (compareParamByRef && (param2.IsByRef != param1.IsByRef))
                {
                    return false;
                }
                if (!param2.Type.Equals(param1.Type, TypeCompareKind.ConsiderEverything))
                {
                    return false;
                }
            }
 
            return true;
        }
 
        /// <summary>
        /// Check whether an event accessor has an appropriate signature.
        /// </summary>
        /// <param name="eventType">Type of the event containing the accessor.</param>
        /// <param name="methodParams">Signature of the accessor (return type and then parameters).</param>
        /// <returns>True if the accessor signature is appropriate for the containing event.</returns>
        internal bool DoesSignatureMatchEvent(TypeSymbol eventType, ParamInfo<TypeSymbol>[] methodParams)
        {
            // Check the number of parameters.
            if (methodParams.Length != 2)
            {
                return false;
            }
 
            // Check the accessor has a void type.
            if (GetPrimitiveTypeCode(methodParams[0].Type) != Cci.PrimitiveTypeCode.Void)
            {
                return false;
            }
 
            var methodParam = methodParams[1];
            return !methodParam.IsByRef && methodParam.Type.Equals(eventType);
        }
    }
}