File: Actions\GrammarActions.Types.References.cs
Web Access
Project: ILAssembler.csproj (ILAssembler)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using Antlr4.Runtime;

namespace ILAssembler;

#pragma warning disable CA1822 // Parser actions are invoked through the per-parser GrammarActions instance.
internal sealed partial class GrammarActions
{
    internal TypeName AddSlashedNamePart(TypeName? containingTypeName, string name)
        => new(containingTypeName, name);

    internal ClassNameValue CreateUnqualifiedClassName(TypeName name)
        => new UnqualifiedClassNameValue(name);

    internal ClassNameValue CreateAssemblyQualifiedClassName(string assemblyName, TypeName name)
        => new AssemblyQualifiedClassNameValue(assemblyName, name);

    internal ClassNameValue CreateModuleQualifiedClassName(
        IToken token,
        string moduleName,
        TypeName name)
        => new ModuleQualifiedClassNameValue(token, moduleName, name);

    internal ClassNameValue CreateTokenQualifiedClassName(int scopeToken, TypeName name)
        => new TokenQualifiedClassNameValue(scopeToken, name);

    internal ClassNameValue CreatePointerQualifiedClassName(TypeName name)
        => new PointerQualifiedClassNameValue(name);

    internal ClassNameValue CreateTokenClassName(int typeToken)
        => new TokenClassNameValue(typeToken);

    internal ClassNameValue CreateThisClassName(IToken token)
        => new SpecialClassNameValue(token, SpecialClassNameKind.This);

    internal ClassNameValue CreateBaseClassName(IToken token)
        => new SpecialClassNameValue(token, SpecialClassNameKind.Base);

    internal ClassNameValue CreateNesterClassName(IToken token)
        => new SpecialClassNameValue(token, SpecialClassNameKind.Nester);

    private EntityRegistry.TypeEntity ResolveClassName(ClassNameValue className)
    {
        switch (className)
        {
            case SpecialClassNameValue { Kind: SpecialClassNameKind.This } special:
                if (_currentTypeDefinition.Count == 0)
                {
                    ReportError(DiagnosticIds.ThisOutsideClass, DiagnosticMessageTemplates.ThisOutsideClass, special.Token);
                    return new EntityRegistry.FakeTypeEntity(default(TypeDefinitionHandle));
                }
                return _currentTypeDefinition.Peek();
            case SpecialClassNameValue { Kind: SpecialClassNameKind.Base } special:
                if (_currentTypeDefinition.Count == 0)
                {
                    ReportError(DiagnosticIds.BaseOutsideClass, DiagnosticMessageTemplates.BaseOutsideClass, special.Token);
                    return new EntityRegistry.FakeTypeEntity(default(TypeDefinitionHandle));
                }
                if (_currentTypeDefinition.Peek().BaseType is not { } baseType)
                {
                    ReportError(DiagnosticIds.NoBaseType, DiagnosticMessageTemplates.NoBaseType, special.Token);
                    return new EntityRegistry.FakeTypeEntity(default(TypeDefinitionHandle));
                }
                return baseType;
            case SpecialClassNameValue { Kind: SpecialClassNameKind.Nester } special:
                if (_currentTypeDefinition.Count < 2)
                {
                    ReportError(DiagnosticIds.NesterOutsideNestedClass, DiagnosticMessageTemplates.NesterOutsideNestedClass, special.Token);
                    return new EntityRegistry.FakeTypeEntity(default(TypeDefinitionHandle));
                }
                return _currentTypeDefinition.Peek().ContainingType!;
            case AssemblyQualifiedClassNameValue assembly:
                return _entityRegistry.GetOrCreateTypeReference(
                    _entityRegistry.GetOrCreateAssemblyReference(assembly.AssemblyName, _ => { }),
                    assembly.Name);
            case ModuleQualifiedClassNameValue module:
                if (_entityRegistry.FindModuleReference(module.ModuleName) is not { } moduleReference)
                {
                    ReportError(
                        DiagnosticIds.ModuleNotFound,
                        string.Format(DiagnosticMessageTemplates.ModuleNotFound, module.ModuleName),
                        module.Token);
                    return new EntityRegistry.FakeTypeEntity(default(TypeDefinitionHandle));
                }
                return _entityRegistry.GetOrCreateTypeReference(moduleReference, module.Name);
            case TokenQualifiedClassNameValue tokenScope:
                return _entityRegistry.GetOrCreateTypeReference(
                    ResolveMetadataToken(tokenScope.Token),
                    tokenScope.Name);
            case PointerQualifiedClassNameValue pointer:
                return _entityRegistry.GetOrCreateTypeReference(
                    new EntityRegistry.FakeTypeEntity(default(ModuleDefinitionHandle)),
                    pointer.Name);
            case UnqualifiedClassNameValue unqualified:
                return ResolveUnqualifiedClassName(unqualified.Name);
            case TokenClassNameValue typeToken:
                EntityRegistry.EntityBase resolvedToken = ResolveMetadataToken(typeToken.Token);
                return resolvedToken is EntityRegistry.TypeEntity type
                    ? type
                    : new EntityRegistry.FakeTypeEntity(resolvedToken.Handle);
            default:
                return new EntityRegistry.FakeTypeEntity(default(TypeDefinitionHandle));
        }
    }

