using System.Collections.Generic; using System.Reflection.Metadata; namespace Semmle.Extraction.CIL { /// /// An ID fragment which is designed to be shared, reused /// and composed using the + operator. /// public abstract class Id : IId { public void AppendTo(ITrapBuilder tb) { tb.Append("@\""); BuildParts(tb); tb.Append("\""); } public abstract void BuildParts(ITrapBuilder tb); public static Id operator +(Id l1, Id l2) => Create(l1, l2); public static Id operator +(Id l1, string l2) => Create(l1, Create(l2)); public static Id operator +(Id l1, int l2) => Create(l1, Create(l2)); public static Id operator +(string l1, Id l2) => Create(Create(l1), l2); public static Id operator +(int l1, Id l2) => Create(Create(l1), l2); public static Id Create(string s) => s == null ? null : new StringId(s); public static Id Create(int i) => new IntId(i); static readonly Id openCurly = Create("{#"); static readonly Id closeCurly = Create("}"); public static Id Create(Label l) => openCurly + l.Value + closeCurly; static readonly Id comma = Id.Create(","); public static Id CommaSeparatedList(IEnumerable items) { Id result = null; bool first = true; foreach (var i in items) { if (first) first = false; else result += comma; result += i; } return result; } public static Id Create(Id l1, Id l2) { return l1 == null ? l2 : l2 == null ? l1 : new ConsId(l1, l2); } public abstract string Value { get; } public override string ToString() => Value; } /// /// An ID concatenating two other IDs. /// public sealed class ConsId : Id { readonly Id left, right; readonly int hash; public ConsId(Id l1, Id l2) { left = l1; right = l2; hash = unchecked(12 + 3 * (left.GetHashCode() + 51 * right.GetHashCode())); } public override void BuildParts(ITrapBuilder tb) { left.BuildParts(tb); right.BuildParts(tb); } public override bool Equals(object other) { return other is ConsId && Equals((ConsId)other); } public bool Equals(ConsId other) { return this == other || (hash == other.hash && left.Equals(other.left) && right.Equals(other.right)); } public override int GetHashCode() => hash; public override string Value => left.Value + right.Value; } /// /// A leaf ID storing a string. /// public sealed class StringId : Id { readonly string value; public override string Value => value; public StringId(string s) { value = s; } public override void BuildParts(ITrapBuilder tb) { tb.Append(value); } public override bool Equals(object obj) { return obj is StringId && ((StringId)obj).value == value; } public override int GetHashCode() => Value.GetHashCode() * 31 + 9; } /// /// A leaf ID storing an integer. /// public sealed class IntId : Id { readonly int value; public override string Value => value.ToString(); public IntId(int i) { value = i; } public override void BuildParts(ITrapBuilder tb) { tb.Append(value); } public override bool Equals(object obj) { return obj is IntId && ((IntId)obj).value == value; } public override int GetHashCode() => unchecked(12 + value * 17); } /// /// Some predefined IDs. /// public static class IdUtils { public static StringId boolId = new StringId("Boolean"); public static StringId byteId = new StringId("Byte"); public static StringId charId = new StringId("Char"); public static StringId doubleId = new StringId("Double"); public static StringId shortId = new StringId("Int16"); public static StringId intId = new StringId("Int32"); public static StringId longId = new StringId("Int64"); public static StringId intptrId = new StringId("IntPtr"); public static StringId objectId = new StringId("Object"); public static StringId sbyteId = new StringId("SByte"); public static StringId floatId = new StringId("Single"); public static StringId stringId = new StringId("String"); public static StringId ushortId = new StringId("UInt16"); public static StringId uintId = new StringId("UInt32"); public static StringId ulongId = new StringId("UInt64"); public static StringId uintptrId = new StringId("UIntPtr"); public static StringId voidId = new StringId("Void"); public static StringId typedReferenceId = new StringId("TypedReference"); public static StringId Id(this PrimitiveTypeCode typeCode) { switch (typeCode) { case PrimitiveTypeCode.Boolean: return boolId; case PrimitiveTypeCode.Byte: return byteId; case PrimitiveTypeCode.Char: return charId; case PrimitiveTypeCode.Double: return doubleId; case PrimitiveTypeCode.Int16: return shortId; case PrimitiveTypeCode.Int32: return intId; case PrimitiveTypeCode.Int64: return longId; case PrimitiveTypeCode.IntPtr: return intptrId; case PrimitiveTypeCode.Object: return objectId; case PrimitiveTypeCode.SByte: return sbyteId; case PrimitiveTypeCode.Single: return floatId; case PrimitiveTypeCode.String: return stringId; case PrimitiveTypeCode.UInt16: return ushortId; case PrimitiveTypeCode.UInt32: return uintId; case PrimitiveTypeCode.UInt64: return ulongId; case PrimitiveTypeCode.UIntPtr: return uintptrId; case PrimitiveTypeCode.Void: return voidId; case PrimitiveTypeCode.TypedReference: return typedReferenceId; default: throw new InternalError("Unhandled type code {0}", typeCode); } } } }