File: MetadataReader\MetadataTypeName.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.
 
#nullable disable
 
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis
{
    /// <summary>
    /// Helper structure to encapsulate/cache various information about metadata name of a type and 
    /// name resolution options.
    /// Also, allows us to stop using strings in the APIs that accept only metadata names, 
    /// making usage of them less bug prone.
    /// </summary>
    [NonCopyable]
    internal partial struct MetadataTypeName
    {
        /// <summary>
        /// Full metadata name of a type, includes namespace name for top level types.
        /// </summary>
        private string _fullName;
 
        /// <summary>
        /// Namespace name for top level types.
        /// </summary>
        private string _namespaceName;
 
        /// <summary>
        /// <see cref="ReadOnlyMemory{T}"/> version of <see cref="_namespaceName"/>.  Preferred when possible to avoid
        /// the copy of the portion of <see cref="_fullName"/> used for <see cref="_namespaceName"/>.
        /// </summary>
        private ReadOnlyMemory<char> _namespaceNameMemory;
 
        /// <summary>
        /// Name of the type without namespace prefix, but possibly with generic arity mangling present.
        /// </summary>
        private string _typeName;
 
        /// <summary>
        /// <see cref="ReadOnlyMemory{T}"/> version of <see cref="_typeName"/>.  Preferred when possible to avoid
        /// the copy of the portion of <see cref="_fullName"/> used for <see cref="_typeName"/>.
        /// </summary>
        private ReadOnlyMemory<char> _typeNameMemory;
 
        /// <summary>
        /// Name of the type without namespace prefix and without generic arity mangling.
        /// </summary>
        private string _unmangledTypeName;
 
        private ReadOnlyMemory<char> _unmangledTypeNameMemory;
 
        /// <summary>
        /// Arity of the type inferred based on the name mangling. It doesn't have to match the actual
        /// arity of the type.
        /// </summary>
        private short _inferredArity;
 
        /// <summary>
        /// While resolving the name, consider only types with this arity.
        /// (-1) means allow any arity.
        /// If forcedArity >= 0 and useCLSCompliantNameArityEncoding, lookup may
        /// fail because forcedArity doesn't match the one encoded in the name.
        /// </summary>
        private short _forcedArity;
 
        /// <summary>
        /// While resolving the name, consider only types following 
        /// CLS-compliant generic type names and arity encoding (ECMA-335, section 10.7.2).
        /// I.e. arity is inferred from the name and matching type must have the same
        /// emitted name and arity.
        /// TODO: PERF: Encode this field elsewhere to save 4 bytes
        /// </summary>
        private bool _useCLSCompliantNameArityEncoding;
 
        /// <summary>
        /// Individual parts of qualified namespace name.
        /// </summary>
        private ImmutableArray<string> _namespaceSegments;
 
        /// <summary>
        /// <see cref="ReadOnlyMemory{T}"/> version of <see cref="_namespaceSegments"/>.  Preferred when possible to
        /// avoid the copies of the portions of <see cref="_fullName"/> used for <see cref="_namespaceSegments"/>.
        /// </summary>
        private ImmutableArray<ReadOnlyMemory<char>> _namespaceSegmentsMemory;
 
        public static MetadataTypeName FromFullName(string fullName, bool useCLSCompliantNameArityEncoding = false, int forcedArity = -1)
        {
            Debug.Assert(fullName != null);
            Debug.Assert(forcedArity >= -1 && forcedArity < short.MaxValue);
            Debug.Assert(forcedArity == -1 ||
                         !useCLSCompliantNameArityEncoding ||
                         forcedArity == MetadataHelpers.InferTypeArityFromMetadataName(fullName),
                         "Conflicting metadata type name resolution options!");
 
            MetadataTypeName name;
 
            name._fullName = fullName;
            name._namespaceName = null;
            name._namespaceNameMemory = default;
            name._typeName = null;
            name._typeNameMemory = default;
            name._unmangledTypeName = null;
            name._unmangledTypeNameMemory = default;
            name._inferredArity = -1;
            name._useCLSCompliantNameArityEncoding = useCLSCompliantNameArityEncoding;
            name._forcedArity = (short)forcedArity;
            name._namespaceSegments = default(ImmutableArray<string>);
            name._namespaceSegmentsMemory = default;
 
            return name;
        }
 
        public static MetadataTypeName FromNamespaceAndTypeName(
            string namespaceName, string typeName,
            bool useCLSCompliantNameArityEncoding = false, int forcedArity = -1
        )
        {
            Debug.Assert(namespaceName != null);
            Debug.Assert(typeName != null);
            Debug.Assert(forcedArity >= -1 && forcedArity < short.MaxValue);
            Debug.Assert(!typeName.Contains(MetadataHelpers.DotDelimiterString));
            Debug.Assert(forcedArity == -1 ||
                         !useCLSCompliantNameArityEncoding ||
                         forcedArity == MetadataHelpers.InferTypeArityFromMetadataName(typeName),
                         "Conflicting metadata type name resolution options!");
 
            MetadataTypeName name;
 
            name._fullName = null;
            name._namespaceName = namespaceName;
            name._namespaceNameMemory = namespaceName.AsMemory();
            name._typeName = typeName;
            name._typeNameMemory = typeName.AsMemory();
            name._unmangledTypeName = null;
            name._unmangledTypeNameMemory = default;
            name._inferredArity = -1;
            name._useCLSCompliantNameArityEncoding = useCLSCompliantNameArityEncoding;
            name._forcedArity = (short)forcedArity;
            name._namespaceSegments = default(ImmutableArray<string>);
            name._namespaceSegmentsMemory = default;
 
            return name;
        }
 
        public static MetadataTypeName FromTypeName(string typeName, bool useCLSCompliantNameArityEncoding = false, int forcedArity = -1)
        {
            Debug.Assert(typeName != null);
            Debug.Assert(!typeName.Contains(MetadataHelpers.DotDelimiterString) || typeName.IndexOf(MetadataHelpers.MangledNameRegionStartChar) >= 0);
            Debug.Assert(forcedArity >= -1 && forcedArity < short.MaxValue);
            Debug.Assert(forcedArity == -1 ||
                         !useCLSCompliantNameArityEncoding ||
                         forcedArity == MetadataHelpers.InferTypeArityFromMetadataName(typeName),
                         "Conflicting metadata type name resolution options!");
 
            MetadataTypeName name;
 
            name._fullName = typeName;
            name._namespaceName = string.Empty;
            name._namespaceNameMemory = string.Empty.AsMemory();
            name._typeName = typeName;
            name._typeNameMemory = typeName.AsMemory();
            name._unmangledTypeName = null;
            name._unmangledTypeNameMemory = default;
            name._inferredArity = -1;
            name._useCLSCompliantNameArityEncoding = useCLSCompliantNameArityEncoding;
            name._forcedArity = (short)forcedArity;
            name._namespaceSegments = ImmutableArray<string>.Empty;
            name._namespaceSegmentsMemory = ImmutableArray<ReadOnlyMemory<char>>.Empty;
 
            return name;
        }
 
        /// <summary>
        /// Full metadata name of a type, includes namespace name for top level types.
        /// </summary>
        public string FullName
        {
            get
            {
                if (_fullName == null)
                {
                    Debug.Assert(_namespaceName != null);
                    Debug.Assert(_typeName != null);
                    _fullName = MetadataHelpers.BuildQualifiedName(_namespaceName, _typeName);
                }
 
                return _fullName;
            }
        }
 
        /// <inheritdoc cref="NamespaceName"/>
        public ReadOnlyMemory<char> NamespaceNameMemory
        {
            get
            {
                if (_namespaceNameMemory.Equals(default(ReadOnlyMemory<char>)))
                {
                    Debug.Assert(_fullName != null);
                    _typeNameMemory = MetadataHelpers.SplitQualifiedName(_fullName, out _namespaceNameMemory);
                }
 
                return _namespaceNameMemory;
            }
        }
 
        /// <summary>
        /// Namespace name for top level types, empty string for nested types.
        /// </summary>
        public string NamespaceName => _namespaceName ??= NamespaceNameMemory.ToString();
 
        /// <inheritdoc cref="TypeName"/>
        public ReadOnlyMemory<char> TypeNameMemory
        {
            get
            {
                if (_typeNameMemory.Equals(default(ReadOnlyMemory<char>)))
                {
                    Debug.Assert(_fullName != null);
                    _typeNameMemory = MetadataHelpers.SplitQualifiedName(_fullName, out _namespaceNameMemory);
                }
 
                return _typeNameMemory;
            }
        }
 
        /// <summary>
        /// Name of the type without namespace prefix, but possibly with generic arity mangling present.
        /// </summary>
        public string TypeName => _typeName ??= TypeNameMemory.ToString();
 
        /// <inheritdoc cref="UnmangledTypeName"/>
        public ReadOnlyMemory<char> UnmangledTypeNameMemory
        {
            get
            {
                if (_unmangledTypeNameMemory.Equals(default(ReadOnlyMemory<char>)))
                {
                    Debug.Assert(_inferredArity == -1);
                    _unmangledTypeNameMemory = MetadataHelpers.InferTypeArityAndUnmangleMetadataName(TypeNameMemory, out _inferredArity);
                }
 
                return _unmangledTypeNameMemory;
            }
        }
 
        /// <summary>
        /// Name of the type without namespace prefix and without generic arity mangling.
        /// </summary>
        public string UnmangledTypeName
        {
            get
            {
                // Common case optimization.  If the type name is not generic, then point directly at TypeName
                // instead of allocating a new string.
                _unmangledTypeName ??= UnmangledTypeNameMemory.Equals(TypeNameMemory)
                    ? TypeName
                    : UnmangledTypeNameMemory.ToString();
 
                return _unmangledTypeName;
            }
        }
 
        /// <summary>
        /// Arity of the type inferred based on the name mangling. It doesn't have to match the actual
        /// arity of the type.
        /// </summary>
        public int InferredArity
        {
            get
            {
                if (_inferredArity == -1)
                {
                    Debug.Assert(_unmangledTypeNameMemory.Equals(default(ReadOnlyMemory<char>)));
                    Debug.Assert(_unmangledTypeName == null);
                    _unmangledTypeNameMemory = MetadataHelpers.InferTypeArityAndUnmangleMetadataName(TypeNameMemory, out _inferredArity);
                }
 
                return _inferredArity;
            }
        }
 
        /// <summary>
        /// Does name include arity mangling suffix.
        /// </summary>
        public bool IsMangled
        {
            get
            {
                return InferredArity > 0;
            }
        }
 
        /// <summary>
        /// While resolving the name, consider only types following 
        /// CLS-compliant generic type names and arity encoding (ECMA-335, section 10.7.2).
        /// I.e. arity is inferred from the name and matching type must have the same
        /// emitted name and arity.
        /// </summary>
        public readonly bool UseCLSCompliantNameArityEncoding
        {
            get
            {
                return _useCLSCompliantNameArityEncoding;
            }
        }
 
        /// <summary>
        /// While resolving the name, consider only types with this arity.
        /// (-1) means allow any arity.
        /// If ForcedArity >= 0 and UseCLSCompliantNameArityEncoding, lookup may
        /// fail because ForcedArity doesn't match the one encoded in the name.
        /// </summary>
        public readonly int ForcedArity
        {
            get
            {
                return _forcedArity;
            }
        }
 
        /// <inheritdoc cref="NamespaceSegments"/>
        public ImmutableArray<ReadOnlyMemory<char>> NamespaceSegmentsMemory
        {
            get
            {
                if (_namespaceSegmentsMemory.IsDefault)
                {
                    _namespaceSegmentsMemory = MetadataHelpers.SplitQualifiedName(NamespaceNameMemory);
                }
 
                return _namespaceSegmentsMemory;
            }
        }
 
        /// <summary>
        /// Individual parts of qualified namespace name.
        /// </summary>
        public ImmutableArray<string> NamespaceSegments
        {
            get
            {
                if (_namespaceSegments.IsDefault)
                {
                    _namespaceSegments = NamespaceSegmentsMemory.SelectAsArray(static s => s.ToString());
                }
 
                return _namespaceSegments;
            }
        }
 
        public readonly bool IsNull
        {
            get
            {
                return _typeName == null && _fullName == null;
            }
        }
 
        public override string ToString()
        {
            if (IsNull)
            {
                return "{Null}";
            }
            else
            {
                return String.Format("{{{0},{1},{2},{3}}}", NamespaceName, TypeName, UseCLSCompliantNameArityEncoding.ToString(), _forcedArity.ToString());
            }
        }
    }
}