File: Emit\EditAndContinue\EmitBaseline.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.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using Microsoft.CodeAnalysis.Collections;
using Microsoft.CodeAnalysis.Symbols;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.Emit
{
    // A MethodImpl entry is a pair of implementing method and implemented
    // method. However, the implemented method is a MemberRef rather
    // than a MethodDef (e.g.: I<int>.M) and currently we are not mapping
    // MemberRefs between generations so it's not possible to track the
    // implemented method. Instead, recognizing that we do not support
    // changes to the set of implemented methods for a particular MethodDef,
    // and that we do not use the implementing methods anywhere, it's
    // sufficient to track a pair of implementing method and index.
    internal readonly struct MethodImplKey : IEquatable<MethodImplKey>
    {
        internal MethodImplKey(int implementingMethod, int index)
        {
            Debug.Assert(implementingMethod > 0);
            Debug.Assert(index > 0);
            this.ImplementingMethod = implementingMethod;
            this.Index = index;
        }
 
        internal readonly int ImplementingMethod;
        internal readonly int Index;
 
        public override bool Equals(object? obj)
        {
            return obj is MethodImplKey && Equals((MethodImplKey)obj);
        }
 
        public bool Equals(MethodImplKey other)
        {
            return this.ImplementingMethod == other.ImplementingMethod &&
                this.Index == other.Index;
        }
 
        public override int GetHashCode()
        {
            return Hash.Combine(this.ImplementingMethod, this.Index);
        }
    }
 
    /// <summary>
    /// Represents a module from a previous compilation. Used in Edit and Continue
    /// to emit the differences in a subsequent compilation.
    /// </summary>
    public sealed class EmitBaseline
    {
        private static readonly ImmutableArray<int> s_emptyTableSizes = ImmutableArray.Create(new int[MetadataTokens.TableCount]);
 
        internal sealed class MetadataSymbols(
            SynthesizedTypeMaps synthesizedTypes,
            object metadataDecoder,
            ImmutableDictionary<AssemblyIdentity, AssemblyIdentity> assemblyReferenceIdentityMap)
        {
            public readonly SynthesizedTypeMaps SynthesizedTypes = synthesizedTypes;
            public readonly object MetadataDecoder = metadataDecoder;
 
            /// <summary>
            /// A map of the assembly identities of the baseline compilation to the identities of the original metadata AssemblyRefs.
            /// Only includes identities that differ between these two.
            /// </summary>
            public readonly ImmutableDictionary<AssemblyIdentity, AssemblyIdentity> AssemblyReferenceIdentityMap = assemblyReferenceIdentityMap;
        }
 
        [Obsolete("This overload is no longer supported", error: true)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public static EmitBaseline CreateInitialBaseline(ModuleMetadata module, Func<MethodDefinitionHandle, EditAndContinueMethodDebugInformation> debugInformationProvider)
            => throw new NotSupportedException();
 
        [Obsolete("This overload is no longer supported", error: true)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public static EmitBaseline CreateInitialBaseline(
            ModuleMetadata module,
            Func<MethodDefinitionHandle, EditAndContinueMethodDebugInformation> debugInformationProvider,
            Func<MethodDefinitionHandle, StandaloneSignatureHandle> localSignatureProvider,
            bool hasPortableDebugInformation)
            => throw new NotSupportedException();
 
        /// <summary>
        /// Creates an <see cref="EmitBaseline"/> from the metadata of the module before editing
        /// and from a function that maps from a method to an array of local names. 
        /// </summary>
        /// <param name="compilation">Initial <see cref="Compilation"/>.</param>
        /// <param name="module">The metadata of the module before editing.</param>
        /// <param name="debugInformationProvider">
        /// A function that for a method handle returns Edit and Continue debug information emitted by the compiler into the PDB.
        /// The function shall throw <see cref="InvalidDataException"/> if the debug information can't be read for the specified method.
        /// This exception and <see cref="IOException"/> are caught and converted to an emit diagnostic. Other exceptions are passed through.
        /// </param>
        /// <param name="localSignatureProvider">
        /// A function that for a method handle returns the signature of its local variables.
        /// The function shall throw <see cref="InvalidDataException"/> if the information can't be read for the specified method.
        /// This exception and <see cref="IOException"/> are caught and converted to an emit diagnostic. Other exceptions are passed through.
        /// </param>
        /// <param name="hasPortableDebugInformation">
        /// True if the baseline PDB is portable.
        /// </param>
        /// <returns>An <see cref="EmitBaseline"/> for the module.</returns>
        /// <remarks>
        /// Only the initial baseline is created using this method; subsequent baselines are created
        /// automatically when emitting the differences in subsequent compilations.
        /// 
        /// When an active method (one for which a frame is allocated on a stack) is updated the values of its local variables need to be preserved.
        /// The mapping of local variable names to their slots in the frame is not included in the metadata and thus needs to be provided by 
        /// <paramref name="debugInformationProvider"/>.
        /// 
        /// The <paramref name="debugInformationProvider"/> is only needed for the initial generation. The mapping for the subsequent generations
        /// is carried over through <see cref="EmitBaseline"/>. The compiler assigns slots to named local variables (including named temporary variables)
        /// it the order in which they appear in the source code. This property allows the compiler to reconstruct the local variable mapping 
        /// for the initial generation. A subsequent generation may add a new variable in between two variables of the previous generation. 
        /// Since the slots of the previous generation variables need to be preserved the only option is to add these new variables to the end.
        /// The slot ordering thus no longer matches the syntax ordering. It is therefore necessary to pass <see cref="EmitDifferenceResult.Baseline"/>
        /// to the next generation (rather than e.g. create new <see cref="EmitBaseline"/>s from scratch based on metadata produced by subsequent compilations).
        /// </remarks>
        /// <exception cref="ArgumentNullException"><paramref name="module"/> is null.</exception>
        /// <exception cref="ArgumentNullException"><paramref name="debugInformationProvider"/> is null.</exception>
        /// <exception cref="ArgumentNullException"><paramref name="localSignatureProvider"/> is null.</exception>
        /// <exception cref="IOException">Error reading module metadata.</exception>
        /// <exception cref="BadImageFormatException">Module metadata is invalid.</exception>
        /// <exception cref="ObjectDisposedException">Module has been disposed.</exception>
        public static EmitBaseline CreateInitialBaseline(
            Compilation compilation,
            ModuleMetadata module,
            Func<MethodDefinitionHandle, EditAndContinueMethodDebugInformation> debugInformationProvider,
            Func<MethodDefinitionHandle, StandaloneSignatureHandle> localSignatureProvider,
            bool hasPortableDebugInformation)
        {
            if (compilation == null)
            {
                throw new ArgumentNullException(nameof(compilation));
            }
 
            if (module == null)
            {
                throw new ArgumentNullException(nameof(module));
            }
 
            if (debugInformationProvider == null)
            {
                throw new ArgumentNullException(nameof(debugInformationProvider));
            }
 
            if (localSignatureProvider == null)
            {
                throw new ArgumentNullException(nameof(localSignatureProvider));
            }
 
            var reader = module.MetadataReader;
 
            return new EmitBaseline(
                null,
                module,
                compilation,
                moduleBuilder: null,
                moduleVersionId: module.GetModuleVersionId(),
                ordinal: 0,
                encId: default,
                hasPortablePdb: hasPortableDebugInformation,
                generationOrdinals: new Dictionary<Cci.IDefinition, int>(),
                typesAdded: new Dictionary<Cci.ITypeDefinition, int>(),
                eventsAdded: new Dictionary<Cci.IEventDefinition, int>(),
                fieldsAdded: new Dictionary<Cci.IFieldDefinition, int>(),
                methodsAdded: new Dictionary<Cci.IMethodDefinition, int>(),
                firstParamRowMap: new Dictionary<MethodDefinitionHandle, int>(),
                propertiesAdded: new Dictionary<Cci.IPropertyDefinition, int>(),
                eventMapAdded: new Dictionary<int, int>(),
                propertyMapAdded: new Dictionary<int, int>(),
                methodImplsAdded: new Dictionary<MethodImplKey, int>(),
                customAttributesAdded: new Dictionary<EntityHandle, ImmutableArray<int>>(),
                tableEntriesAdded: s_emptyTableSizes,
                blobStreamLengthAdded: 0,
                stringStreamLengthAdded: 0,
                userStringStreamLengthAdded: 0,
                guidStreamLengthAdded: 0,
                synthesizedTypes: SynthesizedTypeMaps.Empty,
                synthesizedMembers: ImmutableSegmentedDictionary<ISymbolInternal, ImmutableArray<ISymbolInternal>>.Empty,
                deletedMembers: ImmutableSegmentedDictionary<ISymbolInternal, ImmutableArray<ISymbolInternal>>.Empty,
                methodsAddedOrChanged: new Dictionary<int, AddedOrChangedMethodInfo>(),
                debugInformationProvider: debugInformationProvider,
                localSignatureProvider: localSignatureProvider,
                typeToEventMap: CalculateTypeEventMap(reader),
                typeToPropertyMap: CalculateTypePropertyMap(reader),
                methodImpls: CalculateMethodImpls(reader));
        }
 
        internal EmitBaseline InitialBaseline { get; }
 
        /// <summary>
        /// The original metadata of the module.
        /// </summary>
        public ModuleMetadata OriginalMetadata { get; }
 
        // Symbols hydrated from the original metadata. Lazy since we don't know the language at the time the baseline is constructed.
        internal MetadataSymbols? LazyMetadataSymbols;
 
        internal readonly Compilation Compilation;
        internal readonly CommonPEModuleBuilder? PEModuleBuilder;
        internal readonly Guid ModuleVersionId;
        internal readonly bool HasPortablePdb;
 
        /// <summary>
        /// Metadata generation ordinal. Zero for
        /// full metadata and non-zero for delta.
        /// </summary>
        internal readonly int Ordinal;
 
        /// <summary>
        /// Unique Guid for this delta, or default(Guid)
        /// if full metadata.
        /// </summary>
        internal readonly Guid EncId;
 
        /// <summary>
        /// The latest generation number of each symbol added via <see cref="SemanticEditKind.Replace"/> edit.
        /// </summary>
        internal readonly IReadOnlyDictionary<Cci.IDefinition, int> GenerationOrdinals;
 
        internal readonly IReadOnlyDictionary<Cci.ITypeDefinition, int> TypesAdded;
        internal readonly IReadOnlyDictionary<Cci.IEventDefinition, int> EventsAdded;
        internal readonly IReadOnlyDictionary<Cci.IFieldDefinition, int> FieldsAdded;
        internal readonly IReadOnlyDictionary<Cci.IMethodDefinition, int> MethodsAdded;
        internal readonly IReadOnlyDictionary<MethodDefinitionHandle, int> FirstParamRowMap;
        internal readonly IReadOnlyDictionary<Cci.IPropertyDefinition, int> PropertiesAdded;
        internal readonly IReadOnlyDictionary<int, int> EventMapAdded;
        internal readonly IReadOnlyDictionary<int, int> PropertyMapAdded;
        internal readonly IReadOnlyDictionary<MethodImplKey, int> MethodImplsAdded;
 
        /// <summary>
        /// Maps a parent handle to a non-empty ordered array of row ids of custom attributes added since the initial baseline.
        /// </summary>
        internal readonly IReadOnlyDictionary<EntityHandle, ImmutableArray<int>> CustomAttributesAdded;
 
        internal readonly ImmutableArray<int> TableEntriesAdded;
 
        internal readonly int BlobStreamLengthAdded;
        internal readonly int StringStreamLengthAdded;
        internal readonly int UserStringStreamLengthAdded;
        internal readonly int GuidStreamLengthAdded;
 
        /// <summary>
        /// EnC metadata for methods added or updated since the initial generation, indexed by method row id.
        /// </summary>
        internal readonly IReadOnlyDictionary<int, AddedOrChangedMethodInfo> AddedOrChangedMethods;
 
        /// <summary>
        /// Reads EnC debug information of a method from the initial baseline PDB.
        /// The function shall throw <see cref="InvalidDataException"/> if the debug information can't be read for the specified method.
        /// This exception and <see cref="IOException"/> are caught and converted to an emit diagnostic. Other exceptions are passed through.
        /// The function shall return an empty <see cref="EditAndContinueMethodDebugInformation"/> if the method that corresponds to the specified handle
        /// has no debug information.
        /// </summary>
        internal readonly Func<MethodDefinitionHandle, EditAndContinueMethodDebugInformation> DebugInformationProvider;
 
        /// <summary>
        /// A function that for a method handle returns the signature of its local variables.
        /// The function shall throw <see cref="InvalidDataException"/> if the information can't be read for the specified method.
        /// This exception and <see cref="IOException"/> are caught and converted to an emit diagnostic. Other exceptions are passed through.
        /// The function shall return a nil <see cref="StandaloneSignatureHandle"/> if the method that corresponds to the specified handle
        /// has no local variables.
        /// </summary>
        internal readonly Func<MethodDefinitionHandle, StandaloneSignatureHandle> LocalSignatureProvider;
 
        internal readonly ImmutableArray<int> TableSizes;
        internal readonly IReadOnlyDictionary<int, int> TypeToEventMap;
        internal readonly IReadOnlyDictionary<int, int> TypeToPropertyMap;
        internal readonly IReadOnlyDictionary<MethodImplKey, int> MethodImpls;
        private readonly SynthesizedTypeMaps _synthesizedTypes;
        internal readonly IReadOnlyDictionary<ISymbolInternal, ImmutableArray<ISymbolInternal>> SynthesizedMembers;
        internal readonly IReadOnlyDictionary<ISymbolInternal, ImmutableArray<ISymbolInternal>> DeletedMembers;
 
        private EmitBaseline(
            EmitBaseline? initialBaseline,
            ModuleMetadata module,
            Compilation compilation,
            CommonPEModuleBuilder? moduleBuilder,
            Guid moduleVersionId,
            int ordinal,
            Guid encId,
            bool hasPortablePdb,
            IReadOnlyDictionary<Cci.IDefinition, int> generationOrdinals,
            IReadOnlyDictionary<Cci.ITypeDefinition, int> typesAdded,
            IReadOnlyDictionary<Cci.IEventDefinition, int> eventsAdded,
            IReadOnlyDictionary<Cci.IFieldDefinition, int> fieldsAdded,
            IReadOnlyDictionary<Cci.IMethodDefinition, int> methodsAdded,
            IReadOnlyDictionary<MethodDefinitionHandle, int> firstParamRowMap,
            IReadOnlyDictionary<Cci.IPropertyDefinition, int> propertiesAdded,
            IReadOnlyDictionary<int, int> eventMapAdded,
            IReadOnlyDictionary<int, int> propertyMapAdded,
            IReadOnlyDictionary<MethodImplKey, int> methodImplsAdded,
            IReadOnlyDictionary<EntityHandle, ImmutableArray<int>> customAttributesAdded,
            ImmutableArray<int> tableEntriesAdded,
            int blobStreamLengthAdded,
            int stringStreamLengthAdded,
            int userStringStreamLengthAdded,
            int guidStreamLengthAdded,
            SynthesizedTypeMaps synthesizedTypes,
            IReadOnlyDictionary<ISymbolInternal, ImmutableArray<ISymbolInternal>> synthesizedMembers,
            IReadOnlyDictionary<ISymbolInternal, ImmutableArray<ISymbolInternal>> deletedMembers,
            IReadOnlyDictionary<int, AddedOrChangedMethodInfo> methodsAddedOrChanged,
            Func<MethodDefinitionHandle, EditAndContinueMethodDebugInformation> debugInformationProvider,
            Func<MethodDefinitionHandle, StandaloneSignatureHandle> localSignatureProvider,
            IReadOnlyDictionary<int, int> typeToEventMap,
            IReadOnlyDictionary<int, int> typeToPropertyMap,
            IReadOnlyDictionary<MethodImplKey, int> methodImpls)
        {
            Debug.Assert(module != null);
            Debug.Assert(ordinal is 0 == (encId == default));
            Debug.Assert(ordinal is 0 == initialBaseline is null);
            Debug.Assert(synthesizedTypes.IsEmpty || ordinal > 0);
            Debug.Assert(encId != module.GetModuleVersionId());
            Debug.Assert(debugInformationProvider != null);
            Debug.Assert(localSignatureProvider != null);
            Debug.Assert(typeToEventMap != null);
            Debug.Assert(typeToPropertyMap != null);
            Debug.Assert(moduleVersionId != default);
            Debug.Assert(moduleVersionId == module.GetModuleVersionId());
            Debug.Assert(synthesizedMembers != null);
            Debug.Assert(deletedMembers != null);
 
            Debug.Assert(tableEntriesAdded.Length == MetadataTokens.TableCount);
 
            // The size of each table is the total number of entries added in all previous
            // generations after the initial generation. Depending on the table, some of the
            // entries may not be available in the current generation (say, a synthesized type
            // from a method that was not recompiled for instance)
            Debug.Assert(tableEntriesAdded[(int)TableIndex.TypeDef] >= typesAdded.Count);
            Debug.Assert(tableEntriesAdded[(int)TableIndex.Event] >= eventsAdded.Count);
            Debug.Assert(tableEntriesAdded[(int)TableIndex.Field] >= fieldsAdded.Count);
            Debug.Assert(tableEntriesAdded[(int)TableIndex.MethodDef] >= methodsAdded.Count);
            Debug.Assert(tableEntriesAdded[(int)TableIndex.Property] >= propertiesAdded.Count);
            Debug.Assert(tableEntriesAdded[(int)TableIndex.EventMap] >= eventMapAdded.Count);
            Debug.Assert(tableEntriesAdded[(int)TableIndex.PropertyMap] >= propertyMapAdded.Count);
 
            var reader = module.Module.MetadataReader;
 
            InitialBaseline = initialBaseline ?? this;
            OriginalMetadata = module;
            Compilation = compilation;
            PEModuleBuilder = moduleBuilder;
            ModuleVersionId = moduleVersionId;
            Ordinal = ordinal;
            EncId = encId;
            HasPortablePdb = hasPortablePdb;
 
            GenerationOrdinals = generationOrdinals;
            TypesAdded = typesAdded;
            EventsAdded = eventsAdded;
            FieldsAdded = fieldsAdded;
            MethodsAdded = methodsAdded;
            FirstParamRowMap = firstParamRowMap;
            PropertiesAdded = propertiesAdded;
            EventMapAdded = eventMapAdded;
            PropertyMapAdded = propertyMapAdded;
            MethodImplsAdded = methodImplsAdded;
            CustomAttributesAdded = customAttributesAdded;
            TableEntriesAdded = tableEntriesAdded;
            BlobStreamLengthAdded = blobStreamLengthAdded;
            StringStreamLengthAdded = stringStreamLengthAdded;
            UserStringStreamLengthAdded = userStringStreamLengthAdded;
            GuidStreamLengthAdded = guidStreamLengthAdded;
            _synthesizedTypes = synthesizedTypes;
            SynthesizedMembers = synthesizedMembers;
            DeletedMembers = deletedMembers;
            AddedOrChangedMethods = methodsAddedOrChanged;
 
            DebugInformationProvider = debugInformationProvider;
            LocalSignatureProvider = localSignatureProvider;
            TableSizes = CalculateTableSizes(reader, TableEntriesAdded);
            TypeToEventMap = typeToEventMap;
            TypeToPropertyMap = typeToPropertyMap;
            MethodImpls = methodImpls;
        }
 
        internal EmitBaseline With(
            Compilation compilation,
            CommonPEModuleBuilder moduleBuilder,
            int ordinal,
            Guid encId,
            IReadOnlyDictionary<Cci.IDefinition, int> generationOrdinals,
            IReadOnlyDictionary<Cci.ITypeDefinition, int> typesAdded,
            IReadOnlyDictionary<Cci.IEventDefinition, int> eventsAdded,
            IReadOnlyDictionary<Cci.IFieldDefinition, int> fieldsAdded,
            IReadOnlyDictionary<Cci.IMethodDefinition, int> methodsAdded,
            IReadOnlyDictionary<MethodDefinitionHandle, int> firstParamRowMap,
            IReadOnlyDictionary<Cci.IPropertyDefinition, int> propertiesAdded,
            IReadOnlyDictionary<int, int> eventMapAdded,
            IReadOnlyDictionary<int, int> propertyMapAdded,
            IReadOnlyDictionary<MethodImplKey, int> methodImplsAdded,
            IReadOnlyDictionary<EntityHandle, ImmutableArray<int>> customAttributesAdded,
            ImmutableArray<int> tableEntriesAdded,
            int blobStreamLengthAdded,
            int stringStreamLengthAdded,
            int userStringStreamLengthAdded,
            int guidStreamLengthAdded,
            SynthesizedTypeMaps synthesizedTypes,
            IReadOnlyDictionary<ISymbolInternal, ImmutableArray<ISymbolInternal>> synthesizedMembers,
            IReadOnlyDictionary<ISymbolInternal, ImmutableArray<ISymbolInternal>> deletedMembers,
            IReadOnlyDictionary<int, AddedOrChangedMethodInfo> addedOrChangedMethods,
            Func<MethodDefinitionHandle, EditAndContinueMethodDebugInformation> debugInformationProvider,
            Func<MethodDefinitionHandle, StandaloneSignatureHandle> localSignatureProvider)
        {
            Debug.Assert(synthesizedTypes.AnonymousTypes.Count >= _synthesizedTypes.AnonymousTypes.Count);
            Debug.Assert(synthesizedTypes.AnonymousDelegates.Count >= _synthesizedTypes.AnonymousDelegates.Count);
            Debug.Assert(synthesizedTypes.AnonymousDelegatesWithIndexedNames.Count >= _synthesizedTypes.AnonymousDelegatesWithIndexedNames.Count);
            Debug.Assert(customAttributesAdded.All(entry => !entry.Value.IsDefaultOrEmpty && entry.Value.IsSorted()));
 
            return new EmitBaseline(
                InitialBaseline,
                OriginalMetadata,
                compilation,
                moduleBuilder,
                ModuleVersionId,
                ordinal,
                encId,
                HasPortablePdb,
                generationOrdinals,
                typesAdded,
                eventsAdded,
                fieldsAdded,
                methodsAdded,
                firstParamRowMap,
                propertiesAdded,
                eventMapAdded,
                propertyMapAdded,
                methodImplsAdded,
                customAttributesAdded,
                tableEntriesAdded,
                blobStreamLengthAdded: blobStreamLengthAdded,
                stringStreamLengthAdded: stringStreamLengthAdded,
                userStringStreamLengthAdded: userStringStreamLengthAdded,
                guidStreamLengthAdded: guidStreamLengthAdded,
                synthesizedTypes: synthesizedTypes,
                synthesizedMembers: synthesizedMembers,
                deletedMembers: deletedMembers,
                methodsAddedOrChanged: addedOrChangedMethods,
                debugInformationProvider: debugInformationProvider,
                localSignatureProvider: localSignatureProvider,
                typeToEventMap: TypeToEventMap,
                typeToPropertyMap: TypeToPropertyMap,
                methodImpls: MethodImpls);
        }
 
        internal SynthesizedTypeMaps SynthesizedTypes
        {
            get
            {
                if (Ordinal > 0)
                {
                    return _synthesizedTypes;
                }
 
                Debug.Assert(LazyMetadataSymbols is object);
                return LazyMetadataSymbols.SynthesizedTypes;
            }
        }
 
        internal MetadataReader MetadataReader
            => OriginalMetadata.MetadataReader;
 
        internal int BlobStreamLength
            => BlobStreamLengthAdded + MetadataReader.GetHeapSize(HeapIndex.Blob);
 
        internal int StringStreamLength
            => StringStreamLengthAdded + MetadataReader.GetHeapSize(HeapIndex.String);
 
        internal int UserStringStreamLength
            => UserStringStreamLengthAdded + MetadataReader.GetHeapSize(HeapIndex.UserString);
 
        internal int GuidStreamLength
            => GuidStreamLengthAdded + MetadataReader.GetHeapSize(HeapIndex.Guid);
 
        private static ImmutableArray<int> CalculateTableSizes(MetadataReader reader, ImmutableArray<int> delta)
        {
            var sizes = new int[MetadataTokens.TableCount];
 
            for (int i = 0; i < sizes.Length; i++)
            {
                sizes[i] = reader.GetTableRowCount((TableIndex)i) + delta[i];
            }
 
            return ImmutableArray.Create(sizes);
        }
 
        private static Dictionary<int, int> CalculateTypePropertyMap(MetadataReader reader)
        {
            var result = new Dictionary<int, int>();
 
            int rowId = 1;
            foreach (var parentType in reader.GetTypesWithProperties())
            {
                Debug.Assert(!parentType.IsNil);
                result.Add(reader.GetRowNumber(parentType), rowId);
                rowId++;
            }
 
            return result;
        }
 
        private static Dictionary<int, int> CalculateTypeEventMap(MetadataReader reader)
        {
            var result = new Dictionary<int, int>();
 
            int rowId = 1;
            foreach (var parentType in reader.GetTypesWithEvents())
            {
                Debug.Assert(!parentType.IsNil);
                result.Add(reader.GetRowNumber(parentType), rowId);
                rowId++;
            }
 
            return result;
        }
 
        private static Dictionary<MethodImplKey, int> CalculateMethodImpls(MetadataReader reader)
        {
            var result = new Dictionary<MethodImplKey, int>();
            int n = reader.GetTableRowCount(TableIndex.MethodImpl);
            for (int row = 1; row <= n; row++)
            {
                var methodImpl = reader.GetMethodImplementation(MetadataTokens.MethodImplementationHandle(row));
                // Hold on to the implementing method def but use a simple
                // index for the implemented method ref token. (We do not map
                // member refs currently, and since we don't allow changes to
                // the set of methods a method def implements, the actual
                // tokens of the implemented methods are not needed.)
                int methodDefRow = MetadataTokens.GetRowNumber(methodImpl.MethodBody);
                int index = 1;
                while (true)
                {
                    var key = new MethodImplKey(methodDefRow, index);
                    if (!result.ContainsKey(key))
                    {
                        result.Add(key, row);
                        break;
                    }
                    index++;
                }
            }
            return result;
        }
 
        internal int GetNextAnonymousTypeIndex(bool fromDelegates = false)
        {
            int nextIndex = 0;
            foreach (var (typeKey, typeValue) in SynthesizedTypes.AnonymousTypes)
            {
                if (fromDelegates != typeKey.IsDelegate)
                {
                    continue;
                }
                int index = typeValue.UniqueIndex;
                if (index >= nextIndex)
                {
                    nextIndex = index + 1;
                }
            }
 
            return nextIndex;
        }
 
        internal int GetNextAnonymousDelegateIndex()
        {
            int nextIndex = 0;
            foreach (var (typeKey, typeValues) in SynthesizedTypes.AnonymousDelegatesWithIndexedNames)
            {
                foreach (var typeValue in typeValues)
                {
                    int index = typeValue.UniqueIndex;
                    if (index >= nextIndex)
                    {
                        nextIndex = index + 1;
                    }
                }
            }
 
            return nextIndex;
        }
    }
}