using Microsoft.CodeAnalysis;
using System.Linq;
using Semmle.Extraction.CommentProcessing;
using System.Collections.Generic;
using System;
using Semmle.Util.Logging;
using Semmle.Extraction.Entities;
namespace Semmle.Extraction
{
///
/// State which needs needs to be available throughout the extraction process.
/// There is one Context object per trap output file.
///
public class Context
{
///
/// Interface to various extraction functions, e.g. logger, trap writer.
///
public readonly IExtractor Extractor;
///
/// The program database provided by Roslyn.
/// There's one per syntax tree, which makes things awkward.
///
public SemanticModel Model(SyntaxNode node)
{
if (cachedModel == null || node.SyntaxTree != cachedModel.SyntaxTree)
{
cachedModel = Compilation.GetSemanticModel(node.SyntaxTree);
}
return cachedModel;
}
SemanticModel cachedModel;
///
/// Access to the trap file.
///
public readonly TrapWriter TrapWriter;
int NewId() => TrapWriter.IdCounter++;
///
/// Gets the cached label for the given entity, or creates a new
/// (cached) label if it hasn't already been created. The label
/// is set on the supplied object.
///
/// true iff the label already existed.
public bool GetOrAddCachedLabel(ICachedEntity entity)
{
var id = GetId(entity);
if (id == null)
throw new InternalError("Attempt to create a null entity for {0}", entity.GetType());
Label existingLabel;
if (labelCache.TryGetValue(id, out existingLabel))
{
entity.Label = existingLabel;
return true;
}
entity.Label = new Label(NewId());
DefineLabel(entity.Label, id);
labelCache[id] = entity.Label;
return false;
}
///
/// Should the given entity be extracted?
/// A second call to this method will always return false,
/// on the assumption that it would have been extracted on the first call.
///
/// This is used to track the extraction of generics, which cannot be extracted
/// in a top-down manner.
///
/// The entity to extract.
/// True only on the first call for a particular entity.
public bool ExtractGenerics(ICachedEntity entity)
{
if (extractedGenerics.Contains(entity.Label))
{
return false;
}
else
{
extractedGenerics.Add(entity.Label);
return true;
}
}
///
/// Gets the ID belonging to cached entity .
///
/// The ID itself is also cached, but unlike the label cache (which is used
/// to prevent reextraction/infinite loops), this is a pure performance
/// optimization. Moreover, the label cache is injective, which the ID cache
/// need not be.
///
IId GetId(ICachedEntity entity)
{
IId id;
if (!idCache.TryGetValue(entity, out id))
{
id = entity.Id;
idCache[entity] = id;
}
return id;
}
///
/// Creates a fresh label with ID "*", and set it on the
/// supplied object.
///
public void AddFreshLabel(IEntity entity)
{
var label = new Label(NewId());
TrapWriter.Emit(new DefineFreshLabelEmitter(label));
entity.Label = label;
}
readonly Dictionary labelCache = new Dictionary();
readonly HashSet