File: PEWriter\MetadataWriter.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
 
// We're not actually doing formatter-based serialization in this file.
// We're simply propagating along the attributes of symbols we are emitting to metadata.
#pragma warning disable SYSLIB0050 // 'FieldAttributes.NotSerialized' is obsolete: 'Formatter-based serialization is obsolete and should not be used.'
 
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Reflection.PortableExecutable;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeGen;
using Microsoft.CodeAnalysis.Collections;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.Emit.EditAndContinue;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Symbols;
using Microsoft.DiaSymReader;
using Roslyn.Utilities;
using ReferenceEqualityComparer = Roslyn.Utilities.ReferenceEqualityComparer;
 
namespace Microsoft.Cci
{
    internal abstract partial class MetadataWriter
    {
        internal static readonly Encoding s_utf8Encoding = Encoding.UTF8;
 
        /// <summary>
        /// This is the maximum length of a type or member name in metadata, assuming
        /// the name is in UTF-8 format and not (yet) null-terminated.
        /// </summary>
        /// <remarks>
        /// Source names may have to be shorter still to accommodate mangling.
        /// Used for event names, field names, property names, field names, method def names,
        /// member ref names, type def (full) names, type ref (full) names, exported type
        /// (full) names, parameter names, manifest resource names, and unmanaged method names
        /// (ImplMap table).
        ///
        /// See CLI Part II, section 22.
        /// </remarks>
        internal const int NameLengthLimit = 1024 - 1; //MAX_CLASS_NAME = 1024 in dev11
 
        /// <summary>
        /// This is the maximum length of a path in metadata, assuming the path is in UTF-8
        /// format and not (yet) null-terminated.
        /// </summary>
        /// <remarks>
        /// Used for file names, module names, and module ref names.
        ///
        /// See CLI Part II, section 22.
        /// </remarks>
        internal const int PathLengthLimit = 260 - 1; //MAX_PATH = 1024 in dev11
 
        /// <summary>
        /// This is the maximum length of a string in the PDB, assuming it is in UTF-8 format
        /// and not (yet) null-terminated.
        /// </summary>
        /// <remarks>
        /// Used for import strings, locals, and local constants.
        /// </remarks>
        internal const int PdbLengthLimit = 2046; // Empirical, based on when ISymUnmanagedWriter2 methods start throwing.
 
        private readonly bool _deterministic;
 
        internal readonly bool MetadataOnly;
        internal readonly bool EmitTestCoverageData;
 
        // A map of method body before token translation to RVA. Used for deduplication of small bodies.
        private readonly Dictionary<(ImmutableArray<byte>, bool), int> _smallMethodBodies;
 
        private const byte TinyFormat = 2;
        private const int ThrowNullCodeSize = 2;
        private static readonly ImmutableArray<byte> ThrowNullEncodedBody =
            ImmutableArray.Create(
                (byte)((ThrowNullCodeSize << 2) | TinyFormat),
                (byte)ILOpCode.Ldnull,
                (byte)ILOpCode.Throw);
 
        protected MetadataWriter(
            MetadataBuilder metadata,
            MetadataBuilder debugMetadataOpt,
            DynamicAnalysisDataWriter dynamicAnalysisDataWriterOpt,
            EmitContext context,
            CommonMessageProvider messageProvider,
            bool metadataOnly,
            bool deterministic,
            bool emitTestCoverageData,
            CancellationToken cancellationToken)
        {
            Debug.Assert(metadata != debugMetadataOpt);
 
            this.module = context.Module;
            _deterministic = deterministic;
            this.MetadataOnly = metadataOnly;
            this.EmitTestCoverageData = emitTestCoverageData;
 
            // EDMAURER provide some reasonable size estimates for these that will avoid
            // much of the reallocation that would occur when growing these from empty.
            _signatureIndex = new Dictionary<ISignature, KeyValuePair<BlobHandle, ImmutableArray<byte>>>(module.HintNumberOfMethodDefinitions, ReferenceEqualityComparer.Instance); //ignores field signatures
 
            this.Context = context;
            this.messageProvider = messageProvider;
            _cancellationToken = cancellationToken;
 
            this.metadata = metadata;
            _debugMetadataOpt = debugMetadataOpt;
            _dynamicAnalysisDataWriterOpt = dynamicAnalysisDataWriterOpt;
            _smallMethodBodies = new Dictionary<(ImmutableArray<byte>, bool), int>(ByteSequenceBoolTupleComparer.Instance);
            _scopeIndex = new Dictionary<IImportScope, ImportScopeHandle>(new ImportScopeEqualityComparer(context));
        }
 
        /// <summary>
        /// Returns true if writing full metadata, false if writing delta.
        /// </summary>
        internal bool IsFullMetadata
        {
            get { return this.Generation == 0; }
        }
 
        /// <summary>
        /// True if writing delta metadata in a minimal format.
        /// </summary>
        private bool IsMinimalDelta
        {
            get { return !IsFullMetadata; }
        }
 
        /// <summary>
        /// NetModules and EnC deltas don't have AssemblyDef record.
        /// We don't emit it for EnC deltas since assembly identity has to be preserved across generations (CLR/debugger get confused otherwise).
        /// </summary>
        private bool EmitAssemblyDefinition => module.OutputKind != OutputKind.NetModule && !IsMinimalDelta;
 
        /// <summary>
        /// Returns metadata generation ordinal. Zero for
        /// full metadata and non-zero for delta.
        /// </summary>
        protected abstract ushort Generation { get; }
 
        /// <summary>
        /// Returns unique Guid for this delta, or default(Guid)
        /// if full metadata.
        /// </summary>
        protected abstract Guid EncId { get; }
 
        /// <summary>
        /// Returns Guid of previous delta, or default(Guid)
        /// if full metadata or generation 1 delta.
        /// </summary>
        protected abstract Guid EncBaseId { get; }
 
        /// <summary>
        /// Returns true and full metadata handle of the type definition
        /// if the type definition is recognized. Otherwise returns false.
        /// </summary>
        protected abstract bool TryGetTypeDefinitionHandle(ITypeDefinition def, out TypeDefinitionHandle handle);
 
        /// <summary>
        /// Get full metadata handle of the type definition.
        /// </summary>
        protected abstract TypeDefinitionHandle GetTypeDefinitionHandle(ITypeDefinition def);
 
        /// <summary>
        /// The type definition corresponding to full metadata type handle.
        /// Deltas are only required to support indexing into current generation.
        /// </summary>
        protected abstract ITypeDefinition GetTypeDef(TypeDefinitionHandle handle);
 
        /// <summary>
        /// The type definitions to be emitted, in row order. These
        /// are just the type definitions from the current generation.
        /// </summary>
        protected abstract IReadOnlyList<ITypeDefinition> GetTypeDefs();
 
        /// <summary>
        /// Get full metadata handle of the event definition.
        /// </summary>
        protected abstract EventDefinitionHandle GetEventDefinitionHandle(IEventDefinition def);
 
        /// <summary>
        /// The event definitions to be emitted, in row order. These
        /// are just the event definitions from the current generation.
        /// </summary>
        protected abstract IReadOnlyList<IEventDefinition> GetEventDefs();
 
        /// <summary>
        /// Get full metadata handle of the field definition.
        /// </summary>
        protected abstract FieldDefinitionHandle GetFieldDefinitionHandle(IFieldDefinition def);
 
        /// <summary>
        /// The field definitions to be emitted, in row order. These
        /// are just the field definitions from the current generation.
        /// </summary>
        protected abstract IReadOnlyList<IFieldDefinition> GetFieldDefs();
 
        /// <summary>
        /// Returns true and handle of the method definition
        /// if the method definition is recognized. Otherwise returns false.
        /// The index is into the full metadata.
        /// </summary>
        protected abstract bool TryGetMethodDefinitionHandle(IMethodDefinition def, out MethodDefinitionHandle handle);
 
        /// <summary>
        /// Get full metadata handle of the method definition.
        /// </summary>
        protected abstract MethodDefinitionHandle GetMethodDefinitionHandle(IMethodDefinition def);
 
        /// <summary>
        /// The method definition corresponding to full metadata method handle.
        /// Deltas are only required to support indexing into current generation.
        /// </summary>
        protected abstract IMethodDefinition GetMethodDef(MethodDefinitionHandle handle);
 
        /// <summary>
        /// The method definitions to be emitted, in row order. These
        /// are just the method definitions from the current generation.
        /// </summary>
        protected abstract IReadOnlyList<IMethodDefinition> GetMethodDefs();
 
        /// <summary>
        /// Get full metadata handle of the property definition.
        /// </summary>
        protected abstract PropertyDefinitionHandle GetPropertyDefIndex(IPropertyDefinition def);
 
        /// <summary>
        /// The property definitions to be emitted, in row order. These
        /// are just the property definitions from the current generation.
        /// </summary>
        protected abstract IReadOnlyList<IPropertyDefinition> GetPropertyDefs();
 
        /// <summary>
        /// The full metadata handle of the parameter definition.
        /// </summary>
        protected abstract ParameterHandle GetParameterHandle(IParameterDefinition def);
 
        /// <summary>
        /// The parameter definitions to be emitted, in row order. These
        /// are just the parameter definitions from the current generation.
        /// </summary>
        protected abstract IReadOnlyList<IParameterDefinition> GetParameterDefs();
 
        /// <summary>
        /// The generic parameter definitions to be emitted, in row order. These
        /// are just the generic parameter definitions from the current generation.
        /// </summary>
        protected abstract IReadOnlyList<IGenericParameter> GetGenericParameters();
 
        /// <summary>
        /// The handle of the first field of the type.
        /// </summary>
        protected abstract FieldDefinitionHandle GetFirstFieldDefinitionHandle(INamedTypeDefinition typeDef);
 
        /// <summary>
        /// The handle of the first method of the type.
        /// </summary>
        protected abstract MethodDefinitionHandle GetFirstMethodDefinitionHandle(INamedTypeDefinition typeDef);
 
        /// <summary>
        /// The handle of the first parameter of the method.
        /// </summary>
        protected abstract ParameterHandle GetFirstParameterHandle(IMethodDefinition methodDef);
 
        /// <summary>
        /// Return full metadata handle of the assembly reference, adding
        /// the reference to the index for this generation if missing.
        /// Deltas are not required to return rows from previous generations.
        /// </summary>
        protected abstract AssemblyReferenceHandle GetOrAddAssemblyReferenceHandle(IAssemblyReference reference);
 
        /// <summary>
        /// The assembly references to be emitted, in row order. These
        /// are just the assembly references from the current generation.
        /// </summary>
        protected abstract IReadOnlyList<AssemblyIdentity> GetAssemblyRefs();
 
        // ModuleRef table contains module names for TypeRefs that target types in netmodules (represented by IModuleReference),
        // and module names specified by P/Invokes (plain strings). Names in the table must be unique and are case sensitive.
        //
        // Spec 22.31 (ModuleRef : 0x1A)
        // "Name should match an entry in the Name column of the File table. Moreover, that entry shall enable the
        // CLI to locate the target module (typically it might name the file used to hold the module)"
        //
        // This is not how the Dev10 compilers and ILASM work. An entry is added to File table only for resources and netmodules.
        // Entries aren't added for P/Invoked modules.
 
        /// <summary>
        /// Return full metadata handle of the module reference, adding
        /// the reference to the index for this generation if missing.
        /// Deltas are not required to return rows from previous generations.
        /// </summary>
        protected abstract ModuleReferenceHandle GetOrAddModuleReferenceHandle(string reference);
 
        /// <summary>
        /// The module references to be emitted, in row order. These
        /// are just the module references from the current generation.
        /// </summary>
        protected abstract IReadOnlyList<string> GetModuleRefs();
 
        /// <summary>
        /// Return full metadata handle of the member reference, adding
        /// the reference to the index for this generation if missing.
        /// Deltas are not required to return rows from previous generations.
        /// </summary>
        protected abstract MemberReferenceHandle GetOrAddMemberReferenceHandle(ITypeMemberReference reference);
 
        /// <summary>
        /// The member references to be emitted, in row order. These
        /// are just the member references from the current generation.
        /// </summary>
        protected abstract IReadOnlyList<ITypeMemberReference> GetMemberRefs();
 
        /// <summary>
        /// Return full metadata handle of the method spec, adding
        /// the spec to the index for this generation if missing.
        /// Deltas are not required to return rows from previous generations.
        /// </summary>
        protected abstract MethodSpecificationHandle GetOrAddMethodSpecificationHandle(IGenericMethodInstanceReference reference);
 
        /// <summary>
        /// The method specs to be emitted, in row order. These
        /// are just the method specs from the current generation.
        /// </summary>
        protected abstract IReadOnlyList<IGenericMethodInstanceReference> GetMethodSpecs();
 
        /// <summary>
        /// The greatest index given to any method definition.
        /// </summary>
        protected abstract int GreatestMethodDefIndex { get; }
 
        /// <summary>
        /// Return true and full metadata handle of the type reference
        /// if the reference is available in the current generation.
        /// Deltas are not required to return rows from previous generations.
        /// </summary>
        protected abstract bool TryGetTypeReferenceHandle(ITypeReference reference, out TypeReferenceHandle handle);
 
        /// <summary>
        /// Return full metadata handle of the type reference, adding
        /// the reference to the index for this generation if missing.
        /// Deltas are not required to return rows from previous generations.
        /// </summary>
        protected abstract TypeReferenceHandle GetOrAddTypeReferenceHandle(ITypeReference reference);
 
        /// <summary>
        /// The type references to be emitted, in row order. These
        /// are just the type references from the current generation.
        /// </summary>
        protected abstract IReadOnlyList<ITypeReference> GetTypeRefs();
 
        /// <summary>
        /// Returns full metadata handle of the type spec, adding
        /// the spec to the index for this generation if missing.
        /// Deltas are not required to return rows from previous generations.
        /// </summary>
        protected abstract TypeSpecificationHandle GetOrAddTypeSpecificationHandle(ITypeReference reference);
 
        /// <summary>
        /// The type specs to be emitted, in row order. These
        /// are just the type specs from the current generation.
        /// </summary>
        protected abstract IReadOnlyList<ITypeReference> GetTypeSpecs();
 
        /// <summary>
        /// Returns full metadata handle the standalone signature, adding
        /// the signature to the index for this generation if missing.
        /// Deltas are not required to return rows from previous generations.
        /// </summary>
        protected abstract StandaloneSignatureHandle GetOrAddStandaloneSignatureHandle(BlobHandle handle);
 
        /// <summary>
        /// The signature blob handles to be emitted, in row order. These
        /// are just the signature indices from the current generation.
        /// </summary>
        protected abstract IReadOnlyList<BlobHandle> GetStandaloneSignatureBlobHandles();
 
        protected abstract void CreateIndicesForNonTypeMembers(ITypeDefinition typeDef);
 
        /// <summary>
        /// Return a visitor for traversing all references to be emitted.
        /// </summary>
        protected abstract ReferenceIndexer CreateReferenceVisitor();
 
        /// <summary>
        /// Populate EventMap table.
        /// </summary>
        protected abstract void PopulateEventMapTableRows();
 
        /// <summary>
        /// Populate PropertyMap table.
        /// </summary>
        protected abstract void PopulatePropertyMapTableRows();
 
        protected abstract void ReportReferencesToAddedSymbols();
 
        // If true, it is allowed to have methods not have bodies (for emitting metadata-only
        // assembly)
        private readonly CancellationToken _cancellationToken;
        protected readonly CommonPEModuleBuilder module;
        public readonly EmitContext Context;
        protected readonly CommonMessageProvider messageProvider;
 
        // progress:
        private bool _tableIndicesAreComplete;
        private bool _usingNonSourceDocumentNameEnumerator;
        private ImmutableArray<string>.Enumerator _nonSourceDocumentNameEnumerator;
 
        private EntityHandle[] _pseudoSymbolTokenToTokenMap;
        private object[] _pseudoSymbolTokenToReferenceMap;
        private UserStringHandle[] _pseudoStringTokenToTokenMap;
        private bool _userStringTokenOverflow;
        private string[] _pseudoStringTokenToStringMap;
        private ReferenceIndexer _referenceVisitor;
 
        protected readonly MetadataBuilder metadata;
 
        // A builder for Portable or Embedded PDB metadata, or null if we are not emitting Portable/Embedded PDB.
        protected readonly MetadataBuilder _debugMetadataOpt;
 
        internal bool EmitPortableDebugMetadata => _debugMetadataOpt != null;
 
        private readonly DynamicAnalysisDataWriter _dynamicAnalysisDataWriterOpt;
 
        private readonly Dictionary<ICustomAttribute, BlobHandle> _customAttributeSignatureIndex = new Dictionary<ICustomAttribute, BlobHandle>();
        private readonly Dictionary<ITypeReference, BlobHandle> _typeSpecSignatureIndex = new Dictionary<ITypeReference, BlobHandle>(ReferenceEqualityComparer.Instance);
        private readonly Dictionary<string, int> _fileRefIndex = new Dictionary<string, int>(32);  // more than enough in most cases, value is a RowId
        private readonly List<IFileReference> _fileRefList = new List<IFileReference>(32);
        private readonly Dictionary<IFieldReference, BlobHandle> _fieldSignatureIndex = new Dictionary<IFieldReference, BlobHandle>(ReferenceEqualityComparer.Instance);
 
        // We need to keep track of both the index of the signature and the actual blob to support VB static local naming scheme.
        private readonly Dictionary<ISignature, KeyValuePair<BlobHandle, ImmutableArray<byte>>> _signatureIndex;
 
        private readonly Dictionary<IMarshallingInformation, BlobHandle> _marshallingDescriptorIndex = new Dictionary<IMarshallingInformation, BlobHandle>();
        protected readonly List<MethodImplementation> methodImplList = new List<MethodImplementation>();
        private readonly Dictionary<IGenericMethodInstanceReference, BlobHandle> _methodInstanceSignatureIndex = new Dictionary<IGenericMethodInstanceReference, BlobHandle>(ReferenceEqualityComparer.Instance);
 
        // Well known dummy cor library types whose refs are used for attaching assembly attributes off within net modules
        // There is no guarantee the types actually exist in a cor library
        internal const string dummyAssemblyAttributeParentNamespace = "System.Runtime.CompilerServices";
        internal const string dummyAssemblyAttributeParentName = "AssemblyAttributesGoHere";
        internal static readonly string[,] dummyAssemblyAttributeParentQualifier = { { "", "M" }, { "S", "SM" } };
        private readonly TypeReferenceHandle[,] _dummyAssemblyAttributeParent = { { default(TypeReferenceHandle), default(TypeReferenceHandle) }, { default(TypeReferenceHandle), default(TypeReferenceHandle) } };
 
        internal CommonPEModuleBuilder Module => module;
 
        private void CreateMethodBodyReferenceIndex()
        {
            var referencesInIL = module.ReferencesInIL();
 
            _pseudoSymbolTokenToTokenMap = new EntityHandle[referencesInIL.Length];
            _pseudoSymbolTokenToReferenceMap = referencesInIL.ToArray();
        }
 
        private void CreateIndices()
        {
            _cancellationToken.ThrowIfCancellationRequested();
 
            this.CreateInitialAssemblyRefIndex();
            this.CreateInitialFileRefIndex();
            this.CreateIndicesForModule();
 
            // Snapshot user strings only after indexing all types and members.
            // EnC method deletes discovered during indexing may contribute new strings.
            this.CreateUserStringIndices();
 
            // Find all references and assign tokens.
            _referenceVisitor = this.CreateReferenceVisitor();
            _referenceVisitor.Visit(module);
 
            this.CreateMethodBodyReferenceIndex();
 
            this.OnIndicesCreated();
        }
 
        private void CreateUserStringIndices()
        {
            _pseudoStringTokenToStringMap = module.CopyStrings();
            _pseudoStringTokenToTokenMap = new UserStringHandle[_pseudoStringTokenToStringMap.Length];
        }
 
