File: Actions\GrammarActions.MethodHeaders.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.Reflection;
using System.Reflection.Metadata;

namespace ILAssembler;

internal sealed partial class GrammarActions
{
    internal void BeginMethod(CILParser.MethodHeadContext context, MethodHeaderValue value)
    {
        ResetMethodBodyState();
        if (IsDeclarationSuppressed || !value.IsValid)
        {
            return;
        }

        MethodHeaderValue header = value;

        EntityRegistry.TypeDefinitionEntity containingType =
            _currentTypeDefinition.PeekOrDefault() ?? _entityRegistry.ModuleType;
        EntityRegistry.MethodDefinitionEntity methodDefinition =
            EntityRegistry.CreateUnrecordedMethodDefinition(containingType, header.Name);

        _currentMethod = new(methodDefinition);
        try
        {
            ReportGenericParameterEncodingErrors(header.GenericParameters, context);
            RegisterGenericParameterNames(
                methodDefinition,
                methodDefinition.GenericParameters,
                header.GenericParameters);

            methodDefinition.MethodAttributes = header.Attributes;
            ApplyImplicitMethodAttributes(methodDefinition);

            if (methodDefinition.MethodAttributes.HasFlag(MethodAttributes.Abstract) &&
                !methodDefinition.ContainingType.Attributes.HasFlag(TypeAttributes.Abstract))
            {
                ReportWarning(
                    DiagnosticIds.AbstractMethodNotInAbstractType,
                    string.Format(
                        DiagnosticMessageTemplates.AbstractMethodNotInAbstractType,
                        methodDefinition.Name),
                    context);
            }

            ApplyPInvokeInformation(methodDefinition, header, context);

            byte signatureHeader = header.CallingConvention;
            if (header.GenericParameters.Length != 0)
            {
                signatureHeader |= (byte)SignatureAttributes.Generic;
            }

            SignatureHeader parsedHeader = new(signatureHeader);
            if (!methodDefinition.MethodAttributes.HasFlag(MethodAttributes.Static) &&
                !parsedHeader.IsInstance &&
                _currentTypeDefinition.Count > 0)
            {
                signatureHeader |= (byte)SignatureAttributes.Instance;
                parsedHeader = new(signatureHeader);
            }
            if (parsedHeader.HasExplicitThis && !parsedHeader.IsInstance)
            {
                signatureHeader |= (byte)SignatureAttributes.Instance;
                parsedHeader = new(signatureHeader);
            }

            BlobBuilder methodSignature = new();
            methodSignature.WriteByte(signatureHeader);
            if (header.GenericParameters.Length != 0)
            {
                methodSignature.WriteCompressedInteger(header.GenericParameters.Length);
            }

            MaterializeGenericParameterConstraints(
                methodDefinition.GenericParameters,
                methodDefinition.GenericParameterConstraints,
                header.GenericParameters);

            ImmutableArray<SignatureArg> arguments = MaterializeSignatureArguments(header.Arguments);
            methodSignature.WriteCompressedInteger(arguments.Length);

            SignatureArg returnValue = new(
                (ParameterAttributes)header.ReturnAttributes,
                MaterializeType(header.ReturnType),
                MaterializeMarshallingDescriptor(header.ReturnMarshalling),
                null);
            returnValue.SignatureBlob.WriteContentTo(methodSignature);
            methodDefinition.Parameters.Add(
                EntityRegistry.CreateParameter(
                    returnValue.Attributes,
                    returnValue.Name,
                    returnValue.MarshallingDescriptor,
                    0));

            for (int i = 0; i < arguments.Length; i++)
            {
                SignatureArg argument = arguments[i];
                argument.SignatureBlob.WriteContentTo(methodSignature);
                string? parameterName = argument.Name ?? $"A_{i}";
                methodDefinition.Parameters.Add(
                    EntityRegistry.CreateParameter(
                        argument.Attributes,
                        parameterName,
                        argument.MarshallingDescriptor,
                        i + 1));
            }

            methodDefinition.SignatureHeader = parsedHeader;
            methodDefinition.MethodSignature = methodSignature;
            methodDefinition.ImplementationAttributes = header.ImplementationAttributes;

            if (!EntityRegistry.TryAddMethodDefinitionToContainingType(methodDefinition))
            {
                ReportError(
                    DiagnosticIds.DuplicateMethod,
                    DiagnosticMessageTemplates.DuplicateMethod,
                    context);
            }

            _currentMethod = new(methodDefinition);
            _methodOwner = context.Parent;
        }
        catch
        {
            _currentMethod = null;
            _methodOwner = null;
            ResetMethodBodyState();
            throw;
        }
    }

    private static void ApplyImplicitMethodAttributes(EntityRegistry.MethodDefinitionEntity method)
    {
        if (method.Name is ".ctor" or ".cctor")
        {
            method.MethodAttributes |= MethodAttributes.RTSpecialName | MethodAttributes.SpecialName;
        }
        else if (method.MethodAttributes.HasFlag(MethodAttributes.RTSpecialName))
        {
            method.MethodAttributes |= MethodAttributes.SpecialName;
        }
    }

    private void ApplyPInvokeInformation(
        EntityRegistry.MethodDefinitionEntity method,
        MethodHeaderValue header,
        CILParser.MethodHeadContext context)
    {
        (EntityRegistry.ModuleReferenceEntity Module, string? EntryPoint, MethodImportAttributes Attributes)?
            pInvokeInformation = null;

        foreach (PInvokeValue pInvoke in header.PInvokes)
        {
            if (pInvoke.ModuleName is null)
            {
                ReportError(
                    DiagnosticIds.InvalidPInvokeSignature,
                    DiagnosticMessageTemplates.InvalidPInvokeSignature,
                    context);
                continue;
            }

            pInvokeInformation = (
                _entityRegistry.GetOrCreateModuleReference(pInvoke.ModuleName, _ => { }),
                pInvoke.EntryPointName ?? header.Name,
                pInvoke.Attributes);
        }

        method.MethodImportInformation = pInvokeInformation;
    }

}