File: Emit\EditAndContinue\DeltaMetadataWriter.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.
 
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Threading;
using Microsoft.Cci;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeGen;
using Microsoft.CodeAnalysis.Collections;
using Microsoft.CodeAnalysis.Emit.EditAndContinue;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Symbols;
using Roslyn.Utilities;
using ReferenceEqualityComparer = Roslyn.Utilities.ReferenceEqualityComparer;
 
namespace Microsoft.CodeAnalysis.Emit
{
    internal sealed class DeltaMetadataWriter : MetadataWriter
    {
        private readonly EmitBaseline _previousGeneration;
        private readonly Guid _encId;
        private readonly DefinitionMap _definitionMap;
        private readonly SymbolChanges _changes;
 
        /// <summary>
        /// Type definitions containing any changes (includes added types).
        /// </summary>
        private readonly List<ITypeDefinition> _changedTypeDefs;
 
        /// <summary>
        /// Cache of type definitions used in signatures of deleted members. Used so that if a method 'C M(C c)' is deleted
        /// we use the same <see cref="DeletedSourceTypeDefinition"/> instance for the method return type, and the parameter type.
        /// </summary>
        private readonly Dictionary<ITypeDefinition, DeletedSourceTypeDefinition> _typesUsedByDeletedMembers;
 
        private readonly Dictionary<ITypeDefinition, ImmutableArray<IMethodDefinition>> _deletedTypeMembers;
 
        private readonly DefinitionIndex<ITypeDefinition> _typeDefs;
        private readonly DefinitionIndex<IEventDefinition> _eventDefs;
        private readonly DefinitionIndex<IFieldDefinition> _fieldDefs;
        private readonly DefinitionIndex<IMethodDefinition> _methodDefs;
        private readonly DefinitionIndex<IPropertyDefinition> _propertyDefs;
        private readonly DefinitionIndex<IParameterDefinition> _parameterDefs;
        private readonly Dictionary<IParameterDefinition, IMethodDefinition> _parameterDefList;
        private readonly GenericParameterIndex _genericParameters;
        private readonly EventOrPropertyMapIndex _eventMap;
        private readonly EventOrPropertyMapIndex _propertyMap;
        private readonly MethodImplIndex _methodImpls;
 
        // Keep track of which CustomAttributes rows are added in this and previous deltas, over what is in the
        // original metadata
        private readonly Dictionary<EntityHandle, ImmutableArray<int>> _customAttributesAdded;
 
        private readonly ArrayBuilder<int> _customAttributeRowIds;
        private readonly List<(EntityHandle parentHandle, IEnumerator<ICustomAttribute> attributeEnumerator)> _deferredCustomAttributes = new();
 
        private readonly Dictionary<IParameterDefinition, int> _existingParameterDefs;
        private readonly Dictionary<MethodDefinitionHandle, int> _firstParamRowMap;
 
        private readonly HeapOrReferenceIndex<AssemblyIdentity> _assemblyRefIndex;
        private readonly HeapOrReferenceIndex<string> _moduleRefIndex;
        private readonly InstanceAndStructuralReferenceIndex<ITypeMemberReference> _memberRefIndex;
        private readonly InstanceAndStructuralReferenceIndex<IGenericMethodInstanceReference> _methodSpecIndex;
        private readonly TypeReferenceIndex _typeRefIndex;
        private readonly InstanceAndStructuralReferenceIndex<ITypeReference> _typeSpecIndex;
        private readonly HeapOrReferenceIndex<BlobHandle> _standAloneSignatureIndex;
        private readonly Dictionary<IMethodDefinition, AddedOrChangedMethodInfo> _addedOrChangedMethods;
 
        public DeltaMetadataWriter(
            EmitContext context,
            CommonMessageProvider messageProvider,
            EmitBaseline previousGeneration,
            Guid encId,
            DefinitionMap definitionMap,
            SymbolChanges changes,
            CancellationToken cancellationToken)
            : base(metadata: MakeTablesBuilder(previousGeneration),
                   debugMetadataOpt: (context.Module.DebugInformationFormat == DebugInformationFormat.PortablePdb) ? new MetadataBuilder() : null,
                   dynamicAnalysisDataWriterOpt: null,
                   context: context,
                   messageProvider: messageProvider,
                   metadataOnly: false,
                   deterministic: false,
                   emitTestCoverageData: false,
                   cancellationToken: cancellationToken)
        {
            Debug.Assert(previousGeneration != null);
            Debug.Assert(encId != default(Guid));
            Debug.Assert(encId != previousGeneration.EncId);
            Debug.Assert(context.Module.DebugInformationFormat != DebugInformationFormat.Embedded);
 
            _previousGeneration = previousGeneration;
            _encId = encId;
            _definitionMap = definitionMap;
            _changes = changes;
 
            var sizes = previousGeneration.TableSizes;
 
            _changedTypeDefs = new List<ITypeDefinition>();
            _typesUsedByDeletedMembers = new Dictionary<ITypeDefinition, DeletedSourceTypeDefinition>(ReferenceEqualityComparer.Instance);
            _deletedTypeMembers = new Dictionary<ITypeDefinition, ImmutableArray<IMethodDefinition>>(ReferenceEqualityComparer.Instance);
            _typeDefs = new DefinitionIndex<ITypeDefinition>(this.TryGetExistingTypeDefIndex, sizes[(int)TableIndex.TypeDef]);
            _eventDefs = new DefinitionIndex<IEventDefinition>(this.TryGetExistingEventDefIndex, sizes[(int)TableIndex.Event]);
            _fieldDefs = new DefinitionIndex<IFieldDefinition>(this.TryGetExistingFieldDefIndex, sizes[(int)TableIndex.Field]);
            _methodDefs = new DefinitionIndex<IMethodDefinition>(this.TryGetExistingMethodDefIndex, sizes[(int)TableIndex.MethodDef]);
            _propertyDefs = new DefinitionIndex<IPropertyDefinition>(this.TryGetExistingPropertyDefIndex, sizes[(int)TableIndex.Property]);
            _parameterDefs = new DefinitionIndex<IParameterDefinition>(this.TryGetExistingParameterDefIndex, sizes[(int)TableIndex.Param]);
            _parameterDefList = new Dictionary<IParameterDefinition, IMethodDefinition>(Cci.SymbolEquivalentEqualityComparer.Instance);
            _genericParameters = new GenericParameterIndex(sizes[(int)TableIndex.GenericParam]);
            _eventMap = new EventOrPropertyMapIndex(this.TryGetExistingEventMapIndex, sizes[(int)TableIndex.EventMap]);
            _propertyMap = new EventOrPropertyMapIndex(this.TryGetExistingPropertyMapIndex, sizes[(int)TableIndex.PropertyMap]);
            _methodImpls = new MethodImplIndex(this, sizes[(int)TableIndex.MethodImpl]);
 
            _customAttributesAdded = new Dictionary<EntityHandle, ImmutableArray<int>>();
            _customAttributeRowIds = ArrayBuilder<int>.GetInstance();
 
            _firstParamRowMap = new Dictionary<MethodDefinitionHandle, int>();
            _existingParameterDefs = new Dictionary<IParameterDefinition, int>(ReferenceEqualityComparer.Instance);
 
            _assemblyRefIndex = new HeapOrReferenceIndex<AssemblyIdentity>(this, lastRowId: sizes[(int)TableIndex.AssemblyRef]);
            _moduleRefIndex = new HeapOrReferenceIndex<string>(this, lastRowId: sizes[(int)TableIndex.ModuleRef]);
            _memberRefIndex = new InstanceAndStructuralReferenceIndex<ITypeMemberReference>(this, new MemberRefComparer(this), lastRowId: sizes[(int)TableIndex.MemberRef]);
            _methodSpecIndex = new InstanceAndStructuralReferenceIndex<IGenericMethodInstanceReference>(this, new MethodSpecComparer(this), lastRowId: sizes[(int)TableIndex.MethodSpec]);
            _typeRefIndex = new TypeReferenceIndex(this, lastRowId: sizes[(int)TableIndex.TypeRef]);
            _typeSpecIndex = new InstanceAndStructuralReferenceIndex<ITypeReference>(this, new TypeSpecComparer(this), lastRowId: sizes[(int)TableIndex.TypeSpec]);
            _standAloneSignatureIndex = new HeapOrReferenceIndex<BlobHandle>(this, lastRowId: sizes[(int)TableIndex.StandAloneSig]);
 
            _addedOrChangedMethods = new Dictionary<IMethodDefinition, AddedOrChangedMethodInfo>(Cci.SymbolEquivalentEqualityComparer.Instance);
        }
 
        private static MetadataBuilder MakeTablesBuilder(EmitBaseline previousGeneration)
        {
            return new MetadataBuilder(
                previousGeneration.UserStringStreamLength,
                previousGeneration.StringStreamLength,
                previousGeneration.BlobStreamLength,
                previousGeneration.GuidStreamLength);
        }
 
