File: Compiler\ReadyToRunXmlRootProvider.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 System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
using System.Text;
using System.Threading.Tasks;
using System.Xml.XPath;
using ILCompiler.Dataflow;
using Internal.JitInterface;
using Internal.TypeSystem;
using Internal.TypeSystem.Ecma;

namespace ILCompiler
{
    public class ReadyToRunXmlRootProvider : ICompilationRootProvider
    {
        private readonly TypeSystemContext _context;
        private readonly Stream _documentStream;
        private readonly ManifestResource _resource;
        private readonly ModuleDesc _owningModule;
        private readonly string _xmlDocumentLocation;

        public ReadyToRunXmlRootProvider(Stream documentStream, ManifestResource resource, ModuleDesc owningModule, string xmlDocumentLocation)
        {
            _context = owningModule.Context;
            _documentStream = documentStream;
            _resource = resource;
            _owningModule = owningModule;
            _xmlDocumentLocation = xmlDocumentLocation;
        }

        public void AddCompilationRoots(IRootingServiceProvider rootProvider)
        {
            CompilationRootProvider root = new CompilationRootProvider(rootProvider, _context, _documentStream, _resource, _owningModule, _xmlDocumentLocation);
            root.ProcessXml();
        }

        public static bool TryCreateRootProviderFromEmbeddedDescriptorFile(EcmaModule module, out ReadyToRunXmlRootProvider provider)
        {
            PEMemoryBlock resourceDirectory = module.PEReader.GetSectionData(module.PEReader.PEHeaders.CorHeader.ResourcesDirectory.RelativeVirtualAddress);

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

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

                string resourceName = module.MetadataReader.GetString(resource.Name);
                if (resourceName == "ILLink.Descriptors.xml")
                {
                    BlobReader reader = resourceDirectory.GetReader((int)resource.Offset, resourceDirectory.Length - (int)resource.Offset);
                    int length = (int)reader.ReadUInt32();

                    UnmanagedMemoryStream ms;
                    unsafe
                    {
                        ms = new UnmanagedMemoryStream(reader.CurrentPointer, length);
                    }

                    provider = new ReadyToRunXmlRootProvider(ms, resource, module, "resource " + resourceName + " in " + module.ToString());
                    return true;
                }
            }
            provider = null;
            return false;
        }

        private class CompilationRootProvider : ProcessLinkerXmlBase
        {
            private const string NamespaceElementName = "namespace";
            private const string _preserve = "preserve";
            private readonly IRootingServiceProvider _rootingServiceProvider;
            private InstructionSetSupport _instructionSetSupport;

            public CompilationRootProvider(IRootingServiceProvider provider, TypeSystemContext context, Stream documentStream, ManifestResource resource, ModuleDesc owningModule, string xmlDocumentLocation)
                : base(null , context, documentStream, resource, owningModule, xmlDocumentLocation, ImmutableDictionary<string, bool>.Empty)
            {
                _rootingServiceProvider = provider;
                _instructionSetSupport = ((ReadyToRunCompilerContext)owningModule.Context).InstructionSetSupport;
            }

            public void ProcessXml() => ProcessXml(false);

            protected override void ProcessAssembly(ModuleDesc assembly, XPathNavigator nav, bool warnOnUnresolvedTypes)
            {
                if (GetTypePreserve(nav) == TypePreserve.All)
                {
                    foreach (var type in assembly.GetAllTypes())
                    {
                        PreserveMethodsOnType(type);
                    }
                }
                else
                {
                    ProcessTypes(assembly, nav, warnOnUnresolvedTypes);
                    ProcessNamespaces(assembly, nav);
                }
            }

            private void PreserveMethodsOnType(TypeDesc type)
            {
                MetadataType typeWithMethods = (MetadataType)type;
                if (type.HasInstantiation)
                {
                    typeWithMethods = ReadyToRunLibraryRootProvider.InstantiateIfPossible(typeWithMethods);
                    if (typeWithMethods == null)
                        return;
                }

                foreach (MethodDesc method in typeWithMethods.GetAllMethods())
                {
                    RootMethod(method);
                }
            }

            private void RootMethod(MethodDesc method)
            {
                // Skip methods with no IL
                if (method.IsAbstract)
                    return;

                if (method.IsInternalCall)
                    return;

                MethodDesc methodToRoot = method;
                if (method.HasInstantiation)
                {
                    methodToRoot = ReadyToRunLibraryRootProvider.InstantiateIfPossible(method);

                    if (methodToRoot == null)
                        return;
                }

                try
                {
                    if (!CorInfoImpl.ShouldSkipCompilation(_instructionSetSupport, method))
                    {
                        ReadyToRunLibraryRootProvider.CheckCanGenerateMethod(methodToRoot);
                        _rootingServiceProvider.AddCompilationRoot(methodToRoot, rootMinimalDependencies: false, reason: "Linker XML descriptor");
                    }
                }
                catch (TypeSystemException)
                {
                    // Individual methods can fail to load types referenced in their signatures.
                    // Skip them in library mode since they're not going to be callable.
                    return;
                }
            }

            private void ProcessNamespaces(ModuleDesc assembly, XPathNavigator nav)
            {
                foreach (XPathNavigator namespaceNav in nav.SelectChildren(NamespaceElementName, XmlNamespace))
                {
                    if (!ShouldProcessElement(namespaceNav))
                        continue;

                    string fullname = GetFullName(namespaceNav);
                    foreach (DefType type in assembly.GetAllTypes())
                    {
                        if (!type.Namespace.StringEquals(fullname))
                            continue;

                        ProcessType(type, nav);
                    }
                }
            }

            protected override void ProcessType(TypeDesc type, XPathNavigator nav)
            {
                TypePreserve preserve = GetTypePreserve(nav);
                if (preserve == TypePreserve.All || preserve == TypePreserve.Methods)
                {
                    PreserveMethodsOnType(type);
                }
                else
                {
                    ProcessTypeChildren(type, nav);
                }
            }

            protected override void ProcessMethod(TypeDesc type, MethodDesc method, XPathNavigator nav, object customData)
            {
                MetadataType typeWithMethods = (MetadataType)type;
                if (type.HasInstantiation)
                {
                    InstantiatedType instantiated = ReadyToRunLibraryRootProvider.InstantiateIfPossible(typeWithMethods);
                    if (instantiated is null)
                        return;
                    method = method.Context.GetMethodForInstantiatedType(method, instantiated);
                }

                RootMethod(method);
            }

            private static TypePreserve GetTypePreserve(XPathNavigator nav)
            {
                string attribute = GetAttribute(nav, _preserve);
                if (string.IsNullOrEmpty(attribute))
                    return nav.HasChildren ? TypePreserve.Nothing : TypePreserve.All;

                if (Enum.TryParse(attribute, true, out TypePreserve result))
                    return result;
                return TypePreserve.Nothing;
            }
        }
    }
}