        private void CreateIndicesForModule()
        {
            var nestedTypes = new Queue<INestedTypeDefinition>();
 
            foreach (INamespaceTypeDefinition typeDef in module.GetTopLevelTypeDefinitions(Context))
            {
                this.CreateIndicesFor(typeDef, nestedTypes);
            }
 
            while (nestedTypes.Count > 0)
            {
                var nestedType = nestedTypes.Dequeue();
                this.CreateIndicesFor(nestedType, nestedTypes);
            }
        }
 
        protected virtual void OnIndicesCreated()
        {
        }
 
        private void CreateIndicesFor(ITypeDefinition typeDef, Queue<INestedTypeDefinition> nestedTypes)
        {
            _cancellationToken.ThrowIfCancellationRequested();
 
            this.CreateIndicesForNonTypeMembers(typeDef);
 
            // Metadata spec:
            // The TypeDef table has a special ordering constraint:
            // the definition of an enclosing class shall precede the definition of all classes it encloses.
            foreach (var nestedType in typeDef.GetNestedTypes(Context))
            {
                nestedTypes.Enqueue(nestedType);
            }
        }
 
        protected IEnumerable<IGenericTypeParameter> GetConsolidatedTypeParameters(ITypeDefinition typeDef)
        {
            INestedTypeDefinition nestedTypeDef = typeDef.AsNestedTypeDefinition(Context);
            if (nestedTypeDef == null)
            {
                if (typeDef.IsGeneric)
                {
                    return typeDef.GenericParameters;
                }
 
                return null;
            }
 
            return this.GetConsolidatedTypeParameters(typeDef, typeDef);
        }
 
        private List<IGenericTypeParameter> GetConsolidatedTypeParameters(ITypeDefinition typeDef, ITypeDefinition owner)
        {
            List<IGenericTypeParameter> result = null;
            INestedTypeDefinition nestedTypeDef = typeDef.AsNestedTypeDefinition(Context);
            if (nestedTypeDef != null)
            {
                result = this.GetConsolidatedTypeParameters(nestedTypeDef.ContainingTypeDefinition, owner);
            }
 
            if (typeDef.GenericParameterCount > 0)
            {
                ushort index = 0;
                if (result == null)
                {
                    result = new List<IGenericTypeParameter>();
                }
                else
                {
                    index = (ushort)result.Count;
                }
 
                if (typeDef == owner && index == 0)
                {
                    result.AddRange(typeDef.GenericParameters);
                }
                else
                {
                    foreach (IGenericTypeParameter genericParameter in typeDef.GenericParameters)
                    {
                        result.Add(new InheritedTypeParameter(index++, owner, genericParameter));
                    }
                }
            }
 
            return result;
        }
 
        protected ImmutableArray<IParameterDefinition> GetParametersToEmit(IMethodDefinition methodDef)
        {
            if (methodDef.ParameterCount == 0 && !(methodDef.ReturnValueIsMarshalledExplicitly || IteratorHelper.EnumerableIsNotEmpty(methodDef.GetReturnValueAttributes(Context))))
            {
                return ImmutableArray<IParameterDefinition>.Empty;
            }
 
            return GetParametersToEmitCore(methodDef);
        }
 
        private ImmutableArray<IParameterDefinition> GetParametersToEmitCore(IMethodDefinition methodDef)
        {
            ArrayBuilder<IParameterDefinition> builder = null;
            var parameters = methodDef.Parameters;
 
            if (methodDef.ReturnValueIsMarshalledExplicitly || IteratorHelper.EnumerableIsNotEmpty(methodDef.GetReturnValueAttributes(Context)))
            {
                builder = ArrayBuilder<IParameterDefinition>.GetInstance(parameters.Length + 1);
                builder.Add(new ReturnValueParameter(methodDef));
            }
 
            for (int i = 0; i < parameters.Length; i++)
            {
                IParameterDefinition parDef = parameters[i];
 
                // No explicit param row is needed if param has no flags (other than optionally IN),
                // no name and no references to the param row, such as CustomAttribute, Constant, or FieldMarshal
                if (parDef.Name != String.Empty ||
                    parDef.HasDefaultValue || parDef.IsOptional || parDef.IsOut || parDef.IsMarshalledExplicitly ||
                    IteratorHelper.EnumerableIsNotEmpty(parDef.GetAttributes(Context)))
                {
                    if (builder != null)
                    {
                        builder.Add(parDef);
                    }
                }
                else
                {
                    // we have a parameter that does not need to be emitted (not common)
                    if (builder == null)
                    {
                        builder = ArrayBuilder<IParameterDefinition>.GetInstance(parameters.Length);
                        builder.AddRange(parameters, i);
                    }
                }
            }
 
            return builder?.ToImmutableAndFree() ?? parameters;
        }
 
        /// <summary>
        /// Returns a reference to the unit that defines the given referenced type. If the referenced type is a structural type, such as a pointer or a generic type instance,
        /// then the result is null.
        /// </summary>
        public static IUnitReference GetDefiningUnitReference(ITypeReference typeReference, EmitContext context)
        {
            INestedTypeReference nestedTypeReference = typeReference.AsNestedTypeReference;
            while (nestedTypeReference != null)
            {
                if (nestedTypeReference.AsGenericTypeInstanceReference != null)
                {
                    return null;
                }
 
                typeReference = nestedTypeReference.GetContainingType(context);
                nestedTypeReference = typeReference.AsNestedTypeReference;
            }
 
            INamespaceTypeReference namespaceTypeReference = typeReference.AsNamespaceTypeReference;
            if (namespaceTypeReference == null)
            {
                return null;
            }
 
            Debug.Assert(namespaceTypeReference.AsGenericTypeInstanceReference == null);
 
            return namespaceTypeReference.GetUnit(context);
        }
 
        private void CreateInitialAssemblyRefIndex()
        {
            Debug.Assert(!_tableIndicesAreComplete);
            foreach (IAssemblyReference assemblyRef in this.module.GetAssemblyReferences(Context))
            {
                this.GetOrAddAssemblyReferenceHandle(assemblyRef);
            }
        }
 
        private void CreateInitialFileRefIndex()
        {
            Debug.Assert(!_tableIndicesAreComplete);
 
            foreach (IFileReference fileRef in module.GetFiles(Context))
            {
                string key = fileRef.FileName;
                if (!_fileRefIndex.ContainsKey(key))
                {
                    _fileRefList.Add(fileRef);
                    _fileRefIndex.Add(key, _fileRefList.Count);
                }
            }
        }
 
        internal AssemblyReferenceHandle GetAssemblyReferenceHandle(IAssemblyReference assemblyReference)
        {
            var containingAssembly = this.module.GetContainingAssembly(Context);
 
            if (containingAssembly != null && ReferenceEquals(assemblyReference, containingAssembly))
            {
                return default(AssemblyReferenceHandle);
            }
 
            return this.GetOrAddAssemblyReferenceHandle(assemblyReference);
        }
 
        internal ModuleReferenceHandle GetModuleReferenceHandle(string moduleName)
        {
            return this.GetOrAddModuleReferenceHandle(moduleName);
        }
 
        private BlobHandle GetCustomAttributeSignatureIndex(ICustomAttribute customAttribute)
        {
            BlobHandle result;
            if (_customAttributeSignatureIndex.TryGetValue(customAttribute, out result))
            {
                return result;
            }
 
            var writer = PooledBlobBuilder.GetInstance();
            this.SerializeCustomAttributeSignature(customAttribute, writer);
            result = metadata.GetOrAddBlob(writer);
            _customAttributeSignatureIndex.Add(customAttribute, result);
            writer.Free();
            return result;
        }
 
        private EntityHandle GetCustomAttributeTypeCodedIndex(IMethodReference methodReference)
        {
            IMethodDefinition methodDef = null;
            IUnitReference definingUnit = GetDefiningUnitReference(methodReference.GetContainingType(Context), Context);
            if (definingUnit != null && ReferenceEquals(definingUnit, this.module))
            {
                methodDef = methodReference.GetResolvedMethod(Context);
            }
 
            return methodDef != null
                ? (EntityHandle)GetMethodDefinitionHandle(methodDef)
                : GetMemberReferenceHandle(methodReference);
        }
 
        public static EventAttributes GetEventAttributes(IEventDefinition eventDef)
        {
            EventAttributes result = 0;
            if (eventDef.IsSpecialName)
            {
                result |= EventAttributes.SpecialName;
            }
 
            if (eventDef.IsRuntimeSpecial)
            {
                result |= EventAttributes.RTSpecialName;
            }
 
            return result;
        }
 
        public static FieldAttributes GetFieldAttributes(IFieldDefinition fieldDef)
        {
            var result = (FieldAttributes)fieldDef.Visibility;
            if (fieldDef.IsStatic)
            {
                result |= FieldAttributes.Static;
            }
 
            if (fieldDef.IsReadOnly)
            {
                result |= FieldAttributes.InitOnly;
            }
 
            if (fieldDef.IsCompileTimeConstant)
            {
                result |= FieldAttributes.Literal;
            }
 
            if (fieldDef.IsNotSerialized)
            {
                result |= FieldAttributes.NotSerialized;
            }
 
            if (!fieldDef.MappedData.IsDefault)
            {
                result |= FieldAttributes.HasFieldRVA;
            }
 
            if (fieldDef.IsSpecialName)
            {
                result |= FieldAttributes.SpecialName;
            }
 
            if (fieldDef.IsRuntimeSpecial)
            {
                result |= FieldAttributes.RTSpecialName;
            }
 
            if (fieldDef.IsMarshalledExplicitly)
            {
                result |= FieldAttributes.HasFieldMarshal;
            }
 
            if (fieldDef.IsCompileTimeConstant)
            {
                result |= FieldAttributes.HasDefault;
            }
 
            return result;
        }
 
        internal BlobHandle GetFieldSignatureIndex(IFieldReference fieldReference)
        {
            BlobHandle result;
            ISpecializedFieldReference specializedFieldReference = fieldReference.AsSpecializedFieldReference;
            if (specializedFieldReference != null)
            {
                fieldReference = specializedFieldReference.UnspecializedVersion;
            }
 
            if (_fieldSignatureIndex.TryGetValue(fieldReference, out result))
            {
                return result;
            }
 
            var writer = PooledBlobBuilder.GetInstance();
            this.SerializeFieldSignature(fieldReference, writer);
            result = metadata.GetOrAddBlob(writer);
            _fieldSignatureIndex.Add(fieldReference, result);
            writer.Free();
            return result;
        }
 
        internal EntityHandle GetFieldHandle(IFieldReference fieldReference)
        {
            IFieldDefinition fieldDef = null;
            IUnitReference definingUnit = GetDefiningUnitReference(fieldReference.GetContainingType(Context), Context);
            if (definingUnit != null && ReferenceEquals(definingUnit, this.module))
            {
                fieldDef = fieldReference.GetResolvedField(Context);
            }
 
            return fieldDef != null
                ? (EntityHandle)GetFieldDefinitionHandle(fieldDef)
                : GetMemberReferenceHandle(fieldReference);
        }
 
        internal AssemblyFileHandle GetAssemblyFileHandle(IFileReference fileReference)
        {
            string key = fileReference.FileName;
            int index;
            if (!_fileRefIndex.TryGetValue(key, out index))
            {
                Debug.Assert(!_tableIndicesAreComplete);
                _fileRefList.Add(fileReference);
                _fileRefIndex.Add(key, index = _fileRefList.Count);
            }
 
            return MetadataTokens.AssemblyFileHandle(index);
        }
 
        private AssemblyFileHandle GetAssemblyFileHandle(IModuleReference mref)
        {
            return MetadataTokens.AssemblyFileHandle(_fileRefIndex[mref.Name]);
        }
 
        private static GenericParameterAttributes GetGenericParameterAttributes(IGenericParameter genPar)
        {
            GenericParameterAttributes result = 0;
            switch (genPar.Variance)
            {
                case TypeParameterVariance.Covariant:
                    result |= GenericParameterAttributes.Covariant;
                    break;
                case TypeParameterVariance.Contravariant:
                    result |= GenericParameterAttributes.Contravariant;
                    break;
            }
 
            if (genPar.MustBeReferenceType)
            {
                result |= GenericParameterAttributes.ReferenceTypeConstraint;
            }
 
            if (genPar.MustBeValueType)
            {
                result |= GenericParameterAttributes.NotNullableValueTypeConstraint;
            }
 
            if (genPar.AllowsRefLikeType)
            {
                result |= MetadataHelpers.GenericParameterAttributesAllowByRefLike;
            }
 
            if (genPar.MustHaveDefaultConstructor)
            {
                result |= GenericParameterAttributes.DefaultConstructorConstraint;
            }
 
            return result;
        }
 
        private EntityHandle GetExportedTypeImplementation(INamespaceTypeReference namespaceRef)
        {
            IUnitReference uref = namespaceRef.GetUnit(Context);
            if (uref is IAssemblyReference aref)
            {
                return GetAssemblyReferenceHandle(aref);
            }
 
            var mref = (IModuleReference)uref;
            aref = mref.GetContainingAssembly(Context);
            return aref == null || ReferenceEquals(aref, this.module.GetContainingAssembly(Context))
                ? (EntityHandle)GetAssemblyFileHandle(mref)
                : GetAssemblyReferenceHandle(aref);
        }
 
        public static string GetMetadataName(INamedTypeReference namedType, int generation)
        {
            string nameWithGeneration = (generation == 0) ? namedType.Name : namedType.Name + "#" + generation;
            string fileIdentifier = namedType.AssociatedFileIdentifier;
            return namedType.MangleName || fileIdentifier != null
                ? MetadataHelpers.ComposeAritySuffixedMetadataName(nameWithGeneration, namedType.GenericParameterCount, fileIdentifier)
                : nameWithGeneration;
        }
 
        internal MemberReferenceHandle GetMemberReferenceHandle(ITypeMemberReference memberRef)
        {
            return this.GetOrAddMemberReferenceHandle(memberRef);
        }
 
        internal EntityHandle GetMemberReferenceParent(ITypeMemberReference memberRef)
        {
            ITypeDefinition parentTypeDef = memberRef.GetContainingType(Context).AsTypeDefinition(Context);
            if (parentTypeDef != null)
            {
                TypeDefinitionHandle parentTypeDefHandle;
                TryGetTypeDefinitionHandle(parentTypeDef, out parentTypeDefHandle);
 
                if (!parentTypeDefHandle.IsNil)
                {
                    if (memberRef is IFieldReference)
                    {
                        return parentTypeDefHandle;
                    }
 
                    if (memberRef is IMethodReference methodRef)
                    {
                        if (methodRef.AcceptsExtraArguments)
                        {
                            MethodDefinitionHandle methodHandle;
                            if (this.TryGetMethodDefinitionHandle(methodRef.GetResolvedMethod(Context), out methodHandle))
                            {
                                return methodHandle;
                            }
                        }
 
                        return parentTypeDefHandle;
                    }
                    // TODO: error
                }
            }
 
            // TODO: special treatment for global fields and methods. Object model support would be nice.
            var containingType = memberRef.GetContainingType(Context);
            return containingType.IsTypeSpecification()
                ? (EntityHandle)GetTypeSpecificationHandle(containingType)
                : GetTypeReferenceHandle(containingType);
        }
 
        internal EntityHandle GetMethodDefinitionOrReferenceHandle(IMethodReference methodReference)
        {
            IMethodDefinition methodDef = null;
            IUnitReference definingUnit = GetDefiningUnitReference(methodReference.GetContainingType(Context), Context);
            if (definingUnit != null && ReferenceEquals(definingUnit, this.module))
            {
                methodDef = methodReference.GetResolvedMethod(Context);
            }
 
            return methodDef != null
                ? (EntityHandle)GetMethodDefinitionHandle(methodDef)
                : GetMemberReferenceHandle(methodReference);
        }
 
        public static MethodAttributes GetMethodAttributes(IMethodDefinition methodDef)
        {
            var result = (MethodAttributes)methodDef.Visibility;
            if (methodDef.IsStatic)
            {
                result |= MethodAttributes.Static;
            }
 
            if (methodDef.IsSealed)
            {
                result |= MethodAttributes.Final;
            }
 
            if (methodDef.IsVirtual)
            {
                result |= MethodAttributes.Virtual;
            }
 
            if (methodDef.IsHiddenBySignature)
            {
                result |= MethodAttributes.HideBySig;
            }
 
            if (methodDef.IsNewSlot)
            {
                result |= MethodAttributes.NewSlot;
            }
 
            if (methodDef.IsAccessCheckedOnOverride)
            {
                result |= MethodAttributes.CheckAccessOnOverride;
            }
 
            if (methodDef.IsAbstract)
            {
                result |= MethodAttributes.Abstract;
            }
 
            if (methodDef.IsSpecialName)
            {
                result |= MethodAttributes.SpecialName;
            }
 
            if (methodDef.IsRuntimeSpecial)
            {
                result |= MethodAttributes.RTSpecialName;
            }
 
            if (methodDef.IsPlatformInvoke)
            {
                result |= MethodAttributes.PinvokeImpl;
            }
 
            if (methodDef.HasDeclarativeSecurity)
            {
                result |= MethodAttributes.HasSecurity;
            }
 
            if (methodDef.RequiresSecurityObject)
            {
                result |= MethodAttributes.RequireSecObject;
            }
 
            return result;
        }
 
        internal BlobHandle GetMethodSpecificationSignatureHandle(IGenericMethodInstanceReference methodInstanceReference)
        {
            BlobHandle result;
            if (_methodInstanceSignatureIndex.TryGetValue(methodInstanceReference, out result))
            {
                return result;
            }
 
            var builder = PooledBlobBuilder.GetInstance();
            var encoder = new BlobEncoder(builder).MethodSpecificationSignature(methodInstanceReference.GetGenericMethod(Context).GenericParameterCount);
 
            foreach (ITypeReference typeReference in methodInstanceReference.GetGenericArguments(Context))
            {
                var typeRef = typeReference;
                SerializeTypeReference(encoder.AddArgument(), typeRef);
            }
 
            result = metadata.GetOrAddBlob(builder);
            _methodInstanceSignatureIndex.Add(methodInstanceReference, result);
            builder.Free();
            return result;
        }
 
        private BlobHandle GetMarshallingDescriptorHandle(IMarshallingInformation marshallingInformation)
        {
            BlobHandle result;
            if (_marshallingDescriptorIndex.TryGetValue(marshallingInformation, out result))
            {
                return result;
            }
 
            var writer = PooledBlobBuilder.GetInstance();
            this.SerializeMarshallingDescriptor(marshallingInformation, writer);
            result = metadata.GetOrAddBlob(writer);
            _marshallingDescriptorIndex.Add(marshallingInformation, result);
            writer.Free();
            return result;
        }
 
        private BlobHandle GetMarshallingDescriptorHandle(ImmutableArray<byte> descriptor)
        {
            return metadata.GetOrAddBlob(descriptor);
        }
 
        private BlobHandle GetMemberReferenceSignatureHandle(ITypeMemberReference memberRef)
        {
            return memberRef switch
            {
                IFieldReference fieldReference => this.GetFieldSignatureIndex(fieldReference),
                IMethodReference methodReference => this.GetMethodSignatureHandle(methodReference),
                _ => throw ExceptionUtilities.Unreachable()
            };
        }
 
        internal BlobHandle GetMethodSignatureHandle(IMethodReference methodReference)
        {
            if (methodReference is DeletedPEMethodDefinition { MetadataSignatureHandle: { IsNil: false } handle })
            {
                return handle;
            }
 
            return GetMethodSignatureHandleAndBlob(methodReference, out _);
        }
 