        private ImmutableArray<int> GetDeltaTableSizes(ImmutableArray<int> rowCounts)
        {
            var sizes = new int[MetadataTokens.TableCount];
 
            rowCounts.CopyTo(sizes);
 
            sizes[(int)TableIndex.TypeRef] = _typeRefIndex.Rows.Count;
            sizes[(int)TableIndex.TypeDef] = _typeDefs.GetAdded().Count;
            sizes[(int)TableIndex.Field] = _fieldDefs.GetAdded().Count;
            sizes[(int)TableIndex.MethodDef] = _methodDefs.GetAdded().Count;
            sizes[(int)TableIndex.Param] = _parameterDefs.GetAdded().Count;
            sizes[(int)TableIndex.MemberRef] = _memberRefIndex.Rows.Count;
            sizes[(int)TableIndex.StandAloneSig] = _standAloneSignatureIndex.Rows.Count;
            sizes[(int)TableIndex.EventMap] = _eventMap.GetAdded().Count;
            sizes[(int)TableIndex.Event] = _eventDefs.GetAdded().Count;
            sizes[(int)TableIndex.PropertyMap] = _propertyMap.GetAdded().Count;
            sizes[(int)TableIndex.Property] = _propertyDefs.GetAdded().Count;
            sizes[(int)TableIndex.MethodImpl] = _methodImpls.GetAdded().Count;
            sizes[(int)TableIndex.ModuleRef] = _moduleRefIndex.Rows.Count;
            sizes[(int)TableIndex.TypeSpec] = _typeSpecIndex.Rows.Count;
            sizes[(int)TableIndex.AssemblyRef] = _assemblyRefIndex.Rows.Count;
            sizes[(int)TableIndex.GenericParam] = _genericParameters.GetAdded().Count;
            sizes[(int)TableIndex.MethodSpec] = _methodSpecIndex.Rows.Count;
 
            return ImmutableArray.Create(sizes);
        }
 
        internal EmitBaseline GetDelta(Compilation compilation, Guid encId, MetadataSizes metadataSizes)
        {
            var addedOrChangedMethodsByIndex = new Dictionary<int, AddedOrChangedMethodInfo>();
            foreach (var pair in _addedOrChangedMethods)
            {
                addedOrChangedMethodsByIndex.Add(MetadataTokens.GetRowNumber(GetMethodDefinitionHandle(pair.Key)), pair.Value);
            }
 
            var previousTableSizes = _previousGeneration.TableEntriesAdded;
            var deltaTableSizes = GetDeltaTableSizes(metadataSizes.RowCounts);
            var tableSizes = new int[MetadataTokens.TableCount];
 
            for (int i = 0; i < tableSizes.Length; i++)
            {
                tableSizes[i] = previousTableSizes[i] + deltaTableSizes[i];
            }
 
            // If the previous generation is 0 (metadata) get the synthesized members from the current compilation's builder,
            // otherwise members from the current compilation have already been merged into the baseline.
            var synthesizedMembers = (_previousGeneration.Ordinal == 0) ? module.GetAllSynthesizedMembers() : _previousGeneration.SynthesizedMembers;
 
            Debug.Assert(module.EncSymbolChanges is not null);
            var deletedMembers = (_previousGeneration.Ordinal == 0) ? module.EncSymbolChanges.DeletedMembers : _previousGeneration.DeletedMembers;
 
            var currentGenerationOrdinal = _previousGeneration.Ordinal + 1;
 
            var addedTypes = _typeDefs.GetAdded();
            var generationOrdinals = CreateDictionary(_previousGeneration.GenerationOrdinals, SymbolEquivalentEqualityComparer.Instance);
            foreach (var (addedType, _) in addedTypes)
            {
                if (_changes.IsReplacedDef(addedType))
                {
                    generationOrdinals[addedType] = currentGenerationOrdinal;
                }
            }
 
            return _previousGeneration.With(
                compilation,
                module,
                currentGenerationOrdinal,
                encId,
                generationOrdinals,
                typesAdded: AddRange(_previousGeneration.TypesAdded, addedTypes, comparer: SymbolEquivalentEqualityComparer.Instance),
                eventsAdded: AddRange(_previousGeneration.EventsAdded, _eventDefs.GetAdded(), comparer: SymbolEquivalentEqualityComparer.Instance),
                fieldsAdded: AddRange(_previousGeneration.FieldsAdded, _fieldDefs.GetAdded(), comparer: SymbolEquivalentEqualityComparer.Instance),
                methodsAdded: AddRange(_previousGeneration.MethodsAdded, _methodDefs.GetAdded(), comparer: SymbolEquivalentEqualityComparer.Instance),
                firstParamRowMap: AddRange(_previousGeneration.FirstParamRowMap, _firstParamRowMap),
                propertiesAdded: AddRange(_previousGeneration.PropertiesAdded, _propertyDefs.GetAdded(), comparer: SymbolEquivalentEqualityComparer.Instance),
                eventMapAdded: AddRange(_previousGeneration.EventMapAdded, _eventMap.GetAdded()),
                propertyMapAdded: AddRange(_previousGeneration.PropertyMapAdded, _propertyMap.GetAdded()),
                methodImplsAdded: AddRange(_previousGeneration.MethodImplsAdded, _methodImpls.GetAdded()),
                customAttributesAdded: AddRange(_previousGeneration.CustomAttributesAdded, _customAttributesAdded),
                tableEntriesAdded: ImmutableArray.Create(tableSizes),
                // Blob stream is concatenated aligned.
                blobStreamLengthAdded: metadataSizes.GetAlignedHeapSize(HeapIndex.Blob) + _previousGeneration.BlobStreamLengthAdded,
                // String stream is concatenated unaligned.
                stringStreamLengthAdded: metadataSizes.HeapSizes[(int)HeapIndex.String] + _previousGeneration.StringStreamLengthAdded,
                // UserString stream is concatenated aligned.
                userStringStreamLengthAdded: metadataSizes.GetAlignedHeapSize(HeapIndex.UserString) + _previousGeneration.UserStringStreamLengthAdded,
                // Guid stream accumulates on the GUID heap unlike other heaps, so the previous generations are already included.
                guidStreamLengthAdded: metadataSizes.HeapSizes[(int)HeapIndex.Guid],
                synthesizedTypes: ((IPEDeltaAssemblyBuilder)module).GetSynthesizedTypes(),
                synthesizedMembers: synthesizedMembers,
                deletedMembers: deletedMembers,
                addedOrChangedMethods: AddRange(_previousGeneration.AddedOrChangedMethods, addedOrChangedMethodsByIndex),
                debugInformationProvider: _previousGeneration.DebugInformationProvider,
                localSignatureProvider: _previousGeneration.LocalSignatureProvider);
        }
 
        private static Dictionary<K, V> CreateDictionary<K, V>(IReadOnlyDictionary<K, V> dictionary, IEqualityComparer<K>? comparer)
            where K : notnull
        {
            var result = new Dictionary<K, V>(comparer);
            foreach (var pair in dictionary)
            {
                result.Add(pair.Key, pair.Value);
            }
 
            return result;
        }
 
        private static IReadOnlyDictionary<K, V> AddRange<K, V>(IReadOnlyDictionary<K, V> previous, IReadOnlyDictionary<K, V> current, IEqualityComparer<K>? comparer = null)
            where K : notnull
        {
            if (previous.Count == 0)
            {
                return current;
            }
 
            if (current.Count == 0)
            {
                return previous;
            }
 
            var result = CreateDictionary(previous, comparer);
            foreach (var pair in current)
            {
                // Use the latest symbol.
                result[pair.Key] = pair.Value;
            }
 
            return result;
        }
 
        /// <summary>
        /// Return tokens for all updated debuggable methods.
        /// </summary>
        public void GetUpdatedMethodTokens(ArrayBuilder<MethodDefinitionHandle> methods)
        {
            foreach (var def in _methodDefs.GetRows())
            {
                // The debugger tries to remap all modified methods, which requires presence of sequence points.
                if (!_methodDefs.IsAddedNotChanged(def) && def.GetBody(Context)?.SequencePoints.Length > 0)
                {
                    methods.Add(MetadataTokens.MethodDefinitionHandle(_methodDefs.GetRowId(def)));
                }
            }
        }
 
        /// <summary>
        /// Return tokens for all updated or added types.
        /// </summary>
        public void GetChangedTypeTokens(ArrayBuilder<TypeDefinitionHandle> types)
        {
            foreach (var def in _changedTypeDefs)
            {
                types.Add(GetTypeDefinitionHandle(def));
            }
        }
 
        protected override ushort Generation
        {
            get { return (ushort)(_previousGeneration.Ordinal + 1); }
        }
 
        protected override Guid EncId
        {
            get { return _encId; }
        }
 
        protected override Guid EncBaseId
        {
            get { return _previousGeneration.EncId; }
        }
 
        protected override EventDefinitionHandle GetEventDefinitionHandle(IEventDefinition def)
        {
            return MetadataTokens.EventDefinitionHandle(_eventDefs.GetRowId(def));
        }
 
        protected override IReadOnlyList<IEventDefinition> GetEventDefs()
        {
            return _eventDefs.GetRows();
        }
 
        protected override FieldDefinitionHandle GetFieldDefinitionHandle(IFieldDefinition def)
        {
            return MetadataTokens.FieldDefinitionHandle(_fieldDefs.GetRowId(def));
        }
 
        protected override IReadOnlyList<IFieldDefinition> GetFieldDefs()
        {
            return _fieldDefs.GetRows();
        }
 
        protected override bool TryGetTypeDefinitionHandle(ITypeDefinition def, out TypeDefinitionHandle handle)
        {
            bool result = _typeDefs.TryGetRowId(def, out int rowId);
            handle = MetadataTokens.TypeDefinitionHandle(rowId);
            return result;
        }
 
        protected override TypeDefinitionHandle GetTypeDefinitionHandle(ITypeDefinition def)
        {
            return MetadataTokens.TypeDefinitionHandle(_typeDefs.GetRowId(def));
        }
 
        protected override ITypeDefinition GetTypeDef(TypeDefinitionHandle handle)
        {
            return _typeDefs.GetDefinition(MetadataTokens.GetRowNumber(handle));
        }
 
