File: Compiler\DependencyAnalysis\ReflectionInvokeMapNode.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 Internal.Text;
using Internal.TypeSystem;
using Internal.NativeFormat;

using Debug = System.Diagnostics.Debug;
using InvokeTableFlags = Internal.Runtime.InvokeTableFlags;

namespace ILCompiler.DependencyAnalysis
{
    /// <summary>
    /// Represents a map between reflection metadata and generated method bodies.
    /// </summary>
    internal sealed class ReflectionInvokeMapNode : ObjectNode, ISymbolDefinitionNode
    {
        private ExternalReferencesTableNode _externalReferences;

        public ReflectionInvokeMapNode(ExternalReferencesTableNode externalReferences)
        {
            _externalReferences = externalReferences;
        }

        public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
        {
            sb.Append(nameMangler.CompilationUnitPrefix).Append("__method_to_entrypoint_map"u8);
        }
        public int Offset => 0;
        public override bool IsShareable => false;

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

        public override bool StaticDependenciesAreComputed => true;

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

        public static void AddDependenciesDueToReflectability(ref DependencyList dependencies, NodeFactory factory, MethodDesc method)
        {
            Debug.Assert(factory.MetadataManager.IsReflectionInvokable(method));
            Debug.Assert(method.GetCanonMethodTarget(CanonicalFormKind.Specific) == method);

            dependencies ??= new DependencyList();

            dependencies.Add(factory.MaximallyConstructableType(method.OwningType), "Reflection invoke");

            if (factory.MetadataManager.HasReflectionInvokeStubForInvokableMethod(method))
            {
                MethodDesc invokeStub = factory.MetadataManager.GetReflectionInvokeStub(method);
                dependencies.Add(factory.MethodEntrypoint(invokeStub), "Reflection invoke");

                var signature = method.Signature;
                AddSignatureDependency(ref dependencies, factory, method, signature.ReturnType, "Reflection invoke", isOut: true);
                foreach (var parameterType in signature)
                    AddSignatureDependency(ref dependencies, factory, method, parameterType, "Reflection invoke", isOut: false);
            }

            if (method.OwningType.IsValueType && !method.Signature.IsStatic)
                dependencies.Add(factory.MethodEntrypoint(method, unboxingStub: true), "Reflection unboxing stub");

            if (!method.IsAbstract)
            {
                if (method.IsSharedByGenericInstantiations)
                {
                    dependencies.Add(factory.ShadowNonConcreteMethod(method), "Shadow generic reflectable method");
                }
                dependencies.Add(factory.AddressTakenMethodEntrypoint(method), "Body of a reflectable method");
            }

            if (method.HasInstantiation)
            {
                foreach (var instArg in method.Instantiation)
                {
                    dependencies.Add(factory.NecessaryTypeSymbol(instArg), "Reflectable generic method inst arg");
                }
            }

            ReflectionVirtualInvokeMapNode.GetVirtualInvokeMapDependencies(ref dependencies, factory, method);
        }

        internal static void AddSignatureDependency(ref DependencyList dependencies, NodeFactory factory, TypeSystemEntity referent, TypeDesc type, string reason, bool isOut)
        {
            if (type.IsByRef)
            {
                type = ((ParameterizedType)type).ParameterType;
                isOut = true;
            }

            // Pointer runtime type handles can be created at runtime if necessary
            while (type.IsPointer)
                type = ((ParameterizedType)type).ParameterType;

            // Skip tracking dependencies for primitive types. Assume that they are always present.
            if (type.IsPrimitive || type.IsVoid)
                return;

            try
            {
                factory.TypeSystemContext.DetectGenericCycles(type, referent);

                // Reflection might need to create boxed instances of valuetypes as part of reflection invocation.
                // Non-valuetypes are only needed for the purposes of casting/type checks.
                // If this is a non-exact type, we need the type loader template to get the type handle.
                if (type.IsCanonicalSubtype(CanonicalFormKind.Any))
                    GenericTypesTemplateMap.GetTemplateTypeDependencies(ref dependencies, factory, type.NormalizeInstantiation());
                else if (isOut && !type.IsGCPointer)
                    dependencies.Add(factory.MaximallyConstructableType(type.NormalizeInstantiation()), reason);
                else
                    dependencies.Add(factory.MetadataTypeSymbol(type.NormalizeInstantiation()), reason);
            }
            catch (TypeSystemException)
            {
                // It's fine to continue compiling if there's a problem getting these. There's going to be a MissingMetadata
                // exception when actually trying to invoke this and the exception will be different than the one we'd get with
                // a JIT, but that's fine, we don't need to be bug-for-bug compatible.
            }
        }

