using Microsoft.CodeAnalysis;
using System.IO;
namespace Semmle.Extraction
{
///
/// Any program entity which has a corresponding label in the trap file.
///
/// Entities are divided into two types: normal entities and cached
/// entities.
///
/// Normal entities implement directly, and they
/// (may) emit contents to the trap file during object construction.
///
/// Cached entities implement , and they
/// emit contents to the trap file when
/// is called. Caching prevents
/// from being called on entities that have already been emitted.
///
public interface IEntity
{
///
/// The label of the entity, as it is in the trap file.
/// For example, "#123".
///
Label Label { set; get; }
///
/// Writes the unique identifier of this entitiy to a trap file.
///
/// The trapfile to write to.
void WriteId(TextWriter writrapFileter);
///
/// Writes the quoted identifier of this entity,
/// which could be @"..." or *
///
/// The trapfile to write to.
void WriteQuotedId(TextWriter trapFile);
///
/// The location for reporting purposes.
///
Location ReportingLocation { get; }
///
/// How the entity handles .push and .pop.
///
TrapStackBehaviour TrapStackBehaviour { get; }
}
///
/// How an entity behaves with respect to .push and .pop
///
public enum TrapStackBehaviour
{
///
/// The entity must not be extracted inside a .push/.pop
///
NoLabel,
///
/// The entity defines its own label, creating a .push/.pop
///
PushesLabel,
///
/// The entity must be extracted inside a .push/.pop
///
NeedsLabel,
///
/// The entity can be extracted inside or outside of a .push/.pop
///
OptionalLabel
}
///
/// A cached entity.
///
/// The property is used as label in caching.
///
public interface ICachedEntity : IEntity
{
///
/// Populates the field and generates output in the trap file
/// as required. Is only called when returns
/// true and the entity has not already been populated.
///
void Populate(TextWriter trapFile);
bool NeedsPopulation { get; }
object UnderlyingObject { get; }
}
///
/// A factory for creating cached entities.
///
/// The type of the initializer.
public interface ICachedEntityFactory where Entity : ICachedEntity
{
///
/// Initializes the entity, but does not generate any trap code.
///
Entity Create(Context cx, Initializer init);
}
public static class ICachedEntityFactoryExtensions
{
public static Entity CreateEntity(this ICachedEntityFactory<(T1, T2), Entity> factory, Context cx, T1 t1, T2 t2)
where Entity : ICachedEntity => factory.CreateEntity2(cx, (t1, t2));
public static Entity CreateEntity(this ICachedEntityFactory<(T1, T2, T3), Entity> factory, Context cx, T1 t1, T2 t2, T3 t3)
where Entity : ICachedEntity => factory.CreateEntity2(cx, (t1, t2, t3));
public static Entity CreateEntity(this ICachedEntityFactory<(T1, T2, T3, T4), Entity> factory, Context cx, T1 t1, T2 t2, T3 t3, T4 t4)
where Entity : ICachedEntity => factory.CreateEntity2(cx, (t1, t2, t3, t4));
///
/// Creates and populates a new entity, or returns the existing one from the cache.
///
/// The symbol type used to construct the entity.
/// The type of the entity to create.
/// The extractor context.
/// The factory used to construct the entity.
/// The initializer for the entity, which may not be null.
/// The entity.
public static Entity CreateEntity(this ICachedEntityFactory factory, Context cx, Type init)
where Entity : ICachedEntity => cx.CreateEntity(factory, init);
///
/// Creates and populates a new entity, but uses a different cache.
///
/// The symbol type used to construct the entity.
/// The type of the entity to create.
/// The extractor context.
/// The factory used to construct the entity.
/// The initializer for the entity, which may be null.
/// The entity.
public static Entity CreateEntity2(this ICachedEntityFactory factory, Context cx, Type init)
where Entity : ICachedEntity => cx.CreateEntity2(factory, init);
public static void DefineLabel(this IEntity entity, TextWriter trapFile)
{
trapFile.WriteLabel(entity);
trapFile.Write("=");
entity.WriteQuotedId(trapFile);
trapFile.WriteLine();
}
public static void DefineFreshLabel(this IEntity entity, TextWriter trapFile)
{
trapFile.WriteLabel(entity);
trapFile.WriteLine("=*");
}
///
/// Generates a debug string for this entity.
///
/// The entity to view.
/// The debug string.
public static string GetDebugLabel(this IEntity entity)
{
using (var writer = new StringWriter())
{
writer.WriteLabel(entity.Label.Value);
writer.Write('=');
entity.WriteQuotedId(writer);
return writer.ToString();
}
}
}
}