        protected override IReadOnlyList<ITypeDefinition> GetTypeDefs()
        {
            return _typeDefs.GetRows();
        }
 
        protected override bool TryGetMethodDefinitionHandle(IMethodDefinition def, out MethodDefinitionHandle handle)
        {
            bool result = _methodDefs.TryGetRowId(def, out int rowId);
            handle = MetadataTokens.MethodDefinitionHandle(rowId);
            return result;
        }
 
        protected override MethodDefinitionHandle GetMethodDefinitionHandle(IMethodDefinition def)
            => MetadataTokens.MethodDefinitionHandle(_methodDefs.GetRowId(def));
 
        protected override IMethodDefinition GetMethodDef(MethodDefinitionHandle index)
            => _methodDefs.GetDefinition(MetadataTokens.GetRowNumber(index));
 
        protected override IReadOnlyList<IMethodDefinition> GetMethodDefs()
            => _methodDefs.GetRows();
 
        protected override PropertyDefinitionHandle GetPropertyDefIndex(IPropertyDefinition def)
            => MetadataTokens.PropertyDefinitionHandle(_propertyDefs.GetRowId(def));
 
        protected override IReadOnlyList<IPropertyDefinition> GetPropertyDefs()
            => _propertyDefs.GetRows();
 
        protected override ParameterHandle GetParameterHandle(IParameterDefinition def)
            => MetadataTokens.ParameterHandle(_parameterDefs.GetRowId(def));
 
        protected override IReadOnlyList<IParameterDefinition> GetParameterDefs()
            => _parameterDefs.GetRows();
 
        protected override IReadOnlyList<IGenericParameter> GetGenericParameters()
            => _genericParameters.GetRows();
 
        // Fields are associated with the type through the EncLog table.
        protected override FieldDefinitionHandle GetFirstFieldDefinitionHandle(INamedTypeDefinition typeDef)
            => default;
 
        // Methods are associated with the type through the EncLog table.
        protected override MethodDefinitionHandle GetFirstMethodDefinitionHandle(INamedTypeDefinition typeDef)
            => default;
 
        // Parameters are associated with the method through the EncLog table.
        protected override ParameterHandle GetFirstParameterHandle(IMethodDefinition methodDef)
            => default;
 
        protected override AssemblyReferenceHandle GetOrAddAssemblyReferenceHandle(IAssemblyReference reference)
        {
            var identity = reference.Identity;
            var versionPattern = reference.AssemblyVersionPattern;
 
            if (versionPattern is not null)
            {
                RoslynDebug.AssertNotNull(_previousGeneration.InitialBaseline.LazyMetadataSymbols);
                identity = _previousGeneration.InitialBaseline.LazyMetadataSymbols.AssemblyReferenceIdentityMap[identity.WithVersion(versionPattern)];
            }
 
            return MetadataTokens.AssemblyReferenceHandle(_assemblyRefIndex.GetOrAdd(identity));
        }
 
        protected override IReadOnlyList<AssemblyIdentity> GetAssemblyRefs()
        {
            return _assemblyRefIndex.Rows;
        }
 
        protected override ModuleReferenceHandle GetOrAddModuleReferenceHandle(string reference)
        {
            return MetadataTokens.ModuleReferenceHandle(_moduleRefIndex.GetOrAdd(reference));
        }
 
        protected override IReadOnlyList<string> GetModuleRefs()
        {
            return _moduleRefIndex.Rows;
        }
 
        protected override MemberReferenceHandle GetOrAddMemberReferenceHandle(ITypeMemberReference reference)
        {
            return MetadataTokens.MemberReferenceHandle(_memberRefIndex.GetOrAdd(reference));
        }
 
        protected override IReadOnlyList<ITypeMemberReference> GetMemberRefs()
        {
            return _memberRefIndex.Rows;
        }
 
        protected override MethodSpecificationHandle GetOrAddMethodSpecificationHandle(IGenericMethodInstanceReference reference)
        {
            return MetadataTokens.MethodSpecificationHandle(_methodSpecIndex.GetOrAdd(reference));
        }
 
        protected override IReadOnlyList<IGenericMethodInstanceReference> GetMethodSpecs()
        {
            return _methodSpecIndex.Rows;
        }
 
        protected override int GreatestMethodDefIndex => _methodDefs.NextRowId;
 
        protected override bool TryGetTypeReferenceHandle(ITypeReference reference, out TypeReferenceHandle handle)
        {
            int index;
            bool result = _typeRefIndex.TryGetValue(reference, out index);
            handle = MetadataTokens.TypeReferenceHandle(index);
            return result;
        }
 
        protected override TypeReferenceHandle GetOrAddTypeReferenceHandle(ITypeReference reference)
        {
            return MetadataTokens.TypeReferenceHandle(_typeRefIndex.GetOrAdd(reference));
        }
 
        protected override IReadOnlyList<ITypeReference> GetTypeRefs()
        {
            return _typeRefIndex.Rows;
        }
 
        protected override TypeSpecificationHandle GetOrAddTypeSpecificationHandle(ITypeReference reference)
        {
            return MetadataTokens.TypeSpecificationHandle(_typeSpecIndex.GetOrAdd(reference));
        }
 
        protected override IReadOnlyList<ITypeReference> GetTypeSpecs()
        {
            return _typeSpecIndex.Rows;
        }
 
        protected override StandaloneSignatureHandle GetOrAddStandaloneSignatureHandle(BlobHandle blobIndex)
        {
            return MetadataTokens.StandaloneSignatureHandle(_standAloneSignatureIndex.GetOrAdd(blobIndex));
        }
 
        protected override IReadOnlyList<BlobHandle> GetStandaloneSignatureBlobHandles()
        {
            return _standAloneSignatureIndex.Rows;
        }
 
        protected override void OnIndicesCreated()
        {
            var module = (IPEDeltaAssemblyBuilder)this.module;
            module.OnCreatedIndices(this.Context.Diagnostics);
        }
 
