File: PEWriter\FullMetadataWriter.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.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Collections;
using Microsoft.CodeAnalysis.Emit;
using EmitContext = Microsoft.CodeAnalysis.Emit.EmitContext;
using ReferenceEqualityComparer = Roslyn.Utilities.ReferenceEqualityComparer;
 
namespace Microsoft.Cci
{
    internal sealed class FullMetadataWriter : MetadataWriter
    {
        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 DefinitionIndex<IGenericParameter> _genericParameters;
 
        private readonly Dictionary<ITypeDefinition, int> _fieldDefIndex;
        private readonly Dictionary<ITypeDefinition, int> _methodDefIndex;
        private readonly SegmentedDictionary<IMethodDefinition, int> _parameterListIndex;
 
        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;
 
        public static MetadataWriter Create(
            EmitContext context,
            CommonMessageProvider messageProvider,
            bool metadataOnly,
            bool deterministic,
            bool emitTestCoverageData,
            bool hasPdbStream,
            CancellationToken cancellationToken)
        {
            var builder = new MetadataBuilder();
            MetadataBuilder? debugBuilderOpt;
            switch (context.Module.DebugInformationFormat)
            {
                case DebugInformationFormat.PortablePdb:
                    debugBuilderOpt = hasPdbStream ? new MetadataBuilder() : null;
                    break;
 
                case DebugInformationFormat.Embedded:
                    debugBuilderOpt = metadataOnly ? null : new MetadataBuilder();
                    break;
 
                default:
                    debugBuilderOpt = null;
                    break;
            }
 
            var dynamicAnalysisDataWriterOpt = emitTestCoverageData ?
                new DynamicAnalysisDataWriter(context.Module.DebugDocumentCount, context.Module.HintNumberOfMethodDefinitions) :
                null;
 
            return new FullMetadataWriter(context, builder, debugBuilderOpt, dynamicAnalysisDataWriterOpt, messageProvider, metadataOnly, deterministic,
                emitTestCoverageData, cancellationToken);
        }
 
        private FullMetadataWriter(
            EmitContext context,
            MetadataBuilder builder,
            MetadataBuilder? debugBuilderOpt,
            DynamicAnalysisDataWriter? dynamicAnalysisDataWriterOpt,
            CommonMessageProvider messageProvider,
            bool metadataOnly,
            bool deterministic,
            bool emitTestCoverageData,
            CancellationToken cancellationToken)
            : base(builder, debugBuilderOpt, dynamicAnalysisDataWriterOpt, context, messageProvider, metadataOnly, deterministic,
                  emitTestCoverageData, cancellationToken)
        {
            // EDMAURER make some intelligent guesses for the initial sizes of these things.
            int numMethods = this.module.HintNumberOfMethodDefinitions;
            int numTypeDefsGuess = numMethods / 6;
            int numFieldDefsGuess = numTypeDefsGuess * 4;
            int numPropertyDefsGuess = numMethods / 4;
 
            _typeDefs = new DefinitionIndex<ITypeDefinition>(numTypeDefsGuess);
            _eventDefs = new DefinitionIndex<IEventDefinition>(0);
            _fieldDefs = new DefinitionIndex<IFieldDefinition>(numFieldDefsGuess);
            _methodDefs = new DefinitionIndex<IMethodDefinition>(numMethods);
            _propertyDefs = new DefinitionIndex<IPropertyDefinition>(numPropertyDefsGuess);
            _parameterDefs = new DefinitionIndex<IParameterDefinition>(numMethods);
            _genericParameters = new DefinitionIndex<IGenericParameter>(0);
 
            _fieldDefIndex = new Dictionary<ITypeDefinition, int>(numTypeDefsGuess, ReferenceEqualityComparer.Instance);
            _methodDefIndex = new Dictionary<ITypeDefinition, int>(numTypeDefsGuess, ReferenceEqualityComparer.Instance);
            _parameterListIndex = new SegmentedDictionary<IMethodDefinition, int>(numMethods, ReferenceEqualityComparer.Instance);
 
            _assemblyRefIndex = new HeapOrReferenceIndex<AssemblyIdentity>(this);
            _moduleRefIndex = new HeapOrReferenceIndex<string>(this);
            _memberRefIndex = new InstanceAndStructuralReferenceIndex<ITypeMemberReference>(this, new MemberRefComparer(this));
            _methodSpecIndex = new InstanceAndStructuralReferenceIndex<IGenericMethodInstanceReference>(this, new MethodSpecComparer(this));
            _typeRefIndex = new TypeReferenceIndex(this);
            _typeSpecIndex = new InstanceAndStructuralReferenceIndex<ITypeReference>(this, new TypeSpecComparer(this));
            _standAloneSignatureIndex = new HeapOrReferenceIndex<BlobHandle>(this);
        }
 