        public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
        {
            // This node does not trigger generation of other nodes.
            if (relocsOnly)
                return new ObjectData(Array.Empty<byte>(), Array.Empty<Relocation>(), 1, new ISymbolDefinitionNode[] { this });

            // Ensure the native layout blob has been saved
            factory.MetadataManager.NativeLayoutInfo.SaveNativeLayoutInfoWriter(factory);

            var writer = new NativeWriter();
            var typeMapHashTable = new VertexHashtable();

            Section hashTableSection = writer.NewSection();
            hashTableSection.Place(typeMapHashTable);

            // Get a list of all methods that have a method body and metadata from the metadata manager.
            foreach (var mappingEntry in factory.MetadataManager.GetMethodMapping(factory))
            {
                MethodDesc method = mappingEntry.Entity;

                Debug.Assert(method == method.GetCanonMethodTarget(CanonicalFormKind.Specific));

                if (!factory.MetadataManager.ShouldMethodBeInInvokeMap(method))
                    continue;

                InvokeTableFlags flags = 0;

                if (method.HasInstantiation)
                    flags |= InvokeTableFlags.IsGenericMethod;

                if (method.RequiresInstArg())
                {
                    bool goesThroughInstantiatingUnboxingThunk = method.OwningType.IsValueType && !method.Signature.IsStatic && !method.HasInstantiation;
                    if (!goesThroughInstantiatingUnboxingThunk)
                        flags |= InvokeTableFlags.RequiresInstArg;
                }

                if (method.IsDefaultConstructor)
                    flags |= InvokeTableFlags.IsDefaultConstructor;

                if (ReflectionVirtualInvokeMapNode.NeedsVirtualInvokeInfo(factory, method))
                    flags |= InvokeTableFlags.HasVirtualInvoke;

                if (!method.IsAbstract)
                    flags |= InvokeTableFlags.HasEntrypoint;

                if (!factory.MetadataManager.HasReflectionInvokeStubForInvokableMethod(method))
                    flags |= InvokeTableFlags.NeedsParameterInterpretation;

                // TODO: native signature for P/Invokes and UnmanagedCallersOnly methods
                if (method.IsRawPInvoke() || method.IsUnmanagedCallersOnly)
                    continue;

                // Grammar of an entry in the hash table:
                // Flags + DeclaringType + MetadataHandle/NameAndSig + Entrypoint + DynamicInvokeMethod + [NumGenericArgs + GenericArgs]

                Vertex vertex = writer.GetUnsignedConstant((uint)flags);

                // Only store the offset portion of the metadata handle to get better integer compression
                vertex = writer.GetTuple(vertex,
                    writer.GetUnsignedConstant((uint)(mappingEntry.MetadataHandle & MetadataManager.MetadataOffsetMask)));

                // Go with a necessary type symbol. It will be upgraded to a constructed one if a constructed was emitted.
                IEETypeNode owningTypeSymbol = factory.NecessaryTypeSymbol(method.OwningType);
                vertex = writer.GetTuple(vertex,
                    writer.GetUnsignedConstant(_externalReferences.GetIndex(owningTypeSymbol)));

                if ((flags & InvokeTableFlags.HasEntrypoint) != 0)
                {
                    vertex = writer.GetTuple(vertex,
                        writer.GetUnsignedConstant(_externalReferences.GetIndex(
                            factory.AddressTakenMethodEntrypoint(method,
                            unboxingStub: method.OwningType.IsValueType && !method.Signature.IsStatic))));
                }

                if ((flags & InvokeTableFlags.NeedsParameterInterpretation) == 0)
                {
                    MethodDesc invokeStub = factory.MetadataManager.GetReflectionInvokeStub(method);
                    vertex = writer.GetTuple(vertex,
                        writer.GetUnsignedConstant(_externalReferences.GetIndex(factory.MethodEntrypoint(invokeStub))));
                }

                if ((flags & InvokeTableFlags.IsGenericMethod) != 0)
                {
                    VertexSequence args = new VertexSequence();
                    for (int i = 0; i < method.Instantiation.Length; i++)
                    {
                        uint argId = _externalReferences.GetIndex(factory.NecessaryTypeSymbol(method.Instantiation[i]));
                        args.Append(writer.GetUnsignedConstant(argId));
                    }
                    vertex = writer.GetTuple(vertex, args);
                }

                int hashCode = method.OwningType.GetHashCode();
                typeMapHashTable.Append((uint)hashCode, hashTableSection.Place(vertex));
            }

            byte[] hashTableBytes = writer.Save();

            return new ObjectData(hashTableBytes, Array.Empty<Relocation>(), 1, new ISymbolDefinitionNode[] { this });
        }

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