File: PdbDocumentTable.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;
using System.Collections.Generic;

namespace ILAssembler;

/// <summary>
/// The source documents of a compilation's Portable PDB, in the order they were first defined.
/// </summary>
/// <remarks>
/// <para>
/// A document is defined when the assembler first meets its name: each input file when its parsing
/// begins, and each file named by a <c>.line</c> or <c>#line</c> directive when the directive is applied,
/// whether or not a sequence point ever refers to it. A document is identified by its name alone, after
/// <see cref="Options.PathMap"/>; defining it again returns the existing document, which keeps the language
/// it was first defined with.
/// </para>
/// <para>
/// Sequence points refer to a document by its index in this table
/// (<see cref="EntityRegistry.SequencePoint.DocumentIndex"/>). The PDB's Document table lists the documents in
/// this order.
/// </para>
/// </remarks>
internal sealed class PdbDocumentTable
{
    /// <summary>
    /// The language of a document defined before any <c>.language</c> directive: IL assembly
    /// (<c>CorSym_LanguageType_ILAssembly</c> in src/coreclr/inc/corsym.idl).
    /// </summary>
    public static readonly Guid ILAssemblyLanguage = new("af046cd3-d0e1-11d2-977c-00a0c9b4d50c");

    private readonly List<PdbDocument> _documents = new();
    private readonly Dictionary<string, int> _indices = new(StringComparer.Ordinal);
    private readonly PathMap _pathMap;

    /// <summary>Creates an empty table whose document names are mapped by <paramref name="pathMap"/>.</summary>
    public PdbDocumentTable(PathMap pathMap)
    {
        _pathMap = pathMap;
    }

    /// <summary>Gets the documents in the order they were first defined.</summary>
    public IReadOnlyList<PdbDocument> Documents => _documents;

    /// <summary>
    /// Gets the index of the document with this name after the table's <see cref="PathMap"/>, first adding it at the
    /// end of the table with this language if no document has that name yet.
    /// </summary>
    /// <remarks>
    /// The name is mapped before it is looked up, and the document is named by the mapped name, so two names that
    /// map to the same name are one document, as in the C# compiler. A name that no key of the map matches, such as
    /// a relative <c>.line</c> file name when the keys are full paths, is kept as written.
    /// </remarks>
    public int GetOrAdd(string name, Guid language)
    {
        name = _pathMap.Map(name);
        if (!_indices.TryGetValue(name, out int index))
        {
            index = _documents.Count;
            _documents.Add(new PdbDocument(name, language));
            _indices.Add(name, index);
        }

        return index;
    }
}

/// <summary>
/// A source document of the Portable PDB: its name and the GUID of the language current when it was first defined
/// (<see cref="PdbDocumentTable.ILAssemblyLanguage"/> unless a <c>.language</c> directive set another; it is
/// <see cref="Guid.Empty"/> only if such a directive gave the empty GUID).
/// </summary>
internal readonly record struct PdbDocument(string Name, Guid Language);