File: Compiler\RootingHelpers.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 Internal.TypeSystem;

using ILCompiler.DependencyAnalysis;

using DependencyList = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore<ILCompiler.DependencyAnalysis.NodeFactory>.DependencyList;

namespace ILCompiler
{
    public class RootingHelpers
    {
        public static bool TryRootType(IRootingServiceProvider rootProvider, TypeDesc type, bool rootBaseTypes, string reason)
        {
            try
            {
                RootType(rootProvider, type, rootBaseTypes, reason);
                return true;
            }
            catch (TypeSystemException)
            {
                return false;
            }
        }

        public static void RootType(IRootingServiceProvider rootProvider, TypeDesc type, bool rootBaseTypes, string reason)
        {
            rootProvider.AddReflectionRoot(type, reason);

            // Instantiate generic types over something that will be useful at runtime
            if (type.IsGenericDefinition)
            {
                Instantiation inst = TypeExtensions.GetInstantiationThatMeetsConstraints(type.Instantiation, allowCanon: true);
                if (inst.IsNull)
                    return;

                type = ((MetadataType)type).MakeInstantiatedType(inst);

                rootProvider.AddReflectionRoot(type, reason);
            }

            if (rootBaseTypes)
            {
                TypeDesc baseType = type.BaseType;
                if (baseType != null)
                {
                    RootType(rootProvider, baseType.NormalizeInstantiation(), rootBaseTypes, reason);
                }
            }

            if (type.IsDefType)
            {
                foreach (var method in type.ConvertToCanonForm(CanonicalFormKind.Specific).GetMethods())
                {
                    if (method.HasInstantiation)
                    {
                        Instantiation inst = TypeExtensions.GetInstantiationThatMeetsConstraints(method.Instantiation, allowCanon: true);
                        if (inst.IsNull)
                            continue;

                        TryRootMethod(rootProvider, method.MakeInstantiatedMethod(inst), reason);
                    }
                    else
                    {
                        TryRootMethod(rootProvider, method, reason);
                    }
                }

                foreach (FieldDesc field in type.GetFields())
                {
                    TryRootField(rootProvider, field, reason);
                }
            }
        }

        public static bool TryRootMethod(IRootingServiceProvider rootProvider, MethodDesc method, string reason)
        {
            try
            {
                RootMethod(rootProvider, method, reason);
                return true;
            }
            catch (TypeSystemException)
            {
                return false;
            }
        }

        public static void RootMethod(IRootingServiceProvider rootProvider, MethodDesc method, string reason)
        {
            // Make sure we're not putting something into the graph that will crash later.
            LibraryRootProvider.CheckCanGenerateMethod(method);

            rootProvider.AddReflectionRoot(method, reason);
        }

        public static bool TryRootField(IRootingServiceProvider rootProvider, FieldDesc field, string reason)
        {
            try
            {
                RootField(rootProvider, field, reason);
                return true;
            }
            catch (TypeSystemException)
            {
                return false;
            }
        }

        public static void RootField(IRootingServiceProvider rootProvider, FieldDesc field, string reason)
        {
            // Make sure we're not putting something into the graph that will crash later.
            if (field.IsLiteral)
            {
                // Nothing to check
            }
            else if (field.IsStatic)
            {
                field.OwningType.ComputeStaticFieldLayout(StaticLayoutKind.StaticRegionSizes);
            }
            else
            {
                field.OwningType.ComputeInstanceLayout(InstanceLayoutKind.TypeOnly);
            }

            rootProvider.AddReflectionRoot(field, reason);
        }

