using System.Collections.Generic; using System.Threading; using Semmle.Util.Logging; using CompilationInfo = (string key, string value); namespace Semmle.Extraction.CSharp { /// /// Implementation of the main extractor state. /// public class ExtractionContext { public string Cwd { get; init; } public string[] Args { get; init; } public ExtractorMode Mode { get; } public string OutputPath { get; } public IEnumerable CompilationInfos { get; } public bool IsStandalone => Mode.HasFlag(ExtractorMode.Standalone); public bool IsBinaryLog => Mode.HasFlag(ExtractorMode.BinaryLog); /// /// Creates a new extractor instance for one compilation unit. /// public ExtractionContext(string cwd, string[] args, string outputPath, IEnumerable compilationInfos, ILogger logger, PathTransformer pathTransformer, ExtractorMode mode, bool isQlTest) { OutputPath = outputPath; Logger = logger; PathTransformer = pathTransformer; CompilationInfos = compilationInfos; Cwd = cwd; Args = args; Mode = mode; if (isQlTest) { Mode |= ExtractorMode.QlTest; } } // Limit the number of error messages in the log file // to handle pathological cases. private const int maxErrors = 1000; private readonly Lock mutex = new(); public void Message(Message msg) { lock (mutex) { if (msg.Severity == Severity.Error) { ++Errors; if (Errors == maxErrors) { Logger.LogInfo($" Stopping logging after {Errors} errors"); } } if (Errors >= maxErrors) { return; } Logger.Log(msg.Severity, $" {msg.ToLogString()}"); } } // Roslyn framework has no apparent mechanism to associate assemblies with their files. // So this lookup table needs to be populated. private readonly Dictionary referenceFilenames = []; public void SetAssemblyFile(string assembly, string file) { referenceFilenames[assembly] = file; } public string GetAssemblyFile(string assembly) { return referenceFilenames[assembly]; } public int Errors { get; private set; } private readonly ISet missingTypes = new SortedSet(); private readonly ISet missingNamespaces = new SortedSet(); public void MissingType(string fqn, bool fromSource) { if (fromSource) { lock (mutex) missingTypes.Add(fqn); } } public void MissingNamespace(string fqdn, bool fromSource) { if (fromSource) { lock (mutex) missingNamespaces.Add(fqdn); } } public IEnumerable MissingTypes => missingTypes; public IEnumerable MissingNamespaces => missingNamespaces; public ILogger Logger { get; private set; } public PathTransformer PathTransformer { get; } } }