File: DocumentCompiler.cs
Web Access
Project: src\src\runtime\src\tools\ilasm\src\ILAssembler\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;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Metadata.Ecma335;
using System.Reflection.PortableExecutable;
using Antlr4.Runtime;

namespace ILAssembler;
public sealed class DocumentCompiler
{
    public (ImmutableArray<Diagnostic>, PEBuilder?) Compile(SourceText document, Func<string, SourceText> includedDocumentLoader, Func<string, byte[]> resourceLocator, Options options)
    {
        var inputSource = new AntlrInputStream(document.Text)
        {
            name = document.Path
        };
        CILLexer lexer = new(inputSource);
        Dictionary<string, SourceText> loadedDocuments = new()
        {
            {document.Path!, document }
        };
        PreprocessedTokenSource preprocessor = new(lexer, path =>
        {
            var includedDocument = includedDocumentLoader(path);

            var includedSource = new AntlrInputStream(includedDocument.Text)
            {
                name = includedDocument.Path
            };
            loadedDocuments.Add(includedDocument.Path, includedDocument);
            return new CILLexer(includedSource);
        });

        ImmutableArray<Diagnostic>.Builder diagnostics = ImmutableArray.CreateBuilder<Diagnostic>();
        preprocessor.OnPreprocessorSyntaxError += (source, start, length, msg) =>
        {
            diagnostics.Add(new Diagnostic("Preprocessor", DiagnosticSeverity.Error, msg, new Location(new(start, length), loadedDocuments[source])));
        };

        // Note: Parser must use the preprocessor token stream (not the raw lexer)
        // to properly handle #include, #define, and other preprocessor directives.
        CILParser parser = new(new CommonTokenStream(preprocessor));
        var result = parser.decls();
        GrammarVisitor visitor = new GrammarVisitor(loadedDocuments, options, resourceLocator);
        _ = result.Accept(visitor);

        var image = visitor.BuildImage();

        bool anyErrors = diagnostics.Any(diagnostic => diagnostic.Severity == DiagnosticSeverity.Error);
        anyErrors |= image.Diagnostics.Any(diagnostic => diagnostic.Severity == DiagnosticSeverity.Error);

        diagnostics.AddRange(image.Diagnostics);

        // In error-tolerant mode, return image even with errors
        bool returnImage = !anyErrors || options.ErrorTolerant;
        return (diagnostics.ToImmutable(), returnImage ? image.Image : null);
    }
}