        protected override void CreateIndicesForNonTypeMembers(ITypeDefinition typeDef)
        {
            var change = _changes.GetChange(typeDef);
            switch (change)
            {
                case SymbolChange.Added:
                    _typeDefs.Add(typeDef);
                    _changedTypeDefs.Add(typeDef);
 
                    var typeParameters = this.GetConsolidatedTypeParameters(typeDef);
                    if (typeParameters != null)
                    {
                        foreach (var typeParameter in typeParameters)
                        {
                            _genericParameters.Add(typeParameter);
                        }
                    }
 
                    break;
 
                case SymbolChange.Updated:
                    _typeDefs.AddUpdated(typeDef);
                    _changedTypeDefs.Add(typeDef);
                    break;
 
                case SymbolChange.ContainsChanges:
                    // Members changed.
                    // We keep this list separately because we don't want to output duplicate typedef entries in the EnC log,
                    // which uses _typeDefs, but it's simpler to let the members output those rows for the updated typedefs
                    // with the right update type.
                    _changedTypeDefs.Add(typeDef);
                    break;
 
                case SymbolChange.None:
                    // No changes to type.
                    return;
 
                default:
                    throw ExceptionUtilities.UnexpectedValue(change);
            }
 
            int typeRowId = _typeDefs.GetRowId(typeDef);
 
            foreach (var eventDef in typeDef.GetEvents(this.Context))
            {
                if (!_eventMap.Contains(typeRowId))
                {
                    _eventMap.Add(typeRowId);
                }
 
                var eventChange = _changes.GetChangeForPossibleReAddedMember(eventDef, DefinitionExistsInAnyPreviousGeneration);
                this.AddDefIfNecessary(_eventDefs, eventDef, eventChange);
            }
 
            foreach (var fieldDef in typeDef.GetFields(this.Context))
            {
                var fieldChange = _changes.GetChangeForPossibleReAddedMember(fieldDef, DefinitionExistsInAnyPreviousGeneration);
                this.AddDefIfNecessary(_fieldDefs, fieldDef, fieldChange);
            }
 
            foreach (var methodDef in typeDef.GetMethods(this.Context))
            {
                var methodChange = _changes.GetChangeForPossibleReAddedMember(methodDef, DefinitionExistsInAnyPreviousGeneration);
                this.AddDefIfNecessary(_methodDefs, methodDef, methodChange);
                CreateIndicesForMethod(methodDef, methodChange);
            }
 
            if (typeDef.GetInternalSymbol() is INamedTypeSymbolInternal typeSymbol &&
                (_changes.DeletedMembers.TryGetValue(typeSymbol, out var deletedMembers) |
                 _changes.UpdatedMethods.TryGetValue(typeSymbol, out var updatedMethods)))
            {
                // create representations of the old deleted methods in this compilation:
                var newMethodDefs = ArrayBuilder<IMethodDefinition>.GetInstance();
 
                ImmutableArray<byte>? lazyDeletedMethodIL = null;
                ImmutableArray<byte>? lazyDeletedLambdaIL = null;
 
                foreach (var deletedMember in deletedMembers.NullToEmpty())
                {
                    if (deletedMember is IMethodSymbolInternal deletedMethod)
                    {
                        var deletedMethodHandle = _definitionMap.GetPreviousMethodHandle(deletedMethod);
                        var deletedMethodDef = (IMethodDefinition)deletedMethod.GetCciAdapter();
 
                        lazyDeletedMethodIL ??= DeletedMethodBody.GetIL(Context, rudeEdit: null, isLambdaOrLocalFunction: false);
 
                        newMethodDefs.Add(new DeletedSourceMethodDefinition(deletedMethodDef, deletedMethodHandle, lazyDeletedMethodIL.Value, _typesUsedByDeletedMembers));
 
                        addDeletedClosureMethods(deletedMethod, currentLambdas: ImmutableArray<EncLambdaInfo>.Empty, ImmutableArray<LambdaRuntimeRudeEditInfo>.Empty);
                    }
                }
 
                foreach (var (oldMethod, newMethod) in updatedMethods.NullToEmpty())
                {
                    var newMethodDef = (IMethodDefinition)newMethod.GetCciAdapter();
 
                    var (currentLambdas, rudeEdits) = (newMethodDef.HasBody && newMethodDef.GetBody(Context) is { } body) ?
                        (body.LambdaDebugInfo, body.OrderedLambdaRuntimeRudeEdits) :
                        (ImmutableArray<EncLambdaInfo>.Empty, ImmutableArray<LambdaRuntimeRudeEditInfo>.Empty);
 
                    addDeletedClosureMethods(oldMethod, currentLambdas, rudeEdits);
                }
 
                void addDeletedClosureMethods(IMethodSymbolInternal oldMethod, ImmutableArray<EncLambdaInfo> currentLambdas, ImmutableArray<LambdaRuntimeRudeEditInfo> orderedLambdaRuntimeRudeEdits)
                {
                    foreach (var (lambdaId, deletedClosureMethod) in _definitionMap.GetDeletedSynthesizedMethods(oldMethod, currentLambdas))
                    {
                        var rudeEditIndex = orderedLambdaRuntimeRudeEdits.BinarySearch(lambdaId, static (rudeEdit, lambdaId) => rudeEdit.LambdaId.CompareTo(lambdaId));
 
                        var il = (rudeEditIndex >= 0)
                            ? DeletedMethodBody.GetIL(Context, orderedLambdaRuntimeRudeEdits[rudeEditIndex].RudeEdit, isLambdaOrLocalFunction: true)
                            : lazyDeletedLambdaIL ??= DeletedMethodBody.GetIL(Context, rudeEdit: null, isLambdaOrLocalFunction: true);
 
                        if (deletedClosureMethod.MetadataToken != 0)
                        {
                            newMethodDefs.Add(new DeletedPEMethodDefinition(deletedClosureMethod, il));
                        }
                        else
                        {
                            var deletedClosureMethodDef = (IMethodDefinition)deletedClosureMethod.GetCciAdapter();
                            var deletedClosureMethodHandle = _definitionMap.GetPreviousMethodHandle(deletedClosureMethod);
                            newMethodDefs.Add(new DeletedSourceMethodDefinition(deletedClosureMethodDef, deletedClosureMethodHandle, il, _typesUsedByDeletedMembers));
                        }
                    }
                }
 
                if (newMethodDefs is not [])
                {
                    // Assign the deleted method and its parameters row ids in the delta metadata:
                    foreach (var newMethodDef in newMethodDefs)
                    {
                        _methodDefs.AddUpdated(newMethodDef);
                    }
 
                    _deletedTypeMembers.Add(typeDef, newMethodDefs.ToImmutable());
                }
 
                newMethodDefs.Free();
            }
 
            foreach (var propertyDef in typeDef.GetProperties(this.Context))
            {
                if (!_propertyMap.Contains(typeRowId))
                {
                    _propertyMap.Add(typeRowId);
                }
 
                var propertyChange = _changes.GetChangeForPossibleReAddedMember(propertyDef, DefinitionExistsInAnyPreviousGeneration);
                this.AddDefIfNecessary(_propertyDefs, propertyDef, propertyChange);
            }
 
            var implementingMethods = ArrayBuilder<int>.GetInstance();
 
            // First, visit all MethodImplementations and add to this.methodImplList.
            foreach (var methodImpl in typeDef.GetExplicitImplementationOverrides(Context))
            {
                var methodDef = (IMethodDefinition?)methodImpl.ImplementingMethod.AsDefinition(this.Context);
                RoslynDebug.AssertNotNull(methodDef);
 
                int methodDefRowId = _methodDefs.GetRowId(methodDef);
 
                // If there are N existing MethodImpl entries for this MethodDef,
                // those will be index:1, ..., index:N, so it's sufficient to check for index:1.
                var key = new MethodImplKey(methodDefRowId, index: 1);
                if (!_methodImpls.Contains(key))
                {
                    implementingMethods.Add(methodDefRowId);
                    this.methodImplList.Add(methodImpl);
                }
            }
 
            // Next, add placeholders to this.methodImpls for items added above.
            foreach (var methodDefIndex in implementingMethods)
            {
                int index = 1;
                while (true)
                {
                    var key = new MethodImplKey(methodDefIndex, index);
                    if (!_methodImpls.Contains(key))
                    {
                        _methodImpls.Add(key);
                        break;
                    }
 
                    index++;
                }
            }
 
            implementingMethods.Free();
        }
 
        private bool DefinitionExistsInAnyPreviousGeneration(ITypeDefinitionMember item) => item switch
        {
            IMethodDefinition methodDef => TryGetExistingMethodDefIndex(methodDef, out _),
            IPropertyDefinition propertyDef => TryGetExistingPropertyDefIndex(propertyDef, out _),
            IFieldDefinition fieldDef => TryGetExistingFieldDefIndex(fieldDef, out _),
            IEventDefinition eventDef => TryGetExistingEventDefIndex(eventDef, out _),
            _ => false,
        };
 
        private void CreateIndicesForMethod(IMethodDefinition methodDef, SymbolChange methodChange)
        {
            // Do not emit parameters of method deletions:
            Debug.Assert(!methodDef.IsEncDeleted);
 
            if (methodChange == SymbolChange.Added)
            {
                _firstParamRowMap.Add(GetMethodDefinitionHandle(methodDef), _parameterDefs.NextRowId);
                foreach (var paramDef in this.GetParametersToEmit(methodDef))
                {
                    _parameterDefs.Add(paramDef);
                    _parameterDefList.Add(paramDef, methodDef);
                }
            }
            else if (methodChange == SymbolChange.Updated)
            {
                // If we're re-emitting parameters for an existing method we need to find their original row numbers
                // and reuse them so the EnCLog, EnCMap and CustomAttributes tables refer to the right rows
 
                // Unfortunately we have to check the original metadata and deltas separately as nothing tracks the aggregate data
                // in a way that we can use
                var handle = GetMethodDefinitionHandle(methodDef);
                if (_previousGeneration.OriginalMetadata.MetadataReader.GetTableRowCount(TableIndex.MethodDef) >= MetadataTokens.GetRowNumber(handle))
                {
                    EmitParametersFromOriginalMetadata(methodDef, handle);
                }
                else
                {
                    EmitParametersFromDelta(methodDef, handle);
                }
            }
 
            if (methodChange == SymbolChange.Added)
            {
                if (methodDef.GenericParameterCount > 0)
                {
                    foreach (var typeParameter in methodDef.GenericParameters)
                    {
                        _genericParameters.Add(typeParameter);
                    }
                }
            }
        }
 
        private void EmitParametersFromOriginalMetadata(IMethodDefinition methodDef, MethodDefinitionHandle handle)
        {
            var def = _previousGeneration.OriginalMetadata.MetadataReader.GetMethodDefinition(handle);
 
            var parameters = def.GetParameters();
            var paramDefinitions = this.GetParametersToEmit(methodDef);
            int i = 0;
            foreach (var param in parameters)
            {
                var paramDef = paramDefinitions[i];
                _parameterDefs.AddUpdated(paramDef);
                _existingParameterDefs.Add(paramDef, MetadataTokens.GetRowNumber(param));
                _parameterDefList.Add(paramDef, methodDef);
                i++;
            }
        }
 
        private void EmitParametersFromDelta(IMethodDefinition methodDef, MethodDefinitionHandle handle)
        {
            var ok = _previousGeneration.FirstParamRowMap.TryGetValue(handle, out var firstRowId);
            Debug.Assert(ok);
 
            foreach (var paramDef in GetParametersToEmit(methodDef))
            {
                _parameterDefs.AddUpdated(paramDef);
                _existingParameterDefs.Add(paramDef, firstRowId++);
                _parameterDefList.Add(paramDef, methodDef);
            }
        }
 
        private bool AddDefIfNecessary<T>(DefinitionIndex<T> defIndex, T def, SymbolChange change)
            where T : class, IDefinition
        {
            switch (change)
            {
                case SymbolChange.Added:
                    defIndex.Add(def);
                    return true;
                case SymbolChange.Updated:
                    defIndex.AddUpdated(def);
                    return false;
                case SymbolChange.ContainsChanges:
                    Debug.Assert(def is INestedTypeDefinition or IPropertyDefinition or IEventDefinition);
                    return false;
                default:
                    // No changes to member or container.
                    return false;
            }
        }
 
        protected override ReferenceIndexer CreateReferenceVisitor()
        {
            return new DeltaReferenceIndexer(this);
        }
 
        protected override void ReportReferencesToAddedSymbols()
        {
            foreach (var typeRef in GetTypeRefs())
            {
                ReportReferencesToAddedSymbol(typeRef.GetInternalSymbol());
            }
 
            foreach (var memberRef in GetMemberRefs())
            {
                ReportReferencesToAddedSymbol(memberRef.GetInternalSymbol());
            }
        }
 
        private void ReportReferencesToAddedSymbol(ISymbolInternal? symbol)
        {
            if (symbol != null && _changes.IsAdded(symbol.GetISymbol()))
            {
                Context.Diagnostics.Add(messageProvider.CreateDiagnostic(
                    messageProvider.ERR_EncReferenceToAddedMember,
                    GetSymbolLocation(symbol),
                    symbol.Name,
                    symbol.ContainingAssembly.Name));
            }
        }
 
