using Microsoft.CodeAnalysis; using System; 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. /// Label Label { set; get; } /// /// The ID used for the entity, as it is in the trap file. /// Could be '*'. /// IId Id { get; } /// /// The location for reporting purposes. /// Location ReportingLocation { get; } 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(); bool NeedsPopulation { 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 { return factory.CreateEntity(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 { return factory.CreateEntity(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 { return factory.CreateEntity(cx, (t1, t2, t3, t4)); } /// /// Creates 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. /// public static Entity CreateEntity(this ICachedEntityFactory factory, Context cx, Type init) where Entity : ICachedEntity { using (cx.StackGuard) { var entity = factory.Create(cx, init); if (cx.GetOrAddCachedLabel(entity)) return entity; if (!entity.NeedsPopulation) return entity; cx.Populate(init as ISymbol, entity); return entity; } } } }