From cf98df321808ee0709f9a6563b1e66a9c1ad882f Mon Sep 17 00:00:00 2001 From: Francis Brosseau Date: Tue, 5 Feb 2019 10:51:58 -0500 Subject: [PATCH] ClrDynamic implements IConvertible explicitly Also compacted the boilerplate code of explicit casts with expression bodies --- ClrMD.Extensions/ClrDynamic.cs | 167 ++++++++------------- ClrMD.Extensions/Core/ClrTypeExtensions.cs | 61 ++++++++ 2 files changed, 127 insertions(+), 101 deletions(-) create mode 100644 ClrMD.Extensions/Core/ClrTypeExtensions.cs diff --git a/ClrMD.Extensions/ClrDynamic.cs b/ClrMD.Extensions/ClrDynamic.cs index 9cc4f4d..845b776 100644 --- a/ClrMD.Extensions/ClrDynamic.cs +++ b/ClrMD.Extensions/ClrDynamic.cs @@ -7,6 +7,7 @@ using System.Net; using System.Text; using System.Text.RegularExpressions; +using ClrMD.Extensions.Core; using ClrMD.Extensions.LINQPad; using ClrMD.Extensions.Obfuscation; using LINQPad; @@ -14,7 +15,7 @@ namespace ClrMD.Extensions { - public class ClrDynamic : DynamicObject, IEnumerable, IComparable, ICustomMemberProvider + public class ClrDynamic : DynamicObject, IEnumerable, IComparable, ICustomMemberProvider, IConvertible { public const ulong NullAddress = 0; private const string ToStringFieldIndentation = " "; @@ -25,7 +26,7 @@ public class ClrDynamic : DynamicObject, IEnumerable, IComparable, I public ulong Address { get; } public ClrType Type { get; } - + public bool IsInterior { get; } public string TypeName => m_deobfuscator.OriginalName; @@ -364,131 +365,69 @@ public int CompareTo(object other) return Comparer.DefaultInvariant.Compare(left, right) <= 0; } + #region Explicit Casts + public static bool operator true(ClrDynamic obj) { if (obj == null) throw new ArgumentNullException("obj"); - if (obj.HasSimpleValue) - return (bool)obj.SimpleValue; - - throw new InvalidCastException(string.Format("Cannot cast type '{0}' to bool.", obj.Type)); + return (bool)obj; } public static bool operator false(ClrDynamic obj) { - if (obj == null) - throw new ArgumentNullException("obj"); - - if (obj.HasSimpleValue) - return !(bool)obj.SimpleValue; - - throw new InvalidCastException(string.Format("Cannot cast type '{0}' to bool.", obj.Type)); + return !obj; } public static bool operator !(ClrDynamic obj) { if (obj == null) throw new ArgumentNullException("obj"); - if (obj.HasSimpleValue) return !(bool)obj.SimpleValue; throw new InvalidCastException(string.Format("Cannot cast type '{0}' to bool.", obj.Type)); } - public static explicit operator bool(ClrDynamic obj) - { - return (bool)obj.SimpleValue; - } - - public static explicit operator char(ClrDynamic obj) - { - return (char)obj.SimpleValue; - } - - public static explicit operator sbyte(ClrDynamic obj) - { - return (sbyte)obj.SimpleValue; - } - - public static explicit operator byte(ClrDynamic obj) - { - return (byte)obj.SimpleValue; - } - - public static explicit operator short(ClrDynamic obj) - { - return (short)obj.SimpleValue; - } - - public static explicit operator ushort(ClrDynamic obj) - { - return (ushort)obj.SimpleValue; - } - - public static explicit operator int(ClrDynamic obj) - { - return (int)obj.SimpleValue; - } - - public static explicit operator uint(ClrDynamic obj) - { - return (uint)obj.SimpleValue; - } - - public static explicit operator long(ClrDynamic obj) - { - return (long)obj.SimpleValue; - } - - public static explicit operator ulong(ClrDynamic obj) - { - return (ulong)obj.SimpleValue; - } - - public static explicit operator float(ClrDynamic obj) - { - return (float)obj.SimpleValue; - } - - public static explicit operator double(ClrDynamic obj) - { - return (double)obj.SimpleValue; - } + public static explicit operator bool(ClrDynamic obj) => Convert.ToBoolean(obj.SimpleValue); + public static explicit operator char(ClrDynamic obj) => Convert.ToChar(obj.SimpleValue); + public static explicit operator sbyte(ClrDynamic obj) => Convert.ToSByte(obj.SimpleValue); + public static explicit operator byte(ClrDynamic obj) => Convert.ToByte(obj.SimpleValue); + public static explicit operator short(ClrDynamic obj) => Convert.ToInt16(obj.SimpleValue); + public static explicit operator ushort(ClrDynamic obj) => Convert.ToUInt16(obj.SimpleValue); + public static explicit operator int(ClrDynamic obj) => Convert.ToInt32(obj.SimpleValue); + public static explicit operator uint(ClrDynamic obj) => Convert.ToUInt32(obj.SimpleValue); + public static explicit operator long(ClrDynamic obj) => Convert.ToInt64(obj.SimpleValue); + public static explicit operator ulong(ClrDynamic obj) => Convert.ToUInt64(obj.SimpleValue); + public static explicit operator float(ClrDynamic obj) => Convert.ToSingle(obj.SimpleValue); + public static explicit operator double(ClrDynamic obj) => Convert.ToDouble(obj.SimpleValue); + public static explicit operator DateTime(ClrDynamic obj) => Convert.ToDateTime(obj.SimpleValue); + public static explicit operator decimal(ClrDynamic obj) => Convert.ToDecimal(obj.SimpleValue); + public static explicit operator TimeSpan(ClrDynamic obj) => (TimeSpan)obj.SimpleValue; + public static explicit operator ClrDynamic(ClrObject obj) => new ClrDynamic(obj); public static explicit operator string(ClrDynamic obj) { - if (obj.Type.IsEnum) - return obj.Type.GetEnumName(obj.SimpleValue); - - return (string)obj.SimpleValue; + return obj.Type.IsEnum + ? obj.Type.GetEnumName(obj.SimpleValue) + : Convert.ToString(obj.SimpleValue); } public static explicit operator Guid(ClrDynamic obj) { - return (Guid)obj.SimpleValue; - } - - public static explicit operator TimeSpan(ClrDynamic obj) - { - return (TimeSpan)obj.SimpleValue; - } - - public static explicit operator DateTime(ClrDynamic obj) - { - return (DateTime)obj.SimpleValue; + if (obj.SimpleValue is Guid g) + return g; + return new Guid((string)obj); } public static explicit operator IPAddress(ClrDynamic obj) { - return (IPAddress)obj.SimpleValue; - } - - public static explicit operator ClrDynamic(ClrObject obj) - { - return new ClrDynamic(obj); + if (obj.SimpleValue is IPAddress ip) + return ip; + return IPAddress.Parse((string)obj); } + #endregion #endregion @@ -583,7 +522,7 @@ private string GetAddressString() { if (Address == NullAddress) return "{null}"; - + return "0x" + Address.ToString("X"); } @@ -635,6 +574,32 @@ private void ToDetailedString(StringBuilder builder, string indentation, bool in #endregion + #region IConvertible (for System.Convert methods) + + TypeCode IConvertible.GetTypeCode() => Type.GetTypeCode(); + bool IConvertible.ToBoolean(IFormatProvider provider) => (bool)this; + byte IConvertible.ToByte(IFormatProvider provider) => (byte)this; + char IConvertible.ToChar(IFormatProvider provider) => (char)this; + DateTime IConvertible.ToDateTime(IFormatProvider provider) => (DateTime)this; + decimal IConvertible.ToDecimal(IFormatProvider provider) => (decimal)this; + double IConvertible.ToDouble(IFormatProvider provider) => (double)this; + short IConvertible.ToInt16(IFormatProvider provider) => (short)this; + int IConvertible.ToInt32(IFormatProvider provider) => (int)this; + long IConvertible.ToInt64(IFormatProvider provider) => (long)this; + sbyte IConvertible.ToSByte(IFormatProvider provider) => (sbyte)this; + float IConvertible.ToSingle(IFormatProvider provider) => (float)this; + string IConvertible.ToString(IFormatProvider provider) => (string)this; + ushort IConvertible.ToUInt16(IFormatProvider provider) => (ushort)this; + uint IConvertible.ToUInt32(IFormatProvider provider) => (uint)this; + ulong IConvertible.ToUInt64(IFormatProvider provider) => (ulong)this; + + public object ToType(Type conversionType, IFormatProvider provider) + { + return Convert.ChangeType(SimpleValue, conversionType, provider); + } + + #endregion + #region SimpleValueHelper private static class SimpleValueHelper @@ -825,7 +790,7 @@ public IEnumerable GetNames() yield return ""; yield break; } - + if (Type.IsArray) { yield return "[Type]"; @@ -867,10 +832,10 @@ public IEnumerable GetTypes() if (HasSimpleValue) { - yield return GetSimpleValueType(); + yield return GetSimpleValueType(); yield break; } - + if (Type.IsArray) { // Type Name @@ -935,7 +900,7 @@ public IEnumerable GetValues() yield return SimpleDisplayValue; yield break; } - + if (Type.IsArray) { yield return m_deobfuscator.OriginalName; @@ -981,9 +946,9 @@ private IEnumerable EnumerateArray() private Type GetSimpleValueType() { if (SimpleValue == null) - return typeof (object); + return typeof(object); if (Type.IsEnum) - return typeof (string); + return typeof(string); return SimpleValue.GetType(); } diff --git a/ClrMD.Extensions/Core/ClrTypeExtensions.cs b/ClrMD.Extensions/Core/ClrTypeExtensions.cs new file mode 100644 index 0000000..77efb0e --- /dev/null +++ b/ClrMD.Extensions/Core/ClrTypeExtensions.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Security.Cryptography.X509Certificates; +using Microsoft.Diagnostics.Runtime; + +namespace ClrMD.Extensions.Core +{ + public static class ClrTypeExtensions + { + private static readonly Dictionary s_dumpToClrTypes; + + static ClrTypeExtensions() + { + s_dumpToClrTypes = new Dictionary(); + + void AddType(Type t) + { + s_dumpToClrTypes.Add(t.FullName, t); + } + + AddType(typeof(object)); + AddType(typeof(string)); + AddType(typeof(void)); + AddType(typeof(byte)); + AddType(typeof(sbyte)); + AddType(typeof(short)); + AddType(typeof(ushort)); + AddType(typeof(int)); + AddType(typeof(uint)); + AddType(typeof(long)); + AddType(typeof(ulong)); + AddType(typeof(float)); + AddType(typeof(double)); + AddType(typeof(decimal)); + AddType(typeof(Guid)); + AddType(typeof(DateTime)); + AddType(typeof(TimeSpan)); + AddType(typeof(IPAddress)); + AddType(typeof(IPEndPoint)); + AddType(typeof(DnsEndPoint)); + AddType(typeof(X509Certificate)); + AddType(typeof(X509Certificate2)); + } + + public static Type GetRealType(this ClrType type) + { + if (s_dumpToClrTypes.TryGetValue(type.Name, out var t)) + return t; + + throw new ArgumentException("Only basic types can be matched to the concrete runtime types"); + } + + public static TypeCode GetTypeCode(this ClrType type) + { + return s_dumpToClrTypes.TryGetValue(type.Name, out var t) + ? Type.GetTypeCode(t) + : TypeCode.Object; + } + } +} \ No newline at end of file