File: Compiler\DependencyAnalysis\ReadyToRun\ImportSectionNode.cs
Web Access
Project: src\src\runtime\src\coreclr\tools\aot\ILCompiler.ReadyToRun\ILCompiler.ReadyToRun.csproj (ILCompiler.ReadyToRun)
// 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 Internal.ReadyToRunConstants;

namespace ILCompiler.DependencyAnalysis.ReadyToRun
{
    public class ImportSectionNode : EmbeddedObjectNode
    {
        private class ImportTable : ArrayOfEmbeddedDataNode<Import>
        {
            private byte _alignment;

            public ImportTable(string symbol, byte alignment) : base(symbol, nodeSorter: new EmbeddedObjectNodeComparer(CompilerComparer.Instance))
            {
                _alignment = alignment;
            }

            public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => false;

            public override int ClassCode => (int)ObjectNodeOrder.ImportSectionNode;

            protected override int GetAlignmentRequirement(NodeFactory factory)
            {
                return _alignment;
            }
        }

        private readonly ImportTable _imports;
        // TODO: annoying - today there's no way to put signature RVA's into R/O data section
        private readonly ArrayOfEmbeddedPointersNode<Signature> _signatures;
        // TODO: annoying - cannot enumerate the ArrayOfEmbeddedPointersNode so we must keep a copy.
        private readonly List<Signature> _signatureList;
        private readonly GCRefMapNode _gcRefMap;

        private readonly ReadyToRunImportSectionType _type;
        private readonly ReadyToRunImportSectionFlags _flags;
        private readonly byte _entrySize;
        private readonly string _name;
        private readonly bool _emitPrecode;

        private bool _materializedSignature;

        public ImportSectionNode(string name, ReadyToRunImportSectionType importType, ReadyToRunImportSectionFlags flags, byte entrySize, bool emitPrecode, bool emitGCRefMap)
        {
            _name = name;
            _type = importType;
            _flags = flags;
            _entrySize = entrySize;
            _emitPrecode = emitPrecode;

            _imports = new ImportTable(_name + "_ImportBegin", entrySize);
            _signatures = new ArrayOfEmbeddedPointersNode<Signature>(_name + "_SigBegin", new EmbeddedObjectNodeComparer(CompilerComparer.Instance));
            _signatureList = new List<Signature>();
            _gcRefMap = emitGCRefMap ? new GCRefMapNode(this) : null;
        }

        public void MaterializeSignature(NodeFactory r2rFactory)
        {
            if (!_materializedSignature)
            {
                _signatureList.MergeSortAllowDuplicates(new SortableDependencyNode.ObjectNodeComparer(CompilerComparer.Instance));

                foreach (Signature signature in _signatureList)
                {
                    signature.GetData(r2rFactory, relocsOnly: false);
                }
                _materializedSignature = true;
            }
        }

        public void AddImport(NodeFactory factory, Import import)
        {
            if (_materializedSignature)
            {
                throw new Exception("Cannot call AddImport after MaterializeSignature");
            }

            _imports.AddEmbeddedObject(import);
            _signatures.AddEmbeddedObject(import.ImportSignature);

            lock (_signatureList)
            {
                _signatureList.Add(import.ImportSignature.Target);
            }

            _gcRefMap?.AddImport(import);
        }

        public string Name => _name;

        public bool EmitPrecode => _emitPrecode;

        public bool IsEager => (_flags & ReadyToRunImportSectionFlags.Eager) != 0;

        public override bool StaticDependenciesAreComputed => true;

        protected internal override int Phase => (int)ObjectNodePhase.Ordered;

        public override int ClassCode => (int)ObjectNodeOrder.ImportSectionNode;

        public override void EncodeData(ref ObjectDataBuilder dataBuilder, NodeFactory factory, bool relocsOnly)
        {
            if (!_imports.ShouldSkipEmittingObjectNode(factory))
            {
                dataBuilder.EmitReloc(_imports, RelocType.IMAGE_REL_BASED_ADDR32NB, 0);
            }
            else
            {
                dataBuilder.EmitUInt(0);
            }

            if (!relocsOnly)
            {
                dataBuilder.EmitReloc(_imports, RelocType.IMAGE_REL_SYMBOL_SIZE);
                dataBuilder.EmitShort((short)_flags);
                dataBuilder.EmitByte((byte)_type);
                dataBuilder.EmitByte(_entrySize);
            }

            if (!_signatures.ShouldSkipEmittingObjectNode(factory))
            {
                dataBuilder.EmitReloc(_signatures, RelocType.IMAGE_REL_BASED_ADDR32NB, 0);
            }
            else
            {
                dataBuilder.EmitUInt(0);
            }

            // If we emit an Rva for an empty gcrefmap, it will have no size header and be impossible for r2rdump to read correctly
            if (_gcRefMap?.IsEmpty == false)
            {
                // This indirectly generates the AuxiliaryDataRva by emitting a placeholder 0 which will be replaced later
                dataBuilder.EmitReloc(_gcRefMap, RelocType.IMAGE_REL_BASED_ADDR32NB, 0);
            }
            else
            {
                dataBuilder.EmitUInt(0);
            }
        }

        public override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFactory context)
        {
            yield return new DependencyListEntry(_imports, "Import section fixup data");
            yield return new DependencyListEntry(_signatures, "Import section signatures");
            if (_gcRefMap != null)
            {
                yield return new DependencyListEntry(_gcRefMap, "GC ref map");
            }
        }

        protected override string GetName(NodeFactory context)
        {
            return _name;
        }

        public override int CompareToImpl(ISortableNode other, CompilerComparer comparer)
        {
            return _name.CompareTo(((ImportSectionNode)other)._name);
        }

        public int EntrySize => _entrySize;
    }
}