using System; using System.Collections.Generic; using System.IO; using System.Linq; namespace Semmle.Extraction { /// /// An ID. Either a fresh ID (`*`), a key, or a label (https://semmle.com/wiki/display/IN/TRAP+Files): /// /// ``` /// id ::= '*' | key | label /// ``` /// public interface IId { /// /// Appends this ID to the supplied trap builder. /// void AppendTo(TextWriter trapFile); } /// /// A fresh ID (`*`). /// public class FreshId : IId { FreshId() { } /// /// Gets the singleton instance. /// public static IId Instance { get; } = new FreshId(); public override string ToString() => "*"; public override bool Equals(object obj) => obj.GetType() == GetType(); public override int GetHashCode() => 0; public void AppendTo(TextWriter trapFile) { trapFile.Write('*'); } } /// /// A key. Either a simple key, e.g. `@"bool A.M();method"`, or a compound key, e.g. /// `@"{0} {1}.M();method"` where `0` and `1` are both labels. /// public class Key : IId { readonly StringWriter TrapBuilder = new StringWriter(); /// /// Creates a new key by concatenating the contents of the supplied arguments. /// public Key(params object[] args) { TrapBuilder = new StringWriter(); foreach (var arg in args) { if (arg is IEntity) { var key = ((IEntity)arg).Label; TrapBuilder.Write("{#"); TrapBuilder.Write(key.Value.ToString()); TrapBuilder.Write("}"); } else TrapBuilder.Write(arg.ToString()); } } /// /// Creates a new key by applying the supplied action to an empty /// trap builder. /// public Key(Action action) { action(TrapBuilder); } public override string ToString() { return TrapBuilder.ToString(); } public override bool Equals(object obj) { if (obj.GetType() != GetType()) return false; var id = (Key)obj; return TrapBuilder.ToString() == id.TrapBuilder.ToString(); } public override int GetHashCode() => TrapBuilder.ToString().GetHashCode(); public void AppendTo(TextWriter trapFile) { trapFile.Write("@\""); trapFile.Write(TrapBuilder.ToString()); trapFile.Write("\""); } } /// /// A label referencing an entity, of the form "#123". /// public struct Label { public Label(int value) : this() { Value = value; } public int Value { get; private set; } static public readonly Label InvalidLabel = new Label(0); public bool Valid => Value > 0; public override string ToString() { if (!Valid) throw new NullReferenceException("Attempt to use an invalid label"); return "#" + Value; } public static bool operator ==(Label l1, Label l2) => l1.Value == l2.Value; public static bool operator !=(Label l1, Label l2) => l1.Value != l2.Value; public override bool Equals(object other) { return GetType() == other.GetType() && ((Label)other).Value == Value; } public override int GetHashCode() => 61 * Value; /// /// Constructs a unique string for this label. /// /// The trap builder used to store the result. public void AppendTo(System.IO.TextWriter trapFile) { if (!Valid) throw new NullReferenceException("Attempt to use an invalid label"); trapFile.Write('#'); trapFile.Write(Value); } } }