        protected override StandaloneSignatureHandle SerializeLocalVariablesSignature(IMethodBody body)
        {
            StandaloneSignatureHandle localSignatureHandle;
            var localVariables = body.LocalVariables;
            var encInfos = ArrayBuilder<EncLocalInfo>.GetInstance();
 
            if (localVariables.Length > 0)
            {
                var writer = PooledBlobBuilder.GetInstance();
                var encoder = new BlobEncoder(writer).LocalVariableSignature(localVariables.Length);
 
                foreach (ILocalDefinition local in localVariables)
                {
                    var signature = local.Signature;
                    if (signature == null)
                    {
                        int start = writer.Count;
                        SerializeLocalVariableType(encoder.AddVariable(), local);
                        signature = writer.ToArray(start, writer.Count - start);
                    }
                    else
                    {
                        writer.WriteBytes(signature);
                    }
 
                    encInfos.Add(CreateEncLocalInfo(local, signature));
                }
 
                BlobHandle blobIndex = metadata.GetOrAddBlob(writer);
 
                localSignatureHandle = GetOrAddStandaloneSignatureHandle(blobIndex);
                writer.Free();
            }
            else
            {
                localSignatureHandle = default;
            }
 
            var info = new AddedOrChangedMethodInfo(
                body.MethodId,
                encInfos.ToImmutable(),
                body.LambdaDebugInfo,
                body.ClosureDebugInfo,
                body.StateMachineTypeName,
                body.StateMachineHoistedLocalSlots,
                body.StateMachineAwaiterSlots,
                body.StateMachineStatesDebugInfo);
 
            _addedOrChangedMethods.Add(body.MethodDefinition, info);
 
            encInfos.Free();
            return localSignatureHandle;
        }
 
        private EncLocalInfo CreateEncLocalInfo(ILocalDefinition localDef, byte[] signature)
        {
            if (localDef.SlotInfo.Id.IsNone)
            {
                return new EncLocalInfo(signature);
            }
 
            // local type is already translated, but not recursively
            ITypeReference translatedType = localDef.Type;
            if (translatedType.GetInternalSymbol() is ITypeSymbolInternal typeSymbol)
            {
                translatedType = Context.Module.EncTranslateType(typeSymbol, Context.Diagnostics);
            }
 
            return new EncLocalInfo(localDef.SlotInfo, translatedType, localDef.Constraints, signature);
        }
 
        protected override void AddCustomAttributesToTable(EntityHandle parentHandle, IEnumerable<ICustomAttribute> attributes)
        {
            // Defer adding custom attributes to the metadata table, so that we can order them by parent handle.
            // We can't sort the Custom Attribute table after the fact, like we do with other metadata tables, since 
            // deleted attributes have their parent handle set to nil and thus ordering by parent wouldn't be possible.
 
            _deferredCustomAttributes.Add((parentHandle, attributes.GetEnumerator()));
        }
 
        protected override void FinalizeCustomAttributeTableRows()
        {
            base.FinalizeCustomAttributeTableRows();
 
            if (_deferredCustomAttributes.Count == 0)
            {
                return;
            }
 
            // When serializing EnC delta the entries added to Custom Attribute are not sorted, they will stay in the order in which they are added to the table.
            // Therefore, the final (aggregate) row ids of newly added custom attribute entries will follow the max row id of custom attribute entries
            // added since the initial generation or the number of attributes in the initial generation if no attribute has been added since.
            int lastCustomAttributeRowId = _previousGeneration.CustomAttributesAdded.Count > 0
                ? _previousGeneration.CustomAttributesAdded.Max(static entry => entry.Value[^1])
                : _previousGeneration.OriginalMetadata.MetadataReader.GetTableRowCount(TableIndex.CustomAttribute);
 
            // We emit all attributes that each parent entity has defined in the current generation.
            // These will replace any attributes emitted in the original metadata as well as any previous generation.
            // If the number of attributes the entity has in the current generation is less then the total number of attributes
            // emitted previously the remaining custom attribute entries are zeroed out.
            // 
            // We generate updates to Custom Attribute table in 3 steps in order to ensure the correct ordering.
            // Each step emits Custom Attribute table rows and the corresponding EnC Map rows for all updated entities.
 
            _deferredCustomAttributes.Sort((x, y) =>
            {
                if (x.parentHandle == y.parentHandle)
                {
                    return 0;
                }
 
                int xOrdinal = MetadataTokens.GetRowNumber(_previousGeneration.OriginalMetadata.MetadataReader.GetCustomAttributes(x.parentHandle).FirstOrDefault());
                int yOrdinal = MetadataTokens.GetRowNumber(_previousGeneration.OriginalMetadata.MetadataReader.GetCustomAttributes(y.parentHandle).FirstOrDefault());
 
                // order entities with no attributes in original metadata after those who have some:
                if (xOrdinal == 0) xOrdinal = int.MaxValue;
                if (yOrdinal == 0) yOrdinal = int.MaxValue;
 
                int result = xOrdinal.CompareTo(yOrdinal);
                if (result != 0)
                {
                    return result;
                }
 
                // distinct entities can't compare equal if they have any attributes in original metadata:
                Debug.Assert(xOrdinal == int.MaxValue && yOrdinal == int.MaxValue);
 
                // order entities with no attributes added in previous generations after those who have some:
                xOrdinal = _previousGeneration.CustomAttributesAdded.TryGetValue(x.parentHandle, out var rowIds) ? rowIds[0] : int.MaxValue;
                yOrdinal = _previousGeneration.CustomAttributesAdded.TryGetValue(y.parentHandle, out rowIds) ? rowIds[0] : int.MaxValue;
 
                result = xOrdinal.CompareTo(yOrdinal);
                if (result != 0)
                {
                    return result;
                }
 
                // distinct entities can't compare equal if they have any attributes added in previous generations:
                Debug.Assert(xOrdinal == int.MaxValue && yOrdinal == int.MaxValue);
 
                return HandleComparer.Default.Compare(x.parentHandle, y.parentHandle);
            });
 
            // Step 1: add rows for all attributes defined in the original metadata.
 
            foreach (var (parentHandle, attributeEnumerator) in _deferredCustomAttributes)
            {
                var originalCustomAttributes = _previousGeneration.OriginalMetadata.MetadataReader.GetCustomAttributes(parentHandle);
                foreach (var handle in originalCustomAttributes)
                {
                    _customAttributeRowIds.Add(MetadataTokens.GetRowNumber(handle));
                }
 
                Debug.Assert(_customAttributeRowIds.IsSorted());
 
                addWithCap(parentHandle, attributeEnumerator, originalCustomAttributes.Count);
            }
 
            // Step 2: adds rows for attributes that were added in any previous generation.
 
            foreach (var (parentHandle, attributeEnumerator) in _deferredCustomAttributes)
            {
                var previouslyAddedRowIds = _previousGeneration.CustomAttributesAdded.TryGetValue(parentHandle, out var rowIds) ? rowIds : ImmutableArray<int>.Empty;
                _customAttributeRowIds.AddRange(previouslyAddedRowIds);
 
                Debug.Assert(_customAttributeRowIds.IsSorted());
 
                addWithCap(parentHandle, attributeEnumerator, previouslyAddedRowIds.Length);
            }
 
            // Step 3: adds rows for new attributes added in this generation.
 
            foreach (var (parentHandle, attributeEnumerator) in _deferredCustomAttributes)
            {
                int previousCustomAttributeRowIdsCount = _customAttributeRowIds.Count;
                while (attributeEnumerator.MoveNext())
                {
                    if (AddCustomAttributeToTable(parentHandle, attributeEnumerator.Current))
                    {
                        lastCustomAttributeRowId++;
                        _customAttributeRowIds.Add(lastCustomAttributeRowId);
                    }
                }
 
                if (_customAttributeRowIds.Count > previousCustomAttributeRowIdsCount)
                {
                    var previouslyAddedRowIds = _previousGeneration.CustomAttributesAdded.TryGetValue(parentHandle, out var rowIds) ? rowIds : ImmutableArray<int>.Empty;
                    _customAttributesAdded.Add(parentHandle, previouslyAddedRowIds.AddRange(_customAttributeRowIds.Skip(previousCustomAttributeRowIdsCount)));
                }
            }
 
            void addWithCap(EntityHandle parentHandle, IEnumerator<ICustomAttribute> attributeEnumerator, int limit)
            {
                int emittedAttributeCount = 0;
                while (emittedAttributeCount < limit && attributeEnumerator.MoveNext())
                {
                    if (AddCustomAttributeToTable(parentHandle, attributeEnumerator.Current))
                    {
                        emittedAttributeCount++;
                    }
                }
 
                int unusedRowCount = limit - emittedAttributeCount;
                if (unusedRowCount > 0)
                {
                    _ = MetadataTokens.TryGetTableIndex(parentHandle.Kind, out var parentTableIndex);
                    var deletedParentHandle = MetadataTokens.EntityHandle(parentTableIndex, 0);
                    var deletedMemberRefHandle = MetadataTokens.EntityHandle(TableIndex.MemberRef, 0);
 
                    for (int i = 0; i < unusedRowCount; i++)
                    {
                        metadata.AddCustomAttribute(deletedParentHandle, deletedMemberRefHandle, value: default);
                    }
                }
            }
        }
 
