File: Compiler\DependencyAnalysis\ReadyToRun\CopiedFieldRvaNode.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.Immutable;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Reflection.PortableExecutable;
using Internal.Text;
using Internal.TypeSystem;
using Internal.TypeSystem.Ecma;
using Debug = System.Diagnostics.Debug;

namespace ILCompiler.DependencyAnalysis.ReadyToRun
{
    public class CopiedFieldRvaNode : ObjectNode, ISymbolDefinitionNode
    {
        private int _rva;
        private EcmaModule _module;

        public CopiedFieldRvaNode(EcmaModule module, int rva)
        {
            _rva = rva;
            _module = module;
        }

        public override ObjectNodeSection GetSection(NodeFactory factory)
        {
            return ObjectNodeSection.ReadOnlyDataSection;
        }

        public override bool IsShareable => false;

        public override int ClassCode => 223495;

        public override bool StaticDependenciesAreComputed => true;

        public int Offset => 0;

        public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
        {
            if (relocsOnly)
            {
                return new ObjectData(
                    data: Array.Empty<byte>(),
                    relocs: Array.Empty<Relocation>(),
                    alignment: 1,
                    definedSymbols: new ISymbolDefinitionNode[] { this });
            }

            ObjectDataBuilder builder = new ObjectDataBuilder(factory, relocsOnly);
            byte[] rvaData = GetRvaData(factory.Target.PointerSize, out int requiredAlignment);
            builder.RequireInitialAlignment(requiredAlignment);
            builder.AddSymbol(this);
            builder.EmitBytes(rvaData);
            return builder.ToObjectData();
        }

        private unsafe byte[] GetRvaData(int targetPointerSize, out int requiredAlignment)
        {
            int size = 0;
            requiredAlignment = targetPointerSize;

            MetadataReader metadataReader = _module.MetadataReader;
            BlobReader metadataBlob = new BlobReader(_module.PEReader.GetMetadata().Pointer, _module.PEReader.GetMetadata().Length);
            metadataBlob.Offset = metadataReader.GetTableMetadataOffset(TableIndex.FieldRva);
            bool compressedFieldRef = 6 == metadataReader.GetTableRowSize(TableIndex.FieldRva);

            for (int i = 1; i <= metadataReader.GetTableRowCount(TableIndex.FieldRva); i++)
            {
                int currentFieldRva = metadataBlob.ReadInt32();
                int currentFieldRid;
                if (compressedFieldRef)
                {
                    currentFieldRid = metadataBlob.ReadUInt16();
                }
                else
                {
                    currentFieldRid = metadataBlob.ReadInt32();
                }
                if (currentFieldRva != _rva)
                    continue;

                EcmaField field = _module.GetField(MetadataTokens.FieldDefinitionHandle(currentFieldRid));
                Debug.Assert(field.HasRva);

                int currentSize = field.FieldType.GetElementSize().AsInt;
                requiredAlignment = Math.Max(requiredAlignment, (field.FieldType as MetadataType)?.GetClassLayout().PackingSize ?? 1);
                if (currentSize > size)
                {
                    // We need to handle overlapping fields by reusing blobs based on the rva, and just update
                    // the size and contents
                    size = currentSize;
                }
            }

            Debug.Assert(size > 0);

            PEMemoryBlock block = _module.PEReader.GetSectionData(_rva);
            if (block.Length < size)
                throw new BadImageFormatException();

            byte[] result = new byte[AlignmentHelper.AlignUp(size, targetPointerSize)];
            block.GetContent(0, size).CopyTo(result);
            return result;
        }

        protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler);

        public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
        {
            sb.Append(nameMangler.CompilationUnitPrefix);
            sb.Append($"_FieldRvaData_{_module.Assembly.GetName().Name}_{_rva}");
        }

        public override int CompareToImpl(ISortableNode other, CompilerComparer comparer)
        {
            int result = _module.CompareTo(((CopiedFieldRvaNode)other)._module);
            if (result != 0)
                return result;

            return _rva - ((CopiedFieldRvaNode)other)._rva;
        }
    }
}