File: Actions\GrammarActions.Types.Headers.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.Immutable;
using System.Diagnostics;
using System.Reflection;
using Antlr4.Runtime;

namespace ILAssembler;

internal sealed partial class GrammarActions
{
    internal void BeginNamespace(
        CILParser.NameSpaceHeadContext context,
        string? namespaceName,
        int initialSyntaxErrorCount)
    {
        if (!EnterDeclarationScope(
                context.Parent,
                !HasSyntaxErrorsSince(initialSyntaxErrorCount) &&
                context.exception is null &&
                namespaceName is not null))
        {
            return;
        }

        Debug.Assert(namespaceName is not null);
        string? outerNamespace = _currentNamespace.PeekOrDefault();
        _currentNamespace.Push(
            string.IsNullOrEmpty(outerNamespace)
                ? namespaceName
                : $"{outerNamespace}.{namespaceName}");
        _namespaceOwners.Push(context.Parent);
    }

    internal void BeginType(CILParser.ClassHeadContext context, ClassHeaderValue value)
    {
        if (!EnterDeclarationScope(context.Parent, value.IsValid))
        {
            return;
        }

        EntityRegistry.TypeDefinitionEntity typeDefinition = MaterializeClassHeader(context, value);
        _currentTypeDefinition.Push(typeDefinition);
        _typeOwners.Push(context.Parent);
    }

    private EntityRegistry.TypeDefinitionEntity MaterializeClassHeader(
        CILParser.ClassHeadContext context,
        ClassHeaderValue header)
    {
        (string typeNamespace, string typeName) = GetTypeDefinitionName(header.FullName);
        bool isNewType = false;
        EntityRegistry.TypeDefinitionEntity typeDefinition =
            _entityRegistry.GetOrCreateTypeDefinition(
                _currentTypeDefinition.PeekOrDefault(),
                typeNamespace,
                typeName,
                newTypeDefinition =>
                {
                    isNewType = true;
                    InitializeTypeDefinition(context, header, newTypeDefinition);
                });

        if (!isNewType)
        {
            MergeTypeDefinition(context, header, typeDefinition);
        }

        return typeDefinition;
    }

    private (string Namespace, string Name) GetTypeDefinitionName(string fullName)
    {
        int lastDot = fullName.LastIndexOf('.');
        if (lastDot == 0)
        {
            lastDot = -1;
        }

        string typeNamespace;
        if (_currentTypeDefinition.Count != 0)
        {
            typeNamespace = lastDot == -1 ? string.Empty : fullName.Substring(0, lastDot);
        }
        else if (lastDot == -1)
        {
            typeNamespace = _currentNamespace.PeekOrDefault() ?? string.Empty;
        }
        else
        {
            string containingNamespace = fullName.Substring(0, lastDot);
            string? currentNamespace = _currentNamespace.PeekOrDefault();
            typeNamespace = string.IsNullOrEmpty(currentNamespace)
                ? containingNamespace
                : $"{currentNamespace}.{containingNamespace}";
        }

        return (
            typeNamespace,
            lastDot == -1 ? fullName : fullName.Substring(lastDot + 1));
    }

