File: Compiler\MetadataManager.cs
Web Access
Project: src\src\runtime\src\coreclr\tools\aot\ILCompiler.Compiler\ILCompiler.Compiler.csproj (ILCompiler.Compiler)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

using Internal.TypeSystem;

using ILCompiler.DependencyAnalysis;
using ILCompiler.DependencyAnalysisFramework;
using ILCompiler.Metadata;

using Debug = System.Diagnostics.Debug;
using ReadyToRunSectionType = Internal.Runtime.ReadyToRunSectionType;
using ReflectionMapBlob = Internal.Runtime.ReflectionMapBlob;
using DependencyList = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore<ILCompiler.DependencyAnalysis.NodeFactory>.DependencyList;
using CombinedDependencyList = System.Collections.Generic.List<ILCompiler.DependencyAnalysisFramework.DependencyNodeCore<ILCompiler.DependencyAnalysis.NodeFactory>.CombinedDependencyListEntry>;
using CombinedDependencyListEntry = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore<ILCompiler.DependencyAnalysis.NodeFactory>.CombinedDependencyListEntry;
using MethodIL = Internal.IL.MethodIL;
using CustomAttributeValue = System.Reflection.Metadata.CustomAttributeValue<Internal.TypeSystem.TypeDesc>;
using MethodSignature = Internal.TypeSystem.MethodSignature;
using FlowAnnotations = ILLink.Shared.TrimAnalysis.FlowAnnotations;

using MetadataRecord = Internal.Metadata.NativeFormat.Writer.MetadataRecord;
using Method = Internal.Metadata.NativeFormat.Writer.Method;
using MetadataWriter = Internal.Metadata.NativeFormat.Writer.MetadataWriter;
using TypeReference = Internal.Metadata.NativeFormat.Writer.TypeReference;
using Field = Internal.Metadata.NativeFormat.Writer.Field;
using TypeSpecification = Internal.Metadata.NativeFormat.Writer.TypeSpecification;
using ConstantStringValue = Internal.Metadata.NativeFormat.Writer.ConstantStringValue;
using TypeInstantiationSignature = Internal.Metadata.NativeFormat.Writer.TypeInstantiationSignature;
using ConstantStringArray = Internal.Metadata.NativeFormat.Writer.ConstantStringArray;

namespace ILCompiler
{
    /// <summary>
    /// This class is responsible for managing native metadata to be emitted into the compiled
    /// module. It also helps facilitate mappings between generated runtime structures or code,
    /// and the native metadata.
    /// </summary>
    public abstract class MetadataManager : ICompilationRootProvider
    {
        internal const int MetadataOffsetMask = 0x1FFFFFF;

        protected readonly MetadataManagerOptions _options;

        private byte[] _metadataBlob;
        private List<MetadataMapping<MetadataType>> _typeMappings;
        private List<MetadataMapping<FieldDesc>> _fieldMappings;
        private Dictionary<FieldDesc, int> _fieldHandleMap;
        private List<MetadataMapping<MethodDesc>> _methodMappings;
        private Dictionary<MethodDesc, int> _methodHandleMap;
        private List<StackTraceMapping> _stackTraceMappings;
        private List<ReflectionStackTraceMapping> _reflectionStackTraceMappings;
        protected readonly string _metadataLogFile;
        protected readonly StackTraceEmissionPolicy _stackTraceEmissionPolicy;

        protected readonly CompilerTypeSystemContext _typeSystemContext;
        protected readonly MetadataBlockingPolicy _blockingPolicy;
        protected readonly ManifestResourceBlockingPolicy _resourceBlockingPolicy;
        protected readonly DynamicInvokeThunkGenerationPolicy _dynamicInvokeThunkGenerationPolicy;

        private readonly List<InterfaceDispatchCellNode> _interfaceDispatchCells = new List<InterfaceDispatchCellNode>();
        private readonly SortedSet<NonGCStaticsNode> _cctorContextsGenerated = new SortedSet<NonGCStaticsNode>(CompilerComparer.Instance);
        private readonly SortedSet<MetadataType> _typesWithGCStaticsGenerated = new SortedSet<MetadataType>(CompilerComparer.Instance);
        private readonly SortedSet<MetadataType> _typesWithNonGCStaticsGenerated = new SortedSet<MetadataType>(CompilerComparer.Instance);
        private readonly SortedSet<MetadataType> _typesWithThreadStaticsGenerated = new SortedSet<MetadataType>(CompilerComparer.Instance);
        private readonly SortedSet<TypeDesc> _typesWithEETypesGenerated = new SortedSet<TypeDesc>(TypeSystemComparer.Instance);
        private readonly SortedSet<TypeDesc> _typesWithConstructedEETypesGenerated = new SortedSet<TypeDesc>(TypeSystemComparer.Instance);
        private readonly SortedSet<MethodDesc> _methodsGenerated = new SortedSet<MethodDesc>(TypeSystemComparer.Instance);
        private readonly SortedSet<MethodDesc> _reflectableMethods = new SortedSet<MethodDesc>(TypeSystemComparer.Instance);
        private readonly SortedSet<GenericDictionaryNode> _genericDictionariesGenerated = new SortedSet<GenericDictionaryNode>(CompilerComparer.Instance);
        private readonly SortedSet<IMethodBodyNode> _methodBodiesGenerated = new SortedSet<IMethodBodyNode>(CompilerComparer.Instance);
        private readonly SortedSet<FrozenObjectNode> _frozenObjects = new SortedSet<FrozenObjectNode>(CompilerComparer.Instance);
        private readonly SortedSet<TypeGVMEntriesNode> _typeGVMEntries
            = new SortedSet<TypeGVMEntriesNode>(Comparer<TypeGVMEntriesNode>.Create((a, b) => TypeSystemComparer.Instance.Compare(a.AssociatedType, b.AssociatedType)));
        private readonly SortedSet<DefType> _typesWithDelegateMarshalling = new SortedSet<DefType>(TypeSystemComparer.Instance);
        private readonly SortedSet<DefType> _typesWithStructMarshalling = new SortedSet<DefType>(TypeSystemComparer.Instance);
        private HashSet<NativeLayoutTemplateMethodSignatureVertexNode> _templateMethodEntries = new HashSet<NativeLayoutTemplateMethodSignatureVertexNode>();
        private readonly SortedSet<TypeDesc> _typeTemplates = new SortedSet<TypeDesc>(TypeSystemComparer.Instance);
        private readonly SortedSet<MetadataType> _typesWithGenericStaticBaseInfo = new SortedSet<MetadataType>(TypeSystemComparer.Instance);
        private readonly SortedSet<MethodDesc> _genericMethodHashtableEntries = new SortedSet<MethodDesc>(TypeSystemComparer.Instance);
        private readonly SortedSet<MethodDesc> _exactMethodHashtableEntries = new SortedSet<MethodDesc>(TypeSystemComparer.Instance);
        private readonly HashSet<TypeDesc> _usedInterfaces = new HashSet<TypeDesc>();

        private List<(DehydratableObjectNode Node, ObjectNode.ObjectData Data)> _dehydratableData = new List<(DehydratableObjectNode Node, ObjectNode.ObjectData data)>();

        internal FlowAnnotations FlowAnnotations { get; }

        internal StackTraceEmissionPolicy StackTracePolicy => _stackTraceEmissionPolicy;

