File: Compiler\DependencyAnalysis\ReadyToRun\TypeGenericInfoMapNode.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 Internal.Text;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Metadata;
using Internal.ReadyToRunConstants;
using Internal.TypeSystem;
using Internal.TypeSystem.Ecma;

namespace ILCompiler.DependencyAnalysis.ReadyToRun
{
    public class TypeGenericInfoMapNode : ModuleSpecificHeaderTableNode
    {
        private MetadataReader _metadata;

        public TypeGenericInfoMapNode(EcmaModule module) : base (module)
        {
            _metadata = module.MetadataReader;
        }

        public static bool IsSupported(MetadataReader metadata)
        {
            // Only support this map with R2R images of some size
            return metadata.TypeDefinitions.Count > 16;
        }

        public override int ClassCode => 1329419084;

        protected override string ModuleSpecificName => "__TypeGenericInfoMapNode__";

        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 });

            ObjectDataBuilder builder = new ObjectDataBuilder(factory, relocsOnly);
            builder.AddSymbol(this);

            builder.EmitUInt((uint)_metadata.TypeDefinitions.Count);

            int usedBits = 0;
            byte curByte = 0;
            foreach (var typeDefinitionHandle in _metadata.TypeDefinitions)
            {
                var typeDefinition = _metadata.GetTypeDefinition(typeDefinitionHandle);
                bool isGeneric = typeDefinition.GetGenericParameters().Count > 0;
                ReadyToRunTypeGenericInfo genericInfo = default(ReadyToRunTypeGenericInfo);
                bool hasVariance = false;
                bool hasConstraints = false;

                foreach (var genericParameterHandle in typeDefinition.GetGenericParameters())
                {
                    var genericParameter = _metadata.GetGenericParameter(genericParameterHandle);
                    if ((genericParameter.Attributes & GenericParameterAttributes.VarianceMask) != GenericParameterAttributes.None)
                        hasVariance = true;

                    if ((genericParameter.Attributes & (GenericParameterAttributes.SpecialConstraintMask | (GenericParameterAttributes)GenericConstraints.AllowByRefLike)) != default(GenericParameterAttributes) ||
                        (genericParameter.GetConstraints().Count > 0))
                    {
                        hasConstraints = true;
                    }
                }

                ReadyToRunGenericInfoGenericCount count;
                switch (typeDefinition.GetGenericParameters().Count)
                {
                    case 0:
                        count = ReadyToRunGenericInfoGenericCount.Zero; break;
                    case 1:
                        count = ReadyToRunGenericInfoGenericCount.One; break;
                    case 2:
                        count = ReadyToRunGenericInfoGenericCount.Two; break;
                    default:
                        count = ReadyToRunGenericInfoGenericCount.MoreThanTwo; break;
                }

                genericInfo = (ReadyToRunTypeGenericInfo)count |
                              (hasVariance ? ReadyToRunTypeGenericInfo.HasVariance : default(ReadyToRunTypeGenericInfo)) |
                              (hasConstraints ? ReadyToRunTypeGenericInfo.HasConstraints : default(ReadyToRunTypeGenericInfo));

                curByte |= (byte)genericInfo;
                usedBits += 4;
                if (usedBits == 8)
                {
                    builder.EmitByte(curByte);
                    usedBits = 0;
                    curByte = 0;
                }
                else
                {
                    curByte <<= 4;
                }
            }
            if (usedBits != 0)
                builder.EmitByte(curByte);

            return builder.ToObjectData();
        }
    }
}