File: Actions\GrammarActions.Signatures.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.Immutable;
using System.Linq;
using System.Reflection.Metadata;
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 MethodReferenceValue CreateMethodReference(
        IToken token,
        byte callingConvention,
        TypeValue returnType,
        TypeSpecificationValue? owner,
        string name,
        ImmutableArray<TypeValue>? genericArguments,
        int? genericArity,
        ImmutableArray<SignatureArgumentValue> arguments)
        => new ParsedMethodReferenceValue(
            token,
            callingConvention,
            returnType,
            owner,
            name,
            genericArguments,
            genericArity.GetValueOrDefault(),
            arguments);

    internal MethodReferenceValue CreateTokenMethodReference(int token)
        => new TokenMethodReferenceValue(token);

    internal MethodReferenceValue CreateTypedefMethodReference(IToken token, string alias)
        => new TypedefMethodReferenceValue(token, alias);

    internal FieldReferenceValue CreateFieldReference(
        TypeValue fieldType,
        TypeSpecificationValue? owner,
        string name)
        => new ParsedFieldReferenceValue(
            fieldType,
            owner,
            name);

    internal FieldReferenceValue CreateTypedefFieldReference(IToken token, string alias)
        => new TypedefFieldReferenceValue(token, alias);

    internal MemberReferenceValue CreateMethodMemberReference(MethodReferenceValue method)
        => new MethodMemberReferenceValue(method);

    internal MemberReferenceValue CreateFieldMemberReference(FieldReferenceValue field)
        => new FieldMemberReferenceValue(field);

    internal MemberReferenceValue CreateTokenMemberReference(int token)
        => new TokenMemberReferenceValue(token);

    internal OwnerTypeValue CreateTypeOwner(TypeSpecificationValue type)
        => new TypeOwnerValue(type);

    internal OwnerTypeValue CreateMemberOwner(MemberReferenceValue member)
        => new MemberOwnerValue(member);

    private EntityRegistry.EntityBase MaterializeMethodReference(MethodReferenceValue methodReference)
    {
        switch (methodReference)
        {
            case TokenMethodReferenceValue token:
                return ResolveMetadataToken(token.Token);
            case TypedefMethodReferenceValue typedef:
                if (TryResolveTypedefAsMember(typedef.Alias) is { } resolved)
                {
                    return resolved;
                }
                ReportError(
                    DiagnosticIds.TypedefNotFound,
                    string.Format(DiagnosticMessageTemplates.TypedefNotFound, typedef.Alias),
                    typedef.Token);
                return CreateErrorMethodReference(typedef.Alias);
            case ParsedMethodReferenceValue parsed:
                return MaterializeParsedMethodReference(parsed);
            default:
                return CreateErrorMethodReference("<error>");
        }
    }

    private EntityRegistry.EntityBase MaterializeParsedMethodReference(ParsedMethodReferenceValue methodReference)
    {
        byte callingConvention = methodReference.CallingConvention;
        EntityRegistry.TypeEntity owner = methodReference.Owner is null
            ? _entityRegistry.ModuleType
            : ResolveTypeSpecification(methodReference.Owner);

        BlobBuilder? methodSpecificationSignature = null;
        int genericArity = methodReference.GenericArity;
        if (methodReference.GenericArguments is { } genericArguments)
        {
            genericArity = genericArguments.Length;
            if (genericArity != 0)
            {
                methodSpecificationSignature = new BlobBuilder();
                methodSpecificationSignature.WriteByte((byte)SignatureKind.MethodSpecification);
                MaterializeTypeArguments(genericArguments).WriteContentTo(methodSpecificationSignature);
            }
        }

        if (genericArity != 0)
        {
            callingConvention |= (byte)SignatureAttributes.Generic;
        }

        if (_expectInstance && (callingConvention & (byte)SignatureAttributes.Instance) == 0)
        {
            ReportWarning(
                DiagnosticIds.MissingInstanceCallConv,
                DiagnosticMessageTemplates.MissingInstanceCallConv,
                methodReference.Token);
            callingConvention |= (byte)SignatureAttributes.Instance;
        }

        BlobBuilder signature = new();
        signature.WriteByte(callingConvention);
        if (genericArity != 0)
        {
            signature.WriteCompressedInteger(genericArity);
        }

        ImmutableArray<SignatureArg> arguments = MaterializeSignatureArguments(methodReference.Arguments);
        signature.WriteCompressedInteger(arguments.Count(argument => !argument.IsSentinel));
        MaterializeType(methodReference.ReturnType).WriteContentTo(signature);
        foreach (SignatureArg argument in arguments)
        {
            argument.SignatureBlob.WriteContentTo(signature);
        }

        EntityRegistry.MemberReferenceEntity memberReference =
            _entityRegistry.CreateLazilyRecordedMemberReference(owner, methodReference.Name, signature);
        return methodSpecificationSignature is null
            ? memberReference
            : _entityRegistry.GetOrCreateMethodSpecification(memberReference, methodSpecificationSignature);
    }

    private EntityRegistry.EntityBase MaterializeFieldReference(FieldReferenceValue fieldReference)
    {
        switch (fieldReference)
        {
            case TypedefFieldReferenceValue typedef:
                if (TryResolveTypedefAsMember(typedef.Alias) is { } resolved)
                {
                    return resolved;
                }
                ReportError(
                    DiagnosticIds.TypedefNotFound,
                    string.Format(DiagnosticMessageTemplates.TypedefNotFound, typedef.Alias),
                    typedef.Token);
                return CreateErrorFieldReference(typedef.Alias);
            case ParsedFieldReferenceValue parsed:
                BlobBuilder fieldType = MaterializeType(parsed.FieldType);
                EntityRegistry.TypeEntity owner = parsed.Owner is null
                    ? _entityRegistry.ModuleType
                    : ResolveTypeSpecification(parsed.Owner);
                BlobBuilder signature = new(fieldType.Count + 1);
                signature.WriteByte((byte)SignatureKind.Field);
                fieldType.WriteContentTo(signature);
                return _entityRegistry.CreateLazilyRecordedMemberReference(owner, parsed.Name, signature);
            default:
                return CreateErrorFieldReference("<error>");
        }
    }

    private EntityRegistry.EntityBase MaterializeMemberReference(MemberReferenceValue memberReference)
        => memberReference switch
        {
            MethodMemberReferenceValue method => MaterializeMethodReference(method.Method),
            FieldMemberReferenceValue field => MaterializeFieldReference(field.Field),
            TokenMemberReferenceValue token => ResolveMetadataToken(token.Token),
            _ => CreateErrorMethodReference("<error>")
        };

    private EntityRegistry.MemberReferenceEntity CreateErrorMethodReference(string name)
    {
        BlobBuilder signature = new(3);
        signature.WriteByte((byte)SignatureCallingConvention.Default);
        signature.WriteCompressedInteger(0);
        signature.WriteByte((byte)SignatureTypeCode.Void);
        return _entityRegistry.CreateLazilyRecordedMemberReference(
            _entityRegistry.ModuleType,
            name,
            signature);
    }

    private EntityRegistry.MemberReferenceEntity CreateErrorFieldReference(string name)
    {
        BlobBuilder signature = new(2);
        signature.WriteByte((byte)SignatureKind.Field);
        signature.WriteByte((byte)SignatureTypeCode.Object);
        return _entityRegistry.CreateLazilyRecordedMemberReference(
            _entityRegistry.ModuleType,
            name,
            signature);
    }

    private EntityRegistry.EntityBase MaterializeOwnerType(OwnerTypeValue owner)
        => owner switch
        {
            TypeOwnerValue type => ResolveTypeSpecification(type.Type),
            MemberOwnerValue member => MaterializeMemberReference(member.Member),
            _ => new EntityRegistry.FakeTypeEntity(default(TypeDefinitionHandle))
        };
}