File: Compiler\GenericDictionaryLookup.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.TypeSystem;
using ILCompiler.DependencyAnalysis;

using Debug = System.Diagnostics.Debug;

namespace ILCompiler
{
    /// <summary>
    /// Structure that specifies how a generic dictionary lookup should be performed.
    /// </summary>
    public struct GenericDictionaryLookup
    {
        private const short UseHelperOffset = -1;
        private const short UseNullOffset = -2;

        private readonly object _helperObject;

        private readonly short _offset1;
        private readonly short _offset2;

        /// <summary>
        /// Gets the information about the source of the generic context for shared code.
        /// </summary>
        public readonly GenericContextSource ContextSource;

        /// <summary>
        /// Gets the target object of the lookup. Only valid when <see cref="UseHelper"/> is true.
        /// This is typically a <see cref="TypeDesc"/> whose <see cref="TypeDesc.IsRuntimeDeterminedSubtype"/>
        /// is true, a <see cref="FieldDesc"/> on a runtime determined type, a <see cref="MethodDesc"/>, or
        /// a <see cref="DelegateCreationInfo"/>.
        /// </summary>
        public object HelperObject
        {
            get
            {
                Debug.Assert(_offset1 == UseHelperOffset);
                return _helperObject;
            }
        }

        /// <summary>
        /// Gets the ID of the helper to use if <see cref="UseHelper"/> is true.
        /// </summary>
        public ReadyToRunHelperId HelperId
        {
            get
            {
                Debug.Assert(_offset1 == UseHelperOffset);
                return (ReadyToRunHelperId)_offset2;
            }
        }

        /// <summary>
        /// Gets a value indicating whether the lookup needs to be performed by calling a helper method.
        /// </summary>
        public bool UseHelper
        {
            get
            {
                return _offset1 == UseHelperOffset;
            }
        }

        public bool UseNull
        {
            get
            {
                return _offset1 == UseNullOffset;
            }
        }

        /// <summary>
        /// Gets the number of indirections to follow. Only valid if <see cref="UseHelper"/> is false.
        /// </summary>
        public int NumberOfIndirections
        {
            get
            {
                Debug.Assert(!UseHelper && !UseNull);
                return ContextSource == GenericContextSource.MethodParameter ? 1 : 2;
            }
        }

        public int this[int index]
        {
            get
            {
                Debug.Assert(!UseHelper && !UseNull);
                Debug.Assert(index < NumberOfIndirections);
                switch (index)
                {
                    case 0:
                        return _offset1;
                    case 1:
                        return _offset2;
                }

                // Should be unreachable.
                throw new NotSupportedException();
            }
        }

        private GenericDictionaryLookup(GenericContextSource contextSource, int offset1, int offset2, object helperObject)
        {
            ContextSource = contextSource;
            _offset1 = checked((short)offset1);
            _offset2 = checked((short)offset2);
            _helperObject = helperObject;
        }

        public static GenericDictionaryLookup CreateFixedLookup(GenericContextSource contextSource, int offset1, int offset2 = UseHelperOffset)
        {
            Debug.Assert(offset1 != UseHelperOffset);
            return new GenericDictionaryLookup(contextSource, offset1, offset2, null);
        }

        public static GenericDictionaryLookup CreateHelperLookup(GenericContextSource contextSource, ReadyToRunHelperId helperId, object helperObject)
        {
            return new GenericDictionaryLookup(contextSource, UseHelperOffset, checked((short)helperId), helperObject);
        }

        public static GenericDictionaryLookup CreateNullLookup(GenericContextSource contextSource)
        {
            return new GenericDictionaryLookup(contextSource, UseNullOffset, 0, null);
        }
    }

    /// <summary>
    /// Specifies to source of the generic context.
    /// </summary>
    public enum GenericContextSource
    {
        /// <summary>
        /// Generic context is specified by a hidden parameter that has a method dictionary.
        /// </summary>
        MethodParameter,

        /// <summary>
        /// Generic context is specified by a hidden parameter that has a type.
        /// </summary>
        TypeParameter,

        /// <summary>
        /// Generic context is specified implicitly by the `this` object.
        /// </summary>
        ThisObject,
    }
}