    private void InitializeTypeDefinition(
        CILParser.ClassHeadContext context,
        ClassHeaderValue header,
        EntityRegistry.TypeDefinitionEntity typeDefinition)
    {
        EntityRegistry.WellKnownBaseType? fallbackBase =
            _options.NoAutoInherit ? null : EntityRegistry.WellKnownBaseType.System_Object;
        bool requireSealed = false;
        TypeAttributes attributes = 0;
        foreach (ClassAttributeValue classAttribute in header.Attributes)
        {
            if (classAttribute.FallbackBase is not null)
            {
                fallbackBase = classAttribute.FallbackBase;
            }

            CILParser.AttributeValue<TypeAttributes> attribute = classAttribute.Attribute;
            if (!attribute.ShouldAppend)
            {
                attributes = attribute.Value;
                requireSealed = classAttribute.RequireSealed;
                continue;
            }

            requireSealed |= classAttribute.RequireSealed;
            if (attribute.Value == TypeAttributes.RTSpecialName)
            {
                continue;
            }

            attributes = ApplyAttribute(attributes, attribute);
        }
        typeDefinition.Attributes = attributes;
        ReportGenericParameterEncodingErrors(header.GenericParameters, context);
        RegisterGenericParameterNames(
            typeDefinition,
            typeDefinition.GenericParameters,
            header.GenericParameters);

        _currentTypeDefinition.Push(typeDefinition);
        try
        {
            MaterializeGenericParameterConstraints(
                typeDefinition.GenericParameters,
                typeDefinition.GenericParameterConstraints,
                header.GenericParameters);
            if (header.BaseType is not null)
            {
                typeDefinition.BaseType = ResolveTypeSpecification(header.BaseType);
            }

            AddInterfaceImplementations(typeDefinition, header.Interfaces);
        }
        finally
        {
            _currentTypeDefinition.Pop();
        }

        if (typeDefinition.Attributes.HasFlag(TypeAttributes.Interface))
        {
            fallbackBase = null;
        }

        typeDefinition.BaseType ??= _entityRegistry.ResolveImplicitBaseType(fallbackBase);
        if (!typeDefinition.Attributes.HasFlag(TypeAttributes.Sealed) &&
            (requireSealed || _entityRegistry.SystemValueTypeType.Equals(typeDefinition.BaseType)))
        {
            IToken location = header.NameToken ?? context.Start;
            _diagnostics.Add(
                new Diagnostic(
                    DiagnosticIds.UnsealedValueType,
                    DiagnosticSeverity.Error,
                    string.Format(DiagnosticMessageTemplates.UnsealedValueType, typeDefinition.Name),
                    Location.From(location, _documents)));
            typeDefinition.Attributes |= TypeAttributes.Sealed;
        }
    }

    private void MergeTypeDefinition(
        CILParser.ClassHeadContext context,
        ClassHeaderValue header,
        EntityRegistry.TypeDefinitionEntity typeDefinition)
    {
        TypeAttributes attributes = typeDefinition.Attributes;
        foreach (ClassAttributeValue classAttribute in header.Attributes)
        {
            CILParser.AttributeValue<TypeAttributes> attribute = classAttribute.Attribute;
            if (!attribute.ShouldAppend)
            {
                attributes = attribute.Value;
            }
            else if ((attribute.Value & TypeAttributes.Interface) != 0)
            {
                attributes |= TypeAttributes.Interface | TypeAttributes.Abstract;
            }
            else
            {
                attributes |= attribute.Value;
            }
        }
        typeDefinition.Attributes = attributes;

        bool materializeConstraints = typeDefinition.GenericParameters.Count == 0;
        if (materializeConstraints)
        {
            ReportGenericParameterEncodingErrors(header.GenericParameters, context);
            RegisterGenericParameterNames(
                typeDefinition,
                typeDefinition.GenericParameters,
                header.GenericParameters);
        }

        _currentTypeDefinition.Push(typeDefinition);
        try
        {
            if (materializeConstraints)
            {
                MaterializeGenericParameterConstraints(
                    typeDefinition.GenericParameters,
                    typeDefinition.GenericParameterConstraints,
                    header.GenericParameters);
            }

            if (header.BaseType is not null)
            {
                EntityRegistry.TypeEntity baseType = ResolveTypeSpecification(header.BaseType);
                typeDefinition.BaseType ??= baseType;
            }

            AddInterfaceImplementations(typeDefinition, header.Interfaces);
        }
        finally
        {
            _currentTypeDefinition.Pop();
        }
    }

    private void AddInterfaceImplementations(
        EntityRegistry.TypeDefinitionEntity typeDefinition,
        ImmutableArray<TypeSpecificationValue> interfaces)
    {
        foreach (TypeSpecificationValue interfaceType in interfaces)
        {
            typeDefinition.InterfaceImplementations.Add(
                EntityRegistry.CreateUnrecordedInterfaceImplementation(
                    typeDefinition,
                    ResolveTypeSpecification(interfaceType)));
        }
    }
}