        internal byte[] GetMethodSignature(IMethodReference methodReference)
        {
            ImmutableArray<byte> signatureBlob;
            GetMethodSignatureHandleAndBlob(methodReference, out signatureBlob);
            return signatureBlob.ToArray();
        }
 
        private BlobHandle GetMethodSignatureHandleAndBlob(IMethodReference methodReference, out ImmutableArray<byte> signatureBlob)
        {
            BlobHandle result;
            ISpecializedMethodReference specializedMethodReference = methodReference.AsSpecializedMethodReference;
            if (specializedMethodReference != null)
            {
                methodReference = specializedMethodReference.UnspecializedVersion;
            }
 
            KeyValuePair<BlobHandle, ImmutableArray<byte>> existing;
            if (_signatureIndex.TryGetValue(methodReference, out existing))
            {
                signatureBlob = existing.Value;
                return existing.Key;
            }
 
            Debug.Assert((methodReference.CallingConvention & CallingConvention.Generic) != 0 == (methodReference.GenericParameterCount > 0));
 
            var builder = PooledBlobBuilder.GetInstance();
 
            var encoder = new BlobEncoder(builder).MethodSignature(
                new SignatureHeader((byte)methodReference.CallingConvention).CallingConvention,
                methodReference.GenericParameterCount,
                isInstanceMethod: (methodReference.CallingConvention & CallingConvention.HasThis) != 0);
 
            SerializeReturnValueAndParameters(encoder, methodReference, methodReference.ExtraParameters);
 
            signatureBlob = builder.ToImmutableArray();
            result = metadata.GetOrAddBlob(signatureBlob);
            _signatureIndex.Add(methodReference, KeyValuePairUtil.Create(result, signatureBlob));
            builder.Free();
            return result;
        }
 
        private BlobHandle GetMethodSpecificationBlobHandle(IGenericMethodInstanceReference genericMethodInstanceReference)
        {
            var writer = PooledBlobBuilder.GetInstance();
            SerializeMethodSpecificationSignature(writer, genericMethodInstanceReference);
            BlobHandle result = metadata.GetOrAddBlob(writer);
            writer.Free();
            return result;
        }
 
        private MethodSpecificationHandle GetMethodSpecificationHandle(IGenericMethodInstanceReference methodSpec)
        {
            return this.GetOrAddMethodSpecificationHandle(methodSpec);
        }
 
        internal EntityHandle GetMethodHandle(IMethodReference methodReference)
        {
            if (methodReference is IDeletedMethodDefinition { MetadataHandle: var deletedMethodHandle })
            {
                return deletedMethodHandle;
            }
 
            MethodDefinitionHandle methodDefHandle;
            IMethodDefinition methodDef = null;
            IUnitReference definingUnit = GetDefiningUnitReference(methodReference.GetContainingType(Context), Context);
            if (definingUnit != null && ReferenceEquals(definingUnit, this.module))
            {
                methodDef = methodReference.GetResolvedMethod(Context);
            }
 
            if (methodDef != null && (methodReference == methodDef || !methodReference.AcceptsExtraArguments) && this.TryGetMethodDefinitionHandle(methodDef, out methodDefHandle))
            {
                return methodDefHandle;
            }
 
            IGenericMethodInstanceReference methodSpec = methodReference.AsGenericMethodInstanceReference;
            return methodSpec != null
                ? (EntityHandle)GetMethodSpecificationHandle(methodSpec)
                : GetMemberReferenceHandle(methodReference);
        }
 
        internal EntityHandle GetStandaloneSignatureHandle(ISignature signature)
        {
            Debug.Assert(!(signature is IMethodReference));
            var builder = PooledBlobBuilder.GetInstance();
            var signatureEncoder = new BlobEncoder(builder).MethodSignature(convention: signature.CallingConvention.ToSignatureConvention(), genericParameterCount: 0, isInstanceMethod: false);
            SerializeReturnValueAndParameters(signatureEncoder, signature, varargParameters: ImmutableArray<IParameterTypeInformation>.Empty);
 
            BlobHandle blobIndex = metadata.GetOrAddBlob(builder);
            StandaloneSignatureHandle handle = GetOrAddStandaloneSignatureHandle(blobIndex);
            return handle;
        }
 
        public static ParameterAttributes GetParameterAttributes(IParameterDefinition parDef)
        {
            ParameterAttributes result = 0;
            if (parDef.IsIn)
            {
                result |= ParameterAttributes.In;
            }
 
            if (parDef.IsOut)
            {
                result |= ParameterAttributes.Out;
            }
 
            if (parDef.IsOptional)
            {
                result |= ParameterAttributes.Optional;
            }
 
            if (parDef.HasDefaultValue)
            {
                result |= ParameterAttributes.HasDefault;
            }
 
            if (parDef.IsMarshalledExplicitly)
            {
                result |= ParameterAttributes.HasFieldMarshal;
            }
 
            return result;
        }
 
        private BlobHandle GetPermissionSetBlobHandle(ImmutableArray<ICustomAttribute> permissionSet)
        {
            var writer = PooledBlobBuilder.GetInstance();
            BlobHandle result;
            try
            {
                writer.WriteByte((byte)'.');
                writer.WriteCompressedInteger(permissionSet.Length);
                this.SerializePermissionSet(permissionSet, writer);
                result = metadata.GetOrAddBlob(writer);
            }
            finally
            {
                writer.Free();
            }
 
            return result;
        }
 
        public static PropertyAttributes GetPropertyAttributes(IPropertyDefinition propertyDef)
        {
            PropertyAttributes result = 0;
            if (propertyDef.IsSpecialName)
            {
                result |= PropertyAttributes.SpecialName;
            }
 
            if (propertyDef.IsRuntimeSpecial)
            {
                result |= PropertyAttributes.RTSpecialName;
            }
 
            if (propertyDef.HasDefaultValue)
            {
                result |= PropertyAttributes.HasDefault;
            }
 
            return result;
        }
 
        private BlobHandle GetPropertySignatureHandle(IPropertyDefinition propertyDef)
        {
            KeyValuePair<BlobHandle, ImmutableArray<byte>> existing;
            if (_signatureIndex.TryGetValue(propertyDef, out existing))
            {
                return existing.Key;
            }
 
            var builder = PooledBlobBuilder.GetInstance();
 
            var encoder = new BlobEncoder(builder).PropertySignature(
                isInstanceProperty: (propertyDef.CallingConvention & CallingConvention.HasThis) != 0);
 
            SerializeReturnValueAndParameters(encoder, propertyDef, ImmutableArray<IParameterTypeInformation>.Empty);
 
            var blob = builder.ToImmutableArray();
            var result = metadata.GetOrAddBlob(blob);
 
            _signatureIndex.Add(propertyDef, KeyValuePairUtil.Create(result, blob));
            builder.Free();
            return result;
        }
 
        private EntityHandle GetResolutionScopeHandle(IUnitReference unitReference)
        {
            if (unitReference is IAssemblyReference aref)
            {
                return GetAssemblyReferenceHandle(aref);
            }
 
            // If this is a module from a referenced multi-module assembly,
            // the assembly should be used as the resolution scope.
            var mref = (IModuleReference)unitReference;
            aref = mref.GetContainingAssembly(Context);
 
            if (aref != null && aref != module.GetContainingAssembly(Context))
            {
                return GetAssemblyReferenceHandle(aref);
            }
 
            return GetModuleReferenceHandle(mref.Name);
        }
 
        private StringHandle GetStringHandleForPathAndCheckLength(string path, INamedEntity errorEntity = null)
        {
            CheckPathLength(path, errorEntity);
            return metadata.GetOrAddString(path);
        }
 
        private StringHandle GetStringHandleForNameAndCheckLength(string name, INamedEntity errorEntity = null)
        {
            CheckNameLength(name, errorEntity);
            return metadata.GetOrAddString(name);
        }
 
        /// <summary>
        /// The Microsoft CLR requires that {namespace} + "." + {name} fit in MAX_CLASS_NAME
        /// (even though the name and namespace are stored separately in the Microsoft
        /// implementation).  Note that the namespace name of a nested type is always blank
        /// (since comes from the container).
        /// </summary>
        /// <param name="namespaceType">We're trying to add the containing namespace of this type to the string heap.</param>
        /// <param name="mangledTypeName">Namespace names are never used on their own - this is the type that is adding the namespace name.
        /// Used only for length checking.</param>
        private StringHandle GetStringHandleForNamespaceAndCheckLength(INamespaceTypeReference namespaceType, string mangledTypeName)
        {
            string namespaceName = namespaceType.NamespaceName;
            if (namespaceName.Length == 0) // Optimization: CheckNamespaceLength is relatively expensive.
            {
                return default(StringHandle);
            }
 
            CheckNamespaceLength(namespaceName, mangledTypeName, namespaceType);
            return metadata.GetOrAddString(namespaceName);
        }
 
        private void CheckNameLength(string name, INamedEntity errorEntity)
        {
            // NOTE: ildasm shows quotes around some names (e.g. explicit implementations of members of generic interfaces)
            // but that seems to be tool-specific - they don't seem to and up in the string heap (so they don't count against
            // the length limit).
 
            if (IsTooLongInternal(name, NameLengthLimit))
            {
                Location location = GetNamedEntityLocation(errorEntity);
                this.Context.Diagnostics.Add(this.messageProvider.CreateDiagnostic(this.messageProvider.ERR_MetadataNameTooLong, location, name));
            }
        }
 
        private void CheckPathLength(string path, INamedEntity errorEntity = null)
        {
            if (IsTooLongInternal(path, PathLengthLimit))
            {
                Location location = GetNamedEntityLocation(errorEntity);
                this.Context.Diagnostics.Add(this.messageProvider.CreateDiagnostic(this.messageProvider.ERR_MetadataNameTooLong, location, path));
            }
        }
 
        private void CheckNamespaceLength(string namespaceName, string mangledTypeName, INamespaceTypeReference errorEntity)
        {
            // It's never useful to report that the namespace name is too long.
            // If it's too long, then the full name is too long and that string is
            // more helpful.
 
            // PERF: We expect to check this A LOT, so we'll aggressively inline some
            // of the helpers (esp IsTooLongInternal) in a way that allows us to forego
            // string concatenation (unless a diagnostic is actually reported).
 
            if (namespaceName.Length + 1 + mangledTypeName.Length > NameLengthLimit / 3)
            {
                int utf8Length =
                    s_utf8Encoding.GetByteCount(namespaceName) +
                    1 + // dot
                    s_utf8Encoding.GetByteCount(mangledTypeName);
 
                if (utf8Length > NameLengthLimit)
                {
                    Location location = GetNamedEntityLocation(errorEntity);
                    this.Context.Diagnostics.Add(this.messageProvider.CreateDiagnostic(this.messageProvider.ERR_MetadataNameTooLong, location, namespaceName + "." + mangledTypeName));
                }
            }
        }
 
        internal bool IsUsingStringTooLong(string usingString, INamedEntity errorEntity = null)
        {
            if (IsTooLongInternal(usingString, PdbLengthLimit))
            {
                Location location = GetNamedEntityLocation(errorEntity);
                this.Context.Diagnostics.Add(this.messageProvider.CreateDiagnostic(this.messageProvider.WRN_PdbUsingNameTooLong, location, usingString));
                return true;
            }
 
            return false;
        }
 
        internal bool IsLocalNameTooLong(ILocalDefinition localDefinition)
        {
            string name = localDefinition.Name;
            if (IsTooLongInternal(name, PdbLengthLimit))
            {
                this.Context.Diagnostics.Add(this.messageProvider.CreateDiagnostic(this.messageProvider.WRN_PdbLocalNameTooLong, localDefinition.Location, name));
                return true;
            }
 
            return false;
        }
 
        /// <summary>
        /// Test the given name to see if it fits in metadata.
        /// </summary>
        /// <param name="str">String to test (non-null).</param>
        /// <param name="maxLength">Max length for name.  (Expected to be at least 5.)</param>
        /// <returns>True if the name is too long.</returns>
        /// <remarks>Internal for test purposes.</remarks>
        internal static bool IsTooLongInternal(string str, int maxLength)
        {
            Debug.Assert(str != null); // No need to handle in an internal utility.
 
            if (str.Length < maxLength / 3) //UTF-8 uses at most 3 bytes per char
            {
                return false;
            }
 
            int utf8Length = s_utf8Encoding.GetByteCount(str);
            return utf8Length > maxLength;
        }
 
        private static Location GetNamedEntityLocation(INamedEntity errorEntity)
        {
            ISymbolInternal symbol;
 
            if (errorEntity is Cci.INamespace ns)
            {
                symbol = ns.GetInternalSymbol();
            }
            else
            {
                symbol = (errorEntity as Cci.IReference)?.GetInternalSymbol();
            }
 
            return GetSymbolLocation(symbol);
        }
 
        protected static Location GetSymbolLocation(ISymbolInternal symbolOpt)
        {
            return symbolOpt != null && !symbolOpt.Locations.IsDefaultOrEmpty ? symbolOpt.Locations[0] : Location.None;
        }
 
        internal TypeAttributes GetTypeAttributes(ITypeDefinition typeDef)
        {
            return GetTypeAttributes(typeDef, Context);
        }
 
        public static TypeAttributes GetTypeAttributes(ITypeDefinition typeDef, EmitContext context)
        {
            TypeAttributes result = 0;
 
            switch (typeDef.Layout)
            {
                case LayoutKind.Sequential:
                    result |= TypeAttributes.SequentialLayout;
                    break;
 
                case LayoutKind.Explicit:
                    result |= TypeAttributes.ExplicitLayout;
                    break;
            }
 
            if (typeDef.IsInterface)
            {
                result |= TypeAttributes.Interface;
            }
 
            if (typeDef.IsAbstract)
            {
                result |= TypeAttributes.Abstract;
            }
 
            if (typeDef.IsSealed)
            {
                result |= TypeAttributes.Sealed;
            }
 
            if (typeDef.IsSpecialName)
            {
                result |= TypeAttributes.SpecialName;
            }
 
            if (typeDef.IsRuntimeSpecial)
            {
                result |= TypeAttributes.RTSpecialName;
            }
 
            if (typeDef.IsComObject)
            {
                result |= TypeAttributes.Import;
            }
 
            if (typeDef.IsSerializable)
            {
                result |= TypeAttributes.Serializable;
            }
 
            if (typeDef.IsWindowsRuntimeImport)
            {
                result |= TypeAttributes.WindowsRuntime;
            }
 
            switch (typeDef.StringFormat)
            {
                case CharSet.Unicode:
                    result |= TypeAttributes.UnicodeClass;
                    break;
 
                case Constants.CharSet_Auto:
                    result |= TypeAttributes.AutoClass;
                    break;
            }
 
            if (typeDef.HasDeclarativeSecurity)
            {
                result |= TypeAttributes.HasSecurity;
            }
 
            if (typeDef.IsBeforeFieldInit)
            {
                result |= TypeAttributes.BeforeFieldInit;
            }
 
            INestedTypeDefinition nestedTypeDef = typeDef.AsNestedTypeDefinition(context);
            if (nestedTypeDef != null)
            {
                switch (((ITypeDefinitionMember)typeDef).Visibility)
                {
                    case TypeMemberVisibility.Public:
                        result |= TypeAttributes.NestedPublic;
                        break;
                    case TypeMemberVisibility.Private:
                        result |= TypeAttributes.NestedPrivate;
                        break;
                    case TypeMemberVisibility.Family:
                        result |= TypeAttributes.NestedFamily;
                        break;
                    case TypeMemberVisibility.Assembly:
                        result |= TypeAttributes.NestedAssembly;
                        break;
                    case TypeMemberVisibility.FamilyAndAssembly:
                        result |= TypeAttributes.NestedFamANDAssem;
                        break;
                    case TypeMemberVisibility.FamilyOrAssembly:
                        result |= TypeAttributes.NestedFamORAssem;
                        break;
                }
 
                return result;
            }
 
            INamespaceTypeDefinition namespaceTypeDef = typeDef.AsNamespaceTypeDefinition(context);
            if (namespaceTypeDef != null && namespaceTypeDef.IsPublic)
            {
                result |= TypeAttributes.Public;
            }
 
            return result;
        }
 
        private EntityHandle GetDeclaringTypeOrMethodHandle(IGenericParameter genPar)
        {
            IGenericTypeParameter genTypePar = genPar.AsGenericTypeParameter;
            if (genTypePar != null)
            {
                return GetTypeDefinitionHandle(genTypePar.DefiningType);
            }
 
            IGenericMethodParameter genMethPar = genPar.AsGenericMethodParameter;
            if (genMethPar != null)
            {
                return GetMethodDefinitionHandle(genMethPar.DefiningMethod);
            }
 
            throw ExceptionUtilities.Unreachable();
        }
 
        private TypeReferenceHandle GetTypeReferenceHandle(ITypeReference typeReference)
        {
            TypeReferenceHandle result;
            if (this.TryGetTypeReferenceHandle(typeReference, out result))
            {
                return result;
            }
 
            // NOTE: Even though CLR documentation does not explicitly specify any requirements
            // NOTE: to the order of records in TypeRef table, some tools and/or APIs (e.g.
            // NOTE: IMetaDataEmit::MergeEnd) assume that the containing type referenced as
            // NOTE: ResolutionScope for its nested types should appear in TypeRef table
            // NOTE: *before* any of its nested types.
            // SEE ALSO: bug#570975 and test Bug570975()
            INestedTypeReference nestedTypeRef = typeReference.AsNestedTypeReference;
            if (nestedTypeRef != null)
            {
                GetTypeReferenceHandle(nestedTypeRef.GetContainingType(this.Context));
            }
 
            return this.GetOrAddTypeReferenceHandle(typeReference);
        }
 
        private TypeSpecificationHandle GetTypeSpecificationHandle(ITypeReference typeReference)
        {
            return this.GetOrAddTypeSpecificationHandle(typeReference);
        }
 
        internal ITypeDefinition GetTypeDefinition(int token)
        {
            // The token must refer to a TypeDef row since we are
            // only handling indexes into the full metadata (in EnC)
            // for def tables. Other tables contain deltas only.
            return GetTypeDef(MetadataTokens.TypeDefinitionHandle(token));
        }
 
        internal IMethodDefinition GetMethodDefinition(int token)
        {
            // Must be a def table. (See comment in GetTypeDefinition.)
            return GetMethodDef(MetadataTokens.MethodDefinitionHandle(token));
        }
 
        internal INestedTypeReference GetNestedTypeReference(int token)
        {
            // Must be a def table. (See comment in GetTypeDefinition.)
            return GetTypeDef(MetadataTokens.TypeDefinitionHandle(token)).AsNestedTypeReference;
        }
 
        internal BlobHandle GetTypeSpecSignatureIndex(ITypeReference typeReference)
        {
            BlobHandle result;
            if (_typeSpecSignatureIndex.TryGetValue(typeReference, out result))
            {
                return result;
            }
 
            var builder = PooledBlobBuilder.GetInstance();
            this.SerializeTypeReference(new BlobEncoder(builder).TypeSpecificationSignature(), typeReference);
            result = metadata.GetOrAddBlob(builder);
 
            _typeSpecSignatureIndex.Add(typeReference, result);
            builder.Free();
            return result;
        }
 
        internal EntityHandle GetTypeHandle(ITypeReference typeReference, bool treatRefAsPotentialTypeSpec = true)
        {
            TypeDefinitionHandle handle;
            var typeDefinition = typeReference.AsTypeDefinition(this.Context);
            if (typeDefinition != null && this.TryGetTypeDefinitionHandle(typeDefinition, out handle))
            {
                return handle;
            }
 
            return treatRefAsPotentialTypeSpec && typeReference.IsTypeSpecification()
                ? (EntityHandle)GetTypeSpecificationHandle(typeReference)
                : GetTypeReferenceHandle(typeReference);
        }
 