        internal NativeLayoutInfoNode NativeLayoutInfo { get; private set; }

        public MetadataManager(CompilerTypeSystemContext typeSystemContext, MetadataBlockingPolicy blockingPolicy,
            ManifestResourceBlockingPolicy resourceBlockingPolicy, string logFile, StackTraceEmissionPolicy stackTracePolicy,
            DynamicInvokeThunkGenerationPolicy dynamicInvokeThunkGenerationPolicy,
            MetadataManagerOptions options, FlowAnnotations flowAnnotations)
        {
            _typeSystemContext = typeSystemContext;
            _blockingPolicy = blockingPolicy;
            _resourceBlockingPolicy = resourceBlockingPolicy;
            _dynamicInvokeThunkGenerationPolicy = dynamicInvokeThunkGenerationPolicy;
            _options = options;
            _metadataLogFile = logFile;
            _stackTraceEmissionPolicy = stackTracePolicy;

            FlowAnnotations = flowAnnotations;
        }

        public bool IsDataDehydrated => (_options & MetadataManagerOptions.DehydrateData) != 0;

        internal ObjectNode.ObjectData PrepareForDehydration(DehydratableObjectNode node, ObjectNode.ObjectData hydratedData)
        {
            _dehydratableData.Add((node, hydratedData));

            return new ObjectNode.ObjectData(new byte[hydratedData.Data.Length],
                Array.Empty<Relocation>(),
                hydratedData.Alignment,
                hydratedData.DefinedSymbols);
        }

        public IEnumerable<ObjectNode.ObjectData> GetDehydratableData()
        {
#if DEBUG
            // We're making an assumption that PrepareForDehydration was called in the emission order.
            // Double check that here.
            var comparer = new CompilerComparer();
            for (int i = 1; i < _dehydratableData.Count; i++)
                Debug.Assert(comparer.Compare(_dehydratableData[i - 1].Node, _dehydratableData[i].Node) < 0);
#endif

            foreach (var entry in _dehydratableData)
                yield return entry.Data;
        }

        public void AttachToDependencyGraph(DependencyAnalyzerBase<NodeFactory> graph)
        {
            graph.NewMarkedNode += Graph_NewMarkedNode;
        }

        internal static ReadyToRunSectionType BlobIdToReadyToRunSection(ReflectionMapBlob blobId)
        {
            var result = (ReadyToRunSectionType)((int)blobId + (int)ReadyToRunSectionType.ReadonlyBlobRegionStart);
            Debug.Assert(result <= ReadyToRunSectionType.ReadonlyBlobRegionEnd);
            return result;
        }

        public virtual void AddToReadyToRunHeader(ReadyToRunHeaderNode header, NodeFactory nodeFactory, ExternalReferencesTableNode commonFixupsTableNode)
        {
            var metadataNode = new MetadataNode();
            header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.EmbeddedMetadata), metadataNode);

            var nativeReferencesTableNode = new ExternalReferencesTableNode("NativeReferences", nodeFactory);
            var nativeStaticsTableNode = new ExternalReferencesTableNode("NativeStatics", nodeFactory);