    private EntityRegistry.TypeEntity ResolveUnqualifiedClassName(TypeName typeName)
    {
        if (typeName.ContainingTypeName is null && TryResolveTypedefAsType(typeName.DottedName) is { } typedef)
        {
            return typedef;
        }

        if (typeName.ContainingTypeName is null)
        {
            (string ns, string name) = NameHelpers.SplitDottedNameToNamespaceAndName(typeName.DottedName);
            if (ns == "System" && name is "String" or "Object" or "ValueType" or "Enum"
                or "Type" or "Array" or "Delegate" or "MulticastDelegate"
                or "Exception" or "Attribute")
            {
                return _entityRegistry.GetOrCreateTypeReference(_entityRegistry.GetCoreLibAssemblyReference(), typeName);
            }
        }

        Stack<TypeName> containingTypes = new();
        for (TypeName? containingType = typeName; containingType is not null; containingType = containingType.ContainingTypeName)
        {
            containingTypes.Push(containingType);
        }

        EntityRegistry.TypeDefinitionEntity? typeDefinition = null;
        while (containingTypes.Count != 0)
        {
            TypeName containingType = containingTypes.Pop();
            (string ns, string name) = NameHelpers.SplitDottedNameToNamespaceAndName(containingType.DottedName);
            typeDefinition = _entityRegistry.GetOrCreateTypeDefinition(typeDefinition, ns, name, _ => { });
        }

        Debug.Assert(typeDefinition is not null);
        return typeDefinition;
    }

    private EntityRegistry.TypeEntity ResolveTypeSpecification(TypeSpecificationValue typeSpecification)
    {
        return typeSpecification switch
        {
            ClassTypeSpecificationValue classType => ResolveClassName(classType.ClassName),
            ModuleTypeSpecificationValue module => ResolveModuleTypeSpecification(module.ModuleName),
            AssemblyTypeSpecificationValue assembly => new EntityRegistry.FakeTypeEntity(
                _entityRegistry.GetOrCreateAssemblyReference(assembly.AssemblyName, _ => { }).Handle),
            SignatureTypeSpecificationValue signature => _entityRegistry.GetOrCreateTypeSpec(MaterializeType(signature.Type)),
            _ => new EntityRegistry.FakeTypeEntity(default(TypeDefinitionHandle))
        };
    }

    private EntityRegistry.FakeTypeEntity ResolveModuleTypeSpecification(string moduleName)
    {
        EntityRegistry.ModuleReferenceEntity? module = _entityRegistry.FindModuleReference(moduleName);
        return module is null
            ? new EntityRegistry.FakeTypeEntity(MetadataTokens.ModuleReferenceHandle(0))
            : new EntityRegistry.FakeTypeEntity(module.Handle);
    }

    private EntityRegistry.EntityBase ResolveMetadataToken(int token)
        => _entityRegistry.ResolveHandleToEntity(MetadataTokens.EntityHandle(token));
}