        internal EntityHandle GetDefinitionHandle(IDefinition definition)
        {
            return definition switch
            {
                ITypeDefinition typeDef => (EntityHandle)GetTypeDefinitionHandle(typeDef),
                IMethodDefinition methodDef => GetMethodDefinitionHandle(methodDef),
                IFieldDefinition fieldDef => GetFieldDefinitionHandle(fieldDef),
                IEventDefinition eventDef => GetEventDefinitionHandle(eventDef),
                IPropertyDefinition propertyDef => GetPropertyDefIndex(propertyDef),
                _ => throw ExceptionUtilities.Unreachable()
            };
        }
 
#nullable enable
        public void WriteMetadataAndIL(PdbWriter? nativePdbWriterOpt, Stream metadataStream, Stream ilStream, Stream? portablePdbStreamOpt, out MetadataSizes metadataSizes)
        {
            Debug.Assert(nativePdbWriterOpt == null ^ portablePdbStreamOpt == null);
 
            nativePdbWriterOpt?.SetMetadataEmitter(this);
 
            // TODO: we can precalculate the exact size of IL stream
            var ilBuilder = new BlobBuilder(1024);
            var metadataBuilder = new BlobBuilder(4 * 1024);
            PooledBlobBuilder? mappedFieldDataBuilder = null;
            PooledBlobBuilder? managedResourceDataBuilder = null;
 
            // Add 4B of padding to the start of the separated IL stream,
            // so that method RVAs, which are offsets to this stream, are never 0.
            ilBuilder.WriteUInt32(0);
 
            // this is used to handle edit-and-continue emit, so we should have a module
            // version ID that is imposed by the caller (the same as the previous module version ID).
            // Therefore we do not have to fill in a new module version ID in the generated metadata
            // stream.
            Debug.Assert(module.SerializationProperties.PersistentIdentifier != default(Guid));
 
            BuildMetadataAndIL(
                nativePdbWriterOpt,
                ilBuilder,
                out mappedFieldDataBuilder,
                out managedResourceDataBuilder,
                out Blob mvidFixup,
                out Blob mvidStringFixup);
 
            var typeSystemRowCounts = metadata.GetRowCounts();
            Debug.Assert(typeSystemRowCounts[(int)TableIndex.EncLog] == 0);
            Debug.Assert(typeSystemRowCounts[(int)TableIndex.EncMap] == 0);
            PopulateEncTables(typeSystemRowCounts);
 
            Debug.Assert(mappedFieldDataBuilder == null);
            Debug.Assert(managedResourceDataBuilder == null);
            Debug.Assert(mvidFixup.IsDefault);
            Debug.Assert(mvidStringFixup.IsDefault);
 
            // TODO: Update SRM to not sort Custom Attribute table when emitting EnC delta
            // https://github.com/dotnet/roslyn/issues/70389
            if (!IsFullMetadata)
            {
                metadata.GetType().GetField("_customAttributeTableNeedsSorting", BindingFlags.Instance | BindingFlags.NonPublic)!.SetValue(metadata, false);
            }
 
            // TODO (https://github.com/dotnet/roslyn/issues/3905):
            // InterfaceImpl table emitted by Roslyn is not compliant with ECMA spec.
            // Once fixed enable validation in DEBUG builds.
            var rootBuilder = new MetadataRootBuilder(metadata, module.SerializationProperties.TargetRuntimeVersion, suppressValidation: true);
 
            rootBuilder.Serialize(metadataBuilder, methodBodyStreamRva: 0, mappedFieldDataStreamRva: 0);
            metadataSizes = rootBuilder.Sizes;
 
            try
            {
                ilBuilder.WriteContentTo(ilStream);
                metadataBuilder.WriteContentTo(metadataStream);
            }
            catch (Exception e) when (!(e is OperationCanceledException))
            {
                throw new PeWritingException(e);
            }
 
            if (portablePdbStreamOpt != null)
            {
                var portablePdbBuilder = GetPortablePdbBuilder(
                    typeSystemRowCounts,
                    debugEntryPoint: default(MethodDefinitionHandle),
                    deterministicIdProviderOpt: null);
 
                var portablePdbBlob = new BlobBuilder();
                portablePdbBuilder.Serialize(portablePdbBlob);
 
                try
                {
                    portablePdbBlob.WriteContentTo(portablePdbStreamOpt);
                }
                catch (Exception e) when (!(e is OperationCanceledException))
                {
                    throw new SymUnmanagedWriterException(e.Message, e);
                }
            }
        }
 
        public void BuildMetadataAndIL(
            PdbWriter? nativePdbWriterOpt,
            BlobBuilder ilBuilder,
            out PooledBlobBuilder? mappedFieldDataBuilder,
            out PooledBlobBuilder? managedResourceDataBuilder,
            out Blob mvidFixup,
            out Blob mvidStringFixup)
        {
            // Extract information from object model into tables, indices and streams
            CreateIndices();
 
            if (_debugMetadataOpt != null)
            {
                // Ensure document table lists files in command line order
                // This is key for us to be able to accurately rebuild a binary from a PDB.
                var documentsBuilder = Module.DebugDocumentsBuilder;
                foreach (var tree in Module.CommonCompilation.SyntaxTrees)
                {
                    if (documentsBuilder.TryGetDebugDocument(tree.FilePath, basePath: null) is { } doc && !_documentIndex.ContainsKey(doc))
                    {
                        AddDocument(doc, _documentIndex);
                    }
                }
 
                if (Context.RebuildData is { } rebuildData)
                {
                    _usingNonSourceDocumentNameEnumerator = true;
                    _nonSourceDocumentNameEnumerator = rebuildData.NonSourceFileDocumentNames.GetEnumerator();
                }
 
                DefineModuleImportScope();
 
                if (IsFullMetadata)
                {
                    // Do not emit TypeDefinitionDocuments or Source Link to EnC deltas.
                    // This information is only needed to support navigation to symbols from metadata references.
 
                    EmbedTypeDefinitionDocumentInformation(module);
 
                    if (module.SourceLinkStreamOpt != null)
                    {
                        EmbedSourceLink(module.SourceLinkStreamOpt);
                    }
 
                    EmbedCompilationOptions(module);
                    EmbedMetadataReferenceInformation(module);
                }
            }
 
            int[] methodBodyOffsets;
            if (MetadataOnly)
            {
                methodBodyOffsets = SerializeThrowNullMethodBodies(ilBuilder);
                mvidStringFixup = default(Blob);
            }
            else
            {
                methodBodyOffsets = SerializeMethodBodies(ilBuilder, nativePdbWriterOpt, out mvidStringFixup);
            }
 
            _cancellationToken.ThrowIfCancellationRequested();
 
            // method body serialization adds Stand Alone Signatures
            _tableIndicesAreComplete = true;
 
            ReportReferencesToAddedSymbols();
 
            PooledBlobBuilder? dynamicAnalysisData = null;
            if (_dynamicAnalysisDataWriterOpt != null)
            {
                dynamicAnalysisData = PooledBlobBuilder.GetInstance();
                _dynamicAnalysisDataWriterOpt.SerializeMetadataTables(dynamicAnalysisData);
            }
 
            PopulateTypeSystemTables(methodBodyOffsets, out mappedFieldDataBuilder, out managedResourceDataBuilder, dynamicAnalysisData, out mvidFixup);
            dynamicAnalysisData?.Free();
        }
#nullable disable
 
        public virtual void PopulateEncTables(ImmutableArray<int> typeSystemRowCounts)
        {
        }
 
        public MetadataRootBuilder GetRootBuilder()
        {
            // TODO (https://github.com/dotnet/roslyn/issues/3905):
            // InterfaceImpl table emitted by Roslyn is not compliant with ECMA spec.
            // Once fixed enable validation in DEBUG builds.
            return new MetadataRootBuilder(metadata, module.SerializationProperties.TargetRuntimeVersion, suppressValidation: true);
        }
 
        public PortablePdbBuilder GetPortablePdbBuilder(ImmutableArray<int> typeSystemRowCounts, MethodDefinitionHandle debugEntryPoint, Func<IEnumerable<Blob>, BlobContentId> deterministicIdProviderOpt)
        {
            return new PortablePdbBuilder(_debugMetadataOpt, typeSystemRowCounts, debugEntryPoint, deterministicIdProviderOpt);
        }
 
        internal void GetEntryPoints(out MethodDefinitionHandle entryPointHandle, out MethodDefinitionHandle debugEntryPointHandle)
        {
            if (IsFullMetadata && !MetadataOnly)
            {
                // PE entry point is set for executable programs
                IMethodReference entryPoint = module.PEEntryPoint;
                entryPointHandle = entryPoint != null ? (MethodDefinitionHandle)GetMethodHandle((IMethodDefinition)entryPoint.AsDefinition(Context)) : default(MethodDefinitionHandle);
 
                // debug entry point may be different from PE entry point, it may also be set for libraries
                IMethodReference debugEntryPoint = module.DebugEntryPoint;
                if (debugEntryPoint != null && debugEntryPoint != entryPoint)
                {
                    debugEntryPointHandle = (MethodDefinitionHandle)GetMethodHandle((IMethodDefinition)debugEntryPoint.AsDefinition(Context));
                }
                else
                {
                    debugEntryPointHandle = entryPointHandle;
                }
            }
            else
            {
                entryPointHandle = debugEntryPointHandle = default(MethodDefinitionHandle);
            }
        }
 
        private ImmutableArray<IGenericParameter> GetSortedGenericParameters()
        {
            return GetGenericParameters().OrderBy((x, y) =>
            {
                // Spec: GenericParam table is sorted by Owner and then by Number.
                int result = CodedIndex.TypeOrMethodDef(GetDeclaringTypeOrMethodHandle(x)) - CodedIndex.TypeOrMethodDef(GetDeclaringTypeOrMethodHandle(y));
                if (result != 0)
                {
                    return result;
                }
 
                return x.Index - y.Index;
            }).ToImmutableArray();
        }
 
#nullable enable
        private void PopulateTypeSystemTables(int[] methodBodyOffsets, out PooledBlobBuilder? mappedFieldDataWriter, out PooledBlobBuilder? resourceWriter, BlobBuilder? dynamicAnalysisData, out Blob mvidFixup)
        {
            var sortedGenericParameters = GetSortedGenericParameters();
 
            this.PopulateAssemblyRefTableRows();
            this.PopulateAssemblyTableRows();
            this.PopulateClassLayoutTableRows();
            this.PopulateConstantTableRows();
            this.PopulateDeclSecurityTableRows();
            this.PopulateEventMapTableRows();
            this.PopulateEventTableRows();
            this.PopulateExportedTypeTableRows();
            this.PopulateFieldLayoutTableRows();
            this.PopulateFieldMarshalTableRows();
            this.PopulateFieldRvaTableRows(out mappedFieldDataWriter);
            this.PopulateFieldTableRows();
            this.PopulateFileTableRows();
            this.PopulateGenericParameters(sortedGenericParameters);
            this.PopulateImplMapTableRows();
            this.PopulateInterfaceImplTableRows();
            this.PopulateManifestResourceTableRows(out resourceWriter, dynamicAnalysisData);
            this.PopulateMemberRefTableRows();
            this.PopulateMethodImplTableRows();
            this.PopulateMethodTableRows(methodBodyOffsets);
            this.PopulateMethodSemanticsTableRows();
            this.PopulateMethodSpecTableRows();
            this.PopulateModuleRefTableRows();
            this.PopulateModuleTableRow(out mvidFixup);
            this.PopulateNestedClassTableRows();
            this.PopulateParamTableRows();
            this.PopulatePropertyMapTableRows();
            this.PopulatePropertyTableRows();
            this.PopulateTypeDefTableRows();
            this.PopulateTypeRefTableRows();
            this.PopulateTypeSpecTableRows();
            this.PopulateStandaloneSignatures();
 
            // This table is populated after the others because it depends on the order of the entries of the generic parameter table.
            this.PopulateCustomAttributeTableRows(sortedGenericParameters);
        }
#nullable disable
 
        private void PopulateAssemblyRefTableRows()
        {
            var assemblyRefs = this.GetAssemblyRefs();
            metadata.SetCapacity(TableIndex.AssemblyRef, assemblyRefs.Count);
 
            foreach (var identity in assemblyRefs)
            {
                // reference has token, not full public key
                metadata.AddAssemblyReference(
                    name: GetStringHandleForPathAndCheckLength(identity.Name),
                    version: identity.Version,
                    culture: metadata.GetOrAddString(identity.CultureName),
                    publicKeyOrToken: metadata.GetOrAddBlob(identity.PublicKeyToken),
                    flags: (AssemblyFlags)((int)identity.ContentType << 9) | (identity.IsRetargetable ? AssemblyFlags.Retargetable : 0),
                    hashValue: default(BlobHandle));
            }
        }
 
        private void PopulateAssemblyTableRows()
        {
            if (!EmitAssemblyDefinition)
            {
                return;
            }
 
            var sourceAssembly = module.SourceAssemblyOpt;
            Debug.Assert(sourceAssembly != null);
 
            var flags = sourceAssembly.AssemblyFlags & ~AssemblyFlags.PublicKey;
 
            if (!sourceAssembly.Identity.PublicKey.IsDefaultOrEmpty)
            {
                flags |= AssemblyFlags.PublicKey;
            }
 
            metadata.AddAssembly(
                flags: flags,
                hashAlgorithm: sourceAssembly.HashAlgorithm,
                version: sourceAssembly.Identity.Version,
                publicKey: metadata.GetOrAddBlob(sourceAssembly.Identity.PublicKey),
                name: GetStringHandleForPathAndCheckLength(module.Name, module),
                culture: metadata.GetOrAddString(sourceAssembly.Identity.CultureName));
        }
 
        private void PopulateCustomAttributeTableRows(ImmutableArray<IGenericParameter> sortedGenericParameters)
        {
            if (IsFullMetadata)
            {
                AddAssemblyAttributesToTable();
            }
 
            AddCustomAttributesToTable(GetMethodDefs(), def => GetMethodDefinitionHandle(def));
            AddCustomAttributesToTable(GetFieldDefs(), def => GetFieldDefinitionHandle(def));
 
            var typeDefs = GetTypeDefs();
            AddCustomAttributesToTable(typeDefs, def => GetTypeDefinitionHandle(def));
            AddCustomAttributesToTable(GetParameterDefs(), def => GetParameterHandle(def));
 
            if (IsFullMetadata)
            {
                AddModuleAttributesToTable(module);
            }
 
            AddCustomAttributesToTable(GetPropertyDefs(), def => GetPropertyDefIndex(def));
            AddCustomAttributesToTable(GetEventDefs(), def => GetEventDefinitionHandle(def));
            AddCustomAttributesToTable(sortedGenericParameters, TableIndex.GenericParam);
 
            FinalizeCustomAttributeTableRows();
        }
 
        protected virtual void FinalizeCustomAttributeTableRows()
        {
        }
 
        private void AddAssemblyAttributesToTable()
        {
            bool writingNetModule = module.OutputKind == OutputKind.NetModule;
            if (writingNetModule)
            {
                // When writing netmodules, assembly security attributes are not emitted by PopulateDeclSecurityTableRows().
                // Instead, here we make sure they are emitted as regular attributes, attached off the appropriate placeholder
                // System.Runtime.CompilerServices.AssemblyAttributesGoHere* type refs.  This is the contract for publishing
                // assembly attributes in netmodules so they may be migrated to containing/referencing multi-module assemblies,
                // at multi-module assembly build time.
                AddAssemblyAttributesToTable(
                    this.module.GetSourceAssemblySecurityAttributes().Select(sa => sa.Attribute),
                    needsDummyParent: true,
                    isSecurity: true);
            }
 
            AddAssemblyAttributesToTable(
                this.module.GetSourceAssemblyAttributes(Context.IsRefAssembly),
                needsDummyParent: writingNetModule,
                isSecurity: false);
        }
 
        private void AddAssemblyAttributesToTable(IEnumerable<ICustomAttribute> assemblyAttributes, bool needsDummyParent, bool isSecurity)
        {
            Debug.Assert(this.IsFullMetadata); // parentToken is not relative
            EntityHandle parentHandle = Handle.AssemblyDefinition;
            foreach (ICustomAttribute customAttribute in assemblyAttributes)
            {
                if (needsDummyParent)
                {
                    // When writing netmodules, assembly attributes are attached off the appropriate placeholder
                    // System.Runtime.CompilerServices.AssemblyAttributesGoHere* type refs.  This is the contract for publishing
                    // assembly attributes in netmodules so they may be migrated to containing/referencing multi-module assemblies,
                    // at multi-module assembly build time.
                    parentHandle = GetDummyAssemblyAttributeParent(isSecurity, customAttribute.AllowMultiple);
                }
 
                AddCustomAttributeToTable(parentHandle, customAttribute);
            }
        }
 
        private TypeReferenceHandle GetDummyAssemblyAttributeParent(bool isSecurity, bool allowMultiple)
        {
            // Lazily get or create placeholder assembly attribute parent type ref for the given combination of
            // whether isSecurity and allowMultiple.  Convert type ref row id to corresponding attribute parent tag.
            // Note that according to the defacto contract, although the placeholder type refs have CorLibrary as their
            // resolution scope, the types backing the placeholder type refs need not actually exist.
            int iS = isSecurity ? 1 : 0;
            int iM = allowMultiple ? 1 : 0;
            if (_dummyAssemblyAttributeParent[iS, iM].IsNil)
            {
                _dummyAssemblyAttributeParent[iS, iM] = metadata.AddTypeReference(
                    resolutionScope: GetResolutionScopeHandle(module.GetCorLibrary(Context)),
                    @namespace: metadata.GetOrAddString(dummyAssemblyAttributeParentNamespace),
                    name: metadata.GetOrAddString(dummyAssemblyAttributeParentName + dummyAssemblyAttributeParentQualifier[iS, iM]));
            }
 
            return _dummyAssemblyAttributeParent[iS, iM];
        }
 
        private void AddModuleAttributesToTable(CommonPEModuleBuilder module)
        {
            Debug.Assert(this.IsFullMetadata);
            AddCustomAttributesToTable(EntityHandle.ModuleDefinition, module.GetSourceModuleAttributes());
        }
 
        private void AddCustomAttributesToTable<T>(IEnumerable<T> parentList, TableIndex tableIndex)
            where T : IDefinition
        {
            int parentRowId = 1;
            foreach (var parent in parentList)
            {
                if (parent.IsEncDeleted)
                {
                    // Custom attributes are not needed for EnC definition deletes
                    continue;
                }
 
                var parentHandle = MetadataTokens.Handle(tableIndex, parentRowId++);
                AddCustomAttributesToTable(parentHandle, parent.GetAttributes(Context));
            }
        }
 
        private void AddCustomAttributesToTable<T>(IEnumerable<T> parentList, Func<T, EntityHandle> getDefinitionHandle)
            where T : IDefinition
        {
            foreach (var parent in parentList)
            {
                if (parent.IsEncDeleted)
                {
                    // Custom attributes are not needed for EnC definition deletes
                    continue;
                }
 
                EntityHandle parentHandle = getDefinitionHandle(parent);
                AddCustomAttributesToTable(parentHandle, parent.GetAttributes(Context));
            }
        }
 
        protected virtual void AddCustomAttributesToTable(EntityHandle parentHandle, IEnumerable<ICustomAttribute> attributes)
        {
            foreach (var attr in attributes)
            {
                AddCustomAttributeToTable(parentHandle, attr);
            }
        }
 
        protected bool AddCustomAttributeToTable(EntityHandle parentHandle, ICustomAttribute customAttribute)
        {
            IMethodReference constructor = customAttribute.Constructor(Context, reportDiagnostics: true);
            if (constructor != null)
            {
                metadata.AddCustomAttribute(
                    parent: parentHandle,
                    constructor: GetCustomAttributeTypeCodedIndex(constructor),
                    value: GetCustomAttributeSignatureIndex(customAttribute));
 
                return true;
            }
 
            return false;
        }
 