        public override void PopulateEncTables(ImmutableArray<int> typeSystemRowCounts)
        {
            Debug.Assert(typeSystemRowCounts[(int)TableIndex.EncLog] == 0);
            Debug.Assert(typeSystemRowCounts[(int)TableIndex.EncMap] == 0);
 
            var paramEncMapRows = ArrayBuilder<int>.GetInstance();
 
            PopulateEncLogTableRows(typeSystemRowCounts, paramEncMapRows);
            PopulateEncMapTableRows(typeSystemRowCounts, paramEncMapRows);
 
            _customAttributeRowIds.Free();
            paramEncMapRows.Free();
        }
 
        private void PopulateEncLogTableRows(ImmutableArray<int> rowCounts, ArrayBuilder<int> paramEncMapRows)
        {
            // The EncLog table is a log of all the operations needed
            // to update the previous metadata. That means all
            // new references must be added to the EncLog.
            var previousSizes = _previousGeneration.TableSizes;
            var deltaSizes = this.GetDeltaTableSizes(rowCounts);
 
            PopulateEncLogTableRows(TableIndex.AssemblyRef, previousSizes, deltaSizes);
            PopulateEncLogTableRows(TableIndex.ModuleRef, previousSizes, deltaSizes);
            PopulateEncLogTableRows(TableIndex.MemberRef, previousSizes, deltaSizes);
            PopulateEncLogTableRows(TableIndex.MethodSpec, previousSizes, deltaSizes);
            PopulateEncLogTableRows(TableIndex.TypeRef, previousSizes, deltaSizes);
            PopulateEncLogTableRows(TableIndex.TypeSpec, previousSizes, deltaSizes);
            PopulateEncLogTableRows(TableIndex.StandAloneSig, previousSizes, deltaSizes);
 
            PopulateEncLogTableRows(_typeDefs, TableIndex.TypeDef);
            PopulateEncLogTableRows(TableIndex.EventMap, previousSizes, deltaSizes);
            PopulateEncLogTableRows(TableIndex.PropertyMap, previousSizes, deltaSizes);
 
            PopulateEncLogTableEventsOrProperties(_eventDefs, TableIndex.Event, EditAndContinueOperation.AddEvent, _eventMap, TableIndex.EventMap);
            PopulateEncLogTableFieldsOrMethods(_fieldDefs, TableIndex.Field, EditAndContinueOperation.AddField);
            PopulateEncLogTableFieldsOrMethods(_methodDefs, TableIndex.MethodDef, EditAndContinueOperation.AddMethod);
            PopulateEncLogTableEventsOrProperties(_propertyDefs, TableIndex.Property, EditAndContinueOperation.AddProperty, _propertyMap, TableIndex.PropertyMap);
 
            PopulateEncLogTableParameters(paramEncMapRows);
 
            PopulateEncLogTableRows(TableIndex.Constant, previousSizes, deltaSizes);
 
            foreach (var rowId in _customAttributeRowIds)
            {
                metadata.AddEncLogEntry(
                    entity: MetadataTokens.CustomAttributeHandle(rowId),
                    code: EditAndContinueOperation.Default);
            }
 
            PopulateEncLogTableRows(TableIndex.DeclSecurity, previousSizes, deltaSizes);
            PopulateEncLogTableRows(TableIndex.ClassLayout, previousSizes, deltaSizes);
            PopulateEncLogTableRows(TableIndex.FieldLayout, previousSizes, deltaSizes);
            PopulateEncLogTableRows(TableIndex.MethodSemantics, previousSizes, deltaSizes);
            PopulateEncLogTableRows(TableIndex.MethodImpl, previousSizes, deltaSizes);
            PopulateEncLogTableRows(TableIndex.ImplMap, previousSizes, deltaSizes);
            PopulateEncLogTableRows(TableIndex.FieldRva, previousSizes, deltaSizes);
            PopulateEncLogTableRows(TableIndex.NestedClass, previousSizes, deltaSizes);
            PopulateEncLogTableRows(TableIndex.GenericParam, previousSizes, deltaSizes);
            PopulateEncLogTableRows(TableIndex.InterfaceImpl, previousSizes, deltaSizes);
            PopulateEncLogTableRows(TableIndex.GenericParamConstraint, previousSizes, deltaSizes);
        }
 
        private void PopulateEncLogTableEventsOrProperties<T>(
            DefinitionIndex<T> index,
            TableIndex table,
            EditAndContinueOperation addCode,
            EventOrPropertyMapIndex map,
            TableIndex mapTable)
            where T : class, ITypeDefinitionMember
        {
            foreach (var member in index.GetRows())
            {
                if (index.IsAddedNotChanged(member))
                {
                    int typeRowId = MetadataTokens.GetRowNumber(GetTypeDefinitionHandle(member.ContainingTypeDefinition));
                    int mapRowId = map.GetRowId(typeRowId);
 
                    metadata.AddEncLogEntry(
                        entity: MetadataTokens.Handle(mapTable, mapRowId),
                        code: addCode);
                }
 
                metadata.AddEncLogEntry(
                    entity: MetadataTokens.Handle(table, index.GetRowId(member)),
                    code: EditAndContinueOperation.Default);
            }
        }
 
        private void PopulateEncLogTableFieldsOrMethods<T>(
            DefinitionIndex<T> index,
            TableIndex tableIndex,
            EditAndContinueOperation addCode)
            where T : class, ITypeDefinitionMember
        {
            foreach (var member in index.GetRows())
            {
                if (index.IsAddedNotChanged(member))
                {
                    metadata.AddEncLogEntry(
                        entity: GetTypeDefinitionHandle(member.ContainingTypeDefinition),
                        code: addCode);
                }
 
                metadata.AddEncLogEntry(
                    entity: MetadataTokens.Handle(tableIndex, index.GetRowId(member)),
                    code: EditAndContinueOperation.Default);
            }
        }
 
        private void PopulateEncLogTableParameters(ArrayBuilder<int> paramEncMapRows)
        {
            var parameterFirstId = _parameterDefs.FirstRowId;
            int i = 0;
            foreach (var paramDef in GetParameterDefs())
            {
                var methodDef = _parameterDefList[paramDef];
 
                if (_methodDefs.IsAddedNotChanged(methodDef))
                {
                    // For parameters on new methods we emit AddParameter rows for the method too
                    paramEncMapRows.Add(parameterFirstId + i);
                    metadata.AddEncLogEntry(
                        entity: MetadataTokens.MethodDefinitionHandle(_methodDefs.GetRowId(methodDef)),
                        code: EditAndContinueOperation.AddParameter);
 
                    metadata.AddEncLogEntry(
                        entity: MetadataTokens.ParameterHandle(parameterFirstId + i),
                        code: EditAndContinueOperation.Default);
                    i++;
                }
                else
                {
                    // For previously emitted parameters we just update the Param row
                    var param = GetParameterHandle(paramDef);
                    paramEncMapRows.Add(MetadataTokens.GetRowNumber(param));
                    metadata.AddEncLogEntry(
                        entity: param,
                        code: EditAndContinueOperation.Default);
                }
            }
        }
 
        private void PopulateEncLogTableRows<T>(DefinitionIndex<T> index, TableIndex tableIndex)
            where T : class, IDefinition
        {
            foreach (var member in index.GetRows())
            {
                metadata.AddEncLogEntry(
                    entity: MetadataTokens.Handle(tableIndex, index.GetRowId(member)),
                    code: EditAndContinueOperation.Default);
            }
        }
 
        private void PopulateEncLogTableRows(TableIndex tableIndex, ImmutableArray<int> previousSizes, ImmutableArray<int> deltaSizes)
        {
            PopulateEncLogTableRows(tableIndex, previousSizes[(int)tableIndex] + 1, deltaSizes[(int)tableIndex]);
        }
 
        private void PopulateEncLogTableRows(TableIndex tableIndex, int firstRowId, int tokenCount)
        {
            for (int i = 0; i < tokenCount; i++)
            {
                metadata.AddEncLogEntry(
                    entity: MetadataTokens.Handle(tableIndex, firstRowId + i),
                    code: EditAndContinueOperation.Default);
            }
        }
 