        public static bool TryGetDependenciesForReflectedMethod(ref DependencyList dependencies, NodeFactory factory, MethodDesc method, string reason)
        {
            MethodDesc typicalMethod = method.GetTypicalMethodDefinition();
            if (factory.MetadataManager.IsReflectionBlocked(typicalMethod))
            {
                return false;
            }

            // If this is a generic method, make sure we at minimum have the metadata
            // for it. This hedges against the risk that we fail to figure out a code body
            // for it below.
            if (typicalMethod.IsGenericMethodDefinition || typicalMethod.OwningType.IsGenericDefinition)
            {
                dependencies ??= new DependencyList();
                dependencies.Add(factory.ReflectedMethod(typicalMethod), reason);
            }

            // If there's any genericness involved, try to create a fitting instantiation that would be usable at runtime.
            // This is not a complete solution to the problem.
            // If we ever decide that MakeGenericType/MakeGenericMethod should simply be considered unsafe, this code can be deleted
            // and instantiations that are not fully closed can be ignored.
            if (method.OwningType.IsGenericDefinition || method.OwningType.ContainsSignatureVariables(treatGenericParameterLikeSignatureVariable: true))
            {
                TypeDesc owningType = method.OwningType.GetTypeDefinition();
                Instantiation inst = TypeExtensions.GetInstantiationThatMeetsConstraints(owningType.Instantiation, allowCanon: true);
                if (inst.IsNull)
                {
                    return false;
                }

                method = method.Context.GetMethodForInstantiatedType(
                    method.GetTypicalMethodDefinition(),
                    ((MetadataType)owningType).MakeInstantiatedType(inst));
            }

            if (method.IsGenericMethodDefinition || method.Instantiation.ContainsSignatureVariables())
            {
                method = method.GetMethodDefinition();

                Instantiation inst = TypeExtensions.GetInstantiationThatMeetsConstraints(method.Instantiation, allowCanon: true);
                if (inst.IsNull)
                {
                    return false;
                }

                method = method.MakeInstantiatedMethod(inst);
            }

            try
            {
                // Make sure we're not putting something into the graph that will crash later.
                LibraryRootProvider.CheckCanGenerateMethod(method);
            }
            catch (TypeSystemException)
            {
                return false;
            }

            dependencies ??= new DependencyList();
            dependencies.Add(factory.ReflectedMethod(method.GetCanonMethodTarget(CanonicalFormKind.Specific)), reason);

            return true;
        }

        public static bool TryGetDependenciesForReflectedField(ref DependencyList dependencies, NodeFactory factory, FieldDesc field, string reason)
        {
            FieldDesc typicalField = field.GetTypicalFieldDefinition();
            if (factory.MetadataManager.IsReflectionBlocked(typicalField))
            {
                return false;
            }

            dependencies ??= new DependencyList();

            // If this is a field on generic type, make sure we at minimum have the metadata
            // for it. This hedges against the risk that we fail to figure out an instantiated base
            // for it below.
            if (typicalField.OwningType.HasInstantiation)
            {
                dependencies.Add(factory.ReflectedField(typicalField), reason);
            }

            // If there's any genericness involved, try to create a fitting instantiation that would be usable at runtime.
            // This is not a complete solution to the problem.
            // If we ever decide that MakeGenericType/MakeGenericMethod should simply be considered unsafe, this code can be deleted
            // and instantiations that are not fully closed can be ignored.
            if (field.OwningType.IsGenericDefinition || field.OwningType.ContainsSignatureVariables(treatGenericParameterLikeSignatureVariable: true))
            {
                TypeDesc owningType = field.OwningType.GetTypeDefinition();
                Instantiation inst = TypeExtensions.GetInstantiationThatMeetsConstraints(owningType.Instantiation, allowCanon: true);
                if (inst.IsNull)
                {
                    return false;
                }

                field = field.Context.GetFieldForInstantiatedType(
                    field.GetTypicalFieldDefinition(),
                    ((MetadataType)owningType).MakeInstantiatedType(inst));
            }

            try
            {
                // Make sure we're not putting something into the graph that will crash later.
                factory.TypeSystemContext.EnsureLoadableType(field.FieldType);
            }
            catch (TypeSystemException)
            {
                return false;
            }

            dependencies.Add(factory.ReflectedField(field), reason);

            return true;
        }

        public static bool TryGetDependenciesForReflectedType(ref DependencyList dependencies, NodeFactory factory, TypeDesc type, string reason)
        {
            try
            {
                // Instantiations with signature variables are not helpful - just use the definition.
                if (type.ContainsSignatureVariables(treatGenericParameterLikeSignatureVariable: true))
                {
                    type = type.GetTypeDefinition();
                }

                if (factory.MetadataManager.IsReflectionBlocked(type))
                {
                    return false;
                }

                dependencies ??= new DependencyList();

                dependencies.Add(factory.ReflectedType(type), reason);

                // If there's any unknown genericness involved, try to create a fitting instantiation that would be usable at runtime.
                // This is not a complete solution to the problem.
                // If we ever decide that MakeGenericType/MakeGenericMethod should simply be considered unsafe, this code can be deleted
                // and instantiations that are not fully closed can be ignored.
                if (type.IsGenericDefinition)
                {
                    Instantiation inst = TypeExtensions.GetInstantiationThatMeetsConstraints(type.Instantiation, allowCanon: true);
                    if (!inst.IsNull)
                    {
                        dependencies.Add(factory.ReflectedType(((MetadataType)type).MakeInstantiatedType(inst)), reason);
                    }
                }
            }
            catch (TypeSystemException)
            {
                return false;
            }

            return true;
        }
    }
}