        private void PopulateDeclSecurityTableRows()
        {
            if (module.OutputKind != OutputKind.NetModule)
            {
                this.PopulateDeclSecurityTableRowsFor(EntityHandle.AssemblyDefinition, module.GetSourceAssemblySecurityAttributes());
            }
 
            foreach (ITypeDefinition typeDef in this.GetTypeDefs())
            {
                if (!typeDef.HasDeclarativeSecurity)
                {
                    continue;
                }
 
                this.PopulateDeclSecurityTableRowsFor(GetTypeDefinitionHandle(typeDef), typeDef.SecurityAttributes);
            }
 
            foreach (IMethodDefinition methodDef in this.GetMethodDefs())
            {
                if (methodDef.IsEncDeleted || !methodDef.HasDeclarativeSecurity)
                {
                    continue;
                }
 
                this.PopulateDeclSecurityTableRowsFor(GetMethodDefinitionHandle(methodDef), methodDef.SecurityAttributes);
            }
        }
 
        private void PopulateDeclSecurityTableRowsFor(EntityHandle parentHandle, IEnumerable<SecurityAttribute> attributes)
        {
            OrderPreservingMultiDictionary<DeclarativeSecurityAction, ICustomAttribute> groupedSecurityAttributes = null;
 
            foreach (SecurityAttribute securityAttribute in attributes)
            {
                groupedSecurityAttributes = groupedSecurityAttributes ?? OrderPreservingMultiDictionary<DeclarativeSecurityAction, ICustomAttribute>.GetInstance();
                groupedSecurityAttributes.Add(securityAttribute.Action, securityAttribute.Attribute);
            }
 
            if (groupedSecurityAttributes == null)
            {
                return;
            }
 
            foreach (DeclarativeSecurityAction securityAction in groupedSecurityAttributes.Keys)
            {
                metadata.AddDeclarativeSecurityAttribute(
                    parent: parentHandle,
                    action: securityAction,
                    permissionSet: GetPermissionSetBlobHandle(groupedSecurityAttributes[securityAction]));
            }
 
            groupedSecurityAttributes.Free();
        }
 
        private void PopulateEventTableRows()
        {
            var eventDefs = this.GetEventDefs();
            metadata.SetCapacity(TableIndex.Event, eventDefs.Count);
 
            foreach (IEventDefinition eventDef in eventDefs)
            {
                metadata.AddEvent(
                    attributes: GetEventAttributes(eventDef),
                    name: GetStringHandleForNameAndCheckLength(eventDef.Name, eventDef),
                    type: GetTypeHandle(eventDef.GetType(Context)));
            }
        }
 
        private void PopulateExportedTypeTableRows()
        {
            if (!IsFullMetadata)
            {
                return;
            }
 
            var exportedTypes = module.GetExportedTypes(Context.Diagnostics);
            if (exportedTypes.Length == 0)
            {
                return;
            }
 
            metadata.SetCapacity(TableIndex.ExportedType, exportedTypes.Length);
 
            foreach (var exportedType in exportedTypes)
            {
                INestedTypeReference nestedRef;
                INamespaceTypeReference namespaceTypeRef;
                TypeAttributes attributes;
                StringHandle typeName;
                StringHandle typeNamespace;
                EntityHandle implementation;
 
                if ((namespaceTypeRef = exportedType.Type.AsNamespaceTypeReference) != null)
                {
                    // exported types are not emitted in EnC deltas (hence generation 0):
                    string metadataTypeName = GetMetadataName(namespaceTypeRef, generation: 0);
 
                    typeName = GetStringHandleForNameAndCheckLength(metadataTypeName, namespaceTypeRef);
                    typeNamespace = GetStringHandleForNamespaceAndCheckLength(namespaceTypeRef, metadataTypeName);
                    implementation = GetExportedTypeImplementation(namespaceTypeRef);
                    attributes = exportedType.IsForwarder ? TypeAttributes.NotPublic | Constants.TypeAttributes_TypeForwarder : TypeAttributes.Public;
                }
                else if ((nestedRef = exportedType.Type.AsNestedTypeReference) != null)
                {
                    Debug.Assert(exportedType.ParentIndex != -1);
 
                    // exported types are not emitted in EnC deltas (hence generation 0):
                    string metadataTypeName = GetMetadataName(nestedRef, generation: 0);
 
                    typeName = GetStringHandleForNameAndCheckLength(metadataTypeName, nestedRef);
                    typeNamespace = default(StringHandle);
                    implementation = MetadataTokens.ExportedTypeHandle(exportedType.ParentIndex + 1);
                    attributes = exportedType.IsForwarder ? TypeAttributes.NotPublic : TypeAttributes.NestedPublic;
                }
                else
                {
                    throw ExceptionUtilities.UnexpectedValue(exportedType);
                }
 
                metadata.AddExportedType(
                    attributes: attributes,
                    @namespace: typeNamespace,
                    name: typeName,
                    implementation: implementation,
                    typeDefinitionId: exportedType.IsForwarder ? 0 : MetadataTokens.GetToken(exportedType.Type.TypeDef));
            }
        }
 
        private void PopulateFieldLayoutTableRows()
        {
            foreach (IFieldDefinition fieldDef in this.GetFieldDefs())
            {
                if (fieldDef.ContainingTypeDefinition.Layout != LayoutKind.Explicit || fieldDef.IsStatic)
                {
                    continue;
                }
 
                metadata.AddFieldLayout(
                    field: GetFieldDefinitionHandle(fieldDef),
                    offset: fieldDef.Offset);
            }
        }
 
        private void PopulateFieldMarshalTableRows()
        {
            foreach (IFieldDefinition fieldDef in this.GetFieldDefs())
            {
                if (!fieldDef.IsMarshalledExplicitly)
                {
                    continue;
                }
 
                var marshallingInformation = fieldDef.MarshallingInformation;
 
                BlobHandle descriptor = (marshallingInformation != null)
                    ? GetMarshallingDescriptorHandle(marshallingInformation)
                    : GetMarshallingDescriptorHandle(fieldDef.MarshallingDescriptor);
 
                metadata.AddMarshallingDescriptor(
                    parent: GetFieldDefinitionHandle(fieldDef),
                    descriptor: descriptor);
            }
 
            foreach (IParameterDefinition parDef in this.GetParameterDefs())
            {
                if (!parDef.IsMarshalledExplicitly)
                {
                    continue;
                }
 
                var marshallingInformation = parDef.MarshallingInformation;
 
                BlobHandle descriptor = (marshallingInformation != null)
                     ? GetMarshallingDescriptorHandle(marshallingInformation)
                     : GetMarshallingDescriptorHandle(parDef.MarshallingDescriptor);
 
                metadata.AddMarshallingDescriptor(
                    parent: GetParameterHandle(parDef),
                    descriptor: descriptor);
            }
        }
 
#nullable enable
        private void PopulateFieldRvaTableRows(out PooledBlobBuilder? mappedFieldDataWriter)
        {
            mappedFieldDataWriter = null;
 
            foreach (IFieldDefinition fieldDef in this.GetFieldDefs())
            {
                if (fieldDef.MappedData.IsDefault)
                {
                    continue;
                }
 
                mappedFieldDataWriter ??= PooledBlobBuilder.GetInstance();
 
                // The compiler always aligns each RVA data field to an 8-byte boundary; this accomodates the alignment
                // needs for all primitive types, regardless of which type is actually being used, at the expense of
                // potentially wasting up to 7 bytes per field if the alignment needs are less. In the future, this
                // potentially could be tightened to align each field only as much as is actually required by that
                // field, saving a few bytes per field.
                int offset = mappedFieldDataWriter.Count;
                Debug.Assert(offset % ManagedPEBuilder.MappedFieldDataAlignment == 0, "Expected last write to end at alignment boundary");
                Debug.Assert(ManagedPEBuilder.MappedFieldDataAlignment == 8, "Expected alignment to be 8");
 
                mappedFieldDataWriter.WriteBytes(fieldDef.MappedData);
                mappedFieldDataWriter.Align(ManagedPEBuilder.MappedFieldDataAlignment);
 
                metadata.AddFieldRelativeVirtualAddress(
                    field: GetFieldDefinitionHandle(fieldDef),
                    offset: offset);
            }
        }
#nullable disable
 
        private void PopulateFieldTableRows()
        {
            var fieldDefs = this.GetFieldDefs();
            metadata.SetCapacity(TableIndex.Field, fieldDefs.Count);
 
            foreach (IFieldDefinition fieldDef in fieldDefs)
            {
                if (fieldDef.IsContextualNamedEntity)
                {
                    ((IContextualNamedEntity)fieldDef).AssociateWithMetadataWriter(this);
                }
 
                metadata.AddFieldDefinition(
                    attributes: GetFieldAttributes(fieldDef),
                    name: GetStringHandleForNameAndCheckLength(fieldDef.Name, fieldDef),
                    signature: GetFieldSignatureIndex(fieldDef));
            }
        }
 
        private void PopulateConstantTableRows()
        {
            foreach (IFieldDefinition fieldDef in this.GetFieldDefs())
            {
                var constant = fieldDef.GetCompileTimeValue(Context);
                if (constant == null)
                {
                    continue;
                }
 
                metadata.AddConstant(
                    parent: GetFieldDefinitionHandle(fieldDef),
                    value: constant.Value);
            }
 
            foreach (IParameterDefinition parDef in this.GetParameterDefs())
            {
                var defaultValue = parDef.GetDefaultValue(Context);
                if (defaultValue == null)
                {
                    continue;
                }
 
                metadata.AddConstant(
                    parent: GetParameterHandle(parDef),
                    value: defaultValue.Value);
            }
 
            foreach (IPropertyDefinition propDef in this.GetPropertyDefs())
            {
                if (!propDef.HasDefaultValue)
                {
                    continue;
                }
 
                Debug.Assert(propDef.DefaultValue != null);
                metadata.AddConstant(
                    parent: GetPropertyDefIndex(propDef),
                    value: propDef.DefaultValue.Value);
            }
        }
 
        private void PopulateFileTableRows()
        {
            ISourceAssemblySymbolInternal assembly = module.SourceAssemblyOpt;
            if (assembly == null)
            {
                return;
            }
 
            var hashAlgorithm = assembly.HashAlgorithm;
            metadata.SetCapacity(TableIndex.File, _fileRefList.Count);
 
            foreach (IFileReference fileReference in _fileRefList)
            {
                metadata.AddAssemblyFile(
                    name: GetStringHandleForPathAndCheckLength(fileReference.FileName),
                    hashValue: metadata.GetOrAddBlob(fileReference.GetHashValue(hashAlgorithm)),
                    containsMetadata: fileReference.HasMetadata);
            }
        }
 
        private void PopulateGenericParameters(
            ImmutableArray<IGenericParameter> sortedGenericParameters)
        {
            foreach (IGenericParameter genericParameter in sortedGenericParameters)
            {
                // CONSIDER: The CLI spec doesn't mention a restriction on the Name column of the GenericParam table,
                // but they go in the same string heap as all the other declaration names, so it stands to reason that
                // they should be restricted in the same way.
                var genericParameterHandle = metadata.AddGenericParameter(
                    parent: GetDeclaringTypeOrMethodHandle(genericParameter),
                    attributes: GetGenericParameterAttributes(genericParameter),
                    name: GetStringHandleForNameAndCheckLength(genericParameter.Name, genericParameter),
                    index: genericParameter.Index);
 
                foreach (var refWithAttributes in genericParameter.GetConstraints(Context))
                {
                    var genericConstraintHandle = metadata.AddGenericParameterConstraint(
                        genericParameter: genericParameterHandle,
                        constraint: GetTypeHandle(refWithAttributes.TypeRef));
                    AddCustomAttributesToTable(genericConstraintHandle, refWithAttributes.Attributes);
                }
            }
        }
 
        private void PopulateImplMapTableRows()
        {
            foreach (IMethodDefinition methodDef in this.GetMethodDefs())
            {
                if (methodDef.IsEncDeleted || !methodDef.IsPlatformInvoke)
                {
                    continue;
                }
 
                var data = methodDef.PlatformInvokeData;
                string entryPointName = data.EntryPointName;
 
                StringHandle importName = entryPointName != null && entryPointName != methodDef.Name
                    ? GetStringHandleForNameAndCheckLength(entryPointName, methodDef)
                    : metadata.GetOrAddString(methodDef.Name); // Length checked while populating the method def table.
 
                metadata.AddMethodImport(
                    method: GetMethodDefinitionHandle(methodDef),
                    attributes: data.Flags,
                    name: importName,
                    module: GetModuleReferenceHandle(data.ModuleName));
            }
        }
 
        private void PopulateInterfaceImplTableRows()
        {
            foreach (ITypeDefinition typeDef in this.GetTypeDefs())
            {
                var typeDefHandle = GetTypeDefinitionHandle(typeDef);
                foreach (var interfaceImpl in typeDef.Interfaces(Context))
                {
                    var handle = metadata.AddInterfaceImplementation(
                        type: typeDefHandle,
                        implementedInterface: GetTypeHandle(interfaceImpl.TypeRef));
                    AddCustomAttributesToTable(handle, interfaceImpl.Attributes);
                }
            }
        }
 
#nullable enable
        private void PopulateManifestResourceTableRows(out PooledBlobBuilder? resourceDataWriter, BlobBuilder? dynamicAnalysisData)
        {
            resourceDataWriter = null;
 
            if (dynamicAnalysisData != null)
            {
                resourceDataWriter = PooledBlobBuilder.GetInstance();
                metadata.AddManifestResource(
                    attributes: ManifestResourceAttributes.Private,
                    name: metadata.GetOrAddString("<DynamicAnalysisData>"),
                    implementation: default(EntityHandle),
                    offset: writeBuilderResourceAndGetOffset(dynamicAnalysisData, resourceDataWriter)
                );
            }
 
            foreach (var resource in this.module.GetResources(Context))
            {
                EntityHandle implementation;
                if (resource.ExternalFile != null)
                {
                    // Length checked on insertion into the file table.
                    implementation = GetAssemblyFileHandle(resource.ExternalFile);
                }
                else
                {
                    // This is an embedded resource, we don't support references to resources from referenced assemblies.
                    implementation = default(EntityHandle);
                }
 
                metadata.AddManifestResource(
                    attributes: resource.IsPublic ? ManifestResourceAttributes.Public : ManifestResourceAttributes.Private,
                    name: GetStringHandleForNameAndCheckLength(resource.Name),
                    implementation: implementation,
                    offset: writeManagedResourceAndGetOffset(resource, ref resourceDataWriter));
            }
 
            // the stream should be aligned:
            Debug.Assert(resourceDataWriter == null || (resourceDataWriter.Count % ManagedPEBuilder.ManagedResourcesDataAlignment) == 0);
 
            static uint writeManagedResourceAndGetOffset(ManagedResource resource, ref PooledBlobBuilder? resourceWriter)
            {
                if (resource.ExternalFile != null)
                {
                    return resource.Offset;
                }
 
                resourceWriter ??= PooledBlobBuilder.GetInstance();
                int result = resourceWriter.Count;
                resource.WriteData(resourceWriter);
                return (uint)result;
            }
 
            static uint writeBuilderResourceAndGetOffset(BlobBuilder resource, BlobBuilder resourceWriter)
            {
                int result = resourceWriter.Count;
                resourceWriter.WriteInt32(resource.Count);
                resource.WriteContentTo(resourceWriter);
                resourceWriter.Align(8);
                return (uint)result;
            }
        }
#nullable disable
 
        private void PopulateMemberRefTableRows()
        {
            var memberRefs = this.GetMemberRefs();
            metadata.SetCapacity(TableIndex.MemberRef, memberRefs.Count);
 
            foreach (ITypeMemberReference memberRef in memberRefs)
            {
                metadata.AddMemberReference(
                    parent: GetMemberReferenceParent(memberRef),
                    name: GetStringHandleForNameAndCheckLength(memberRef.Name, memberRef),
                    signature: GetMemberReferenceSignatureHandle(memberRef));
            }
        }
 
        private void PopulateMethodImplTableRows()
        {
            metadata.SetCapacity(TableIndex.MethodImpl, methodImplList.Count);
 
            foreach (MethodImplementation methodImplementation in this.methodImplList)
            {
                metadata.AddMethodImplementation(
                    type: GetTypeDefinitionHandle(methodImplementation.ContainingType),
                    methodBody: GetMethodDefinitionOrReferenceHandle(methodImplementation.ImplementingMethod),
                    methodDeclaration: GetMethodDefinitionOrReferenceHandle(methodImplementation.ImplementedMethod));
            }
        }
 
        private void PopulateMethodSpecTableRows()
        {
            var methodSpecs = this.GetMethodSpecs();
            metadata.SetCapacity(TableIndex.MethodSpec, methodSpecs.Count);
 
            foreach (IGenericMethodInstanceReference genericMethodInstanceReference in methodSpecs)
            {
                metadata.AddMethodSpecification(
                    method: GetMethodDefinitionOrReferenceHandle(genericMethodInstanceReference.GetGenericMethod(Context)),
                    instantiation: GetMethodSpecificationBlobHandle(genericMethodInstanceReference));
            }
        }
 
        private void PopulateMethodTableRows(int[] methodBodyOffsets)
        {
            var methodDefs = this.GetMethodDefs();
            metadata.SetCapacity(TableIndex.MethodDef, methodDefs.Count);
 
            int i = 0;
            foreach (IMethodDefinition methodDef in methodDefs)
            {
                metadata.AddMethodDefinition(
                    attributes: GetMethodAttributes(methodDef),
                    implAttributes: methodDef.GetImplementationAttributes(Context),
                    name: GetStringHandleForNameAndCheckLength(methodDef.Name, methodDef),
                    signature: GetMethodSignatureHandle(methodDef),
                    bodyOffset: methodBodyOffsets[i],
                    parameterList: GetFirstParameterHandle(methodDef));
 
                i++;
            }
        }
 
        private void PopulateMethodSemanticsTableRows()
        {
            var propertyDefs = this.GetPropertyDefs();
            var eventDefs = this.GetEventDefs();
 
            // an estimate, not necessarily accurate.
            metadata.SetCapacity(TableIndex.MethodSemantics, propertyDefs.Count * 2 + eventDefs.Count * 2);
 
            foreach (IPropertyDefinition propertyDef in this.GetPropertyDefs())
            {
                var association = GetPropertyDefIndex(propertyDef);
                foreach (IMethodReference accessorMethod in propertyDef.GetAccessors(Context))
                {
                    MethodSemanticsAttributes semantics;
                    if (accessorMethod == propertyDef.Setter)
                    {
                        semantics = MethodSemanticsAttributes.Setter;
                    }
                    else if (accessorMethod == propertyDef.Getter)
                    {
                        semantics = MethodSemanticsAttributes.Getter;
                    }
                    else
                    {
                        semantics = MethodSemanticsAttributes.Other;
                    }
 
                    metadata.AddMethodSemantics(
                        association: association,
                        semantics: semantics,
                        methodDefinition: GetMethodDefinitionHandle(accessorMethod.GetResolvedMethod(Context)));
                }
            }
 
            foreach (IEventDefinition eventDef in this.GetEventDefs())
            {
                var association = GetEventDefinitionHandle(eventDef);
                foreach (IMethodReference accessorMethod in eventDef.GetAccessors(Context))
                {
                    MethodSemanticsAttributes semantics;
                    if (accessorMethod == eventDef.Adder)
                    {
                        semantics = MethodSemanticsAttributes.Adder;
                    }
                    else if (accessorMethod == eventDef.Remover)
                    {
                        semantics = MethodSemanticsAttributes.Remover;
                    }
                    else if (accessorMethod == eventDef.Caller)
                    {
                        semantics = MethodSemanticsAttributes.Raiser;
                    }
                    else
                    {
                        semantics = MethodSemanticsAttributes.Other;
                    }
 
                    metadata.AddMethodSemantics(
                        association: association,
                        semantics: semantics,
                        methodDefinition: GetMethodDefinitionHandle(accessorMethod.GetResolvedMethod(Context)));
                }
            }
        }
 
