File: Language\TypeNameObject.cs
Web Access
Project: src\src\Razor\src\Compiler\Microsoft.CodeAnalysis.Razor.Compiler\src\Microsoft.CodeAnalysis.Razor.Compiler.csproj (Microsoft.CodeAnalysis.Razor.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 System.Collections.Frozen;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using Microsoft.AspNetCore.Razor.Utilities;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Razor;
 
namespace Microsoft.AspNetCore.Razor.Language;
 
internal readonly struct TypeNameObject
{
    private readonly struct TypeNameInfo(string fullName, string? @namespace, string? name, string? alias)
    {
        public string FullName { get; } = fullName;
        public string? Namespace { get; } = @namespace;
        public string? Name { get; } = name;
        public string? Alias { get; } = alias;
    }
 
    private static readonly ImmutableArray<TypeNameInfo> s_knownTypeNames;
    private static readonly FrozenDictionary<string, byte> s_typeNameToIndex;
 
    private static readonly int s_booleanIndex;
    private static readonly int s_stringIndex;
 
    static TypeNameObject()
    {
        var knownTypeNames = ImmutableArray.CreateBuilder<TypeNameInfo>();
        var typeNameToIndex = new Dictionary<string, byte>(StringComparer.Ordinal);
 
        Add<object>("object");
        s_booleanIndex = Add<bool>("bool");
        Add<int>("int");
        Add<long>("long");
        Add<short>("short");
        Add<byte>("byte");
        Add<sbyte>("sbyte");
        Add<uint>("uint");
        Add<ulong>("ulong");
        Add<ushort>("ushort");
        Add<float>("float");
        Add<double>("double");
        Add<decimal>("decimal");
        Add<char>("char");
        s_stringIndex = Add<string>("string");
        Add<System.Globalization.CultureInfo>();
        Add<Delegate>();
        Add<Type>();
 
        // Add any additional types here.
 
        s_knownTypeNames = knownTypeNames.ToImmutable();
        s_typeNameToIndex = typeNameToIndex.ToFrozenDictionary(StringComparer.Ordinal);
 
        int Add<T>(string? alias = null)
        {
            Debug.Assert(knownTypeNames.Count < byte.MaxValue, "Too many known type names to fit in a byte index.");
 
            var fullName = typeof(T).FullName!;
            var @namespace = typeof(T).Namespace;
            var name = typeof(T).Name;
 
            var index = (byte)knownTypeNames.Count;
            knownTypeNames.Add(new(fullName, @namespace, name, alias));
            typeNameToIndex.Add(fullName, index);
 
            if (alias is not null)
            {
                typeNameToIndex.Add(alias, index);
            }
 
            return index;
        }
    }
 
    private readonly byte? _index;
    private readonly TypeNameInfo? _info;
 
    public TypeNameObject(byte index)
    {
        Debug.Assert(index >= 0 && index < s_knownTypeNames.Length);
 
        _index = index;
        _info = null;
    }
 
    public TypeNameObject(string? stringValue)
    {
        Debug.Assert(stringValue is null || !s_typeNameToIndex.ContainsKey(stringValue));
 
        _index = null;
        _info = new(stringValue!, @namespace: null, name: null, alias: null);
    }
 
    private TypeNameObject(TypeNameInfo info, byte? index)
    {
        _index = index;
        _info = info;
    }
 
    public bool IsNull => _index is null && _info is null;
 
    public byte? Index => _index;
 
    public static TypeNameObject From(string? fullName)
    {
        if (fullName is null)
        {
            return default;
        }
 
        return From(fullName, namespaceName: null, name: null);
    }
 
    public static TypeNameObject From(string fullName, string? namespaceName, string? name)
    {
        if (s_typeNameToIndex.TryGetValue(fullName, out var index))
        {
            var info = s_knownTypeNames[index];
            return new(info, index);
        }
 
        return new(new(fullName, namespaceName, name, alias: null), index: null);
    }
 
    public static TypeNameObject From<T>()
        => From(typeof(T));
 
    public static TypeNameObject From(Type type)
    {
        var fullName = type.FullName!;
 
        if (s_typeNameToIndex.TryGetValue(fullName, out var index))
        {
            var info = s_knownTypeNames[index];
            return new(info, index);
        }
 
        var @namespace = type.Namespace;
        var name = type.Name;
 
        return new(new(fullName, @namespace, name, alias: null), index: null);
    }
 
    public static TypeNameObject From(INamedTypeSymbol namedTypeSymbol)
    {
        var fullName = namedTypeSymbol.GetFullName();
 
        if (s_typeNameToIndex.TryGetValue(fullName, out var index))
        {
            var info = s_knownTypeNames[index];
            return new(info, index);
        }
 
        var @namespace = namedTypeSymbol.ContainingNamespace.GetFullName();
        var name = namedTypeSymbol.Name;
 
        return new(new(fullName, @namespace, name, alias: null), index: null);
    }
 
    public bool IsBoolean => _index == s_booleanIndex;
    public bool IsString => _index == s_stringIndex;
 
    public readonly string? FullName
    {
        get
        {
            if (_info is { FullName: var fullName })
            {
                return fullName;
            }
 
            if (_index is byte index)
            {
                return s_knownTypeNames[index].FullName;
            }
 
            return null;
        }
    }
 
    public readonly string? Namespace
    {
        get
        {
            if (_info is { Namespace: var @namespace })
            {
                return @namespace;
            }
 
            if (_index is byte index)
            {
                return s_knownTypeNames[index].Namespace;
            }
 
            return null;
        }
    }
 
    public readonly string? Name
    {
        get
        {
            if (_info is { Name: var name })
            {
                return name;
            }
 
            if (_index is byte index)
            {
                return s_knownTypeNames[index].Name;
            }
 
            return null;
        }
    }
 
    public void AppendToChecksum(in Checksum.Builder builder)
    {
        if (_index is byte index)
        {
            builder.Append(index);
        }
        else if (_info is TypeNameInfo info)
        {
            builder.Append(info.FullName);
            builder.Append(info.Namespace);
            builder.Append(info.Name);
        }
        else
        {
            builder.AppendNull();
        }
    }
}