File: Actions\GrammarActions.Manifest.Resources.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 Antlr4.Runtime;

namespace ILAssembler;

#pragma warning disable CA1822 // Parser actions are invoked through the per-parser GrammarActions instance.
internal sealed partial class GrammarActions
{
    internal ManifestResourceAttributes AddManifestResourceAttribute(
        ManifestResourceAttributes attributes,
        ManifestResourceAttributes value)
        => attributes | value;

    internal ManifestResourceAttributes ParseManifestResourceAttribute(IToken token)
        => token.Text switch
        {
            "public" => ManifestResourceAttributes.Public,
            "private" => ManifestResourceAttributes.Private,
            _ => 0
        };

    internal ManifestResourceHeaderValue CreateManifestResourceHeader(
        ManifestResourceAttributes attributes,
        string name,
        string alias,
        IToken location)
        => new ManifestResourceHeaderValue(true, attributes, name, alias, location);

    internal ManifestResourceValue CreateManifestResource(
        ManifestResourceHeaderValue header,
        ImmutableArray<ManifestResourceDeclarationValue> declarations)
        => new ManifestResourceValue(
            header,
            declarations);

    internal ManifestResourceDeclarationValue CreateManifestResourceFileDeclaration(
        string name,
        IToken offset,
        IToken location)
        => new ManifestResourceFileDirectiveValue(name, (uint)ParseInt32(offset), location);

    internal ManifestResourceDeclarationValue CreateManifestResourceAssemblyDeclaration(
        string name)
        => new ManifestResourceAssemblyDirectiveValue(name);

    internal ManifestResourceDeclarationValue CreateManifestResourceCustomAttributeDeclaration(
        CustomAttributeDeclarationValue? value,
        IToken location)
        => new ManifestResourceCustomAttributeDirectiveValue(value, location);

    private void MaterializeManifestResource(ManifestResourceValue value)
    {
        ManifestResourceHeaderValue header = value.Header;
        if (!header.IsValid ||
            header.Location is not IToken location)
        {
            return;
        }

        (
            EntityRegistry.EntityBase? implementation,
            uint offset,
            ImmutableArray<EntityRegistry.CustomAttributeEntity> customAttributes
        ) = MaterializeManifestResourceDeclarations(value.Declarations);

        if (implementation is null)
        {
            offset = (uint)_manifestResources.Count;
            byte[]? resourceData = _resourceLocator(header.Alias);
            if (resourceData is null)
            {
                ReportError(
                    DiagnosticIds.FileNotFound,
                    string.Format(
                        DiagnosticMessageTemplates.FileNotFound,
                        header.Alias),
                    location);
                _manifestResources.WriteInt32(0);
            }
            else
            {
                _manifestResources.WriteInt32(resourceData.Length);
                _manifestResources.WriteBytes(resourceData);
            }
        }

        EntityRegistry.ManifestResourceEntity resource =
            _entityRegistry.CreateManifestResource(header.Name, offset);
        resource.Attributes = header.Attributes;
        resource.Implementation = implementation;
        foreach (EntityRegistry.CustomAttributeEntity customAttribute in customAttributes)
        {
            customAttribute.Owner ??= resource;
        }
    }

    private (
        EntityRegistry.EntityBase? Implementation,
        uint Offset,
        ImmutableArray<EntityRegistry.CustomAttributeEntity> CustomAttributes
    ) MaterializeManifestResourceDeclarations(
        ImmutableArray<ManifestResourceDeclarationValue> declarations)
    {
        EntityRegistry.EntityBase? implementation = null;
        uint offset = 0;
        ImmutableArray<EntityRegistry.CustomAttributeEntity>.Builder customAttributes =
            ImmutableArray.CreateBuilder<EntityRegistry.CustomAttributeEntity>();

        foreach (ManifestResourceDeclarationValue declaration in declarations)
        {
            switch (declaration)
            {
                case ManifestResourceCustomAttributeDirectiveValue customAttribute:
                    if (MaterializeCustomAttributeDeclaration(
                            customAttribute.Value,
                            customAttribute.Location) is { } attribute)
                    {
                        customAttributes.Add(attribute);
                    }
                    break;
                case ManifestResourceFileDirectiveValue file
                    when implementation is not EntityRegistry.AssemblyReferenceEntity:
                    EntityRegistry.FileEntity? fileEntity = _entityRegistry.FindFile(file.Name);
                    if (fileEntity is null)
                    {
                        ReportError(
                            DiagnosticIds.FileNotFound,
                            string.Format(DiagnosticMessageTemplates.FileNotFound, file.Name),
                            file.Location);
                    }
                    else
                    {
                        implementation = fileEntity;
                        offset = file.Offset;
                    }
                    break;
                case ManifestResourceAssemblyDirectiveValue assembly:
                    implementation =
                        _entityRegistry.GetOrCreateAssemblyReference(assembly.Name, _ => { });
                    break;
            }
        }

        return (implementation, offset, customAttributes.ToImmutable());
    }

    internal void MaterializeManifestResource(CILParser.ManifestResBlockContext context)
    {
        if (context.Value is ManifestResourceValue value)
        {
            MaterializeManifestResource(value);
        }
    }

}