using System;
using System.Collections.Generic;
using Microsoft.CodeAnalysis;
using Semmle.Util;
using Semmle.Util.Logging;
namespace Semmle.Extraction
{
///
/// Provides common extraction functions for use during extraction.
///
///
///
/// This is held in the passed to each entity.
///
public interface IExtractor
{
///
/// Logs a message (to csharp.log).
/// Increases the error count if msg.severity is Error.
///
/// The message to log.
void Message(Message msg);
///
/// Cache assembly names.
///
/// The assembly name.
/// The file defining the assembly.
void SetAssemblyFile(string assembly, string file);
///
/// Maps assembly names to file names.
///
/// The assembly name
/// The file defining the assmebly.
string GetAssemblyFile(string assembly);
///
/// How many errors encountered during extraction?
///
int Errors { get; }
///
/// The extraction is standalone - meaning there will be a lot of errors.
///
bool Standalone { get; }
///
/// Record a new error type.
///
/// The display name of the type, qualified where possible.
/// If the missing type was referenced from a source file.
void MissingType(string fqn, bool fromSource);
///
/// Record an unresolved `using namespace` directive.
///
/// The full name of the namespace.
/// If the missing namespace was referenced from a source file.
void MissingNamespace(string fqn, bool fromSource);
///
/// The list of missing types.
///
IEnumerable MissingTypes { get; }
///
/// The list of missing namespaces.
///
IEnumerable MissingNamespaces { get; }
///
/// The full path of the generated DLL/EXE.
/// null if not specified.
///
string OutputPath { get; }
///
/// The object used for logging.
///
ILogger Logger { get; }
///
/// The path transformer to apply.
///
PathTransformer PathTransformer { get; }
///
/// Creates a new context.
///
/// The C# compilation.
/// The trap writer.
/// The extraction scope (what to include in this trap file).
/// Whether to add assembly prefixes to TRAP labels.
///
Context CreateContext(Compilation c, TrapWriter trapWriter, IExtractionScope scope, bool addAssemblyTrapPrefix);
}
///
/// Implementation of the main extractor state.
///
public class Extractor : IExtractor
{
///
/// The default number of threads to use for extraction.
///
public static readonly int DefaultNumberOfThreads = Environment.ProcessorCount;
public bool Standalone
{
get; private set;
}
///
/// Creates a new extractor instance for one compilation unit.
///
/// If the extraction is standalone.
/// The name of the output DLL/EXE, or null if not specified (standalone extraction).
/// The object used for logging.
/// The object used for path transformations.
public Extractor(bool standalone, string outputPath, ILogger logger, PathTransformer pathTransformer)
{
Standalone = standalone;
OutputPath = outputPath;
Logger = logger;
PathTransformer = pathTransformer;
}
// Limit the number of error messages in the log file
// to handle pathological cases.
const int maxErrors = 1000;
readonly object mutex = new object();
public void Message(Message msg)
{
lock (mutex)
{
if (msg.Severity == Severity.Error)
{
++Errors;
if (Errors == maxErrors)
{
Logger.Log(Severity.Info, " Stopping logging after {0} 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.
readonly Dictionary referenceFilenames = new Dictionary();
public void SetAssemblyFile(string assembly, string file)
{
referenceFilenames[assembly] = file;
}
public string GetAssemblyFile(string assembly)
{
return referenceFilenames[assembly];
}
public int Errors
{
get; private set;
}
readonly ISet missingTypes = new SortedSet();
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 Context CreateContext(Compilation c, TrapWriter trapWriter, IExtractionScope scope, bool addAssemblyTrapPrefix)
{
return new Context(this, c, trapWriter, scope, addAssemblyTrapPrefix);
}
public IEnumerable MissingTypes => missingTypes;
public IEnumerable MissingNamespaces => missingNamespaces;
public string OutputPath
{
get;
private set;
}
public ILogger Logger { get; private set; }
public static string Version => $"{ThisAssembly.Git.BaseTag} ({ThisAssembly.Git.Sha})";
public PathTransformer PathTransformer { get; }
}
}