File: CodeGen\PrivateImplementationDetails.cs
Web Access
Project: src\src\Compilers\Core\Portable\Microsoft.CodeAnalysis.csproj (Microsoft.CodeAnalysis)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Reflection.Metadata;
using System.Runtime.InteropServices;
using System.Threading;
using Microsoft.CodeAnalysis.Collections;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.CodeGen
{
    /// <summary>
    /// TypeDefinition that represents &lt;PrivateImplementationDetails&gt; class.
    /// The main purpose of this class so far is to contain mapped fields and their types.
    /// </summary>
    internal sealed class PrivateImplementationDetails : DefaultTypeDef, Cci.INamespaceTypeDefinition
    {
        private const string TypeNamePrefix = "<PrivateImplementationDetails>";
 
        // Note: Dev11 uses the source method token as the prefix, rather than a fixed token
        // value, and data field offsets are unique within the method, not across all methods.
        internal const string SynthesizedStringHashFunctionName = "ComputeStringHash";
        internal const string SynthesizedReadOnlySpanHashFunctionName = "ComputeReadOnlySpanHash";
        internal const string SynthesizedSpanHashFunctionName = "ComputeSpanHash";
 
        internal const string SynthesizedThrowSwitchExpressionExceptionFunctionName = "ThrowSwitchExpressionException";
        internal const string SynthesizedThrowSwitchExpressionExceptionParameterlessFunctionName = "ThrowSwitchExpressionExceptionParameterless";
        internal const string SynthesizedThrowInvalidOperationExceptionFunctionName = "ThrowInvalidOperationException";
 
        internal const string SynthesizedInlineArrayAsSpanName = "InlineArrayAsSpan";
        internal const string SynthesizedInlineArrayAsReadOnlySpanName = "InlineArrayAsReadOnlySpan";
 
        internal const string SynthesizedInlineArrayElementRefName = "InlineArrayElementRef";
        internal const string SynthesizedInlineArrayElementRefReadOnlyName = "InlineArrayElementRefReadOnly";
 
        internal const string SynthesizedInlineArrayFirstElementRefName = "InlineArrayFirstElementRef";
        internal const string SynthesizedInlineArrayFirstElementRefReadOnlyName = "InlineArrayFirstElementRefReadOnly";
 
        private readonly CommonPEModuleBuilder _moduleBuilder;       //the module builder
        private readonly Cci.ITypeReference _systemObject;           //base type
        private readonly Cci.ITypeReference _systemValueType;        //base for nested structs
 
        private readonly Cci.ITypeReference _systemInt8Type;         //for metadata init of byte arrays
        private readonly Cci.ITypeReference _systemInt16Type;        //for metadata init of short arrays
        private readonly Cci.ITypeReference _systemInt32Type;        //for metadata init of int arrays
        private readonly Cci.ITypeReference _systemInt64Type;        //for metadata init of long arrays
 
        private readonly Cci.ICustomAttribute _compilerGeneratedAttribute;
 
        private readonly string _name;
 
        // Once frozen the collections of fields, methods and types are immutable.
        private int _frozen;
 
        private ImmutableArray<SynthesizedStaticField> _orderedSynthesizedFields;
 
        // fields mapped to metadata blocks
        private readonly ConcurrentDictionary<(ImmutableArray<byte> Data, ushort Alignment), MappedField> _mappedFields =
            new ConcurrentDictionary<(ImmutableArray<byte> Data, ushort Alignment), MappedField>(DataAndUShortEqualityComparer.Instance);
 
        // fields for cached arrays
        private readonly ConcurrentDictionary<(ImmutableArray<byte> Data, ushort ElementType), CachedArrayField> _cachedArrayFields =
            new ConcurrentDictionary<(ImmutableArray<byte> Data, ushort ElementType), CachedArrayField>(DataAndUShortEqualityComparer.Instance);
 
        // fields for cached arrays for constants
        private readonly ConcurrentDictionary<(ImmutableArray<ConstantValue> Constants, ushort ElementType), CachedArrayField> _cachedArrayFieldsForConstants =
            new ConcurrentDictionary<(ImmutableArray<ConstantValue> Constants, ushort ElementType), CachedArrayField>(ConstantValueAndUShortEqualityComparer.Instance);
 
        private ModuleVersionIdField? _mvidField;
        private ModuleCancellationTokenField? _moduleCancellationTokenField;
 
        // Dictionary that maps from analysis kind to instrumentation payload field.
        private readonly ConcurrentDictionary<int, InstrumentationPayloadRootField> _instrumentationPayloadRootFields = new ConcurrentDictionary<int, InstrumentationPayloadRootField>();
 
        // synthesized methods
        private ImmutableArray<Cci.IMethodDefinition> _orderedSynthesizedMethods;
        private readonly ConcurrentDictionary<string, Cci.IMethodDefinition> _synthesizedMethods =
            new ConcurrentDictionary<string, Cci.IMethodDefinition>();
 
        // synthesized top-level types (for inline arrays and collection expression types currently)
        private ImmutableArray<Cci.INamespaceTypeDefinition> _orderedTopLevelTypes;
        private readonly ConcurrentDictionary<string, Cci.INamespaceTypeDefinition> _synthesizedTopLevelTypes = new ConcurrentDictionary<string, Cci.INamespaceTypeDefinition>();
 
        // field types for different block sizes.
        private ImmutableArray<Cci.ITypeReference> _orderedProxyTypes;
        private readonly ConcurrentDictionary<(uint Size, ushort Alignment), Cci.ITypeReference> _proxyTypes = new ConcurrentDictionary<(uint Size, ushort Alignment), Cci.ITypeReference>();
 
        internal PrivateImplementationDetails(
            CommonPEModuleBuilder moduleBuilder,
            string moduleName,
            int submissionSlotIndex,
            Cci.ITypeReference systemObject,
            Cci.ITypeReference systemValueType,
            Cci.ITypeReference systemInt8Type,
            Cci.ITypeReference systemInt16Type,
            Cci.ITypeReference systemInt32Type,
            Cci.ITypeReference systemInt64Type,
            Cci.ICustomAttribute compilerGeneratedAttribute)
        {
            RoslynDebug.Assert(systemObject != null);
            RoslynDebug.Assert(systemValueType != null);
 
            _moduleBuilder = moduleBuilder;
            _systemObject = systemObject;
            _systemValueType = systemValueType;
 
            _systemInt8Type = systemInt8Type;
            _systemInt16Type = systemInt16Type;
            _systemInt32Type = systemInt32Type;
            _systemInt64Type = systemInt64Type;
 
            _compilerGeneratedAttribute = compilerGeneratedAttribute;
 
            _name = getClassName();
 
            string getClassName()
            {
                // we include the module name in the name of the PrivateImplementationDetails class so that more than
                // one of them can be included in an assembly as part of netmodules.    
                var name = (moduleBuilder.OutputKind == OutputKind.NetModule) ?
                    $"{TypeNamePrefix}<{MetadataHelpers.MangleForTypeNameIfNeeded(moduleName)}>" : TypeNamePrefix;
 
                if (submissionSlotIndex >= 0)
                {
                    name += submissionSlotIndex.ToString();
                }
 
                if (moduleBuilder.CurrentGenerationOrdinal > 0)
                {
                    name += "#" + moduleBuilder.CurrentGenerationOrdinal;
                }
 
                return name;
            }
        }
 
        internal void Freeze()
        {
            var wasFrozen = Interlocked.Exchange(ref _frozen, 1);
            if (wasFrozen != 0)
            {
                throw new InvalidOperationException();
            }
 
            // Sort fields.
            ArrayBuilder<SynthesizedStaticField> fieldsBuilder = ArrayBuilder<SynthesizedStaticField>.GetInstance(
                _mappedFields.Count + _cachedArrayFields.Count + _cachedArrayFieldsForConstants.Count + (_mvidField != null ? 1 : 0));
 
            fieldsBuilder.AddRange(_mappedFields.Values);
            fieldsBuilder.AddRange(_cachedArrayFields.Values);
            fieldsBuilder.AddRange(_cachedArrayFieldsForConstants.Values);
 
            if (_mvidField != null)
                fieldsBuilder.Add(_mvidField);
 
            if (_moduleCancellationTokenField != null)
                fieldsBuilder.Add(_moduleCancellationTokenField);
 
            fieldsBuilder.AddRange(_instrumentationPayloadRootFields.Values);
            fieldsBuilder.Sort(FieldComparer.Instance);
            _orderedSynthesizedFields = fieldsBuilder.ToImmutableAndFree();
 
            // Sort methods.
            _orderedSynthesizedMethods = _synthesizedMethods.OrderBy(kvp => kvp.Key).Select(kvp => kvp.Value).AsImmutable();
 
            // Sort top-level types.
            _orderedTopLevelTypes = _synthesizedTopLevelTypes.OrderBy(kvp => kvp.Key).Select(kvp => (Cci.INamespaceTypeDefinition)kvp.Value).AsImmutable();
 
            // Sort proxy types.
            _orderedProxyTypes = _proxyTypes.OrderBy(kvp => kvp.Key.Size).ThenBy(kvp => kvp.Key.Alignment).Select(kvp => kvp.Value).AsImmutable();
        }
 
        internal bool IsFrozen => _frozen != 0;
 
        /// <summary>
        /// Gets a field that can be used to cache an array allocated to store data from a corresponding <see cref="CreateDataField"/> call.
        /// </summary>
        /// <param name="data">The data that will be used to initialize the field.</param>
        /// <param name="arrayType">The type of the field, e.g. int[].</param>
        /// <param name="emitContext">The emit context to use with the array type to extract its element type.</param>
        /// <returns>The field to use to cache an array for this data and alignment.</returns>
        internal Cci.IFieldReference CreateArrayCachingField(ImmutableArray<byte> data, Cci.IArrayTypeReference arrayType, EmitContext emitContext)
        {
            Debug.Assert(!IsFrozen);
 
            // Get the type code for the array's element type.
            Cci.PrimitiveTypeCode typeCode = arrayType.GetElementType(emitContext).TypeCode;
            Debug.Assert(typeCode is
                Cci.PrimitiveTypeCode.Int16 or Cci.PrimitiveTypeCode.UInt16 or Cci.PrimitiveTypeCode.Char or
                Cci.PrimitiveTypeCode.Int32 or Cci.PrimitiveTypeCode.UInt32 or Cci.PrimitiveTypeCode.Float32 or
                Cci.PrimitiveTypeCode.Int64 or Cci.PrimitiveTypeCode.UInt64 or Cci.PrimitiveTypeCode.Float64);
 
            // Create a dedicated mapped field for the array type, separate from the data that'll be stored into that array.
            // Call sites will lazily instantiate the array to cache in this field, rather than forcibly instantiating
            // all of them when the private implementation details class is first used.
            return _cachedArrayFields.GetOrAdd((data, (ushort)typeCode), key =>
            {
                // Hash the data to hex, but then tack on _A(ElementType). This is needed both to differentiate the array field from
                // the data field, but also to differentiate multiple fields that may have the same raw data but different array types.
                string name = $"{DataToHex(key.Data)}_A{key.ElementType}";
 
                return new CachedArrayField(name, this, arrayType);
            });
        }
 
        internal Cci.IFieldReference CreateArrayCachingField(ImmutableArray<ConstantValue> constants, Cci.IArrayTypeReference arrayType, EmitContext emitContext)
        {
            Debug.Assert(!IsFrozen);
            Cci.PrimitiveTypeCode typeCode = arrayType.GetElementType(emitContext).TypeCode;
            Debug.Assert(typeCode is not Cci.PrimitiveTypeCode.Reference);
 
            // Call sites will lazily instantiate the array to cache in this field, rather than forcibly instantiating
            // all of them when the private implementation details class is first used.
            return _cachedArrayFieldsForConstants.GetOrAdd((constants, (ushort)typeCode), key =>
            {
                // Hash the data to hex, but then tack on _B(ElementType). This is needed to differentiate multiple fields
                // that may have the same raw data but different array types.
                string name = $"{ConstantsToHex(key.Constants)}_B{key.ElementType}";
                return new CachedArrayField(name, this, arrayType);
            });
        }
 
        /// <summary>
        /// Gets a field that can be used to to store data directly in an RVA field.
        /// </summary>
        /// <param name="data">The data for the field.</param>
        /// <param name="alignment">
        /// The alignment value is the necessary alignment for addresses for the underlying element type of the array.
        /// The data is stored by using a type whose size is equal to the total size of the blob. If a built-in system
        /// type has an appropriate size and .pack, it can be used. Otherwise, a type is generated of the same size as
        /// the data, and that type needs its .pack set to the alignment required for the underlying data. While that
        /// .pack value isn't required by anything else in the compiler (the compiler always aligns RVA fields at 8-byte
        /// boundaries, which accomodates any element type that's relevant), it is necessary for IL rewriters. Such rewriters
        /// also need to ensure an appropriate alignment is maintained for the RVA field, and while they could also simplify
        /// by choosing a worst-case alignment as does the compiler, they may instead use the .pack value as the alignment
        /// to use for that field, since it's an opaque blob with no other indication as to what kind of data is
        /// stored and what alignment might be required.
        /// </param>
        /// <returns>The field. This may have been newly created or may be an existing field previously created for the same data and alignment.</returns>
        internal Cci.IFieldReference CreateDataField(ImmutableArray<byte> data, ushort alignment)
        {
            Debug.Assert(!IsFrozen);
            Debug.Assert(alignment is 1 or 2 or 4 or 8);
            Debug.Assert(data.Length != 1 || alignment == 1);
 
            Cci.ITypeReference type = _proxyTypes.GetOrAdd(
                ((uint)data.Length, Alignment: alignment), key =>
                {
                    // We need a type that's both the same size as the data and that has a .pack
                    // that matches the data's alignment requirements. If the size of the data
                    // is 1 byte, then the alignment will also be 1, and we can use byte as the type.
                    // If the size of the data is 2, 4, or 8 bytes, we can use short, int, or long rather than
                    // creating a custom type, but we can only do so if the required alignment is also 1, as
                    // these types have a .pack value of 1.
                    if (key.Alignment == 1)
                    {
                        switch (key.Size)
                        {
                            case 1 when _systemInt8Type is not null: return _systemInt8Type;
                            case 2 when _systemInt16Type is not null: return _systemInt16Type;
                            case 4 when _systemInt32Type is not null: return _systemInt32Type;
                            case 8 when _systemInt64Type is not null: return _systemInt64Type;
                        }
                    }
 
                    // Use a custom type.
                    return new ExplicitSizeStruct(key.Size, key.Alignment, this, _systemValueType);
                });
 
            return _mappedFields.GetOrAdd((data, alignment), key =>
            {
                // For alignment of 1 (which is used in cases other than in fields for ReadOnlySpan<byte>),
                // just use the hex value of the data hash.  For other alignments, tack on a '2', '4', or '8'
                // accordingly.  As every byte will yield two chars, the odd number of chars used for 2/4/8
                // alignments will never produce a name that conflicts with names for an alignment of 1.
                RoslynDebug.Assert(alignment is 1 or 2 or 4 or 8, $"Unexpected alignment: {alignment}");
                string hex = DataToHex(key.Data);
                string name = alignment switch
                {
                    2 => hex + "2",
                    4 => hex + "4",
                    8 => hex + "8",
                    _ => hex
                };
 
                return new MappedField(name, this, type, key.Data);
            });
        }
 
        internal Cci.IFieldReference GetModuleVersionId(Cci.ITypeReference mvidType)
        {
            if (_mvidField == null)
            {
                Debug.Assert(!IsFrozen);
                Interlocked.CompareExchange(ref _mvidField, new ModuleVersionIdField(this, mvidType), null);
            }
 
            Debug.Assert(_mvidField.Type == mvidType);
            return _mvidField;
        }
 
        internal Cci.IFieldReference GetModuleCancellationToken(Cci.ITypeReference cancellationTokenType)
        {
            if (_moduleCancellationTokenField == null)
            {
                Debug.Assert(!IsFrozen);
                Interlocked.CompareExchange(ref _moduleCancellationTokenField, new ModuleCancellationTokenField(this, cancellationTokenType), null);
            }
 
            Debug.Assert(_moduleCancellationTokenField.Type == cancellationTokenType);
            return _moduleCancellationTokenField;
        }
 
        internal Cci.IFieldReference GetOrAddInstrumentationPayloadRoot(int analysisKind, Cci.ITypeReference payloadRootType)
        {
            InstrumentationPayloadRootField? payloadRootField;
            if (!_instrumentationPayloadRootFields.TryGetValue(analysisKind, out payloadRootField))
            {
                Debug.Assert(!IsFrozen);
                payloadRootField = _instrumentationPayloadRootFields.GetOrAdd(analysisKind, kind => new InstrumentationPayloadRootField(this, kind, payloadRootType));
            }
 
            Debug.Assert(payloadRootField.Type == payloadRootType);
            return payloadRootField;
        }
 
        // Get the instrumentation payload roots ordered by analysis kind.
        internal IOrderedEnumerable<KeyValuePair<int, InstrumentationPayloadRootField>> GetInstrumentationPayloadRoots()
        {
            Debug.Assert(IsFrozen);
            return _instrumentationPayloadRootFields.OrderBy(analysis => analysis.Key);
        }
 
        // Add a new synthesized method indexed by its name if the method isn't already present.
        internal bool TryAddSynthesizedMethod(Cci.IMethodDefinition method)
        {
            Debug.Assert(!IsFrozen);
#nullable disable // Can 'method.Name' be null? https://github.com/dotnet/roslyn/issues/39166
            return _synthesizedMethods.TryAdd(method.Name, method);
#nullable enable
        }
 
        public override IEnumerable<Cci.IFieldDefinition> GetFields(EmitContext context)
        {
            Debug.Assert(IsFrozen);
            return _orderedSynthesizedFields;
        }
 
        public override IEnumerable<Cci.IMethodDefinition> GetMethods(EmitContext context)
        {
            Debug.Assert(IsFrozen);
            return _orderedSynthesizedMethods;
        }
 
        public IEnumerable<Cci.IMethodDefinition> GetTopLevelAndNestedTypeMethods(EmitContext context)
        {
            Debug.Assert(IsFrozen);
            foreach (var type in _orderedTopLevelTypes)
            {
                foreach (var method in type.GetMethods(context))
                {
                    yield return method;
                }
 
                foreach (var nestedType in type.GetNestedTypes(context))
                {
                    foreach (var method in nestedType.GetMethods(context))
                    {
                        yield return method;
                    }
                }
            }
        }
 
        // Get method by name, if one exists. Otherwise return null.
        internal Cci.IMethodDefinition? GetMethod(string name)
        {
            Cci.IMethodDefinition? method;
            _synthesizedMethods.TryGetValue(name, out method);
            return method;
        }
 
        internal bool TryAddSynthesizedType(Cci.INamespaceTypeDefinition type)
        {
            Debug.Assert(!IsFrozen);
            Debug.Assert(type.Name is { });
            return _synthesizedTopLevelTypes.TryAdd(type.Name, type);
        }
 
        internal Cci.INamespaceTypeDefinition? GetSynthesizedType(string name)
        {
            _synthesizedTopLevelTypes.TryGetValue(name, out var type);
            return type;
        }
 
        internal IEnumerable<Cci.INamespaceTypeDefinition> GetAdditionalTopLevelTypes()
        {
            Debug.Assert(IsFrozen);
            return _orderedTopLevelTypes;
        }
 
        public override IEnumerable<Cci.INestedTypeDefinition> GetNestedTypes(EmitContext context)
        {
            Debug.Assert(IsFrozen);
            return _orderedProxyTypes.OfType<ExplicitSizeStruct>();
        }
 
        public override string ToString() => this.Name;
 
        public override Cci.ITypeReference GetBaseClass(EmitContext context) => _systemObject;
 
        public override IEnumerable<Cci.ICustomAttribute> GetAttributes(EmitContext context)
        {
            if (_compilerGeneratedAttribute != null)
            {
                return SpecializedCollections.SingletonEnumerable(_compilerGeneratedAttribute);
            }
 
            return SpecializedCollections.EmptyEnumerable<Cci.ICustomAttribute>();
        }
 
        public override void Dispatch(Cci.MetadataVisitor visitor)
        {
            visitor.Visit(this);
        }
 
        public override Cci.INamespaceTypeDefinition AsNamespaceTypeDefinition(EmitContext context) => this;
 
        public override Cci.INamespaceTypeReference AsNamespaceTypeReference => this;
 
        public string Name => _name;
 
        public bool IsPublic => false;
 
        public Cci.IUnitReference GetUnit(EmitContext context)
        {
            Debug.Assert(context.Module == _moduleBuilder);
            return _moduleBuilder;
        }
 
        public string NamespaceName => string.Empty;
 
        private static string DataToHex(ImmutableArray<byte> data)
        {
            ImmutableArray<byte> hash = CryptographicHashProvider.ComputeSourceHash(data);
            return HashToHex(hash);
        }
 
        private static string ConstantsToHex(ImmutableArray<ConstantValue> constants)
        {
            ImmutableArray<byte> hash = CryptographicHashProvider.ComputeSourceHash(constants);
            return HashToHex(hash);
        }
 
        private static string HashToHex(ImmutableArray<byte> hash)
        {
#if NETCOREAPP2_1_OR_GREATER
            return string.Create(hash.Length * 2, hash, (destination, hash) => toHex(hash, destination));
#else
            char[] c = new char[hash.Length * 2];
            toHex(hash, c);
            return new string(c);
#endif
 
            static void toHex(ImmutableArray<byte> source, Span<char> destination)
            {
                int i = 0;
                foreach (var b in source.AsSpan())
                {
                    destination[i++] = hexchar(b >> 4);
                    destination[i++] = hexchar(b & 0xF);
                }
            }
 
            static char hexchar(int x) => (char)((x <= 9) ? (x + '0') : (x + ('A' - 10)));
        }
 
        private sealed class FieldComparer : IComparer<SynthesizedStaticField>
        {
            public static readonly FieldComparer Instance = new FieldComparer();
 
            private FieldComparer()
            {
            }
 
            public int Compare(SynthesizedStaticField? x, SynthesizedStaticField? y)
            {
                RoslynDebug.Assert(x is object && y is object);
 
                // Fields are always synthesized with non-null names.
                RoslynDebug.Assert(x.Name != null && y.Name != null);
                return x.Name.CompareTo(y.Name);
            }
        }
 
        private sealed class DataAndUShortEqualityComparer : EqualityComparer<(ImmutableArray<byte> Data, ushort Value)>
        {
            public static readonly DataAndUShortEqualityComparer Instance = new DataAndUShortEqualityComparer();
 
            private DataAndUShortEqualityComparer() { }
 
            public override bool Equals((ImmutableArray<byte> Data, ushort Value) x, (ImmutableArray<byte> Data, ushort Value) y) =>
                x.Value == y.Value &&
                ByteSequenceComparer.Equals(x.Data, y.Data);
 
            public override int GetHashCode((ImmutableArray<byte> Data, ushort Value) obj) =>
                ByteSequenceComparer.GetHashCode(obj.Data); // purposefully not including Value, as it won't add meaningfully to the hash code
        }
 
        private sealed class ConstantValueAndUShortEqualityComparer : EqualityComparer<(ImmutableArray<ConstantValue> Constants, ushort Value)>
        {
            public static readonly ConstantValueAndUShortEqualityComparer Instance = new ConstantValueAndUShortEqualityComparer();
 
            private ConstantValueAndUShortEqualityComparer() { }
 
            public override bool Equals((ImmutableArray<ConstantValue> Constants, ushort Value) x, (ImmutableArray<ConstantValue> Constants, ushort Value) y)
            {
                if (x.Value != y.Value)
                {
                    return false;
                }
 
                if (x.Constants.Length != y.Constants.Length)
                {
                    return false;
                }
 
                for (int i = 0; i < x.Constants.Length; i++)
                {
                    if (x.Constants[i] != y.Constants[i])
                    {
                        return false;
                    }
                }
 
                return true;
            }
 
            public override int GetHashCode((ImmutableArray<ConstantValue> Constants, ushort Value) obj)
            {
                int hash = 0;
                foreach (var constant in obj.Constants)
                {
                    Hash.Combine(constant.GetHashCode(), hash);
                }
 
                // purposefully not including Value, as it won't add meaningfully to the hash code
                return hash;
            }
        }
    }
 
    /// <summary>
    /// Simple struct type with explicit size and no members.
    /// </summary>
    internal sealed class ExplicitSizeStruct : DefaultTypeDef, Cci.INestedTypeDefinition
    {
        private readonly uint _size;
        private readonly ushort _alignment;
        private readonly Cci.INamedTypeDefinition _containingType;
        private readonly Cci.ITypeReference _sysValueType;
 
        internal ExplicitSizeStruct(uint size, ushort alignment, PrivateImplementationDetails containingType, Cci.ITypeReference sysValueType)
        {
            RoslynDebug.Assert(alignment is 1 or 2 or 4 or 8, $"Unexpected alignment: {alignment}");
 
            _size = size;
            _alignment = alignment;
            _containingType = containingType;
            _sysValueType = sysValueType;
        }
 
        public override string ToString()
            => _containingType.ToString() + "." + this.Name;
 
        public override ushort Alignment => _alignment;
 
        public override Cci.ITypeReference GetBaseClass(EmitContext context) => _sysValueType;
 
        public override LayoutKind Layout => LayoutKind.Explicit;
 
        public override uint SizeOf => _size;
 
        public override void Dispatch(Cci.MetadataVisitor visitor)
        {
            visitor.Visit(this);
        }
 
        public string Name => _alignment == 1 ?
            $"__StaticArrayInitTypeSize={_size}" :
            $"__StaticArrayInitTypeSize={_size}_Align={_alignment}";
 
        public Cci.ITypeDefinition ContainingTypeDefinition => _containingType;
 
        public Cci.TypeMemberVisibility Visibility => Cci.TypeMemberVisibility.Assembly;
 
        public override bool IsValueType => true;
 
        public Cci.ITypeReference GetContainingType(EmitContext context) => _containingType;
 
        public override Cci.INestedTypeDefinition AsNestedTypeDefinition(EmitContext context) => this;
 
        public override Cci.INestedTypeReference AsNestedTypeReference => this;
    }
 
    internal abstract class SynthesizedStaticField : Cci.IFieldDefinition
    {
        private readonly Cci.INamedTypeDefinition _containingType;
        private readonly Cci.ITypeReference _type;
        private readonly string _name;
 
        internal SynthesizedStaticField(string name, Cci.INamedTypeDefinition containingType, Cci.ITypeReference type)
        {
            RoslynDebug.Assert(name != null);
            RoslynDebug.Assert(containingType != null);
            RoslynDebug.Assert(type != null);
 
            _containingType = containingType;
            _type = type;
            _name = name;
        }
 
        public override string ToString() => $"{(object?)_type.GetInternalSymbol() ?? _type} {(object?)_containingType.GetInternalSymbol() ?? _containingType}.{this.Name}";
 
        public MetadataConstant? GetCompileTimeValue(EmitContext context) => null;
 
        public abstract ImmutableArray<byte> MappedData { get; }
 
        public bool IsEncDeleted => false;
 
        public bool IsCompileTimeConstant => false;
 
        public bool IsNotSerialized => false;
 
        public abstract bool IsReadOnly { get; }
 
        public bool IsRuntimeSpecial => false;
 
        public bool IsSpecialName => false;
 
        public bool IsStatic => true;
 
        public bool IsMarshalledExplicitly => false;
 
        public Cci.IMarshallingInformation? MarshallingInformation => null;
 
        public ImmutableArray<byte> MarshallingDescriptor => default(ImmutableArray<byte>);
 
        public int Offset
        {
            get { throw ExceptionUtilities.Unreachable(); }
        }
 
        public Cci.ITypeDefinition ContainingTypeDefinition => _containingType;
 
        public Cci.TypeMemberVisibility Visibility => Cci.TypeMemberVisibility.Assembly;
 
        public Cci.ITypeReference GetContainingType(EmitContext context) => _containingType;
 
        public IEnumerable<Cci.ICustomAttribute> GetAttributes(EmitContext context)
            => SpecializedCollections.EmptyEnumerable<Cci.ICustomAttribute>();
 
        public void Dispatch(Cci.MetadataVisitor visitor)
        {
            visitor.Visit(this);
        }
 
        public Cci.IDefinition AsDefinition(EmitContext context)
        {
            throw ExceptionUtilities.Unreachable();
        }
 
        Symbols.ISymbolInternal? Cci.IReference.GetInternalSymbol() => null;
 
        public string Name => _name;
 
        public bool IsContextualNamedEntity => false;
 
        public Cci.ITypeReference GetType(EmitContext context) => _type;
 
        public ImmutableArray<Cci.ICustomModifier> RefCustomModifiers => ImmutableArray<Cci.ICustomModifier>.Empty;
 
        public bool IsByReference => false;
 
        internal Cci.ITypeReference Type => _type;
 
        public Cci.IFieldDefinition GetResolvedField(EmitContext context) => this;
 
        public Cci.ISpecializedFieldReference? AsSpecializedFieldReference => null;
 
        public MetadataConstant Constant
        {
            get { throw ExceptionUtilities.Unreachable(); }
        }
 
        public sealed override bool Equals(object? obj)
        {
            // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used.
            throw Roslyn.Utilities.ExceptionUtilities.Unreachable();
        }
 
        public sealed override int GetHashCode()
        {
            // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used.
            throw Roslyn.Utilities.ExceptionUtilities.Unreachable();
        }
    }
 
    internal sealed class ModuleVersionIdField : SynthesizedStaticField
    {
        internal ModuleVersionIdField(Cci.INamedTypeDefinition containingType, Cci.ITypeReference type)
            : base("MVID", containingType, type)
        {
        }
 
        public override ImmutableArray<byte> MappedData => default(ImmutableArray<byte>);
        public override bool IsReadOnly => true;
    }
 
    /// <summary>
    /// Synthesized by <see cref="InstrumentationKind.ModuleCancellation"/> instrumentation.
    /// </summary>
    internal sealed class ModuleCancellationTokenField(Cci.INamedTypeDefinition containingType, Cci.ITypeReference type)
        : SynthesizedStaticField("ModuleCancellationToken", containingType, type)
    {
        public override ImmutableArray<byte> MappedData => default;
        public override bool IsReadOnly => false;
    }
 
    internal sealed class InstrumentationPayloadRootField : SynthesizedStaticField
    {
        internal InstrumentationPayloadRootField(Cci.INamedTypeDefinition containingType, int analysisIndex, Cci.ITypeReference payloadType)
            : base("PayloadRoot" + analysisIndex.ToString(), containingType, payloadType)
        {
        }
 
        public override ImmutableArray<byte> MappedData => default(ImmutableArray<byte>);
        public override bool IsReadOnly => true;
    }
 
    /// <summary>
    /// Definition of a simple field mapped to a metadata block
    /// </summary>
    internal sealed class MappedField : SynthesizedStaticField
    {
        private readonly ImmutableArray<byte> _block;
 
        internal MappedField(string name, Cci.INamedTypeDefinition containingType, Cci.ITypeReference type, ImmutableArray<byte> block)
            : base(name, containingType, type)
        {
            Debug.Assert(!block.IsDefault);
            _block = block;
        }
 
        public override ImmutableArray<byte> MappedData => _block;
        public override bool IsReadOnly => true;
    }
 
    /// <summary>
    /// Definition of a field for storing an array caching the data from a metadata block or array of constants.
    /// </summary>
    internal sealed class CachedArrayField : SynthesizedStaticField
    {
        internal CachedArrayField(string name, Cci.INamedTypeDefinition containingType, Cci.ITypeReference type)
            : base(name, containingType, type)
        {
        }
 
        public override ImmutableArray<byte> MappedData => default(ImmutableArray<byte>);
        public override bool IsReadOnly => false;
    }
 
    /// <summary>
    /// Just a default implementation of a type definition.
    /// </summary>
    internal abstract class DefaultTypeDef : Cci.ITypeDefinition
    {
        public IEnumerable<Cci.IEventDefinition> GetEvents(EmitContext context)
            => SpecializedCollections.EmptyEnumerable<Cci.IEventDefinition>();
 
        public IEnumerable<Cci.MethodImplementation> GetExplicitImplementationOverrides(EmitContext context)
            => SpecializedCollections.EmptyEnumerable<Cci.MethodImplementation>();
 
        public virtual IEnumerable<Cci.IFieldDefinition> GetFields(EmitContext context)
            => SpecializedCollections.EmptyEnumerable<Cci.IFieldDefinition>();
 
        public IEnumerable<Cci.IGenericTypeParameter> GenericParameters
            => SpecializedCollections.EmptyEnumerable<Cci.IGenericTypeParameter>();
 
        public ushort GenericParameterCount => 0;
 
        public bool HasDeclarativeSecurity => false;
 
        public IEnumerable<Cci.TypeReferenceWithAttributes> Interfaces(EmitContext context)
            => SpecializedCollections.EmptyEnumerable<Cci.TypeReferenceWithAttributes>();
 
        public bool IsEncDeleted => false;
 
        public bool IsAbstract => false;
 
        public bool IsBeforeFieldInit => false;
 
        public bool IsComObject => false;
 
        public bool IsGeneric => false;
 
        public bool IsInterface => false;
 
        public bool IsDelegate => false;
 
        public bool IsRuntimeSpecial => false;
 
        public bool IsSerializable => false;
 
        public bool IsSpecialName => false;
 
        public bool IsWindowsRuntimeImport => false;
 
        public bool IsSealed => true;
 
        public virtual IEnumerable<Cci.IMethodDefinition> GetMethods(EmitContext context)
            => SpecializedCollections.EmptyEnumerable<Cci.IMethodDefinition>();
 
        public virtual IEnumerable<Cci.INestedTypeDefinition> GetNestedTypes(EmitContext context)
            => SpecializedCollections.EmptyEnumerable<Cci.INestedTypeDefinition>();
 
        public IEnumerable<Cci.IPropertyDefinition> GetProperties(EmitContext context)
            => SpecializedCollections.EmptyEnumerable<Cci.IPropertyDefinition>();
 
        public IEnumerable<Cci.SecurityAttribute> SecurityAttributes
            => SpecializedCollections.EmptyEnumerable<Cci.SecurityAttribute>();
 
        public CharSet StringFormat => CharSet.Ansi;
 
        public virtual IEnumerable<Cci.ICustomAttribute> GetAttributes(EmitContext context)
            => SpecializedCollections.EmptyEnumerable<Cci.ICustomAttribute>();
 
        public Cci.IDefinition AsDefinition(EmitContext context) => this;
 
        Symbols.ISymbolInternal? Cci.IReference.GetInternalSymbol() => null;
 
        public bool IsEnum => false;
 
        public Cci.ITypeDefinition GetResolvedType(EmitContext context) => this;
 
        public Cci.PrimitiveTypeCode TypeCode => Cci.PrimitiveTypeCode.NotPrimitive;
 
        public TypeDefinitionHandle TypeDef
        {
            get { throw ExceptionUtilities.Unreachable(); }
        }
 
        public Cci.IGenericMethodParameterReference? AsGenericMethodParameterReference => null;
 
        public Cci.IGenericTypeInstanceReference? AsGenericTypeInstanceReference => null;
 
        public Cci.IGenericTypeParameterReference? AsGenericTypeParameterReference => null;
 
        public virtual Cci.INamespaceTypeDefinition? AsNamespaceTypeDefinition(EmitContext context) => null;
 
        public virtual Cci.INamespaceTypeReference? AsNamespaceTypeReference => null;
 
        public Cci.ISpecializedNestedTypeReference? AsSpecializedNestedTypeReference => null;
 
        public virtual Cci.INestedTypeDefinition? AsNestedTypeDefinition(EmitContext context) => null;
 
        public virtual Cci.INestedTypeReference? AsNestedTypeReference => null;
 
        public Cci.ITypeDefinition AsTypeDefinition(EmitContext context) => this;
 
        public bool MangleName => false;
 
        public string? AssociatedFileIdentifier => null;
 
        public virtual ushort Alignment => 0;
 
        public virtual Cci.ITypeReference GetBaseClass(EmitContext context)
        {
            throw ExceptionUtilities.Unreachable();
        }
 
        public virtual LayoutKind Layout => LayoutKind.Auto;
 
        public virtual uint SizeOf => 0;
 
        public virtual void Dispatch(Cci.MetadataVisitor visitor)
        {
            throw ExceptionUtilities.Unreachable();
        }
 
        public virtual bool IsValueType => false;
 
        public sealed override bool Equals(object? obj)
        {
            // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used.
            throw Roslyn.Utilities.ExceptionUtilities.Unreachable();
        }
 
        public sealed override int GetHashCode()
        {
            // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used.
            throw Roslyn.Utilities.ExceptionUtilities.Unreachable();
        }
    }
}