        private void PopulateModuleRefTableRows()
        {
            var moduleRefs = this.GetModuleRefs();
            metadata.SetCapacity(TableIndex.ModuleRef, moduleRefs.Count);
 
            foreach (string moduleName in moduleRefs)
            {
                metadata.AddModuleReference(GetStringHandleForPathAndCheckLength(moduleName));
            }
        }
 
        private void PopulateModuleTableRow(out Blob mvidFixup)
        {
            CheckPathLength(this.module.ModuleName);
 
            GuidHandle mvidHandle;
            Guid mvid = this.module.SerializationProperties.PersistentIdentifier;
            if (mvid != default(Guid))
            {
                // MVID is specified upfront when emitting EnC delta:
                mvidHandle = metadata.GetOrAddGuid(mvid);
                mvidFixup = default(Blob);
            }
            else
            {
                // The guid will be filled in later:
                var reservedGuid = metadata.ReserveGuid();
                mvidFixup = reservedGuid.Content;
                mvidHandle = reservedGuid.Handle;
                reservedGuid.CreateWriter().WriteBytes(0, mvidFixup.Length);
            }
 
            metadata.AddModule(
                generation: this.Generation,
                moduleName: metadata.GetOrAddString(this.module.ModuleName),
                mvid: mvidHandle,
                encId: metadata.GetOrAddGuid(EncId),
                encBaseId: metadata.GetOrAddGuid(EncBaseId));
        }
 
        private void PopulateParamTableRows()
        {
            var parameterDefs = this.GetParameterDefs();
            metadata.SetCapacity(TableIndex.Param, parameterDefs.Count);
 
            foreach (IParameterDefinition parDef in parameterDefs)
            {
                metadata.AddParameter(
                    attributes: GetParameterAttributes(parDef),
                    sequenceNumber: (parDef is ReturnValueParameter) ? 0 : parDef.Index + 1,
                    name: GetStringHandleForNameAndCheckLength(parDef.Name, parDef));
            }
        }
 
        private void PopulatePropertyTableRows()
        {
            var propertyDefs = this.GetPropertyDefs();
            metadata.SetCapacity(TableIndex.Property, propertyDefs.Count);
 
            foreach (IPropertyDefinition propertyDef in propertyDefs)
            {
                metadata.AddProperty(
                    attributes: GetPropertyAttributes(propertyDef),
                    name: GetStringHandleForNameAndCheckLength(propertyDef.Name, propertyDef),
                    signature: GetPropertySignatureHandle(propertyDef));
            }
        }
 
        private void PopulateTypeDefTableRows()
        {
            var typeDefs = this.GetTypeDefs();
            metadata.SetCapacity(TableIndex.TypeDef, typeDefs.Count);
 
            foreach (INamedTypeDefinition typeDef in typeDefs)
            {
                INamespaceTypeDefinition namespaceType = typeDef.AsNamespaceTypeDefinition(Context);
 
                var moduleBuilder = Context.Module;
                int generation = moduleBuilder.GetTypeDefinitionGeneration(typeDef);
 
                string metadataTypeName = GetMetadataName(typeDef, generation);
                ITypeReference baseType = typeDef.GetBaseClass(Context);
 
                metadata.AddTypeDefinition(
                    attributes: GetTypeAttributes(typeDef),
                    @namespace: (namespaceType != null) ? GetStringHandleForNamespaceAndCheckLength(namespaceType, metadataTypeName) : default(StringHandle),
                    name: GetStringHandleForNameAndCheckLength(metadataTypeName, typeDef),
                    baseType: (baseType != null) ? GetTypeHandle(baseType) : default(EntityHandle),
                    fieldList: GetFirstFieldDefinitionHandle(typeDef),
                    methodList: GetFirstMethodDefinitionHandle(typeDef));
            }
        }
 
        private void PopulateNestedClassTableRows()
        {
            foreach (ITypeDefinition typeDef in this.GetTypeDefs())
            {
                INestedTypeDefinition nestedTypeDef = typeDef.AsNestedTypeDefinition(Context);
                if (nestedTypeDef == null)
                {
                    continue;
                }
 
                metadata.AddNestedType(
                    type: GetTypeDefinitionHandle(typeDef),
                    enclosingType: GetTypeDefinitionHandle(nestedTypeDef.ContainingTypeDefinition));
            }
        }
 
        private void PopulateClassLayoutTableRows()
        {
            foreach (ITypeDefinition typeDef in this.GetTypeDefs())
            {
                if (typeDef.Alignment == 0 && typeDef.SizeOf == 0)
                {
                    continue;
                }
 
                metadata.AddTypeLayout(
                    type: GetTypeDefinitionHandle(typeDef),
                    packingSize: typeDef.Alignment,
                    size: typeDef.SizeOf);
            }
        }
 
        private void PopulateTypeRefTableRows()
        {
            var typeRefs = this.GetTypeRefs();
            metadata.SetCapacity(TableIndex.TypeRef, typeRefs.Count);
 
            foreach (ITypeReference typeRef in typeRefs)
            {
                EntityHandle resolutionScope;
                StringHandle name;
                StringHandle @namespace;
 
                INestedTypeReference nestedTypeRef = typeRef.AsNestedTypeReference;
                if (nestedTypeRef != null)
                {
                    ITypeReference scopeTypeRef;
 
                    ISpecializedNestedTypeReference sneTypeRef = nestedTypeRef.AsSpecializedNestedTypeReference;
                    if (sneTypeRef != null)
                    {
                        scopeTypeRef = sneTypeRef.GetUnspecializedVersion(Context).GetContainingType(Context);
                    }
                    else
                    {
                        scopeTypeRef = nestedTypeRef.GetContainingType(Context);
                    }
 
                    resolutionScope = GetTypeReferenceHandle(scopeTypeRef);
 
                    // It's not possible to reference newer versions of reloadable types from another assembly, hence generation 0:
                    // TODO: https://github.com/dotnet/roslyn/issues/54981
                    string metadataTypeName = GetMetadataName(nestedTypeRef, generation: 0);
 
                    name = this.GetStringHandleForNameAndCheckLength(metadataTypeName, nestedTypeRef);
                    @namespace = default(StringHandle);
                }
                else
                {
                    INamespaceTypeReference namespaceTypeRef = typeRef.AsNamespaceTypeReference;
                    if (namespaceTypeRef == null)
                    {
                        throw ExceptionUtilities.UnexpectedValue(typeRef);
                    }
 
                    resolutionScope = this.GetResolutionScopeHandle(namespaceTypeRef.GetUnit(Context));
 
                    // It's not possible to reference newer versions of reloadable types from another assembly, hence generation 0:
                    // TODO: https://github.com/dotnet/roslyn/issues/54981
                    string metadataTypeName = GetMetadataName(namespaceTypeRef, generation: 0);
 
                    name = this.GetStringHandleForNameAndCheckLength(metadataTypeName, namespaceTypeRef);
                    @namespace = this.GetStringHandleForNamespaceAndCheckLength(namespaceTypeRef, metadataTypeName);
                }
 
                metadata.AddTypeReference(
                    resolutionScope: resolutionScope,
                    @namespace: @namespace,
                    name: name);
            }
        }
 
        private void PopulateTypeSpecTableRows()
        {
            var typeSpecs = this.GetTypeSpecs();
            metadata.SetCapacity(TableIndex.TypeSpec, typeSpecs.Count);
 
            foreach (ITypeReference typeSpec in typeSpecs)
            {
                metadata.AddTypeSpecification(GetTypeSpecSignatureIndex(typeSpec));
            }
        }
 
        private void PopulateStandaloneSignatures()
        {
            var signatures = GetStandaloneSignatureBlobHandles();
 
            foreach (BlobHandle signature in signatures)
            {
                metadata.AddStandaloneSignature(signature);
            }
        }
 
        private int[] SerializeThrowNullMethodBodies(BlobBuilder ilBuilder)
        {
            Debug.Assert(MetadataOnly);
            var methods = this.GetMethodDefs();
            int[] bodyOffsets = new int[methods.Count];
 
            int bodyOffsetCache = -1;
            int methodRid = 0;
            foreach (IMethodDefinition method in methods)
            {
                if (method.HasBody)
                {
                    if (bodyOffsetCache == -1)
                    {
                        bodyOffsetCache = ilBuilder.Count;
                        ilBuilder.WriteBytes(ThrowNullEncodedBody);
                    }
                    bodyOffsets[methodRid] = bodyOffsetCache;
                }
                else
                {
                    bodyOffsets[methodRid] = -1;
                }
                methodRid++;
            }
 
            return bodyOffsets;
        }
 
        private int[] SerializeMethodBodies(BlobBuilder ilBuilder, PdbWriter nativePdbWriterOpt, out Blob mvidStringFixup)
        {
            Debug.Assert(!MetadataOnly);
 
            CustomDebugInfoWriter customDebugInfoWriter = (nativePdbWriterOpt != null) ? new CustomDebugInfoWriter(nativePdbWriterOpt) : null;
 
            var methods = this.GetMethodDefs();
            int[] bodyOffsets = new int[methods.Count];
 
            var lastLocalVariableHandle = default(LocalVariableHandle);
            var lastLocalConstantHandle = default(LocalConstantHandle);
 
            var encoder = new MethodBodyStreamEncoder(ilBuilder);
 
            var mvidStringHandle = default(UserStringHandle);
            mvidStringFixup = default(Blob);
 
            int methodRid = 1;
            foreach (IMethodDefinition method in methods)
            {
                _cancellationToken.ThrowIfCancellationRequested();
                int bodyOffset;
                IMethodBody body;
                StandaloneSignatureHandle localSignatureHandleOpt;
 
                if (method.HasBody)
                {
                    body = method.GetBody(Context);
                    Debug.Assert(body != null);
 
                    localSignatureHandleOpt = this.SerializeLocalVariablesSignature(body);
 
                    // TODO: consider parallelizing these (local signature tokens can be piped into IL serialization & debug info generation)
                    bodyOffset = SerializeMethodBody(encoder, body, localSignatureHandleOpt, ref mvidStringHandle, ref mvidStringFixup);
 
                    nativePdbWriterOpt?.SerializeDebugInfo(body, localSignatureHandleOpt, customDebugInfoWriter);
                }
                else
                {
                    // 0 is actually written to metadata when the row is serialized
                    bodyOffset = -1;
                    body = null;
                    localSignatureHandleOpt = default(StandaloneSignatureHandle);
                }
 
                if (_debugMetadataOpt != null)
                {
                    // methodRid is based on this delta but for async state machine debug info we need the "real" row number
                    // of the method aggregated across generations
                    var aggregateMethodRid = MetadataTokens.GetRowNumber(GetMethodDefinitionHandle(method));
                    SerializeMethodDebugInfo(body, methodRid, aggregateMethodRid, localSignatureHandleOpt, ref lastLocalVariableHandle, ref lastLocalConstantHandle);
                }
 
                _dynamicAnalysisDataWriterOpt?.SerializeMethodCodeCoverageData(body);
 
                bodyOffsets[methodRid - 1] = bodyOffset;
 
                methodRid++;
            }
 
            return bodyOffsets;
        }
 
        private int SerializeMethodBody(MethodBodyStreamEncoder encoder, IMethodBody methodBody, StandaloneSignatureHandle localSignatureHandleOpt, ref UserStringHandle mvidStringHandle, ref Blob mvidStringFixup)
        {
            int ilLength = methodBody.IL.Length;
            var exceptionRegions = methodBody.ExceptionRegions;
            bool isSmallBody = ilLength < 64 && methodBody.MaxStack <= 8 && localSignatureHandleOpt.IsNil && exceptionRegions.Length == 0;
            var smallBodyKey = (methodBody.IL, methodBody.AreLocalsZeroed);
 
            // Check if an identical method body has already been serialized.
            // If so, use the RVA of the already serialized one.
            // Note that we don't need to rewrite the fake tokens in the body before looking it up.
 
            // Don't do small body method caching during deterministic builds until this issue is fixed
            // https://github.com/dotnet/roslyn/issues/7595
            int bodyOffset;
            if (!_deterministic && isSmallBody && _smallMethodBodies.TryGetValue(smallBodyKey, out bodyOffset))
            {
                return bodyOffset;
            }
 
            var encodedBody = encoder.AddMethodBody(
                codeSize: methodBody.IL.Length,
                maxStack: methodBody.MaxStack,
                exceptionRegionCount: exceptionRegions.Length,
                hasSmallExceptionRegions: MayUseSmallExceptionHeaders(exceptionRegions),
                localVariablesSignature: localSignatureHandleOpt,
                attributes: (methodBody.AreLocalsZeroed ? MethodBodyAttributes.InitLocals : 0),
                hasDynamicStackAllocation: methodBody.HasStackalloc);
 
            // Don't do small body method caching during deterministic builds until this issue is fixed
            // https://github.com/dotnet/roslyn/issues/7595
            if (isSmallBody && !_deterministic)
            {
                _smallMethodBodies.Add(smallBodyKey, encodedBody.Offset);
            }
 
            WriteInstructions(encodedBody.Instructions, methodBody.IL, ref mvidStringHandle, ref mvidStringFixup);
            SerializeMethodBodyExceptionHandlerTable(encodedBody.ExceptionRegions, exceptionRegions);
 
            return encodedBody.Offset;
        }
 
        /// <summary>
        /// Serialize the method local signature to the blob.
        /// </summary>
        /// <returns>Standalone signature token</returns>
        protected virtual StandaloneSignatureHandle SerializeLocalVariablesSignature(IMethodBody body)
        {
            Debug.Assert(!_tableIndicesAreComplete);
 
            var localVariables = body.LocalVariables;
            if (localVariables.Length == 0)
            {
                return default(StandaloneSignatureHandle);
            }
 
            var builder = PooledBlobBuilder.GetInstance();
 
            var encoder = new BlobEncoder(builder).LocalVariableSignature(localVariables.Length);
            foreach (ILocalDefinition local in localVariables)
            {
                SerializeLocalVariableType(encoder.AddVariable(), local);
            }
 
            BlobHandle blobIndex = metadata.GetOrAddBlob(builder);
 
            var handle = GetOrAddStandaloneSignatureHandle(blobIndex);
            builder.Free();
 
            return handle;
        }
 
        protected void SerializeLocalVariableType(LocalVariableTypeEncoder encoder, ILocalDefinition local)
        {
            if (local.CustomModifiers.Length > 0)
            {
                SerializeCustomModifiers(encoder.CustomModifiers(), local.CustomModifiers);
            }
 
            SerializeTypeReference(encoder.Type(local.IsReference, local.IsPinned), local.Type);
        }
 
        internal StandaloneSignatureHandle SerializeLocalConstantStandAloneSignature(ILocalDefinition localConstant)
        {
            var builder = PooledBlobBuilder.GetInstance();
            var typeEncoder = new BlobEncoder(builder).FieldSignature();
 
            if (localConstant.CustomModifiers.Length > 0)
            {
                SerializeCustomModifiers(typeEncoder.CustomModifiers(), localConstant.CustomModifiers);
            }
 
            SerializeTypeReference(typeEncoder, localConstant.Type);
 
            BlobHandle blobIndex = metadata.GetOrAddBlob(builder);
            var signatureHandle = GetOrAddStandaloneSignatureHandle(blobIndex);
            builder.Free();
 
            return signatureHandle;
        }
 
        private static byte ReadByte(ImmutableArray<byte> buffer, int pos)
        {
            return buffer[pos];
        }
 
        private static int ReadInt32(ImmutableArray<byte> buffer, int pos)
        {
            return buffer[pos] | buffer[pos + 1] << 8 | buffer[pos + 2] << 16 | buffer[pos + 3] << 24;
        }
 
        private EntityHandle GetHandle(object reference)
        {
            return reference switch
            {
                ITypeReference typeReference => GetTypeHandle(typeReference),
                IFieldReference fieldReference => GetFieldHandle(fieldReference),
                IMethodReference methodReference => GetMethodHandle(methodReference),
                ISignature signature => GetStandaloneSignatureHandle(signature),
                _ => throw ExceptionUtilities.UnexpectedValue(reference)
            };
        }
 
        private EntityHandle ResolveEntityHandleFromPseudoToken(int pseudoSymbolToken)
        {
            int index = pseudoSymbolToken;
            var entity = _pseudoSymbolTokenToReferenceMap[index];
            if (entity != null)
            {
                // EDMAURER since method bodies are not visited as they are in CCI, the operations
                // that would have been done on them are done here.
                if (entity is IReference reference)
                {
                    _referenceVisitor.VisitMethodBodyReference(reference);
                }
                else if (entity is ISignature signature)
                {
                    _referenceVisitor.VisitSignature(signature);
                }
 
                EntityHandle handle = GetHandle(entity);
                _pseudoSymbolTokenToTokenMap[index] = handle;
                _pseudoSymbolTokenToReferenceMap[index] = null; // Set to null to bypass next lookup
                return handle;
            }
 
            return _pseudoSymbolTokenToTokenMap[index];
        }
 
        private UserStringHandle ResolveUserStringHandleFromPseudoToken(int pseudoStringToken)
        {
            int index = pseudoStringToken;
            var str = _pseudoStringTokenToStringMap[index];
            if (str != null)
            {
                var handle = GetOrAddUserString(str);
                _pseudoStringTokenToTokenMap[index] = handle;
                _pseudoStringTokenToStringMap[index] = null; // Set to null to bypass next lookup
                return handle;
            }
 
            return _pseudoStringTokenToTokenMap[index];
        }
 
        private UserStringHandle GetOrAddUserString(string str)
        {
            if (!_userStringTokenOverflow)
            {
                try
                {
                    return metadata.GetOrAddUserString(str);
                }
                catch (ImageFormatLimitationException)
                {
                    this.Context.Diagnostics.Add(this.messageProvider.CreateDiagnostic(this.messageProvider.ERR_TooManyUserStrings, NoLocation.Singleton));
                    _userStringTokenOverflow = true;
                }
            }
 
            return default(UserStringHandle);
        }
 
        private ReservedBlob<UserStringHandle> ReserveUserString(int length)
        {
            if (!_userStringTokenOverflow)
            {
                try
                {
                    return metadata.ReserveUserString(length);
                }
                catch (ImageFormatLimitationException)
                {
                    this.Context.Diagnostics.Add(this.messageProvider.CreateDiagnostic(this.messageProvider.ERR_TooManyUserStrings, NoLocation.Singleton));
                    _userStringTokenOverflow = true;
                }
            }
 
            return default(ReservedBlob<UserStringHandle>);
        }
 
        public enum RawTokenEncoding : byte
        {
            None = 0,
 
            /// <summary>
            /// Emit ldc.i4 of row id of an entity represented by the pseudo-token.
            /// </summary>
            RowId,
 
            /// <summary>
            /// Emit ldc.i4 of the greatest row id assigned to a method definition in the module being built.
            /// </summary>
            GreatestMethodDefinitionRowId,
 
            /// <summary>
            /// Emit ldc.i4 of row id of a source document represented by the pseudo-token.
            /// </summary>
            DocumentRowId,
 
            /// <summary>
            /// Emit ldc.i4 of row id of the hoisted local variable or parameter field that the pseudo-token represents, 
            /// increased by <see cref="LiftedVariableBaseIndex"/>.
            /// </summary>
            LiftedVariableId,
        }
 
        public static uint GetRawToken(RawTokenEncoding encoding, uint pseudoToken)
        {
            Debug.Assert(pseudoToken <= 0xffffff);
            return unchecked((uint)encoding << 24 | pseudoToken);
        }
 
