using System;
using System.Collections.Generic;
using System.IO;
namespace Semmle.Extraction.CIL
{
///
/// Something that is extracted from an entity.
///
///
///
/// The extraction algorithm proceeds as follows:
/// - Construct entity
/// - Call Extract()
/// - IExtractedEntity check if already extracted
/// - Enumerate Contents to produce more extraction products
/// - Extract these until there is nothing left to extract
///
public interface IExtractionProduct
{
///
/// Perform further extraction/population of this item as necessary.
///
///
/// The extraction context.
void Extract(Context cx);
}
///
/// An entity which has been extracted.
///
public interface IExtractedEntity : IEntity, IExtractionProduct
{
///
/// The contents of the entity.
///
IEnumerable Contents { get; }
}
///
/// An entity that has contents to extract. There is no need to populate
/// a key as it's done in the contructor.
///
public abstract class UnlabelledEntity : IExtractedEntity
{
public abstract IEnumerable Contents { get; }
public Label Label { get; set; }
public void WriteId(System.IO.TextWriter trapFile)
{
trapFile.Write('*');
}
public void WriteQuotedId(TextWriter trapFile)
{
WriteId(trapFile);
}
public Microsoft.CodeAnalysis.Location ReportingLocation => throw new NotImplementedException();
public virtual void Extract(Context cx2)
{
cx2.Extract(this);
}
public Context Cx { get; }
protected UnlabelledEntity(Context cx)
{
this.Cx = cx;
cx.Cx.AddFreshLabel(this);
}
TrapStackBehaviour IEntity.TrapStackBehaviour => TrapStackBehaviour.NoLabel;
}
///
/// An entity that needs to be populated during extraction.
/// This assigns a key and optionally extracts its contents.
///
public abstract class LabelledEntity : IExtractedEntity
{
public abstract IEnumerable Contents { get; }
public Label Label { get; set; }
public Microsoft.CodeAnalysis.Location ReportingLocation => throw new NotImplementedException();
public abstract void WriteId(System.IO.TextWriter trapFile);
public abstract string IdSuffix { get; }
public void WriteQuotedId(TextWriter trapFile)
{
trapFile.Write("@\"");
WriteId(trapFile);
trapFile.Write(IdSuffix);
trapFile.Write('\"');
}
public void Extract(Context cx2)
{
cx2.Populate(this);
}
public Context Cx { get; }
protected LabelledEntity(Context cx)
{
this.Cx = cx;
}
public override string ToString()
{
using var writer = new StringWriter();
WriteQuotedId(writer);
return writer.ToString();
}
TrapStackBehaviour IEntity.TrapStackBehaviour => TrapStackBehaviour.NoLabel;
}
///
/// A tuple that is an extraction product.
///
internal class Tuple : IExtractionProduct
{
private readonly Extraction.Tuple tuple;
public Tuple(string name, params object[] args)
{
tuple = new Extraction.Tuple(name, args);
}
public void Extract(Context cx)
{
cx.Cx.Emit(tuple);
}
public override string ToString() => tuple.ToString();
}
}