        protected override ushort Generation
        {
            get { return 0; }
        }
 
        protected override Guid EncId
        {
            get { return Guid.Empty; }
        }
 
        protected override Guid EncBaseId
        {
            get { return Guid.Empty; }
        }
 
        protected override bool TryGetTypeDefinitionHandle(ITypeDefinition def, out TypeDefinitionHandle handle)
        {
            int index;
            bool result = _typeDefs.TryGetValue(def, out index);
            handle = MetadataTokens.TypeDefinitionHandle(index);
            return result;
        }
 
        protected override TypeDefinitionHandle GetTypeDefinitionHandle(ITypeDefinition def)
        {
            return MetadataTokens.TypeDefinitionHandle(_typeDefs[def]);
        }
 
        protected override ITypeDefinition GetTypeDef(TypeDefinitionHandle handle)
        {
            return _typeDefs[MetadataTokens.GetRowNumber(handle)];
        }
 
        protected override IReadOnlyList<ITypeDefinition> GetTypeDefs()
        {
            return _typeDefs.Rows;
        }
 
        protected override EventDefinitionHandle GetEventDefinitionHandle(IEventDefinition def)
        {
            return MetadataTokens.EventDefinitionHandle(_eventDefs[def]);
        }
 
        protected override IReadOnlyList<IEventDefinition> GetEventDefs()
        {
            return _eventDefs.Rows;
        }
 
        protected override FieldDefinitionHandle GetFieldDefinitionHandle(IFieldDefinition def)
        {
            return MetadataTokens.FieldDefinitionHandle(_fieldDefs[def]);
        }
 
        protected override IReadOnlyList<IFieldDefinition> GetFieldDefs()
        {
            return _fieldDefs.Rows;
        }
 
        protected override bool TryGetMethodDefinitionHandle(IMethodDefinition def, out MethodDefinitionHandle handle)
        {
            int index;
            bool result = _methodDefs.TryGetValue(def, out index);
            handle = MetadataTokens.MethodDefinitionHandle(index);
            return result;
        }
 
        protected override MethodDefinitionHandle GetMethodDefinitionHandle(IMethodDefinition def)
        {
            return MetadataTokens.MethodDefinitionHandle(_methodDefs[def]);
        }
 
        protected override IMethodDefinition GetMethodDef(MethodDefinitionHandle handle)
        {
            return _methodDefs[MetadataTokens.GetRowNumber(handle)];
        }
 
        protected override IReadOnlyList<IMethodDefinition> GetMethodDefs()
        {
            return _methodDefs.Rows;
        }
 
        protected override PropertyDefinitionHandle GetPropertyDefIndex(IPropertyDefinition def)
        {
            return MetadataTokens.PropertyDefinitionHandle(_propertyDefs[def]);
        }
 
        protected override IReadOnlyList<IPropertyDefinition> GetPropertyDefs()
        {
            return _propertyDefs.Rows;
        }
 
        protected override ParameterHandle GetParameterHandle(IParameterDefinition def)
        {
            return MetadataTokens.ParameterHandle(_parameterDefs[def]);
        }
 
        protected override IReadOnlyList<IParameterDefinition> GetParameterDefs()
        {
            return _parameterDefs.Rows;
        }
 
        protected override IReadOnlyList<IGenericParameter> GetGenericParameters()
        {
            return _genericParameters.Rows;
        }
 
        protected override FieldDefinitionHandle GetFirstFieldDefinitionHandle(INamedTypeDefinition typeDef)
        {
            return MetadataTokens.FieldDefinitionHandle(_fieldDefIndex[typeDef]);
        }
 
        protected override MethodDefinitionHandle GetFirstMethodDefinitionHandle(INamedTypeDefinition typeDef)
        {
            return MetadataTokens.MethodDefinitionHandle(_methodDefIndex[typeDef]);
        }
 
        protected override ParameterHandle GetFirstParameterHandle(IMethodDefinition methodDef)
        {
            return MetadataTokens.ParameterHandle(_parameterListIndex[methodDef]);
        }
 