            var resourceDataNode = new ResourceDataNode();
            header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.BlobIdResourceData), resourceDataNode);

            var resourceIndexNode = new ResourceIndexNode(resourceDataNode);
            header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.BlobIdResourceIndex), resourceIndexNode);

            var typeMapNode = new TypeMetadataMapNode(commonFixupsTableNode);

            header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.TypeMap), typeMapNode);

            var cctorContextMapNode = new ClassConstructorContextMap(commonFixupsTableNode);
            header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.CCtorContextMap), cctorContextMapNode);

            var invokeMapNode = new ReflectionInvokeMapNode(commonFixupsTableNode);
            header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.InvokeMap), invokeMapNode);

            var arrayMapNode = new ArrayMapNode(commonFixupsTableNode);
            header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.ArrayMap), arrayMapNode);

            var byRefMapNode = new ByRefTypeMapNode(commonFixupsTableNode);
            header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.ByRefTypeMap), byRefMapNode);

            var pointerMapNode = new PointerTypeMapNode(commonFixupsTableNode);
            header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.PointerTypeMap), pointerMapNode);

            var functionPointerMapNode = new FunctionPointerMapNode(commonFixupsTableNode);
            header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.FunctionPointerTypeMap), functionPointerMapNode);

            var fieldMapNode = new ReflectionFieldMapNode(commonFixupsTableNode);
            header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.FieldAccessMap), fieldMapNode);

            NativeLayoutInfo = new NativeLayoutInfoNode(nativeReferencesTableNode, nativeStaticsTableNode);
            header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.NativeLayoutInfo), NativeLayoutInfo);

            var exactMethodInstantiations = new ExactMethodInstantiationsNode(nativeReferencesTableNode);
            header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.ExactMethodInstantiationsHashtable), exactMethodInstantiations);

            var genericsTypesHashtableNode = new GenericTypesHashtableNode(nativeReferencesTableNode);
            header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.GenericsHashtable), genericsTypesHashtableNode);

            var genericMethodsHashtableNode = new GenericMethodsHashtableNode(nativeReferencesTableNode);
            header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.GenericMethodsHashtable), genericMethodsHashtableNode);

            var genericVirtualMethodTableNode = new GenericVirtualMethodTableNode(commonFixupsTableNode);
            header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.GenericVirtualMethodTable), genericVirtualMethodTableNode);

            var interfaceGenericVirtualMethodTableNode = new InterfaceGenericVirtualMethodTableNode(commonFixupsTableNode);
            header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.InterfaceGenericVirtualMethodTable), interfaceGenericVirtualMethodTableNode);

            var genericMethodsTemplatesMapNode = new GenericMethodsTemplateMap(commonFixupsTableNode);
            header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.GenericMethodsTemplateMap), genericMethodsTemplatesMapNode);

            var genericTypesTemplatesMapNode = new GenericTypesTemplateMap(commonFixupsTableNode);
            header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.TypeTemplateMap), genericTypesTemplatesMapNode);

            var staticsInfoHashtableNode = new StaticsInfoHashtableNode(nativeReferencesTableNode, nativeStaticsTableNode);
            header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.StaticsInfoHashtable), staticsInfoHashtableNode);

            var virtualInvokeMapNode = new ReflectionVirtualInvokeMapNode(commonFixupsTableNode);
            header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.VirtualInvokeMap), virtualInvokeMapNode);

            var stackTraceMethodMappingNode = new StackTraceMethodMappingNode();
            header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.BlobIdStackTraceMethodRvaToTokenMapping), stackTraceMethodMappingNode);

            var stackTraceDocumentsNode = new StackTraceDocumentsNode();
            header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.BlobIdStackTraceDocuments), stackTraceDocumentsNode);

            var stackTraceLineNumbersNode = new StackTraceLineNumbersNode(commonFixupsTableNode, stackTraceDocumentsNode);
            header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.BlobIdStackTraceLineNumbers), stackTraceLineNumbersNode);

            // The external references tables should go last
            header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.NativeReferences), nativeReferencesTableNode);
            header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.NativeStatics), nativeStaticsTableNode);

            if (IsDataDehydrated)
            {
                var dehydratedDataNode = new DehydratedDataNode();
                header.Add(ReadyToRunSectionType.DehydratedData, dehydratedDataNode);
            }
        }

        protected virtual void Graph_NewMarkedNode(DependencyNodeCore<NodeFactory> obj)
        {
            var eetypeNode = obj as EETypeNode;
            if (eetypeNode != null)
            {
                _typesWithEETypesGenerated.Add(eetypeNode.Type);

                if (eetypeNode is ConstructedEETypeNode)
                {
                    _typesWithConstructedEETypesGenerated.Add(eetypeNode.Type);
                }

                return;
            }

            IMethodBodyNode methodBodyNode = obj as IMethodBodyNode;
            if (methodBodyNode != null)
            {
                _methodBodiesGenerated.Add(methodBodyNode);
            }

            IMethodNode methodNode = methodBodyNode;
            if (methodNode != null)
            {
                if (AllMethodsCanBeReflectable && !IsReflectionBlocked(methodNode.Method))
                    _reflectableMethods.Add(methodNode.Method);
            }

            methodNode ??= obj as ShadowConcreteMethodNode;

            if (methodNode != null)
            {
                _methodsGenerated.Add(methodNode.Method);
                return;
            }

            var reflectedMethodNode = obj as ReflectedMethodNode;
            if (reflectedMethodNode != null)
            {
                _reflectableMethods.Add(reflectedMethodNode.Method);
            }

            if (obj is NonGCStaticsNode nonGcStaticSectionNode)
            {
                if (nonGcStaticSectionNode.HasLazyStaticConstructor)
                    _cctorContextsGenerated.Add(nonGcStaticSectionNode);

                _typesWithNonGCStaticsGenerated.Add(nonGcStaticSectionNode.Type);
            }

            if (obj is GCStaticsNode gcStaticsNode)
            {
                _typesWithGCStaticsGenerated.Add(gcStaticsNode.Type);
            }

            if (obj is TypeThreadStaticIndexNode threadStaticsNode)
            {
                _typesWithThreadStaticsGenerated.Add(threadStaticsNode.Type);
            }

            var gvmEntryNode = obj as TypeGVMEntriesNode;
            if (gvmEntryNode != null)
            {
                _typeGVMEntries.Add(gvmEntryNode);
            }

            var dictionaryNode = obj as GenericDictionaryNode;
            if (dictionaryNode != null)
            {
                _genericDictionariesGenerated.Add(dictionaryNode);
            }

            if (obj is InterfaceDispatchCellNode dispatchCell)
            {
                _interfaceDispatchCells.Add(dispatchCell);
            }

            if (obj is StructMarshallingDataNode structMarshallingDataNode)
            {
                _typesWithStructMarshalling.Add(structMarshallingDataNode.Type);
            }

            if (obj is DelegateMarshallingDataNode delegateMarshallingDataNode)
            {
                _typesWithDelegateMarshalling.Add(delegateMarshallingDataNode.Type);
            }

            if (obj is NativeLayoutTemplateMethodSignatureVertexNode templateMethodEntry)
            {
                _templateMethodEntries.Add(templateMethodEntry);
            }

            if (obj is NativeLayoutTemplateTypeLayoutVertexNode typeTemplate)
            {
                _typeTemplates.Add(typeTemplate.CanonType);
            }

            if (obj is FrozenObjectNode frozenObj)
            {
                _frozenObjects.Add(frozenObj);
            }

            if (obj is GenericStaticBaseInfoNode genericStaticBaseInfo)
            {
                _typesWithGenericStaticBaseInfo.Add(genericStaticBaseInfo.Type);
            }

            if (obj is GenericMethodsHashtableEntryNode genericMethodsHashtableEntryNode)
            {
                _genericMethodHashtableEntries.Add(genericMethodsHashtableEntryNode.Method);
            }

            if (obj is ExactMethodInstantiationsEntryNode exactMethodsHashtableEntryNode)
            {
                _exactMethodHashtableEntries.Add(exactMethodsHashtableEntryNode.Method);
            }

            if (obj is InterfaceUseNode interfaceUse)
            {
                _usedInterfaces.Add(interfaceUse.Type);
            }
        }

        protected virtual bool AllMethodsCanBeReflectable => false;

        public bool IsTypeInstantiationReflectionVisible(TypeDesc type)
        {
            if (FlowAnnotations == null)
                return false;

            if (FlowAnnotations.HasGenericParameterAnnotation(type))
                return true;

            foreach (TypeDesc instArg in type.Instantiation)
            {
                if (IsTypeInstantiationReflectionVisible(instArg))
                    return true;
            }

            return false;
        }

        /// <summary>
        /// Is a method that is reflectable a method which should be placed into the invoke map as invokable?
        /// </summary>
        public virtual bool IsReflectionInvokable(MethodDesc method)
        {
            return IsMethodSupportedInReflectionInvoke(method);
        }

        public static bool IsMethodSupportedInReflectionInvoke(MethodDesc method)
        {
            TypeDesc owningType = method.OwningType;

            // Methods on nullable are special cased in the runtime reflection
            if (owningType.IsNullable)
                return false;

            // Methods on arrays are special cased in the runtime reflection
            if (owningType.IsArray)
                return false;

            // Finalizers are not reflection invokable
            if (method.IsFinalizer)
                return false;

            // Static constructors are not reflection invokable
            if (method.IsStaticConstructor)
                return false;

            // Non-task returning async methods have special calling convention
            if (method.IsAsync && !method.GetTypicalMethodDefinition().Signature.ReturnsTaskOrValueTask())
                return false;

            // And so do async variants but we shouldn't even see them here
            Debug.Assert(!method.IsAsyncVariant());

            if (method.IsConstructor)
            {
                // Delegate construction is only allowed through specific IL sequences
                if (owningType.IsDelegate)
                    return false;

                // String constructors are intrinsic and special cased in runtime reflection
                if (owningType.IsString)
                    return false;
            }

            // Everything else can go in the mapping table.
            return true;
        }

        /// <summary>
        /// Is there a reflection invoke stub for a method that is invokable?
        /// </summary>
        public bool HasReflectionInvokeStub(MethodDesc method)
        {
            if (!IsReflectionInvokable(method))
                return false;

            return HasReflectionInvokeStubForInvokableMethod(method);
        }

        /// <summary>
        /// Is there a reflection invoke stub for a method that is invokable?
        /// </summary>
        public bool ShouldMethodBeInInvokeMap(MethodDesc method)
        {
            // The current format requires us to have an MethodTable for the owning type. We might want to lift this.
            if (!TypeGeneratesEEType(method.OwningType))
                return false;

            // We have a method body, we have a metadata token, but we can't get an invoke stub. Bail.
            if (!IsReflectionInvokable(method))
                return false;

            return true;
        }

        public void GetDependenciesDueToGenericDictionary(ref DependencyList dependencies, NodeFactory factory, MethodDesc method)
        {
            if (method.Signature.IsStatic && method.IsSynchronized)
            {
                dependencies ??= new DependencyList();
                dependencies.Add(factory.GenericMethodsHashtableEntry(method), "Will need to look up owning type from dictionary");
            }
        }

        public IEnumerable<CombinedDependencyListEntry> GetConditionalDependenciesDueToGenericDictionary(NodeFactory factory, MethodDesc method)
        {
            // If there's a template for this method, we need to keep track of the dictionary so that we
            // don't accidentally create a new dictionary for the same method at runtime.
            yield return new CombinedDependencyListEntry(
                factory.GenericMethodsHashtableEntry(method),
                factory.NativeLayout.TemplateMethodEntry(method.GetCanonMethodTarget(CanonicalFormKind.Specific)),
                "Runtime-constructable dictionary");
        }

        /// <summary>
        /// This method is an extension point that can provide additional metadata-based dependencies to compiled method bodies.
        /// </summary>
        public void GetDependenciesDueToReflectability(ref DependencyList dependencies, NodeFactory factory, MethodDesc method)
        {
            MetadataCategory category = GetMetadataCategory(method);

            if ((category & MetadataCategory.Description) != 0)
            {
                GetMetadataDependenciesDueToReflectability(ref dependencies, factory, method);
            }

            if ((category & MetadataCategory.RuntimeMapping) != 0)
            {
                if (IsReflectionInvokable(method))
                {
                    // We're going to generate a mapping table entry for this. Collect dependencies.
                    ReflectionInvokeMapNode.AddDependenciesDueToReflectability(ref dependencies, factory, method);

                    ReflectionInvokeSupportDependencyAlgorithm.GetDependenciesFromParamsArray(ref dependencies, factory, method);
                }

                if (!method.IsCanonicalMethod(CanonicalFormKind.Any) && method.IsStaticConstructor)
                {
                    // Information about the static constructor prefixes the non-GC static base
                    dependencies ??= new DependencyList();
                    dependencies.Add(factory.TypeNonGCStaticsSymbol((MetadataType)method.OwningType), "Static constructor is reflection-callable");
                }

                GenericMethodsTemplateMap.GetTemplateMethodDependencies(ref dependencies, factory, method);
                GenericTypesTemplateMap.GetTemplateTypeDependencies(ref dependencies, factory, method.OwningType);
            }
        }

        /// <summary>
        /// This method is an extension point that can provide additional metadata-based dependencies to generated fields.
        /// </summary>
        public void GetDependenciesDueToReflectability(ref DependencyList dependencies, NodeFactory factory, FieldDesc field)
        {
            MetadataCategory category = GetMetadataCategory(field);

            if ((category & MetadataCategory.Description) != 0)
            {
                GetMetadataDependenciesDueToReflectability(ref dependencies, factory, field);
            }

            if ((category & MetadataCategory.RuntimeMapping) != 0)
            {
                TypeDesc owningCanonicalType = field.OwningType.ConvertToCanonForm(CanonicalFormKind.Specific);
                GenericTypesTemplateMap.GetTemplateTypeDependencies(ref dependencies, factory, owningCanonicalType);
            }
        }

        /// <summary>
        /// This method is an extension point that can provide additional metadata-based dependencies on a virtual method.
        /// </summary>
        public virtual void GetDependenciesDueToVirtualMethodReflectability(ref DependencyList dependencies, NodeFactory factory, MethodDesc method)
        {
        }

        protected virtual void GetMetadataDependenciesDueToReflectability(ref DependencyList dependencies, NodeFactory factory, MethodDesc method)
        {
            // MetadataManagers can override this to provide additional dependencies caused by the emission of metadata
            // (E.g. dependencies caused by the method having custom attributes applied to it: making sure we compile the attribute constructor
            // and property setters)
        }

        public virtual void GetNativeLayoutMetadataDependencies(ref DependencyList dependencies, NodeFactory factory, MethodDesc method)
        {
            // MetadataManagers can override this to provide additional dependencies caused by the emission of metadata
        }

        protected virtual void GetMetadataDependenciesDueToReflectability(ref DependencyList dependencies, NodeFactory factory, FieldDesc field)
        {
            // MetadataManagers can override this to provide additional dependencies caused by the emission of metadata
            // (E.g. dependencies caused by the field having custom attributes applied to it: making sure we compile the attribute constructor
            // and property setters)
        }

        /// <summary>
        /// This method is an extension point that can provide additional metadata-based dependencies to generated EETypes.
        /// </summary>
        public virtual void GetDependenciesDueToEETypePresence(ref DependencyList dependencies, NodeFactory factory, TypeDesc type)
        {
            MetadataCategory category = GetMetadataCategory(type);

            if ((category & MetadataCategory.Description) != 0)
            {
                GetMetadataDependenciesDueToReflectability(ref dependencies, factory, type);
            }
        }

        internal virtual void GetDependenciesDueToModuleUse(ref DependencyList dependencies, NodeFactory factory, ModuleDesc module)
        {
            // MetadataManagers can override this to provide additional dependencies caused by using a module
        }

        protected virtual void GetMetadataDependenciesDueToReflectability(ref DependencyList dependencies, NodeFactory factory, TypeDesc type)
        {
            // MetadataManagers can override this to provide additional dependencies caused by the emission of metadata
            // (E.g. dependencies caused by the type having custom attributes applied to it: making sure we compile the attribute constructor
            // and property setters)
        }

        public virtual void GetConditionalDependenciesDueToEETypePresence(ref CombinedDependencyList dependencies, NodeFactory factory, TypeDesc type, bool allocated)
        {
            // MetadataManagers can override this to provide additional dependencies caused by the presence of
            // an MethodTable.
        }

        public virtual bool HasConditionalDependenciesDueToEETypePresence(TypeDesc type)
        {
            return false;
        }

        /// <summary>
        /// This method is an extension point that can provide additional metadata-based dependencies to generated RuntimeMethodHandles.
        /// </summary>
        public virtual void GetDependenciesDueToLdToken(ref DependencyList dependencies, NodeFactory factory, MethodDesc method)
        {
            // MetadataManagers can override this to provide additional dependencies caused by the presence of a
            // RuntimeMethodHandle data structure.
        }

        /// <summary>
        /// This method is an extension point that can provide additional metadata-based dependencies to generated RuntimeFieldHandles.
        /// </summary>
        public virtual void GetDependenciesDueToLdToken(ref DependencyList dependencies, NodeFactory factory, FieldDesc field)
        {
            // MetadataManagers can override this to provide additional dependencies caused by the presence of a
            // RuntimeFieldHandle data structure.
        }

        public void GetDependenciesDueToDelegateCreation(ref DependencyList dependencies, NodeFactory factory, TypeDesc delegateType, MethodDesc target)
        {
            if (target.IsVirtual)
            {
                dependencies ??= new DependencyList();
                dependencies.Add(factory.DelegateTargetVirtualMethod(target), "Delegate to a virtual method created");
            }
        }

        /// <summary>
        /// This method is an extension point that can provide additional metadata-based dependencies to delegate targets.
        /// </summary>
        public virtual void GetDependenciesDueToDelegateCreation(ref CombinedDependencyList dependencies, NodeFactory factory, TypeDesc delegateType, MethodDesc target)
        {
            // MetadataManagers can override this to provide additional dependencies caused by the construction
            // of a delegate to a method.
        }

        /// <summary>
        /// This method is an extension point that can provide additional dependencies for overridden methods on constructed types.
        /// </summary>
        public virtual void GetDependenciesForOverridingMethod(ref CombinedDependencyList dependencies, NodeFactory factory, MethodDesc decl, MethodDesc impl)
        {
        }

        /// <summary>
        /// Gets a list of fields that got "compiled" and are eligible for a runtime mapping.
        /// </summary>
        /// <returns></returns>
        protected abstract IEnumerable<FieldDesc> GetFieldsWithRuntimeMapping();

        /// <summary>
        /// This method is an extension point that can provide additional metadata-based dependencies to generated method bodies.
        /// </summary>
        public void GetDependenciesDueToMethodCodePresence(ref DependencyList dependencies, NodeFactory factory, MethodDesc method, MethodIL methodIL)
        {
            InlineableStringsResourceNode.AddDependenciesDueToResourceStringUse(ref dependencies, factory, method);

            GetDependenciesDueToMethodCodePresenceInternal(ref dependencies, factory, method, methodIL);
        }

        public virtual void GetConditionalDependenciesDueToMethodCodePresence(ref CombinedDependencyList dependencies, NodeFactory factory, MethodDesc method)
        {
            // MetadataManagers can override this to provide additional dependencies caused by the presence of
            // method code.
        }

        protected virtual void GetDependenciesDueToMethodCodePresenceInternal(ref DependencyList dependencies, NodeFactory factory, MethodDesc method, MethodIL methodIL)
        {
            // MetadataManagers can override this to provide additional dependencies caused by the presence of a
            // compiled method body.
        }

        /// <summary>
        /// Given that a method is invokable, does there exist a reflection invoke stub?
        /// </summary>
        public bool HasReflectionInvokeStubForInvokableMethod(MethodDesc method)
        {
            Debug.Assert(IsReflectionInvokable(method));
            return _dynamicInvokeThunkGenerationPolicy.HasStaticInvokeThunk(method);
        }

        /// <summary>
        /// Given that a method is invokable, if it is inserted into the reflection invoke table
        /// will it use a method token to be referenced, or not?
        /// </summary>
        public bool WillUseMetadataTokenToReferenceMethod(MethodDesc method)
        {
            return (GetMetadataCategory(method) & MetadataCategory.Description) != 0;
        }

        /// <summary>
        /// Given that a method is invokable, if it is inserted into the reflection invoke table
        /// will it use a field token to be referenced, or not?
        /// </summary>
        public bool WillUseMetadataTokenToReferenceField(FieldDesc field)
        {
            return (GetMetadataCategory(field) & MetadataCategory.Description) != 0;
        }

        /// <summary>
        /// Gets a stub that can be used to reflection-invoke a method with a given signature.
        /// </summary>
        public MethodDesc GetReflectionInvokeStub(MethodDesc method)
        {
            return _typeSystemContext.GetDynamicInvokeThunk(method.Signature,
                !method.Signature.IsStatic && method.OwningType.IsValueType);
        }

        protected void EnsureMetadataGenerated(NodeFactory factory)
        {
            if (_metadataBlob != null)
                return;

            ComputeMetadata(factory, out _metadataBlob, out _typeMappings, out _methodMappings, out _methodHandleMap, out _fieldMappings, out _fieldHandleMap, out _stackTraceMappings, out _reflectionStackTraceMappings);
        }

        void ICompilationRootProvider.AddCompilationRoots(IRootingServiceProvider rootProvider)
        {
            // MetadataManagers can override this to provide metadata compilation roots that need to be added to the graph ahead of time.
            // (E.g. reflection roots computed by IL analyzers, or non-compilation-based roots)
        }

        protected abstract void ComputeMetadata(NodeFactory factory,
                                                out byte[] metadataBlob,
                                                out List<MetadataMapping<MetadataType>> typeMappings,
                                                out List<MetadataMapping<MethodDesc>> methodMappings,
                                                out Dictionary<MethodDesc, int> methodMetadataMappings,
                                                out List<MetadataMapping<FieldDesc>> fieldMappings,
                                                out Dictionary<FieldDesc, int> fieldMetadataMappings,
                                                out List<StackTraceMapping> stackTraceMapping,
                                                out List<ReflectionStackTraceMapping> reflectionStackTraceMapping);

        protected void ComputeMetadata<TPolicy>(
            TPolicy policy,
            NodeFactory factory,
            out byte[] metadataBlob,
            out List<MetadataMapping<MetadataType>> typeMappings,
            out List<MetadataMapping<MethodDesc>> methodMappings,
            out Dictionary<MethodDesc, int> methodMetadataMappings,
            out List<MetadataMapping<FieldDesc>> fieldMappings,
            out Dictionary<FieldDesc, int> fieldMetadataMappings,
            out List<StackTraceMapping> stackTraceMapping,
            out List<ReflectionStackTraceMapping> reflectionBasedStackTraceMapping) where TPolicy : struct, IMetadataPolicy
        {
            var transformed = MetadataTransform.Run(policy, GetCompilationModulesWithMetadata());
            MetadataTransform transform = transformed.Transform;

            // Generate metadata blob
            var writer = new MetadataWriter();
            writer.ScopeDefinitions.AddRange(transformed.Scopes);

            var methodsWithMappings = new HashSet<MethodDesc>();
            foreach (var method in GetReflectableMethods())
            {
                if ((GetMetadataCategory(method) & MetadataCategory.RuntimeMapping) == 0)
                    continue;

                methodsWithMappings.Add(method);
            }

            // Generate entries in the blob for methods that will be necessary for stack trace purposes.
            var stackTraceRecords = new List<StackTraceRecordData>();
            var methodBodies = new HashSet<MethodDesc>();
            foreach (var methodBody in GetCompiledMethodBodies())
            {
                MethodDesc method = methodBody.Method;

                // If the method will be folded, no need to emit stack trace info for this one
                ISymbolNode internedBody = factory.ObjectInterner.GetDeduplicatedSymbol(factory, methodBody);
                if (internedBody != methodBody)
                    continue;

                methodBodies.Add(method);

                // Methods that will end up in the reflection invoke table should not have an entry in stack trace table
                // We'll try looking them up in reflection data at runtime.
                if (methodsWithMappings.Contains(method))
                    continue;

                MethodStackTraceVisibilityFlags stackVisibility = _stackTraceEmissionPolicy.GetMethodVisibility(method);

                StackTraceRecordFlags flags = 0;
                if ((stackVisibility & MethodStackTraceVisibilityFlags.IsHidden) != 0)
                    flags |= StackTraceRecordFlags.IsHidden;
                if ((stackVisibility & MethodStackTraceVisibilityFlags.HasLineNumbers) != 0)
                    flags |= StackTraceRecordFlags.HasLineNumbers;

                if ((stackVisibility & MethodStackTraceVisibilityFlags.HasMetadata) != 0)
                {
                    StackTraceRecordData record = CreateStackTraceRecord(transform, method, flags);

                    stackTraceRecords.Add(record);

                    writer.AdditionalRootRecords.Add(record.OwningType);
                    writer.AdditionalRootRecords.Add(record.MethodName);
                    writer.AdditionalRootRecords.Add(record.MethodSignature);
                    writer.AdditionalRootRecords.Add(record.MethodInstantiationArgumentCollection);
                }
                else if ((stackVisibility & MethodStackTraceVisibilityFlags.IsHidden) != 0)
                {
                    stackTraceRecords.Add(new StackTraceRecordData(method, null, null, null, null, flags));
                }
            }

            var ms = new MemoryStream();

            // .NET metadata is UTF-16 and UTF-16 contains code points that don't translate to UTF-8.
            var noThrowUtf8Encoding = new UTF8Encoding(false, false);

            using (var logWriter = _metadataLogFile != null ? new StreamWriter(File.Open(_metadataLogFile, FileMode.Create, FileAccess.Write, FileShare.Read), noThrowUtf8Encoding) : null)
            {
                writer.LogWriter = logWriter;
                writer.Write(ms);
            }

            metadataBlob = ms.ToArray();

            const int MaxAllowedMetadataOffset = 0x1FFFFFF;
            if (metadataBlob.Length > MaxAllowedMetadataOffset)
            {
                // Offset portion of metadata handles is limited to 32 MB.
                throw new InvalidOperationException($"Metadata blob exceeded the addressing range (allowed: {MaxAllowedMetadataOffset}, actual: {metadataBlob.Length})");
            }

            typeMappings = new List<MetadataMapping<MetadataType>>();
            methodMappings = new List<MetadataMapping<MethodDesc>>();
            methodMetadataMappings = new Dictionary<MethodDesc, int>();
            fieldMappings = new List<MetadataMapping<FieldDesc>>();
            fieldMetadataMappings = new Dictionary<FieldDesc, int>();
            stackTraceMapping = new List<StackTraceMapping>();
            reflectionBasedStackTraceMapping = new List<ReflectionStackTraceMapping>();

            // Generate type definition mappings
            foreach (var type in factory.MetadataManager.GetTypesWithEETypes())
            {
                MetadataType definition = type.IsTypeDefinition ? type as MetadataType : null;
                if (definition == null)
                    continue;

                MetadataRecord record = transformed.GetTransformedTypeDefinition(definition);

                // Reflection requires that we maintain type identity. Even if we only generated a TypeReference record,
                // if there is an MethodTable for it, we also need a mapping table entry for it.
                record ??= transformed.GetTransformedTypeReference(definition);

                if (record != null)
                    typeMappings.Add(new MetadataMapping<MetadataType>(definition, writer.GetRecordHandle(record)));
            }

            foreach (var methodMapping in transformed.GetTransformedMethodDefinitions())
                methodMetadataMappings[methodMapping.Key] = writer.GetRecordHandle(methodMapping.Value);

            foreach (var method in GetReflectableMethods())
            {
                MethodDesc typicalMethod = method.GetTypicalMethodDefinition();

                if ((GetMetadataCategory(method) & MetadataCategory.Description) == 0)
                {
                    // If we're in multifile compilation, we could end up with methods that should get runtime mapping here.
                    // But multifile reflection is broken, so just don't crash the compiler.
                    Debug.Assert(!factory.CompilationModuleGroup.IsSingleFileCompilation);
                    continue;
                }

                Method record = transformed.GetTransformedMethodDefinition(typicalMethod);
                Debug.Assert(record != null);
                Debug.Assert(method.GetCanonMethodTarget(CanonicalFormKind.Specific) == method);

                if ((GetMetadataCategory(method) & MetadataCategory.RuntimeMapping) == 0)
                    continue;

                Debug.Assert(!method.IsGenericMethodDefinition && !method.OwningType.IsGenericDefinition);
                Debug.Assert(!IsReflectionBlocked(method));

                if (methodBodies.Contains(method)
                    && (_stackTraceEmissionPolicy.GetMethodVisibility(method) & (MethodStackTraceVisibilityFlags.HasMetadata | MethodStackTraceVisibilityFlags.HasLineNumbers))
                    == (MethodStackTraceVisibilityFlags.HasMetadata | MethodStackTraceVisibilityFlags.HasLineNumbers))
                {
                    ReflectionStackTraceMapping mapping = new ReflectionStackTraceMapping(
                        method,
                        writer.GetRecordHandle(transformed.GetTransformedTypeDefinition((MetadataType)method.OwningType.GetTypeDefinition())),
                        writer.GetRecordHandle(record));
                    reflectionBasedStackTraceMapping.Add(mapping);
                }

                methodMappings.Add(new MetadataMapping<MethodDesc>(method, writer.GetRecordHandle(record)));
            }

            HashSet<FieldDesc> canonicalFields = new HashSet<FieldDesc>();
            foreach (var field in GetFieldsWithRuntimeMapping())
            {
                Field record = transformed.GetTransformedFieldDefinition(field.GetTypicalFieldDefinition());
                if (record == null)
                    continue;

                fieldMetadataMappings[field.GetTypicalFieldDefinition()] = writer.GetRecordHandle(record);

                FieldDesc fieldToAdd = field;
                TypeDesc canonOwningType = field.OwningType.ConvertToCanonForm(CanonicalFormKind.Specific);
                if (canonOwningType.IsCanonicalSubtype(CanonicalFormKind.Any))
                {
                    FieldDesc canonField = _typeSystemContext.GetFieldForInstantiatedType(field.GetTypicalFieldDefinition(), (InstantiatedType)canonOwningType);

                    // If we already added a canonically equivalent field, skip this one.
                    if (!canonicalFields.Add(canonField))
                        continue;

                    fieldToAdd = canonField;
                }

                fieldMappings.Add(new MetadataMapping<FieldDesc>(fieldToAdd, writer.GetRecordHandle(record)));
            }

            // Generate stack trace metadata mapping
            foreach (var stackTraceRecord in stackTraceRecords)
            {
                if (stackTraceRecord.OwningType != null)
                {
                    StackTraceMapping mapping = new StackTraceMapping(
                        stackTraceRecord.Method,
                        writer.GetRecordHandle(stackTraceRecord.OwningType),
                        writer.GetRecordHandle(stackTraceRecord.MethodSignature),
                        writer.GetRecordHandle(stackTraceRecord.MethodName),
                        stackTraceRecord.MethodInstantiationArgumentCollection != null ? writer.GetRecordHandle(stackTraceRecord.MethodInstantiationArgumentCollection) : 0,
                        stackTraceRecord.Flags);
                    stackTraceMapping.Add(mapping);
                }
                else
                {
                    Debug.Assert((stackTraceRecord.Flags & StackTraceRecordFlags.IsHidden) != 0);
                    stackTraceMapping.Add(new StackTraceMapping(stackTraceRecord.Method, 0, 0, 0, 0, stackTraceRecord.Flags));
                }
            }
        }

        protected StackTraceRecordData CreateStackTraceRecord(Metadata.MetadataTransform transform, MethodDesc method, StackTraceRecordFlags flags)
        {
            // In the metadata, we only represent the generic definition
            MethodDesc methodToGenerateMetadataFor = method.GetTypicalMethodDefinition();

            ConstantStringValue name = (ConstantStringValue)methodToGenerateMetadataFor.GetName();
            MetadataRecord signature = transform.HandleMethodSignature(methodToGenerateMetadataFor.Signature);
            MetadataRecord owningType = transform.HandleType(methodToGenerateMetadataFor.OwningType);

            // If we're generating record for a method on a generic type, the owning type
            // should appear as if instantiated over its formals
            TypeDesc owningTypeToGenerateMetadataFor = methodToGenerateMetadataFor.OwningType;
            if (owningTypeToGenerateMetadataFor.HasInstantiation
                && owningType is TypeReference)
            {
                List<MetadataRecord> genericArgs = new List<MetadataRecord>();
                foreach (Internal.TypeSystem.Ecma.EcmaGenericParameter genericParam in owningTypeToGenerateMetadataFor.Instantiation)
                {
                    genericArgs.Add(new TypeReference
                    {
                        TypeName = (ConstantStringValue)genericParam.Name,
                    });
                }

                owningType = new TypeSpecification
                {
                    Signature = new TypeInstantiationSignature
                    {
                        GenericType = owningType,
                        GenericTypeArguments = genericArgs,
                    }
                };
            }

            // Generate metadata for the method instantiation arguments
            ConstantStringArray methodInst;
            if (methodToGenerateMetadataFor.HasInstantiation)
            {
                methodInst = new ConstantStringArray();
                foreach (Internal.TypeSystem.Ecma.EcmaGenericParameter typeArgument in methodToGenerateMetadataFor.Instantiation)
                {
                    methodInst.Value.Add((ConstantStringValue)typeArgument.Name);
                }
            }
            else
            {
                methodInst = null;
            }

            return new StackTraceRecordData(method, owningType, signature, name, methodInst, flags);
        }

        /// <summary>
        /// Returns a set of modules that will get some metadata emitted into the output module
        /// </summary>
        public abstract IEnumerable<ModuleDesc> GetCompilationModulesWithMetadata();

        public byte[] GetMetadataBlob(NodeFactory factory)
        {
            EnsureMetadataGenerated(factory);
            return _metadataBlob;
        }

        public IEnumerable<MetadataMapping<MetadataType>> GetTypeDefinitionMapping(NodeFactory factory)
        {
            EnsureMetadataGenerated(factory);
            return _typeMappings;
        }

        public IEnumerable<MetadataMapping<MethodDesc>> GetMethodMapping(NodeFactory factory)
        {
            EnsureMetadataGenerated(factory);
            return _methodMappings;
        }

        public int GetMetadataHandleForMethod(NodeFactory factory, MethodDesc method)
        {
            if (!CanGenerateMetadata(method))
            {
                // We can end up here with reflection disabled or multifile compilation.
                // If we ever productize either, we'll need to do something different.
                // Scenarios that currently need this won't work in these modes.
                return 0;
            }

            EnsureMetadataGenerated(factory);
            return _methodHandleMap[method];
        }

        public IEnumerable<MetadataMapping<FieldDesc>> GetFieldMapping(NodeFactory factory)
        {
            EnsureMetadataGenerated(factory);
            return _fieldMappings;
        }

        public int GetMetadataHandleForField(NodeFactory factory, FieldDesc field)
        {
            if (!CanGenerateMetadata(field))
            {
                // We can end up here with reflection disabled or multifile compilation.
                // If we ever productize either, we'll need to do something different.
                // Scenarios that currently need this won't work in these modes.
                return 0;
            }

            EnsureMetadataGenerated(factory);
            return _fieldHandleMap[field];
        }

        public IEnumerable<StackTraceMapping> GetStackTraceMapping(NodeFactory factory)
        {
            EnsureMetadataGenerated(factory);
            return _stackTraceMappings;
        }

        public IEnumerable<ReflectionStackTraceMapping> GetReflectionStackTraceMappings(NodeFactory factory)
        {
            EnsureMetadataGenerated(factory);
            return _reflectionStackTraceMappings;
        }

        internal IEnumerable<InterfaceDispatchCellNode> GetInterfaceDispatchCells()
        {
            return _interfaceDispatchCells;
        }

        internal IEnumerable<NonGCStaticsNode> GetCctorContextMapping()
        {
            return _cctorContextsGenerated;
        }

        internal IEnumerable<MetadataType> GetTypesWithStaticBases()
        {
            var allTypes = new SortedSet<MetadataType>(CompilerComparer.Instance);
            allTypes.UnionWith(_typesWithNonGCStaticsGenerated);
            allTypes.UnionWith(_typesWithGCStaticsGenerated);
            allTypes.UnionWith(_typesWithThreadStaticsGenerated);
            return allTypes;
        }

        internal bool HasNonGcStaticBase(MetadataType type) => _typesWithNonGCStaticsGenerated.Contains(type);
        internal bool HasGcStaticBase(MetadataType type) => _typesWithGCStaticsGenerated.Contains(type);
        internal bool HasThreadStaticBase(MetadataType type) => _typesWithThreadStaticsGenerated.Contains(type);
        internal bool HasConstructedEEType(TypeDesc type) => _typesWithConstructedEETypesGenerated.Contains(type);

        internal IEnumerable<TypeGVMEntriesNode> GetTypeGVMEntries()
        {
            return _typeGVMEntries;
        }

        internal IReadOnlyCollection<GenericDictionaryNode> GetCompiledGenericDictionaries()
        {
            return _genericDictionariesGenerated;
        }

        internal IEnumerable<DefType> GetTypesWithStructMarshalling()
        {
            return _typesWithStructMarshalling;
        }

        internal IEnumerable<DefType> GetTypesWithDelegateMarshalling()
        {
            return _typesWithDelegateMarshalling;
        }

        public IEnumerable<MethodDesc> GetCompiledMethods()
        {
            return _methodsGenerated;
        }

        public IEnumerable<MethodDesc> GetReflectableMethods()
        {
            return _reflectableMethods;
        }

        public IEnumerable<TypeDesc> GetTypeTemplates()
        {
            return _typeTemplates;
        }

        public IEnumerable<FrozenObjectNode> GetFrozenObjects()
        {
            return _frozenObjects;
        }

        public IEnumerable<MetadataType> GetTypesWithGenericStaticBaseInfos()
        {
            return _typesWithGenericStaticBaseInfo;
        }

        public IEnumerable<MethodDesc> GetGenericMethodHashtableEntries()
        {
            return _genericMethodHashtableEntries;
        }

        public IEnumerable<MethodDesc> GetExactMethodHashtableEntries()
        {
            return _exactMethodHashtableEntries;
        }

        public bool IsInterfaceUsed(TypeDesc type)
        {
            Debug.Assert(type.IsTypeDefinition);
            return _usedInterfaces.Contains(type);
        }

        internal IEnumerable<IMethodBodyNode> GetCompiledMethodBodies()
        {
            return _methodBodiesGenerated;
        }

        internal bool TypeGeneratesEEType(TypeDesc type)
        {
            return _typesWithEETypesGenerated.Contains(type);
        }

        internal IEnumerable<TypeDesc> GetTypesWithEETypes()
        {
            return _typesWithEETypesGenerated;
        }

        internal IEnumerable<TypeDesc> GetTypesWithConstructedEETypes()
        {
            return _typesWithConstructedEETypesGenerated;
        }

        internal IEnumerable<NativeLayoutTemplateMethodSignatureVertexNode> GetTemplateMethodEntries()
        {
            return _templateMethodEntries;
        }

        public bool IsReflectionBlocked(TypeDesc type)
        {
            switch (type.Category)
            {
                case TypeFlags.SzArray:
                case TypeFlags.Array:
                case TypeFlags.Pointer:
                case TypeFlags.ByRef:
                    TypeDesc parameterType = ((ParameterizedType)type).ParameterType;

                    if (parameterType.IsCanonicalDefinitionType(CanonicalFormKind.Any))
                        return false;

                    return IsReflectionBlocked(parameterType);

                case TypeFlags.FunctionPointer:
                    MethodSignature pointerSignature = ((FunctionPointerType)type).Signature;

                    for (int i = 0; i < pointerSignature.Length; i++)
                        if (IsReflectionBlocked(pointerSignature[i]))
                            return true;

                    return IsReflectionBlocked(pointerSignature.ReturnType);

                default:
                    Debug.Assert(type.IsDefType);

                    TypeDesc typeDefinition = type.GetTypeDefinition();
                    if (type != typeDefinition)
                    {
                        if (_blockingPolicy.IsBlocked((MetadataType)typeDefinition))
                            return true;

                        if (IsReflectionBlocked(type.Instantiation))
                            return true;

                        return false;
                    }

                    return _blockingPolicy.IsBlocked((MetadataType)type);
            }
        }

        protected bool IsReflectionBlocked(Instantiation instantiation)
        {
            foreach (TypeDesc type in instantiation)
            {
                if (IsReflectionBlocked(type) && !type.IsCanonicalDefinitionType(CanonicalFormKind.Any))
                    return true;
            }
            return false;
        }

        public bool IsReflectionBlocked(FieldDesc field)
        {
            FieldDesc typicalFieldDefinition = field.GetTypicalFieldDefinition();
            if (typicalFieldDefinition != field && IsReflectionBlocked(field.OwningType.Instantiation))
            {
                return true;
            }

            return _blockingPolicy.IsBlocked(typicalFieldDefinition);
        }

        public bool IsReflectionBlocked(MethodDesc method)
        {
            MethodDesc methodDefinition = method.GetMethodDefinition();
            if (method != methodDefinition && IsReflectionBlocked(method.Instantiation))
            {
                return true;
            }

            MethodDesc typicalMethodDefinition = methodDefinition.GetTypicalMethodDefinition();
            if (typicalMethodDefinition != methodDefinition && IsReflectionBlocked(method.OwningType.Instantiation))
            {
                return true;
            }

            return _blockingPolicy.IsBlocked(typicalMethodDefinition);
        }

        public bool IsManifestResourceBlocked(NodeFactory factory, Internal.TypeSystem.Ecma.EcmaModule module, string resourceName)
        {
            if (_resourceBlockingPolicy.IsManifestResourceBlocked(module, resourceName))
                return true;

            // If this is a resource strings resource but we don't actually need it, block it.
            if (InlineableStringsResourceNode.IsInlineableStringsResource(module, resourceName)
                && !factory.InlineableStringResource(module).Marked)
                return true;

            return false;
        }

        public bool CanGenerateMetadata(MetadataType type)
        {
            return (GetMetadataCategory(type) & MetadataCategory.Description) != 0;
        }

        public bool CanGenerateMetadata(MethodDesc method)
        {
            Debug.Assert(method.IsTypicalMethodDefinition);
            return (GetMetadataCategory(method) & MetadataCategory.Description) != 0;
        }

        public bool CanGenerateMetadata(FieldDesc field)
        {
            Debug.Assert(field.IsTypicalFieldDefinition);
            return (GetMetadataCategory(field) & MetadataCategory.Description) != 0;
        }

        /// <summary>
        /// Gets the metadata category for a compiled method body in the current compilation.
        /// The method will only get called with '<paramref name="method"/>' that has a compiled method body
        /// in this compilation.
        /// Note that if this method doesn't return <see cref="MetadataCategory.Description"/>, it doesn't mean
        /// that the method never has metadata. The metadata might just be generated in a different compilation.
        /// </summary>
        protected abstract MetadataCategory GetMetadataCategory(MethodDesc method);

        /// <summary>
        /// Gets the metadata category for a generated type in the current compilation.
        /// The method can assume it will only get called with '<paramref name="type"/>' that has an MethodTable generated
        /// in the current compilation.
        /// Note that if this method doesn't return <see cref="MetadataCategory.Description"/>, it doesn't mean
        /// that the method never has metadata. The metadata might just be generated in a different compilation.
        /// </summary>
        protected abstract MetadataCategory GetMetadataCategory(TypeDesc type);
        protected abstract MetadataCategory GetMetadataCategory(FieldDesc field);

        public virtual void GetDependenciesDueToAccess(ref DependencyList dependencies, NodeFactory factory, MethodIL methodIL, TypeDesc accessedType)
        {
        }

        public virtual void GetDependenciesDueToAccess(ref DependencyList dependencies, NodeFactory factory, MethodIL methodIL, MethodDesc calledMethod)
        {
        }

        public virtual void GetDependenciesDueToAccess(ref DependencyList dependencies, NodeFactory factory, MethodIL methodIL, FieldDesc writtenField)
        {
        }

        public virtual DependencyList GetDependenciesForCustomAttribute(NodeFactory factory, MethodDesc attributeCtor, CustomAttributeValue decodedValue, TypeSystemEntity parent)
        {
            return null;
        }

        public virtual void NoteOverridingMethod(MethodDesc baseMethod, MethodDesc overridingMethod, TypeSystemEntity origin = null)
        {
        }
    }

    public readonly struct MetadataMapping<TEntity>
    {
        public readonly TEntity Entity;
        public readonly int MetadataHandle;

        public MetadataMapping(TEntity entity, int metadataHandle)
            => (Entity, MetadataHandle) = (entity, metadataHandle);
    }

    public readonly struct StackTraceMapping
    {
        public readonly MethodDesc Method;
        public readonly int OwningTypeHandle;
        public readonly int MethodSignatureHandle;
        public readonly int MethodNameHandle;
        public readonly int MethodInstantiationArgumentCollectionHandle;
        public readonly StackTraceRecordFlags Flags;

        public StackTraceMapping(MethodDesc method, int owningTypeHandle, int methodSignatureHandle, int methodNameHandle, int methodInstantiationArgumentCollectionHandle, StackTraceRecordFlags flags)
            => (Method, OwningTypeHandle, MethodSignatureHandle, MethodNameHandle, MethodInstantiationArgumentCollectionHandle, Flags)
            = (method, owningTypeHandle, methodSignatureHandle, methodNameHandle, methodInstantiationArgumentCollectionHandle, flags);
    }

    public readonly struct ReflectionStackTraceMapping
    {
        public readonly MethodDesc Method;
        public readonly int OwningTypeHandle;
        public readonly int MethodHandle;

        public ReflectionStackTraceMapping(MethodDesc method, int owningTypeHandle, int methodHandle)
            => (Method, OwningTypeHandle, MethodHandle) = (method, owningTypeHandle, methodHandle);
    }

    [Flags]
    public enum StackTraceRecordFlags
    {
        None = 0,
        IsHidden = 1,
        HasLineNumbers = 2,
    }

    public readonly struct StackTraceRecordData
    {
        public readonly MethodDesc Method;
        public readonly MetadataRecord OwningType;
        public readonly MetadataRecord MethodSignature;
        public readonly MetadataRecord MethodName;
        public readonly MetadataRecord MethodInstantiationArgumentCollection;
        public readonly StackTraceRecordFlags Flags;

        public StackTraceRecordData(MethodDesc method, MetadataRecord owningType, MetadataRecord methodSignature, MetadataRecord methodName, MetadataRecord methodInstantiationArgumentCollection, StackTraceRecordFlags flags)
            => (Method, OwningType, MethodSignature, MethodName, MethodInstantiationArgumentCollection, Flags)
            = (method, owningType, methodSignature, methodName, methodInstantiationArgumentCollection, flags);
    }

    [Flags]
    public enum MetadataCategory
    {
        None = 0x00,
        Description = 0x01,
        RuntimeMapping = 0x02,
    }

    [Flags]
    public enum MetadataManagerOptions
    {
        DehydrateData = 0x01,
    }
}