        internal const uint ModuleVersionIdStringToken = 0x80000000;
 
        /// <summary>
        /// Greater than any valid ordinal of a local variable or a parameter (0xffff).
        /// </summary>
        internal const int LiftedVariableBaseIndex = 0x10000;
 
        private void WriteInstructions(Blob finalIL, ImmutableArray<byte> generatedIL, ref UserStringHandle mvidStringHandle, ref Blob mvidStringFixup)
        {
            // write the raw body first and then patch tokens:
            var writer = new BlobWriter(finalIL);
 
            writer.WriteBytes(generatedIL);
            writer.Offset = 0;
 
            int offset = 0;
            while (offset < generatedIL.Length)
            {
                var operandType = InstructionOperandTypes.ReadOperandType(generatedIL, ref offset);
                switch (operandType)
                {
                    case OperandType.InlineField:
                    case OperandType.InlineMethod:
                    case OperandType.InlineTok:
                    case OperandType.InlineType:
                    case OperandType.InlineSig:
                        {
                            int pseudoToken = ReadInt32(generatedIL, offset);
                            int token = 0;
                            // If any bits in the high-order byte of the pseudotoken are nonzero, replace the opcode with Ldc_i4
                            // and either clear the high-order byte in the pseudotoken or ignore the pseudotoken.
                            // This is a trick to enable loading raw metadata token indices as integers.
                            if (operandType == OperandType.InlineTok)
                            {
                                var rawTokenEncoding = (RawTokenEncoding)(pseudoToken >> 24);
                                if (rawTokenEncoding != RawTokenEncoding.None && (uint)pseudoToken != 0xffffffff)
                                {
                                    Debug.Assert(ReadByte(generatedIL, offset - 1) == (byte)ILOpCode.Ldtoken);
                                    writer.Offset = offset - 1;
                                    writer.WriteByte((byte)ILOpCode.Ldc_i4);
                                    switch (rawTokenEncoding)
                                    {
                                        case RawTokenEncoding.RowId:
                                            {
                                                var handle = ResolveEntityHandleFromPseudoToken(pseudoToken & 0x00ffffff);
                                                Debug.Assert(handle.Kind is HandleKind.MethodDefinition);
                                                token = MetadataTokens.GetRowNumber(handle);
                                            }
                                            break;
 
                                        case RawTokenEncoding.LiftedVariableId:
                                            {
                                                var handle = ResolveEntityHandleFromPseudoToken(pseudoToken & 0x00ffffff);
                                                Debug.Assert(handle.Kind is HandleKind.FieldDefinition);
                                                token = MetadataTokens.GetRowNumber(handle) + LiftedVariableBaseIndex;
                                            }
                                            break;
 
                                        case RawTokenEncoding.GreatestMethodDefinitionRowId:
                                            token = GreatestMethodDefIndex;
                                            break;
 
                                        case RawTokenEncoding.DocumentRowId:
                                            token = _dynamicAnalysisDataWriterOpt.GetOrAddDocument(module.GetSourceDocumentFromIndex((uint)(pseudoToken & 0x00ffffff)));
                                            break;
 
                                        default:
                                            throw ExceptionUtilities.UnexpectedValue(rawTokenEncoding);
                                    }
                                }
                            }
                            writer.Offset = offset;
                            writer.WriteInt32(token == 0 ? MetadataTokens.GetToken(ResolveEntityHandleFromPseudoToken(pseudoToken)) : token);
                            offset += 4;
                            break;
                        }
 
                    case OperandType.InlineString:
                        {
                            writer.Offset = offset;
 
                            int pseudoToken = ReadInt32(generatedIL, offset);
                            UserStringHandle handle;
 
                            if ((uint)pseudoToken == ModuleVersionIdStringToken)
                            {
                                // The pseudotoken encoding indicates that the string should refer to a textual encoding of the
                                // current module's module version ID (such that the MVID can be realized using Guid.Parse).
                                // The value cannot be determined until very late in the compilation, so reserve a slot for it now and fill in the value later.
                                if (mvidStringHandle.IsNil)
                                {
                                    const int guidStringLength = 36;
                                    Debug.Assert(guidStringLength == default(Guid).ToString().Length);
                                    var reserved = ReserveUserString(guidStringLength);
                                    mvidStringHandle = reserved.Handle;
                                    mvidStringFixup = reserved.Content;
                                }
 
                                handle = mvidStringHandle;
                            }
                            else
                            {
                                handle = ResolveUserStringHandleFromPseudoToken(pseudoToken);
                            }
 
                            writer.WriteInt32(MetadataTokens.GetToken(handle));
 
                            offset += 4;
                            break;
                        }
 
                    case OperandType.InlineBrTarget:
                    case OperandType.InlineI:
                    case OperandType.ShortInlineR:
                        offset += 4;
                        break;
 
                    case OperandType.InlineSwitch:
                        int argCount = ReadInt32(generatedIL, offset);
                        // skip switch arguments count and arguments
                        offset += (argCount + 1) * 4;
                        break;
 
                    case OperandType.InlineI8:
                    case OperandType.InlineR:
                        offset += 8;
                        break;
 
                    case OperandType.InlineNone:
                        break;
 
                    case OperandType.InlineVar:
                        offset += 2;
                        break;
 
                    case OperandType.ShortInlineBrTarget:
                    case OperandType.ShortInlineI:
                    case OperandType.ShortInlineVar:
                        offset += 1;
                        break;
 
                    default:
                        throw ExceptionUtilities.UnexpectedValue(operandType);
                }
            }
        }
 
        private void SerializeMethodBodyExceptionHandlerTable(ExceptionRegionEncoder encoder, ImmutableArray<ExceptionHandlerRegion> regions)
        {
            foreach (var region in regions)
            {
                var exceptionType = region.ExceptionType;
 
                encoder.Add(
                    region.HandlerKind,
                    region.TryStartOffset,
                    region.TryLength,
                    region.HandlerStartOffset,
                    region.HandlerLength,
                    (exceptionType != null) ? GetTypeHandle(exceptionType) : default(EntityHandle),
                    region.FilterDecisionStartOffset);
            }
        }
 
        private static bool MayUseSmallExceptionHeaders(ImmutableArray<ExceptionHandlerRegion> exceptionRegions)
        {
            if (!ExceptionRegionEncoder.IsSmallRegionCount(exceptionRegions.Length))
            {
                return false;
            }
 
            foreach (var region in exceptionRegions)
            {
                if (!ExceptionRegionEncoder.IsSmallExceptionRegion(region.TryStartOffset, region.TryLength) ||
                    !ExceptionRegionEncoder.IsSmallExceptionRegion(region.HandlerStartOffset, region.HandlerLength))
                {
                    return false;
                }
            }
 
            return true;
        }
 
        private void SerializeParameterInformation(ParameterTypeEncoder encoder, IParameterTypeInformation parameterTypeInformation)
        {
            var type = parameterTypeInformation.GetType(Context);
 
            Debug.Assert(parameterTypeInformation.RefCustomModifiers.Length == 0 || parameterTypeInformation.IsByReference);
            SerializeCustomModifiers(encoder.CustomModifiers(), parameterTypeInformation.RefCustomModifiers);
 
            var typeEncoder = encoder.Type(parameterTypeInformation.IsByReference);
 
            SerializeCustomModifiers(typeEncoder.CustomModifiers(), parameterTypeInformation.CustomModifiers);
            SerializeTypeReference(typeEncoder, type);
        }
 
        private void SerializeFieldSignature(IFieldReference fieldReference, BlobBuilder builder)
        {
            Debug.Assert(fieldReference.RefCustomModifiers.Length == 0 || fieldReference.IsByReference);
 
            // https://github.com/dotnet/roslyn/issues/61385: Use System.Reflection.Metadata.Ecma335.FieldTypeEncoder
            // instead, since that type supports ref fields directly.
            var typeEncoder = new BlobEncoder(builder).FieldSignature();
            SerializeCustomModifiers(new CustomModifiersEncoder(builder), fieldReference.RefCustomModifiers);
            if (fieldReference.IsByReference)
            {
                typeEncoder.Builder.WriteByte((byte)SignatureTypeCode.ByReference);
            }
            SerializeTypeReference(typeEncoder, fieldReference.GetType(Context));
        }
 
        private void SerializeMethodSpecificationSignature(BlobBuilder builder, IGenericMethodInstanceReference genericMethodInstanceReference)
        {
            var argsEncoder = new BlobEncoder(builder).MethodSpecificationSignature(genericMethodInstanceReference.GetGenericMethod(Context).GenericParameterCount);
            foreach (ITypeReference genericArgument in genericMethodInstanceReference.GetGenericArguments(Context))
            {
                ITypeReference typeRef = genericArgument;
                SerializeTypeReference(argsEncoder.AddArgument(), typeRef);
            }
        }
 
        private EmitContext GetEmitContextForAttribute(ICustomAttribute customAttribute)
        {
            if (customAttribute is AttributeData { ApplicationSyntaxReference: { } syntaxReference })
            {
                return new EmitContext(
                    Context.Module,
                    Context.Diagnostics,
                    metadataOnly: Context.MetadataOnly,
                    includePrivateMembers: Context.IncludePrivateMembers,
                    rebuildData: Context.RebuildData,
                    syntaxReference: syntaxReference);
            }
            return Context;
        }
 
        private void SerializeCustomAttributeSignature(ICustomAttribute customAttribute, BlobBuilder builder)
        {
            var parameters = customAttribute.Constructor(Context, reportDiagnostics: false).GetParameters(Context);
            var arguments = customAttribute.GetArguments(Context);
            Debug.Assert(parameters.Length == arguments.Length);
 
            FixedArgumentsEncoder fixedArgsEncoder;
            CustomAttributeNamedArgumentsEncoder namedArgsEncoder;
            new BlobEncoder(builder).CustomAttributeSignature(out fixedArgsEncoder, out namedArgsEncoder);
 
            var attributeContext = GetEmitContextForAttribute(customAttribute);
 
            for (int i = 0; i < parameters.Length; i++)
            {
                SerializeMetadataExpression(in attributeContext, fixedArgsEncoder.AddArgument(), arguments[i], parameters[i].GetType(Context));
            }
 
            SerializeCustomAttributeNamedArguments(in attributeContext, namedArgsEncoder.Count(customAttribute.NamedArgumentCount), customAttribute);
        }
 
        private void SerializeCustomAttributeNamedArguments(in EmitContext context, NamedArgumentsEncoder encoder, ICustomAttribute customAttribute)
        {
            foreach (IMetadataNamedArgument namedArgument in customAttribute.GetNamedArguments(Context))
            {
                NamedArgumentTypeEncoder typeEncoder;
                NameEncoder nameEncoder;
                LiteralEncoder literalEncoder;
                encoder.AddArgument(namedArgument.IsField, out typeEncoder, out nameEncoder, out literalEncoder);
 
                SerializeNamedArgumentType(in context, typeEncoder, namedArgument.Type);
                nameEncoder.Name(namedArgument.ArgumentName);
                SerializeMetadataExpression(in context, literalEncoder, namedArgument.ArgumentValue, namedArgument.Type);
            }
        }
 
        private void SerializeNamedArgumentType(in EmitContext context, NamedArgumentTypeEncoder encoder, ITypeReference type)
        {
            if (type is IArrayTypeReference arrayType)
            {
                SerializeCustomAttributeArrayType(in context, encoder.SZArray(), arrayType);
            }
            else if (module.IsPlatformType(type, PlatformType.SystemObject))
            {
                encoder.Object();
            }
            else
            {
                SerializeCustomAttributeElementType(in context, encoder.ScalarType(), type);
            }
        }
 
        private void SerializeMetadataExpression(in EmitContext context, LiteralEncoder encoder, IMetadataExpression expression, ITypeReference targetType)
        {
            if (expression is MetadataCreateArray a)
            {
                ITypeReference targetElementType;
                VectorEncoder vectorEncoder;
                if (!(targetType is IArrayTypeReference targetArrayType))
                {
                    // implicit conversion from array to object
                    Debug.Assert(this.module.IsPlatformType(targetType, PlatformType.SystemObject));
 
                    CustomAttributeArrayTypeEncoder arrayTypeEncoder;
                    encoder.TaggedVector(out arrayTypeEncoder, out vectorEncoder);
                    SerializeCustomAttributeArrayType(in context, arrayTypeEncoder, a.ArrayType);
 
                    targetElementType = a.ElementType;
                }
                else
                {
                    vectorEncoder = encoder.Vector();
 
                    // In FixedArg the element type of the parameter array has to match the element type of the argument array,
                    // but in NamedArg T[] can be assigned to object[]. In that case we need to encode the arguments using
                    // the parameter element type not the argument element type.
                    targetElementType = targetArrayType.GetElementType(this.Context);
                }
 
                var literalsEncoder = vectorEncoder.Count(a.Elements.Length);
 
                foreach (IMetadataExpression elemValue in a.Elements)
                {
                    SerializeMetadataExpression(in context, literalsEncoder.AddLiteral(), elemValue, targetElementType);
                }
            }
            else
            {
                ScalarEncoder scalarEncoder;
                MetadataConstant c = expression as MetadataConstant;
 
                if (this.module.IsPlatformType(targetType, PlatformType.SystemObject))
                {
                    CustomAttributeElementTypeEncoder typeEncoder;
                    encoder.TaggedScalar(out typeEncoder, out scalarEncoder);
 
                    // special case null argument assigned to Object parameter - treat as null string
                    if (c != null &&
                        c.Value == null &&
                        this.module.IsPlatformType(c.Type, PlatformType.SystemObject))
                    {
                        typeEncoder.String();
                    }
                    else
                    {
                        SerializeCustomAttributeElementType(in context, typeEncoder, expression.Type);
                    }
                }
                else
                {
                    scalarEncoder = encoder.Scalar();
                }
 
                if (c != null)
                {
                    if (c.Type is IArrayTypeReference)
                    {
                        scalarEncoder.NullArray();
                        return;
                    }
 
                    Debug.Assert(!module.IsPlatformType(c.Type, PlatformType.SystemType) || c.Value == null);
                    scalarEncoder.Constant(c.Value);
                }
                else
                {
                    scalarEncoder.SystemType(((MetadataTypeOf)expression).TypeToGet.GetSerializedTypeName(context));
                }
            }
        }
 
        private void SerializeMarshallingDescriptor(IMarshallingInformation marshallingInformation, BlobBuilder writer)
        {
            writer.WriteCompressedInteger((int)marshallingInformation.UnmanagedType);
            switch (marshallingInformation.UnmanagedType)
            {
                case UnmanagedType.ByValArray: // NATIVE_TYPE_FIXEDARRAY
                    Debug.Assert(marshallingInformation.NumberOfElements >= 0);
                    writer.WriteCompressedInteger(marshallingInformation.NumberOfElements);
                    if (marshallingInformation.ElementType >= 0)
                    {
                        writer.WriteCompressedInteger((int)marshallingInformation.ElementType);
                    }
 
                    break;
 
                case Constants.UnmanagedType_CustomMarshaler:
                    writer.WriteUInt16(0); // padding
 
                    object marshaller = marshallingInformation.GetCustomMarshaller(Context);
                    switch (marshaller)
                    {
                        case ITypeReference marshallerTypeRef:
                            this.SerializeTypeName(marshallerTypeRef, writer);
                            break;
                        case null:
                            writer.WriteByte(0);
                            break;
                        default:
                            writer.WriteSerializedString((string)marshaller);
                            break;
                    }
 
                    var arg = marshallingInformation.CustomMarshallerRuntimeArgument;
                    if (arg != null)
                    {
                        writer.WriteSerializedString(arg);
                    }
                    else
                    {
                        writer.WriteByte(0);
                    }
 
                    break;
 
                case UnmanagedType.LPArray: // NATIVE_TYPE_ARRAY
                    Debug.Assert(marshallingInformation.ElementType >= 0);
                    writer.WriteCompressedInteger((int)marshallingInformation.ElementType);
                    if (marshallingInformation.ParamIndex >= 0)
                    {
                        writer.WriteCompressedInteger(marshallingInformation.ParamIndex);
                        if (marshallingInformation.NumberOfElements >= 0)
                        {
                            writer.WriteCompressedInteger(marshallingInformation.NumberOfElements);
                            writer.WriteByte(1); // The parameter number is valid
                        }
                    }
                    else if (marshallingInformation.NumberOfElements >= 0)
                    {
                        writer.WriteByte(0); // Dummy parameter value emitted so that NumberOfElements can be in a known position
                        writer.WriteCompressedInteger(marshallingInformation.NumberOfElements);
                        writer.WriteByte(0); // The parameter number is not valid
                    }
 
                    break;
 
                case Constants.UnmanagedType_SafeArray:
                    if (marshallingInformation.SafeArrayElementSubtype >= 0)
                    {
                        writer.WriteCompressedInteger((int)marshallingInformation.SafeArrayElementSubtype);
                        var elementType = marshallingInformation.GetSafeArrayElementUserDefinedSubtype(Context);
                        if (elementType != null)
                        {
                            this.SerializeTypeName(elementType, writer);
                        }
                    }
 
                    break;
 
                case UnmanagedType.ByValTStr: // NATIVE_TYPE_FIXEDSYSSTRING
                    writer.WriteCompressedInteger(marshallingInformation.NumberOfElements);
                    break;
 
                case UnmanagedType.Interface:
                case Constants.UnmanagedType_IDispatch:
                case UnmanagedType.IUnknown:
                    if (marshallingInformation.IidParameterIndex >= 0)
                    {
                        writer.WriteCompressedInteger(marshallingInformation.IidParameterIndex);
                    }
 
                    break;
            }
        }
 
        private void SerializeTypeName(ITypeReference typeReference, BlobBuilder writer)
        {
            writer.WriteSerializedString(typeReference.GetSerializedTypeName(this.Context));
        }
 
        /// <summary>
        /// Computes the string representing the strong name of the given assembly reference.
        /// </summary>
        internal static string StrongName(IAssemblyReference assemblyReference)
        {
            var identity = assemblyReference.Identity;
 
            var pooled = PooledStringBuilder.GetInstance();
            StringBuilder sb = pooled.Builder;
            sb.Append(identity.Name);
            sb.AppendFormat(CultureInfo.InvariantCulture, ", Version={0}.{1}.{2}.{3}", identity.Version.Major, identity.Version.Minor, identity.Version.Build, identity.Version.Revision);
            if (!string.IsNullOrEmpty(identity.CultureName))
            {
                sb.AppendFormat(CultureInfo.InvariantCulture, ", Culture={0}", identity.CultureName);
            }
            else
            {
                sb.Append(", Culture=neutral");
            }
 
            sb.Append(", PublicKeyToken=");
            if (identity.PublicKeyToken.Length > 0)
            {
                foreach (byte b in identity.PublicKeyToken)
                {
                    sb.Append(b.ToString("x2"));
                }
            }
            else
            {
                sb.Append("null");
            }
 
            if (identity.IsRetargetable)
            {
                sb.Append(", Retargetable=Yes");
            }
 
            if (identity.ContentType == AssemblyContentType.WindowsRuntime)
            {
                sb.Append(", ContentType=WindowsRuntime");
            }
            else
            {
                Debug.Assert(identity.ContentType == AssemblyContentType.Default);
            }
 
            return pooled.ToStringAndFree();
        }
 