        protected override AssemblyReferenceHandle GetOrAddAssemblyReferenceHandle(IAssemblyReference reference)
        {
            return MetadataTokens.AssemblyReferenceHandle(_assemblyRefIndex.GetOrAdd(reference.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 ReferenceIndexer CreateReferenceVisitor()
        {
            return new FullReferenceIndexer(this);
        }
 
        protected override void ReportReferencesToAddedSymbols()
        {
            // noop
        }
 
        private sealed class FullReferenceIndexer : ReferenceIndexer
        {
            internal FullReferenceIndexer(MetadataWriter metadataWriter)
                : base(metadataWriter)
            {
            }
        }
 
        protected override void PopulateEventMapTableRows()
        {
            ITypeDefinition? lastParent = null;
            foreach (IEventDefinition eventDef in this.GetEventDefs())
            {
                if (eventDef.ContainingTypeDefinition == lastParent)
                {
                    continue;
                }
 
                lastParent = eventDef.ContainingTypeDefinition;
 
                metadata.AddEventMap(
                    declaringType: GetTypeDefinitionHandle(lastParent),
                    eventList: GetEventDefinitionHandle(eventDef));
            }
        }
 
        protected override void PopulatePropertyMapTableRows()
        {
            ITypeDefinition? lastParent = null;
            foreach (IPropertyDefinition propertyDef in this.GetPropertyDefs())
            {
                if (propertyDef.ContainingTypeDefinition == lastParent)
                {
                    continue;
                }
 
                lastParent = propertyDef.ContainingTypeDefinition;
 
                metadata.AddPropertyMap(
                    declaringType: GetTypeDefinitionHandle(lastParent),
                    propertyList: GetPropertyDefIndex(propertyDef));
            }
        }
 
        protected override void CreateIndicesForNonTypeMembers(ITypeDefinition typeDef)
        {
            _typeDefs.Add(typeDef);
 
            IEnumerable<IGenericTypeParameter> typeParameters = this.GetConsolidatedTypeParameters(typeDef);
            if (typeParameters != null)
            {
                foreach (IGenericTypeParameter genericParameter in typeParameters)
                {
                    _genericParameters.Add(genericParameter);
                }
            }
 
            foreach (MethodImplementation methodImplementation in typeDef.GetExplicitImplementationOverrides(Context))
            {
                this.methodImplList.Add(methodImplementation);
            }
 
            foreach (IEventDefinition eventDef in typeDef.GetEvents(Context))
            {
                _eventDefs.Add(eventDef);
            }
 
            _fieldDefIndex.Add(typeDef, _fieldDefs.NextRowId);
            foreach (IFieldDefinition fieldDef in typeDef.GetFields(Context))
            {
                _fieldDefs.Add(fieldDef);
            }
 
            _methodDefIndex.Add(typeDef, _methodDefs.NextRowId);
            foreach (IMethodDefinition methodDef in typeDef.GetMethods(Context))
            {
                this.CreateIndicesFor(methodDef);
                _methodDefs.Add(methodDef);
            }
 
            foreach (IPropertyDefinition propertyDef in typeDef.GetProperties(Context))
            {
                _propertyDefs.Add(propertyDef);
            }
        }
 
        private void CreateIndicesFor(IMethodDefinition methodDef)
        {
            _parameterListIndex.Add(methodDef, _parameterDefs.NextRowId);
 
            foreach (var paramDef in this.GetParametersToEmit(methodDef))
            {
                _parameterDefs.Add(paramDef);
            }
 
            if (methodDef.GenericParameterCount > 0)
            {
                foreach (IGenericMethodParameter genericParameter in methodDef.GenericParameters)
                {
                    _genericParameters.Add(genericParameter);
                }
            }
        }
 
        private readonly struct DefinitionIndex<T> where T : class, IReference
        {
            // IReference to RowId
            private readonly SegmentedDictionary<T, int> _index;
            private readonly SegmentedList<T> _rows;
 
            public DefinitionIndex(int capacity)
            {
                _index = new SegmentedDictionary<T, int>(capacity, ReferenceEqualityComparer.Instance);
                _rows = new SegmentedList<T>(capacity);
            }
 
            public bool TryGetValue(T item, out int rowId)
            {
                return _index.TryGetValue(item, out rowId);
            }
 
            public int this[T item]
            {
                get { return _index[item]; }
            }
 
            public T this[int rowId]
            {
                get { return _rows[rowId - 1]; }
            }
 
            public IReadOnlyList<T> Rows
            {
                get { return _rows; }
            }
 
            public int NextRowId
            {
                get { return _rows.Count + 1; }
            }
 
            public void Add(T item)
            {
                _index.Add(item, NextRowId);
                _rows.Add(item);
            }
        }
    }
}