        private void PopulateEncMapTableRows(ImmutableArray<int> rowCounts, ArrayBuilder<int> paramEncMapRows)
        {
            // The EncMap table maps from offset in each table in the delta
            // metadata to token. As such, the EncMap is a concatenated
            // list of all tokens in all tables from the delta sorted by table
            // and, within each table, sorted by row.
            var tokens = ArrayBuilder<EntityHandle>.GetInstance();
            var previousSizes = _previousGeneration.TableSizes;
            var deltaSizes = GetDeltaTableSizes(rowCounts);
 
            // Add tokens in order based on TableIndex. Rows for each table are assumed to have been already ordered.
            for (var tableIndex = (TableIndex)0; tableIndex <= TableIndex.GenericParamConstraint; tableIndex++)
            {
                switch (tableIndex)
                {
                    case TableIndex.TypeRef:
                    case TableIndex.InterfaceImpl:
                    case TableIndex.MemberRef:
                    case TableIndex.Constant:
                    case TableIndex.DeclSecurity:
                    case TableIndex.ClassLayout:
                    case TableIndex.FieldLayout:
                    case TableIndex.StandAloneSig:
                    case TableIndex.EventMap:
                    case TableIndex.PropertyMap:
                    case TableIndex.MethodSemantics:
                    case TableIndex.MethodImpl:
                    case TableIndex.ModuleRef:
                    case TableIndex.TypeSpec:
                    case TableIndex.ImplMap:
                    case TableIndex.FieldRva:
                    case TableIndex.NestedClass:
                    case TableIndex.GenericParam:
                    case TableIndex.AssemblyRef:
                    case TableIndex.MethodSpec:
                    case TableIndex.GenericParamConstraint:
                        AddReferencedTokens(tokens, tableIndex, previousSizes, deltaSizes);
                        break;
 
                    case TableIndex.TypeDef:
                        AddDefinitionTokens(tokens, tableIndex, _typeDefs);
                        break;
 
                    case TableIndex.Field:
                        AddDefinitionTokens(tokens, tableIndex, _fieldDefs);
                        break;
 
                    case TableIndex.MethodDef:
                        AddDefinitionTokens(tokens, tableIndex, _methodDefs);
                        break;
 
                    case TableIndex.Event:
                        AddDefinitionTokens(tokens, tableIndex, _eventDefs);
                        break;
 
                    case TableIndex.Property:
                        AddDefinitionTokens(tokens, tableIndex, _propertyDefs);
                        break;
 
                    case TableIndex.Param:
                        AddRowNumberTokens(tokens, TableIndex.Param, paramEncMapRows);
                        break;
 
                    case TableIndex.CustomAttribute:
                        AddRowNumberTokens(tokens, TableIndex.CustomAttribute, _customAttributeRowIds);
                        break;
                }
 
                // Should be sorted (strictly increasing):
                Debug.Assert(tokens.IsSorted(HandleComparer.Default));
            }
 
            foreach (var token in tokens)
            {
                metadata.AddEncMapEntry(token);
            }
 
            tokens.Free();
 
            // Populate Portable PDB EncMap table with MethodDebugInformation mapping,
            // which corresponds 1:1 to MethodDef mapping.
            if (_debugMetadataOpt != null)
            {
                var debugTokens = ArrayBuilder<EntityHandle>.GetInstance();
                AddDefinitionTokens(debugTokens, TableIndex.MethodDebugInformation, _methodDefs);
                debugTokens.Sort(HandleComparer.Default);
 
                // Should not be any duplicates.
                Debug.Assert(debugTokens.Distinct().Count() == debugTokens.Count);
 
                foreach (var token in debugTokens)
                {
                    _debugMetadataOpt.AddEncMapEntry(token);
                }
 
                debugTokens.Free();
            }
 
#if DEBUG
            // The following tables are either represented in the EncMap
            // or specifically ignored. The rest should be empty.
            var handledTables = new TableIndex[]
            {
                TableIndex.Module,
                TableIndex.TypeRef,
                TableIndex.TypeDef,
                TableIndex.Field,
                TableIndex.MethodDef,
                TableIndex.Param,
                TableIndex.MemberRef,
                TableIndex.Constant,
                TableIndex.CustomAttribute,
                TableIndex.DeclSecurity,
                TableIndex.ClassLayout,
                TableIndex.FieldLayout,
                TableIndex.StandAloneSig,
                TableIndex.EventMap,
                TableIndex.Event,
                TableIndex.PropertyMap,
                TableIndex.Property,
                TableIndex.MethodSemantics,
                TableIndex.MethodImpl,
                TableIndex.ModuleRef,
                TableIndex.TypeSpec,
                TableIndex.ImplMap,
                // FieldRva is not needed since we do not emit fields with explicit mapping during EnC.
                // https://github.com/dotnet/roslyn/issues/69480
                //TableIndex.FieldRva,
                TableIndex.EncLog,
                TableIndex.EncMap,
                TableIndex.Assembly,
                TableIndex.AssemblyRef,
                TableIndex.MethodSpec,
                TableIndex.NestedClass,
                TableIndex.GenericParam,
                TableIndex.InterfaceImpl,
                TableIndex.GenericParamConstraint,
            };
 
            for (int i = 0; i < rowCounts.Length; i++)
            {
                if (handledTables.Contains((TableIndex)i))
                {
                    continue;
                }
 
                Debug.Assert(rowCounts[i] == 0);
            }
#endif
        }
 
        private static void AddReferencedTokens(
            ArrayBuilder<EntityHandle> builder,
            TableIndex tableIndex,
            ImmutableArray<int> previousSizes,
            ImmutableArray<int> deltaSizes)
        {
            AddReferencedTokens(builder, tableIndex, previousSizes[(int)tableIndex] + 1, deltaSizes[(int)tableIndex]);
        }
 
        private static void AddReferencedTokens(ArrayBuilder<EntityHandle> tokens, TableIndex tableIndex, int firstRowId, int nTokens)
        {
            for (int i = 0; i < nTokens; i++)
            {
                tokens.Add(MetadataTokens.Handle(tableIndex, firstRowId + i));
            }
        }
 
        private static void AddDefinitionTokens<T>(ArrayBuilder<EntityHandle> tokens, TableIndex tableIndex, DefinitionIndex<T> index)
            where T : class, IDefinition
        {
            foreach (var member in index.GetRows())
            {
                tokens.Add(MetadataTokens.Handle(tableIndex, index.GetRowId(member)));
            }
        }
 
        private static void AddRowNumberTokens(ArrayBuilder<EntityHandle> tokens, TableIndex tableIndex, ArrayBuilder<int> rowNumbers)
        {
            foreach (var row in rowNumbers)
            {
                tokens.Add(MetadataTokens.Handle(tableIndex, row));
            }
        }
 
        protected override void PopulateEventMapTableRows()
        {
            foreach (var typeId in _eventMap.GetRows())
            {
                metadata.AddEventMap(
                    declaringType: MetadataTokens.TypeDefinitionHandle(typeId),
                    eventList: MetadataTokens.EventDefinitionHandle(_eventMap.GetRowId(typeId)));
            }
        }
 
        protected override void PopulatePropertyMapTableRows()
        {
            foreach (var typeId in _propertyMap.GetRows())
            {
                metadata.AddPropertyMap(
                    declaringType: MetadataTokens.TypeDefinitionHandle(typeId),
                    propertyList: MetadataTokens.PropertyDefinitionHandle(_propertyMap.GetRowId(typeId)));
            }
        }
 
        private abstract class DefinitionIndexBase<T>
            where T : notnull
        {
            protected readonly Dictionary<T, int> added; // Definitions added in this generation.
            protected readonly List<T> rows; // Rows in this generation, containing adds and updates.
            private readonly int _firstRowId; // First row in this generation.
            private bool _frozen;
 
            public DefinitionIndexBase(int lastRowId, IEqualityComparer<T>? comparer = null)
            {
                this.added = new Dictionary<T, int>(comparer);
                this.rows = new List<T>();
                _firstRowId = lastRowId + 1;
            }
 
            public abstract bool TryGetRowId(T item, out int rowId);
 
            public int GetRowId(T item)
            {
                bool containsItem = TryGetRowId(item, out int rowId);
 
                // Fails if we are attempting to make a change that should have been reported as rude,
                // e.g. the corresponding definitions type don't match, etc.
                if (!containsItem || rowId == 0)
                {
                    throw ExceptionUtilities.UnexpectedValue(item);
                }
 
                return rowId;
            }
 
            public bool Contains(T item)
                => TryGetRowId(item, out _);
 
            // A method rather than a property since it freezes the table.
            public IReadOnlyDictionary<T, int> GetAdded()
            {
                this.Freeze();
                return this.added;
            }
 
            // A method rather than a property since it freezes the table.
            public IReadOnlyList<T> GetRows()
            {
                this.Freeze();
                return this.rows;
            }
 
            public int FirstRowId
            {
                get { return _firstRowId; }
            }
 
            public int NextRowId
            {
                get { return this.added.Count + _firstRowId; }
            }
 
            public bool IsFrozen
            {
                get { return _frozen; }
            }
 
            protected virtual void OnFrozen()
            {
#if DEBUG
                // Verify the rows are sorted.
                int prev = 0;
                foreach (var row in this.rows)
                {
                    int next = this.added[row];
                    Debug.Assert(prev < next);
                    prev = next;
                }
#endif
            }
 
            private void Freeze()
            {
                if (!_frozen)
                {
                    _frozen = true;
                    this.OnFrozen();
                }
            }
        }
 
        private sealed class DefinitionIndex<T> : DefinitionIndexBase<T> where T : class, IDefinition
        {
            public delegate bool TryGetExistingIndex(T item, out int index);
 
            private readonly TryGetExistingIndex _tryGetExistingIndex;
 
            // Map of row id to def for all defs. This could be an array indexed
            // by row id but the array could be large and sparsely populated
            // if there are many defs in the previous generation but few
            // references to those defs in the current generation.
            private readonly Dictionary<int, T> _map;
 
            public DefinitionIndex(TryGetExistingIndex tryGetExistingIndex, int lastRowId)
                : base(lastRowId, ReferenceEqualityComparer.Instance)
            {
                _tryGetExistingIndex = tryGetExistingIndex;
                _map = new Dictionary<int, T>();
            }
 
            public override bool TryGetRowId(T item, out int index)
            {
                if (this.added.TryGetValue(item, out index))
                {
                    return true;
                }
 
                if (_tryGetExistingIndex(item, out index))
                {
#if DEBUG
                    // We expect that either we couldn't find the item in the map, because its new, or if we
                    // found it, we found the same one (ie, no item representing the same item is there twice),
                    // or it represents a deleted type. The deleted type, since we create it during emit, will
                    // never equal the original symbol that it wraps, even though it represents the same type,
                    // because the map uses reference equality.
                    Debug.Assert(!_map.TryGetValue(index, out var other) || ((object)other == (object)item) || other is DeletedSourceTypeDefinition || item is DeletedSourceTypeDefinition);
#endif
 
                    _map[index] = item;
                    return true;
                }
 
                return false;
            }
 
