File: Compiler\DependencyAnalysis\ReadyToRun\GenericLookupSignature.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.Diagnostics;

using Internal.JitInterface;
using Internal.Text;
using Internal.TypeSystem;
using Internal.TypeSystem.Ecma;
using Internal.ReadyToRunConstants;

namespace ILCompiler.DependencyAnalysis.ReadyToRun
{
    public class GenericLookupSignature : Signature
    {
        private readonly CORINFO_RUNTIME_LOOKUP_KIND _runtimeLookupKind;

        private readonly ReadyToRunFixupKind _fixupKind;

        private readonly TypeDesc _typeArgument;

        private readonly MethodWithToken _methodArgument;

        private readonly FieldWithToken _fieldArgument;

        private readonly GenericContext _methodContext;

        public GenericLookupSignature(
            CORINFO_RUNTIME_LOOKUP_KIND runtimeLookupKind,
            ReadyToRunFixupKind fixupKind,
            TypeDesc typeArgument,
            MethodWithToken methodArgument,
            FieldWithToken fieldArgument,
            GenericContext methodContext)
        {
            Debug.Assert(typeArgument != null || methodArgument != null || fieldArgument != null);
            _runtimeLookupKind = runtimeLookupKind;
            _fixupKind = fixupKind;
            _typeArgument = typeArgument;
            _methodArgument = methodArgument;
            _fieldArgument = fieldArgument;
            _methodContext = methodContext;
        }

        public override int ClassCode => 258608008;

        public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
        {
            if (relocsOnly)
            {
                return new ObjectData(Array.Empty<byte>(), null, 1, null);
            }

            // Determine the need for module override
            IEcmaModule targetModule;
            if (_methodArgument != null)
            {
                targetModule = _methodArgument.Token.Module;
            }
            else if (_typeArgument != null)
            {
                targetModule = factory.SignatureContext.GetTargetModule(_typeArgument);
            }
            else if (_fieldArgument != null)
            {
                targetModule = _fieldArgument.Token.Module;
            }
            else
            {
                throw new NotImplementedException();
            }

            ReadyToRunFixupKind fixupToEmit;
            TypeDesc contextTypeToEmit = null;

            switch (_runtimeLookupKind)
            {
                case CORINFO_RUNTIME_LOOKUP_KIND.CORINFO_LOOKUP_CLASSPARAM:
                    fixupToEmit = ReadyToRunFixupKind.TypeDictionaryLookup;
                    break;

                case CORINFO_RUNTIME_LOOKUP_KIND.CORINFO_LOOKUP_METHODPARAM:
                    fixupToEmit = ReadyToRunFixupKind.MethodDictionaryLookup;
                    break;

                case CORINFO_RUNTIME_LOOKUP_KIND.CORINFO_LOOKUP_THISOBJ:
                    fixupToEmit = ReadyToRunFixupKind.ThisObjDictionaryLookup;
                    contextTypeToEmit = _methodContext.ContextType;
                    break;

                default:
                    throw new NotImplementedException();
            }

            ObjectDataSignatureBuilder dataBuilder = new ObjectDataSignatureBuilder(factory, relocsOnly);
            dataBuilder.AddSymbol(this);

            SignatureContext innerContext = dataBuilder.EmitFixup(factory, fixupToEmit, targetModule, factory.SignatureContext);
            if (contextTypeToEmit != null)
            {
                dataBuilder.EmitTypeSignature(contextTypeToEmit, innerContext);
            }

            dataBuilder.EmitByte((byte)_fixupKind);
            if (_methodArgument != null)
            {
                Debug.Assert(_methodArgument.Unboxing == false);

                dataBuilder.EmitMethodSignature(
                    _methodArgument,
                    enforceDefEncoding: false,
                    enforceOwningType: false,
                    context: innerContext,
                    isInstantiatingStub: true);
            }
            else if (_typeArgument != null)
            {
                dataBuilder.EmitTypeSignature(_typeArgument, innerContext);
            }
            else if (_fieldArgument != null)
            {
                dataBuilder.EmitFieldSignature(_fieldArgument, innerContext);
            }
            else
            {
                throw new NotImplementedException();
            }

            return dataBuilder.ToObjectData();
        }

        protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory)
        {
            DependencyList dependencies = null;
            if (_fixupKind == ReadyToRunFixupKind.TypeHandle)
            {
                TypeFixupSignature.AddDependenciesForAsyncStateMachineBox(ref dependencies, factory, _typeArgument);
            }
            return dependencies;
        }

        public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
        {
            sb.Append(nameMangler.CompilationUnitPrefix);
            sb.Append("GenericLookupSignature("u8);
            sb.Append(_runtimeLookupKind.ToString());
            sb.Append(" / "u8);
            sb.Append(_fixupKind.ToString());
            sb.Append(": "u8);
            if (_methodArgument != null)
            {
                sb.Append(nameMangler.GetMangledTypeName(_methodArgument.OwningType));
                sb.Append("::"u8);
                sb.Append(nameMangler.GetMangledMethodName(_methodArgument.Method));
                if (_methodArgument.ConstrainedType != null)
                {
                    sb.Append("@"u8);
                    sb.Append(nameMangler.GetMangledTypeName(_methodArgument.ConstrainedType));
                }
                if (!_methodArgument.Token.IsNull)
                {
                    sb.Append(" ["u8);
                    sb.Append(_methodArgument.Token.MetadataReader.GetString(_methodArgument.Token.MetadataReader.GetAssemblyDefinition().Name));
                    sb.Append(":"u8);
                    sb.Append(((uint)_methodArgument.Token.Token).ToString("X8"));
                    sb.Append("]"u8);
                }
            }
            if (_typeArgument != null)
            {
                sb.Append(nameMangler.GetMangledTypeName(_typeArgument));
            }
            if (_fieldArgument != null)
            {
                _fieldArgument.AppendMangledName(nameMangler, sb);
            }
            sb.Append(" ("u8);
            _methodContext.AppendMangledName(nameMangler, sb);
            sb.Append(")"u8);
        }

        public override int CompareToImpl(ISortableNode other, CompilerComparer comparer)
        {
            GenericLookupSignature otherNode = (GenericLookupSignature)other;
            int result = ((int)_runtimeLookupKind).CompareTo((int)otherNode._runtimeLookupKind);
            if (result != 0)
                return result;

            result = ((int)_fixupKind).CompareTo((int)otherNode._fixupKind);
            if (result != 0)
                return result;

            if (_typeArgument != null || otherNode._typeArgument != null)
            {
                if (_typeArgument == null)
                    return -1;
                if (otherNode._typeArgument == null)
                    return 1;

                result = comparer.Compare(_typeArgument, otherNode._typeArgument);
                if (result != 0)
                    return result;
            }

            if (_fieldArgument != null || otherNode._fieldArgument != null)
            {
                if (_fieldArgument == null)
                    return -1;
                if (otherNode._fieldArgument == null)
                    return 1;

                result = _fieldArgument.CompareTo(otherNode._fieldArgument, comparer);
                if (result != 0)
                    return result;
            }

            if (_methodArgument != null || otherNode._methodArgument != null)
            {
                if (_methodArgument == null)
                    return -1;
                if (otherNode._methodArgument == null)
                    return 1;

                result = _methodArgument.CompareTo(otherNode._methodArgument, comparer);
                if (result != 0)
                    return result;
            }

            var contextAsMethod = _methodContext.Context as MethodDesc;
            var otherContextAsMethod = otherNode._methodContext.Context as MethodDesc;
            if (contextAsMethod != null || otherContextAsMethod != null)
            {
                if (contextAsMethod == null)
                    return -1;
                if (otherContextAsMethod == null)
                    return 1;

                return comparer.Compare(contextAsMethod, otherContextAsMethod);
            }
            else
            {
                return comparer.Compare(_methodContext.ContextType, otherNode._methodContext.ContextType);
            }
        }
    }
}