File: Compiler\DependencyAnalysis\ResourceDataNode.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.Diagnostics;
using System.Linq;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;

using Internal.Text;
using Internal.TypeSystem.Ecma;

namespace ILCompiler.DependencyAnalysis
{
    /// <summary>
    /// Blob of data containing resources for all assemblies generated into the image.
    /// Resources are simply copied from the inputs and concatenated into this blob.
    /// All format information is provided by <see cref="ResourceIndexNode"/>
    /// </summary>
    internal sealed class ResourceDataNode : ObjectNode, ISymbolDefinitionNode
    {

        /// <summary>
        /// Resource index information generated while extracting resources into the data blob
        /// </summary>
        private List<ResourceIndexData> _indexData;
        private int _totalLength;

        public override bool IsShareable => false;

        public override ObjectNodeSection GetSection(NodeFactory factory) => ObjectNodeSection.ReadOnlyDataSection;

        public override bool StaticDependenciesAreComputed => true;

        public int Offset => 0;

        public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
        {
            sb.Append(nameMangler.CompilationUnitPrefix).Append("__embedded_resourcedata"u8);
        }

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

        public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
        {
            // This node has no relocations.
            if (relocsOnly)
                return new ObjectData(Array.Empty<byte>(), Array.Empty<Relocation>(), 1, new ISymbolDefinitionNode[] { this });

            byte[] blob = GenerateResourceBlob(factory);
            return new ObjectData(
                blob,
                Array.Empty<Relocation>(),
                1,
                new ISymbolDefinitionNode[]
                {
                    this
                });
        }

        public IReadOnlyList<ResourceIndexData> GetOrCreateIndexData(NodeFactory factory)
        {
            if (_indexData != null)
            {
                return _indexData;
            }

            _totalLength = 0;
            _indexData = new List<ResourceIndexData>();
            // Build up index information
            foreach (EcmaAssembly module in factory.MetadataManager.GetCompilationModulesWithMetadata().OfType<EcmaAssembly>())
            {
                PEMemoryBlock resourceDirectory = module.PEReader.GetSectionData(module.PEReader.PEHeaders.CorHeader.ResourcesDirectory.RelativeVirtualAddress);

                try
                {
                    checked
                    {
                        foreach (var resourceHandle in module.MetadataReader.ManifestResources)
                        {
                            ManifestResource resource = module.MetadataReader.GetManifestResource(resourceHandle);

                            // Don't try to embed linked resources or resources in other assemblies
                            if (!resource.Implementation.IsNil)
                            {
                                continue;
                            }

                            string resourceName = module.MetadataReader.GetString(resource.Name);

                            // Check if emitting the manifest resource is blocked by policy.
                            if (factory.MetadataManager.IsManifestResourceBlocked(factory, module, resourceName))
                                continue;

                            BlobReader reader = resourceDirectory.GetReader((int)resource.Offset, resourceDirectory.Length - (int)resource.Offset);
                            int length = (int)reader.ReadUInt32();
                            ResourceIndexData indexData = new ResourceIndexData(module, resourceName, _totalLength, (int)resource.Offset + sizeof(int), module, length);
                            _indexData.Add(indexData);
                            _totalLength += length;
                        }
                    }
                }
                catch (OverflowException)
                {
                    throw new BadImageFormatException();
                }
            }

            return _indexData;
        }

        /// <summary>
        /// Extracts resources from all modules being compiled into a single blob and saves
        /// the information needed to create an index into that blob.
        /// </summary>
        private byte[] GenerateResourceBlob(NodeFactory factory)
        {
            GetOrCreateIndexData(factory);

            // Read resources into the blob
            byte[] resourceBlob = new byte[_totalLength];
            int currentPos = 0;
            foreach (ResourceIndexData indexData in _indexData)
            {
                EcmaModule module = indexData.EcmaModule;
                PEMemoryBlock resourceDirectory = module.PEReader.GetSectionData(module.PEReader.PEHeaders.CorHeader.ResourcesDirectory.RelativeVirtualAddress);
                Debug.Assert(currentPos == indexData.NativeOffset);
                BlobReader reader = resourceDirectory.GetReader(indexData.EcmaOffset, indexData.Length);
                byte[] resourceData = reader.ReadBytes(indexData.Length);
                Buffer.BlockCopy(resourceData, 0, resourceBlob, currentPos, resourceData.Length);
                currentPos += resourceData.Length;
            }

            return resourceBlob;
        }

        protected internal override int Phase => (int)ObjectNodePhase.Ordered;
        public override int ClassCode => (int)ObjectNodeOrder.ResourceDataNode;
    }

    /// <summary>
    /// Data about individual manifest resources
    /// </summary>
    internal sealed class ResourceIndexData
    {
        public ResourceIndexData(EcmaAssembly assembly, string resourceName, int nativeOffset, int ecmaOffset, EcmaModule ecmaModule, int length)
        {
            Assembly = assembly;
            ResourceName = resourceName;
            NativeOffset = nativeOffset;
            EcmaOffset = ecmaOffset;
            EcmaModule = ecmaModule;
            Length = length;
        }

        /// <summary>
        /// Assembly that contains the resource
        /// </summary>
        public EcmaAssembly Assembly { get; }

        /// <summary>
        /// Name of the resource
        /// </summary>
        public string ResourceName { get; }

        /// <summary>
        /// Offset of the resource within the native resource blob
        /// </summary>
        public int NativeOffset { get; }

        /// <summary>
        /// Offset of the resource within the .mresources section of the ECMA module
        /// </summary>
        public int EcmaOffset { get; }

        /// <summary>
        /// Module the resource is defined in
        /// </summary>
        public EcmaModule EcmaModule { get; }

        /// <summary>
        /// Length of the resource
        /// </summary>
        public int Length { get; }
    }
}