            public T GetDefinition(int rowId)
                => _map[rowId];
 
            public void Add(T item)
            {
                Debug.Assert(!this.IsFrozen);
 
                int index = this.NextRowId;
                this.added.Add(item, index);
                _map[index] = item;
                this.rows.Add(item);
            }
 
            /// <summary>
            /// Add an item from a previous generation
            /// that has been updated in this generation.
            /// </summary>
            public void AddUpdated(T item)
            {
                Debug.Assert(!this.IsFrozen);
                this.rows.Add(item);
            }
 
            public bool IsAddedNotChanged(T item)
                => added.ContainsKey(item);
 
            protected override void OnFrozen()
                => rows.Sort((x, y) => GetRowId(x).CompareTo(GetRowId(y)));
        }
 
        private bool TryGetExistingTypeDefIndex(ITypeDefinition item, out int index)
        {
            if (_previousGeneration.TypesAdded.TryGetValue(item, out index))
            {
                return true;
            }
 
            var handle = _definitionMap.GetInitialMetadataHandle(item);
            index = MetadataTokens.GetRowNumber(handle);
            return !handle.IsNil;
        }
 
        private bool TryGetExistingEventDefIndex(IEventDefinition item, out int index)
        {
            if (_previousGeneration.EventsAdded.TryGetValue(item, out index))
            {
                return true;
            }
 
            var handle = _definitionMap.GetInitialMetadataHandle(item);
            index = MetadataTokens.GetRowNumber(handle);
            return !handle.IsNil;
        }
 
        private bool TryGetExistingFieldDefIndex(IFieldDefinition item, out int index)
        {
            if (_previousGeneration.FieldsAdded.TryGetValue(item, out index))
            {
                return true;
            }
 
            var handle = _definitionMap.GetInitialMetadataHandle(item);
            index = MetadataTokens.GetRowNumber(handle);
            return !handle.IsNil;
        }
 
        private bool TryGetExistingMethodDefIndex(IMethodDefinition item, out int index)
        {
            if (item is IDeletedMethodDefinition { MetadataHandle: var deletedHandle })
            {
                index = MetadataTokens.GetRowNumber(deletedHandle);
                Debug.Assert(index > 0);
                return true;
            }
 
            if (_previousGeneration.MethodsAdded.TryGetValue(item, out index))
            {
                return true;
            }
 
            var handle = _definitionMap.GetInitialMetadataHandle(item);
            index = MetadataTokens.GetRowNumber(handle);
            return !handle.IsNil;
        }
 
        private bool TryGetExistingPropertyDefIndex(IPropertyDefinition item, out int index)
        {
            if (_previousGeneration.PropertiesAdded.TryGetValue(item, out index))
            {
                return true;
            }
 
            var handle = _definitionMap.GetInitialMetadataHandle(item);
            index = MetadataTokens.GetRowNumber(handle);
            return !handle.IsNil;
        }
 
        private bool TryGetExistingParameterDefIndex(IParameterDefinition item, out int index)
        {
            return _existingParameterDefs.TryGetValue(item, out index);
        }
 
        private bool TryGetExistingEventMapIndex(int item, out int index)
        {
            if (_previousGeneration.EventMapAdded.TryGetValue(item, out index))
            {
                return true;
            }
 
            if (_previousGeneration.TypeToEventMap.TryGetValue(item, out index))
            {
                return true;
            }
 
            index = 0;
            return false;
        }
 
        private bool TryGetExistingPropertyMapIndex(int item, out int index)
        {
            if (_previousGeneration.PropertyMapAdded.TryGetValue(item, out index))
            {
                return true;
            }
 
            if (_previousGeneration.TypeToPropertyMap.TryGetValue(item, out index))
            {
                return true;
            }
 
            index = 0;
            return false;
        }
 
        private bool TryGetExistingMethodImplIndex(MethodImplKey item, out int index)
        {
            if (_previousGeneration.MethodImplsAdded.TryGetValue(item, out index))
            {
                return true;
            }
 
            if (_previousGeneration.MethodImpls.TryGetValue(item, out index))
            {
                return true;
            }
 
            index = 0;
            return false;
        }
 
        private sealed class GenericParameterIndex : DefinitionIndexBase<IGenericParameter>
        {
            public GenericParameterIndex(int lastRowId)
                : base(lastRowId, ReferenceEqualityComparer.Instance)
            {
            }
 
            public override bool TryGetRowId(IGenericParameter item, out int index)
            {
                return this.added.TryGetValue(item, out index);
            }
 
            public void Add(IGenericParameter item)
            {
                Debug.Assert(!this.IsFrozen);
 
                int index = this.NextRowId;
                this.added.Add(item, index);
                this.rows.Add(item);
            }
        }
 
        private sealed class EventOrPropertyMapIndex : DefinitionIndexBase<int>
        {
            public delegate bool TryGetExistingIndex(int item, out int index);
 
            private readonly TryGetExistingIndex _tryGetExistingIndex;
 
            public EventOrPropertyMapIndex(TryGetExistingIndex tryGetExistingIndex, int lastRowId)
                : base(lastRowId)
            {
                _tryGetExistingIndex = tryGetExistingIndex;
            }
 
            public override bool TryGetRowId(int item, out int index)
            {
                if (this.added.TryGetValue(item, out index))
                {
                    return true;
                }
 
                if (_tryGetExistingIndex(item, out index))
                {
                    return true;
                }
 
                index = 0;
                return false;
            }
 
            public void Add(int item)
            {
                Debug.Assert(!this.IsFrozen);
 
                int index = this.NextRowId;
                this.added.Add(item, index);
                this.rows.Add(item);
            }
        }
 
        private sealed class MethodImplIndex : DefinitionIndexBase<MethodImplKey>
        {
            private readonly DeltaMetadataWriter _writer;
 
            public MethodImplIndex(DeltaMetadataWriter writer, int lastRowId)
                : base(lastRowId)
            {
                _writer = writer;
            }
 
            public override bool TryGetRowId(MethodImplKey item, out int index)
            {
                if (this.added.TryGetValue(item, out index))
                {
                    return true;
                }
 
                if (_writer.TryGetExistingMethodImplIndex(item, out index))
                {
                    return true;
                }
 
                index = 0;
                return false;
            }
 
            public void Add(MethodImplKey item)
            {
                Debug.Assert(!this.IsFrozen);
 
                int index = this.NextRowId;
                this.added.Add(item, index);
                this.rows.Add(item);
            }
        }
 
        private sealed class DeltaReferenceIndexer : ReferenceIndexer
        {
            private readonly SymbolChanges _changes;
            private readonly IReadOnlyDictionary<ITypeDefinition, ImmutableArray<IMethodDefinition>> _deletedTypeMembers;
 
            public DeltaReferenceIndexer(DeltaMetadataWriter writer)
                : base(writer)
            {
                _changes = writer._changes;
                _deletedTypeMembers = writer._deletedTypeMembers;
            }
 
            public override void Visit(CommonPEModuleBuilder module)
            {
                Visit(module.GetTopLevelTypeDefinitions(metadataWriter.Context));
            }
 
            public override void Visit(IEventDefinition eventDefinition)
            {
                Debug.Assert(this.ShouldVisit(eventDefinition));
                base.Visit(eventDefinition);
            }
 
            public override void Visit(IFieldDefinition fieldDefinition)
            {
                Debug.Assert(this.ShouldVisit(fieldDefinition));
                base.Visit(fieldDefinition);
            }
 
            public override void Visit(ILocalDefinition localDefinition)
            {
                if (localDefinition.Signature == null)
                {
                    base.Visit(localDefinition);
                }
            }
 
            public override void Visit(IMethodDefinition method)
            {
                Debug.Assert(this.ShouldVisit(method));
                base.Visit(method);
            }
 
            public override void Visit(Cci.MethodImplementation methodImplementation)
            {
                // Unless the implementing method was added,
                // the method implementation already exists.
                var methodDef = (IMethodDefinition?)methodImplementation.ImplementingMethod.AsDefinition(this.Context);
                RoslynDebug.AssertNotNull(methodDef);
 
                if (_changes.GetChange(methodDef) == SymbolChange.Added)
                {
                    base.Visit(methodImplementation);
                }
            }
 
            public override void Visit(INamespaceTypeDefinition namespaceTypeDefinition)
            {
                Debug.Assert(this.ShouldVisit(namespaceTypeDefinition));
                base.Visit(namespaceTypeDefinition);
            }
 
            public override void Visit(INestedTypeDefinition nestedTypeDefinition)
            {
                Debug.Assert(this.ShouldVisit(nestedTypeDefinition));
                base.Visit(nestedTypeDefinition);
            }
 
            public override void Visit(IPropertyDefinition propertyDefinition)
            {
                Debug.Assert(this.ShouldVisit(propertyDefinition));
                base.Visit(propertyDefinition);
            }
 
            public override void Visit(ITypeDefinition typeDefinition)
            {
                if (this.ShouldVisit(typeDefinition))
                {
                    base.Visit(typeDefinition);
 
                    // We need to visit deleted members to ensure their parameters and bodies are visited:
                    if (_deletedTypeMembers.TryGetValue(typeDefinition, out var deletedMembers))
                    {
                        this.Visit(deletedMembers);
                    }
                }
            }
 
            public override void Visit(ITypeDefinitionMember typeMember)
            {
                if (this.ShouldVisit(typeMember))
                {
                    base.Visit(typeMember);
                }
            }
 
            private bool ShouldVisit(IDefinition def)
            {
                return def.IsEncDeleted || _changes.GetChange(def) != SymbolChange.None;
            }
        }
    }
}