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);
}
}
}
}