        private void SerializePermissionSet(ImmutableArray<ICustomAttribute> permissionSet, BlobBuilder writer)
        {
            EmitContext context = this.Context;
            foreach (ICustomAttribute customAttribute in permissionSet)
            {
                bool isAssemblyQualified = true;
                string typeName = customAttribute.GetType(context).GetSerializedTypeName(context, ref isAssemblyQualified);
                if (!isAssemblyQualified)
                {
                    INamespaceTypeReference namespaceType = customAttribute.GetType(context).AsNamespaceTypeReference;
                    if (namespaceType?.GetUnit(context) is IAssemblyReference referencedAssembly)
                    {
                        typeName = typeName + ", " + StrongName(referencedAssembly);
                    }
                }
 
                writer.WriteSerializedString(typeName);
 
                var customAttributeArgsBuilder = PooledBlobBuilder.GetInstance();
                var namedArgsEncoder = new BlobEncoder(customAttributeArgsBuilder).PermissionSetArguments(customAttribute.NamedArgumentCount);
                SerializeCustomAttributeNamedArguments(GetEmitContextForAttribute(customAttribute), namedArgsEncoder, customAttribute);
                writer.WriteCompressedInteger(customAttributeArgsBuilder.Count);
 
                customAttributeArgsBuilder.WriteContentTo(writer);
                customAttributeArgsBuilder.Free();
            }
            // TODO: xml for older platforms
        }
 
        private void SerializeReturnValueAndParameters(MethodSignatureEncoder encoder, ISignature signature, ImmutableArray<IParameterTypeInformation> varargParameters)
        {
            var declaredParameters = signature.GetParameters(Context);
            var returnType = signature.GetType(Context);
 
            ReturnTypeEncoder returnTypeEncoder;
            ParametersEncoder parametersEncoder;
 
            encoder.Parameters(declaredParameters.Length + varargParameters.Length, out returnTypeEncoder, out parametersEncoder);
 
            if (module.IsPlatformType(returnType, PlatformType.SystemVoid))
            {
                Debug.Assert(!signature.ReturnValueIsByRef);
                Debug.Assert(signature.RefCustomModifiers.IsEmpty);
                SerializeCustomModifiers(returnTypeEncoder.CustomModifiers(), signature.ReturnValueCustomModifiers);
 
                returnTypeEncoder.Void();
            }
            else
            {
                Debug.Assert(signature.RefCustomModifiers.IsEmpty || signature.ReturnValueIsByRef);
                SerializeCustomModifiers(returnTypeEncoder.CustomModifiers(), signature.RefCustomModifiers);
 
                var typeEncoder = returnTypeEncoder.Type(signature.ReturnValueIsByRef);
 
                SerializeCustomModifiers(typeEncoder.CustomModifiers(), signature.ReturnValueCustomModifiers);
                SerializeTypeReference(typeEncoder, returnType);
            }
 
            foreach (IParameterTypeInformation parameter in declaredParameters)
            {
                SerializeParameterInformation(parametersEncoder.AddParameter(), parameter);
            }
 
            if (varargParameters.Length > 0)
            {
                parametersEncoder = parametersEncoder.StartVarArgs();
                foreach (IParameterTypeInformation parameter in varargParameters)
                {
                    SerializeParameterInformation(parametersEncoder.AddParameter(), parameter);
                }
            }
        }
 
        private void SerializeTypeReference(SignatureTypeEncoder encoder, ITypeReference typeReference)
        {
            while (true)
            {
                if (module.IsPlatformType(typeReference, PlatformType.SystemTypedReference))
                {
                    // We should use `SignatureTypeEncoder.TypedReference()` once such a method is available
                    // Tracked by https://github.com/dotnet/runtime/issues/80812
                    encoder.Builder.WriteByte((byte)SignatureTypeCode.TypedReference);
                    return;
                }
 
                if (typeReference is IModifiedTypeReference modifiedTypeReference)
                {
                    SerializeCustomModifiers(encoder.CustomModifiers(), modifiedTypeReference.CustomModifiers);
                    typeReference = modifiedTypeReference.UnmodifiedType;
                    continue;
                }
 
                var primitiveType = typeReference.TypeCode;
                switch (primitiveType)
                {
                    case PrimitiveTypeCode.Pointer:
                    case PrimitiveTypeCode.FunctionPointer:
                    case PrimitiveTypeCode.NotPrimitive:
                        break;
 
                    default:
                        SerializePrimitiveType(encoder, primitiveType);
                        return;
                }
 
                if (typeReference is IPointerTypeReference pointerTypeReference)
                {
                    typeReference = pointerTypeReference.GetTargetType(Context);
                    encoder = encoder.Pointer();
                    continue;
                }
 
                if (typeReference is IFunctionPointerTypeReference functionPointerTypeReference)
                {
                    var signature = functionPointerTypeReference.Signature;
                    var signatureEncoder = encoder.FunctionPointer(convention: signature.CallingConvention.ToSignatureConvention());
                    SerializeReturnValueAndParameters(signatureEncoder, signature, varargParameters: ImmutableArray<IParameterTypeInformation>.Empty);
                    return;
                }
 
                IGenericTypeParameterReference genericTypeParameterReference = typeReference.AsGenericTypeParameterReference;
                if (genericTypeParameterReference != null)
                {
                    encoder.GenericTypeParameter(
                        GetNumberOfInheritedTypeParameters(genericTypeParameterReference.DefiningType) +
                        genericTypeParameterReference.Index);
                    return;
                }
 
                if (typeReference is IArrayTypeReference arrayTypeReference)
                {
                    typeReference = arrayTypeReference.GetElementType(Context);
 
                    if (arrayTypeReference.IsSZArray)
                    {
                        encoder = encoder.SZArray();
                        continue;
                    }
                    else
                    {
                        SignatureTypeEncoder elementType;
                        ArrayShapeEncoder arrayShape;
                        encoder.Array(out elementType, out arrayShape);
                        SerializeTypeReference(elementType, typeReference);
                        arrayShape.Shape(arrayTypeReference.Rank, arrayTypeReference.Sizes, arrayTypeReference.LowerBounds);
                        return;
                    }
                }
 
                if (module.IsPlatformType(typeReference, PlatformType.SystemObject))
                {
                    encoder.Object();
                    return;
                }
 
                IGenericMethodParameterReference genericMethodParameterReference = typeReference.AsGenericMethodParameterReference;
                if (genericMethodParameterReference != null)
                {
                    encoder.GenericMethodTypeParameter(genericMethodParameterReference.Index);
                    return;
                }
 
                if (typeReference.IsTypeSpecification())
                {
                    ITypeReference uninstantiatedTypeReference = typeReference.GetUninstantiatedGenericType(Context);
 
                    // Roslyn's uninstantiated type is the same object as the instantiated type for
                    // types closed over their type parameters, so to speak.
 
                    var consolidatedTypeArguments = ArrayBuilder<ITypeReference>.GetInstance();
                    typeReference.GetConsolidatedTypeArguments(consolidatedTypeArguments, this.Context);
 
                    var genericArgsEncoder = encoder.GenericInstantiation(
                        GetTypeHandle(uninstantiatedTypeReference, treatRefAsPotentialTypeSpec: false),
                        consolidatedTypeArguments.Count,
                        typeReference.IsValueType);
 
                    foreach (ITypeReference typeArgument in consolidatedTypeArguments)
                    {
                        SerializeTypeReference(genericArgsEncoder.AddArgument(), typeArgument);
                    }
 
                    consolidatedTypeArguments.Free();
                    return;
                }
 
                encoder.Type(GetTypeHandle(typeReference), typeReference.IsValueType);
                return;
            }
        }
 
        private static void SerializePrimitiveType(SignatureTypeEncoder encoder, PrimitiveTypeCode primitiveType)
        {
            switch (primitiveType)
            {
                case PrimitiveTypeCode.Boolean:
                    encoder.Boolean();
                    break;
 
                case PrimitiveTypeCode.UInt8:
                    encoder.Byte();
                    break;
 
                case PrimitiveTypeCode.Int8:
                    encoder.SByte();
                    break;
 
                case PrimitiveTypeCode.Char:
                    encoder.Char();
                    break;
 
                case PrimitiveTypeCode.Int16:
                    encoder.Int16();
                    break;
 
                case PrimitiveTypeCode.UInt16:
                    encoder.UInt16();
                    break;
 
                case PrimitiveTypeCode.Int32:
                    encoder.Int32();
                    break;
 
                case PrimitiveTypeCode.UInt32:
                    encoder.UInt32();
                    break;
 
                case PrimitiveTypeCode.Int64:
                    encoder.Int64();
                    break;
 
                case PrimitiveTypeCode.UInt64:
                    encoder.UInt64();
                    break;
 
                case PrimitiveTypeCode.Float32:
                    encoder.Single();
                    break;
 
                case PrimitiveTypeCode.Float64:
                    encoder.Double();
                    break;
 
                case PrimitiveTypeCode.IntPtr:
                    encoder.IntPtr();
                    break;
 
                case PrimitiveTypeCode.UIntPtr:
                    encoder.UIntPtr();
                    break;
 
                case PrimitiveTypeCode.String:
                    encoder.String();
                    break;
 
                case PrimitiveTypeCode.Void:
                    // "void" is handled specifically for "void*" with custom modifiers.
                    // If SignatureTypeEncoder supports such cases directly, this can
                    // be removed. See https://github.com/dotnet/corefx/issues/14571.
                    encoder.Builder.WriteByte((byte)System.Reflection.Metadata.PrimitiveTypeCode.Void);
                    break;
 
                default:
                    throw ExceptionUtilities.UnexpectedValue(primitiveType);
            }
        }
 
        private void SerializeCustomAttributeArrayType(in EmitContext context, CustomAttributeArrayTypeEncoder encoder, IArrayTypeReference arrayTypeReference)
        {
            // A single-dimensional, zero-based array is specified as a single byte 0x1D followed by the FieldOrPropType of the element type.
 
            // only non-jagged SZ arrays are allowed in attributes
            // (need to encode the type of the SZ array if the parameter type is Object):
            Debug.Assert(arrayTypeReference.IsSZArray);
 
            var elementType = arrayTypeReference.GetElementType(Context);
            Debug.Assert(!(elementType is IModifiedTypeReference));
 
            if (module.IsPlatformType(elementType, PlatformType.SystemObject))
            {
                encoder.ObjectArray();
            }
            else
            {
                SerializeCustomAttributeElementType(in context, encoder.ElementType(), elementType);
            }
        }
 
        private void SerializeCustomAttributeElementType(in EmitContext context, CustomAttributeElementTypeEncoder encoder, ITypeReference typeReference)
        {
            // Spec:
            // The FieldOrPropType 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.
            // An enum is specified as a single byte 0x55 followed by a SerString.
 
            var primitiveType = typeReference.TypeCode;
            if (primitiveType != PrimitiveTypeCode.NotPrimitive)
            {
                SerializePrimitiveType(encoder, primitiveType);
            }
            else if (module.IsPlatformType(typeReference, PlatformType.SystemType))
            {
                encoder.SystemType();
            }
            else
            {
                Debug.Assert(typeReference.IsEnum);
                encoder.Enum(typeReference.GetSerializedTypeName(context));
            }
        }
 
        private static void SerializePrimitiveType(CustomAttributeElementTypeEncoder encoder, PrimitiveTypeCode primitiveType)
        {
            switch (primitiveType)
            {
                case PrimitiveTypeCode.Boolean:
                    encoder.Boolean();
                    break;
 
                case PrimitiveTypeCode.UInt8:
                    encoder.Byte();
                    break;
 
                case PrimitiveTypeCode.Int8:
                    encoder.SByte();
                    break;
 
                case PrimitiveTypeCode.Char:
                    encoder.Char();
                    break;
 
                case PrimitiveTypeCode.Int16:
                    encoder.Int16();
                    break;
 
                case PrimitiveTypeCode.UInt16:
                    encoder.UInt16();
                    break;
 
                case PrimitiveTypeCode.Int32:
                    encoder.Int32();
                    break;
 
                case PrimitiveTypeCode.UInt32:
                    encoder.UInt32();
                    break;
 
                case PrimitiveTypeCode.Int64:
                    encoder.Int64();
                    break;
 
                case PrimitiveTypeCode.UInt64:
                    encoder.UInt64();
                    break;
 
                case PrimitiveTypeCode.Float32:
                    encoder.Single();
                    break;
 
                case PrimitiveTypeCode.Float64:
                    encoder.Double();
                    break;
 
                case PrimitiveTypeCode.String:
                    encoder.String();
                    break;
 
                default:
                    throw ExceptionUtilities.UnexpectedValue(primitiveType);
            }
        }
 
        private void SerializeCustomModifiers(CustomModifiersEncoder encoder, ImmutableArray<ICustomModifier> modifiers)
        {
            foreach (var modifier in modifiers)
            {
                encoder = encoder.AddModifier(GetTypeHandle(modifier.GetModifier(Context)), modifier.IsOptional);
            }
        }
 
        private int GetNumberOfInheritedTypeParameters(ITypeReference type)
        {
            INestedTypeReference nestedType = type.AsNestedTypeReference;
            if (nestedType == null)
            {
                return 0;
            }
 
            ISpecializedNestedTypeReference specializedNestedType = nestedType.AsSpecializedNestedTypeReference;
            if (specializedNestedType != null)
            {
                nestedType = specializedNestedType.GetUnspecializedVersion(Context);
            }
 
            int result = 0;
            type = nestedType.GetContainingType(Context);
            nestedType = type.AsNestedTypeReference;
            while (nestedType != null)
            {
                result += nestedType.GenericParameterCount;
                type = nestedType.GetContainingType(Context);
                nestedType = type.AsNestedTypeReference;
            }
 
            result += type.AsNamespaceTypeReference.GenericParameterCount;
            return result;
        }
 
        internal static EditAndContinueMethodDebugInformation GetEncMethodDebugInfo(IMethodBody methodBody)
        {
            ImmutableArray<LocalSlotDebugInfo> encLocalSlots;
 
            // Kickoff method of a state machine (async/iterator method) doesn't have any interesting locals,
            // so we use its EnC method debug info to store information about locals hoisted to the state machine.
            var encSlotInfo = methodBody.StateMachineHoistedLocalSlots;
            if (encSlotInfo.IsDefault)
            {
                encLocalSlots = GetLocalSlotDebugInfos(methodBody.LocalVariables);
            }
            else
            {
                encLocalSlots = GetLocalSlotDebugInfos(encSlotInfo);
            }
 
            return new EditAndContinueMethodDebugInformation(
                methodBody.MethodId.Ordinal,
                encLocalSlots,
                methodBody.ClosureDebugInfo.SelectAsArray(static info => info.DebugInfo),
                methodBody.LambdaDebugInfo.SelectAsArray(static info => info.DebugInfo),
                methodBody.StateMachineStatesDebugInfo.States);
        }
 
        internal static ImmutableArray<LocalSlotDebugInfo> GetLocalSlotDebugInfos(ImmutableArray<ILocalDefinition> locals)
        {
            if (!locals.Any(static variable => !variable.SlotInfo.Id.IsNone))
            {
                return ImmutableArray<LocalSlotDebugInfo>.Empty;
            }
 
            return locals.SelectAsArray(variable => variable.SlotInfo);
        }
 
        internal static ImmutableArray<LocalSlotDebugInfo> GetLocalSlotDebugInfos(ImmutableArray<EncHoistedLocalInfo> locals)
        {
            if (!locals.Any(static variable => !variable.SlotInfo.Id.IsNone))
            {
                return ImmutableArray<LocalSlotDebugInfo>.Empty;
            }
 
            return locals.SelectAsArray(variable => variable.SlotInfo);
        }
 
        protected abstract class HeapOrReferenceIndexBase<T>
        {
            private readonly MetadataWriter _writer;
            private readonly List<T> _rows;
            private readonly int _firstRowId;
 
            protected HeapOrReferenceIndexBase(MetadataWriter writer, int lastRowId)
            {
                _writer = writer;
                _rows = new List<T>();
                _firstRowId = lastRowId + 1;
            }
 
            public abstract bool TryGetValue(T item, out int index);
 
            public int GetOrAdd(T item)
            {
                int index;
                if (!this.TryGetValue(item, out index))
                {
                    index = Add(item);
                }
 
                return index;
            }
 
            public IReadOnlyList<T> Rows
            {
                get { return _rows; }
            }
 
            public int Add(T item)
            {
                Debug.Assert(!_writer._tableIndicesAreComplete);
#if DEBUG
                int i;
                Debug.Assert(!this.TryGetValue(item, out i));
#endif
                int index = _firstRowId + _rows.Count;
                _rows.Add(item);
                this.AddItem(item, index);
                return index;
            }
 
            protected abstract void AddItem(T item, int index);
        }
 
        protected sealed class HeapOrReferenceIndex<T> : HeapOrReferenceIndexBase<T>
        {
            private readonly Dictionary<T, int> _index;
 
            public HeapOrReferenceIndex(MetadataWriter writer, int lastRowId = 0)
                : this(writer, new Dictionary<T, int>(), lastRowId)
            {
            }
 
            private HeapOrReferenceIndex(MetadataWriter writer, Dictionary<T, int> index, int lastRowId)
                : base(writer, lastRowId)
            {
                Debug.Assert(index.Count == 0);
                _index = index;
            }
 
            public override bool TryGetValue(T item, out int index)
            {
                return _index.TryGetValue(item, out index);
            }
 
            protected override void AddItem(T item, int index)
            {
                _index.Add(item, index);
            }
        }
 
        protected sealed class TypeReferenceIndex : HeapOrReferenceIndexBase<ITypeReference>
        {
            private readonly Dictionary<ITypeReference, int> _index;
 
            public TypeReferenceIndex(MetadataWriter writer, int lastRowId = 0)
                : this(writer, new Dictionary<ITypeReference, int>(ReferenceEqualityComparer.Instance), lastRowId)
            {
            }
 
            private TypeReferenceIndex(MetadataWriter writer, Dictionary<ITypeReference, int> index, int lastRowId)
                : base(writer, lastRowId)
            {
                Debug.Assert(index.Count == 0);
                _index = index;
            }
 
            public override bool TryGetValue(ITypeReference item, out int index)
            {
                return _index.TryGetValue(item, out index);
            }
 
            protected override void AddItem(ITypeReference item, int index)
            {
                _index.Add(item, index);
            }
        }
 
        protected sealed class InstanceAndStructuralReferenceIndex<T> : HeapOrReferenceIndexBase<T> where T : class, IReference
        {
            private readonly Dictionary<T, int> _instanceIndex;
            private readonly Dictionary<T, int> _structuralIndex;
 
            public InstanceAndStructuralReferenceIndex(MetadataWriter writer, IEqualityComparer<T> structuralComparer, int lastRowId = 0)
                : base(writer, lastRowId)
            {
                _instanceIndex = new Dictionary<T, int>(ReferenceEqualityComparer.Instance);
                _structuralIndex = new Dictionary<T, int>(structuralComparer);
            }
 
            public override bool TryGetValue(T item, out int index)
            {
                if (_instanceIndex.TryGetValue(item, out index))
                {
                    return true;
                }
                if (_structuralIndex.TryGetValue(item, out index))
                {
                    _instanceIndex.Add(item, index);
                    return true;
                }
                return false;
            }
 
            protected override void AddItem(T item, int index)
            {
                _instanceIndex.Add(item, index);
                _structuralIndex.Add(item, index);
            }
        }
 
        private class ByteSequenceBoolTupleComparer : IEqualityComparer<(ImmutableArray<byte>, bool)>
        {
            internal static readonly ByteSequenceBoolTupleComparer Instance = new ByteSequenceBoolTupleComparer();
 
            private ByteSequenceBoolTupleComparer()
            {
            }
 
            bool IEqualityComparer<(ImmutableArray<byte>, bool)>.Equals((ImmutableArray<byte>, bool) x, (ImmutableArray<byte>, bool) y)
            {
                return x.Item2 == y.Item2 && ByteSequenceComparer.Equals(x.Item1, y.Item1);
            }
 
            int IEqualityComparer<(ImmutableArray<byte>, bool)>.GetHashCode((ImmutableArray<byte>, bool) x)
            {
                return Hash.Combine(ByteSequenceComparer.GetHashCode(x.Item1), x.Item2.GetHashCode());
            }
        }
    }
}