2021-04-16 21:07:45 +10:00
|
|
|
|
using System;
|
2021-05-01 20:55:27 +10:00
|
|
|
|
using System.Collections;
|
2021-04-16 21:07:45 +10:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Reflection;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using UnityEngine;
|
|
|
|
|
using UnityEngine.EventSystems;
|
|
|
|
|
using UnityExplorer.Core.Runtime;
|
|
|
|
|
|
|
|
|
|
namespace UnityExplorer.UI.Utility
|
|
|
|
|
{
|
|
|
|
|
public static class ToStringUtility
|
|
|
|
|
{
|
2021-04-27 21:22:48 +10:00
|
|
|
|
internal static Dictionary<string, MethodInfo> toStringMethods = new Dictionary<string, MethodInfo>();
|
|
|
|
|
internal static Dictionary<string, MethodInfo> toStringFormattedMethods = new Dictionary<string, MethodInfo>();
|
2021-04-16 21:07:45 +10:00
|
|
|
|
|
2021-04-25 21:21:05 +10:00
|
|
|
|
// string allocs
|
|
|
|
|
private static readonly StringBuilder _stringBuilder = new StringBuilder(16384);
|
2021-04-28 20:47:48 +10:00
|
|
|
|
private const string nullString = "<color=grey>null</color>";
|
|
|
|
|
private const string destroyedString = "<color=red>Destroyed</color>";
|
|
|
|
|
private const string untitledString = "<i><color=grey>untitled</color></i>";
|
2021-04-25 21:21:05 +10:00
|
|
|
|
|
2021-04-27 21:22:48 +10:00
|
|
|
|
public static string ToStringWithType(object value, Type fallbackType, bool includeNamespace = true)
|
2021-04-16 21:07:45 +10:00
|
|
|
|
{
|
|
|
|
|
if (value == null && fallbackType == null)
|
2021-04-28 20:47:48 +10:00
|
|
|
|
return nullString;
|
2021-04-16 21:07:45 +10:00
|
|
|
|
|
2021-04-24 04:03:33 +10:00
|
|
|
|
Type type = value?.GetActualType() ?? fallbackType;
|
2021-04-16 21:07:45 +10:00
|
|
|
|
|
2021-04-25 21:21:05 +10:00
|
|
|
|
string richType = SignatureHighlighter.ParseFullSyntax(type, includeNamespace);
|
2021-04-16 21:07:45 +10:00
|
|
|
|
|
2021-04-25 21:21:05 +10:00
|
|
|
|
_stringBuilder.Clear();
|
|
|
|
|
|
2021-04-16 21:07:45 +10:00
|
|
|
|
if (value.IsNullOrDestroyed())
|
2021-04-24 04:03:33 +10:00
|
|
|
|
{
|
|
|
|
|
if (value == null)
|
2021-04-25 21:21:05 +10:00
|
|
|
|
{
|
|
|
|
|
_stringBuilder.Append(nullString);
|
|
|
|
|
AppendRichType(_stringBuilder, richType);
|
|
|
|
|
return _stringBuilder.ToString();
|
|
|
|
|
}
|
|
|
|
|
else // destroyed unity object
|
|
|
|
|
{
|
|
|
|
|
_stringBuilder.Append(destroyedString);
|
|
|
|
|
AppendRichType(_stringBuilder, richType);
|
|
|
|
|
return _stringBuilder.ToString();
|
|
|
|
|
}
|
2021-04-24 04:03:33 +10:00
|
|
|
|
}
|
|
|
|
|
|
2021-04-25 21:21:05 +10:00
|
|
|
|
if (value is UnityEngine.Object obj)
|
|
|
|
|
{
|
2021-04-28 20:47:48 +10:00
|
|
|
|
_stringBuilder.Append(string.IsNullOrEmpty(obj.name) ? untitledString : obj.name);
|
2021-04-25 21:21:05 +10:00
|
|
|
|
AppendRichType(_stringBuilder, richType);
|
2021-04-16 21:07:45 +10:00
|
|
|
|
}
|
2021-04-25 21:21:05 +10:00
|
|
|
|
else
|
2021-04-16 21:07:45 +10:00
|
|
|
|
{
|
2021-04-28 20:47:48 +10:00
|
|
|
|
var toString = ToString(value);
|
2021-04-16 21:07:45 +10:00
|
|
|
|
|
2021-05-01 20:55:27 +10:00
|
|
|
|
if (type.IsEnumerable())
|
|
|
|
|
{
|
|
|
|
|
if (value is IList iList)
|
|
|
|
|
_stringBuilder.Append($"[{iList.Count}] ");
|
|
|
|
|
else
|
|
|
|
|
if (value is ICollection iCol)
|
|
|
|
|
_stringBuilder.Append($"[{iCol.Count}] ");
|
|
|
|
|
else
|
|
|
|
|
_stringBuilder.Append("[?] ");
|
|
|
|
|
}
|
|
|
|
|
else if (type.IsDictionary())
|
|
|
|
|
{
|
|
|
|
|
if (value is IDictionary iDict)
|
|
|
|
|
_stringBuilder.Append($"[{iDict.Count}] ");
|
|
|
|
|
else
|
|
|
|
|
_stringBuilder.Append("[?] ");
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-28 20:47:48 +10:00
|
|
|
|
if (type.IsGenericType
|
|
|
|
|
|| toString == type.FullName
|
|
|
|
|
|| toString == $"{type.FullName} {type.FullName}"
|
|
|
|
|
|| toString == $"Il2Cpp{type.FullName}" || type.FullName == $"Il2Cpp{toString}")
|
2021-04-16 21:07:45 +10:00
|
|
|
|
{
|
2021-04-25 21:21:05 +10:00
|
|
|
|
_stringBuilder.Append(richType);
|
2021-04-16 21:07:45 +10:00
|
|
|
|
}
|
2021-04-25 21:21:05 +10:00
|
|
|
|
else // the ToString contains some actual implementation, use that value.
|
2021-04-16 21:07:45 +10:00
|
|
|
|
{
|
|
|
|
|
if (toString.Length > 200)
|
2021-04-25 21:21:05 +10:00
|
|
|
|
_stringBuilder.Append(toString.Substring(0, 200));
|
2021-04-16 21:07:45 +10:00
|
|
|
|
else
|
2021-04-25 21:21:05 +10:00
|
|
|
|
_stringBuilder.Append(toString);
|
|
|
|
|
|
|
|
|
|
AppendRichType(_stringBuilder, richType);
|
2021-04-16 21:07:45 +10:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-25 21:21:05 +10:00
|
|
|
|
return _stringBuilder.ToString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static void AppendRichType(StringBuilder sb, string richType)
|
|
|
|
|
{
|
|
|
|
|
sb.Append(' ');
|
|
|
|
|
sb.Append('(');
|
|
|
|
|
sb.Append(richType);
|
|
|
|
|
sb.Append(')');
|
2021-04-16 21:07:45 +10:00
|
|
|
|
}
|
2021-05-01 20:55:27 +10:00
|
|
|
|
|
|
|
|
|
private static string ToString(object value)
|
|
|
|
|
{
|
|
|
|
|
if (value.IsNullOrDestroyed())
|
|
|
|
|
{
|
|
|
|
|
if (value == null)
|
|
|
|
|
return nullString;
|
|
|
|
|
else // destroyed unity object
|
|
|
|
|
return destroyedString;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var type = value.GetActualType();
|
|
|
|
|
|
|
|
|
|
// Find and cache the relevant ToString method for this Type, if haven't already.
|
|
|
|
|
|
|
|
|
|
if (!toStringMethods.ContainsKey(type.AssemblyQualifiedName))
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var formatMethod = type.GetMethod("ToString", new Type[] { typeof(string) });
|
|
|
|
|
formatMethod.Invoke(value, new object[] { "F3" });
|
|
|
|
|
toStringFormattedMethods.Add(type.AssemblyQualifiedName, formatMethod);
|
|
|
|
|
toStringMethods.Add(type.AssemblyQualifiedName, null);
|
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
|
|
|
|
var toStringMethod = type.GetMethod("ToString", new Type[0]);
|
|
|
|
|
toStringMethods.Add(type.AssemblyQualifiedName, toStringMethod);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Invoke the ToString method on the object
|
|
|
|
|
|
|
|
|
|
value = value.TryCast(type);
|
|
|
|
|
|
|
|
|
|
string toString;
|
2021-05-04 20:10:46 +10:00
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (toStringFormattedMethods.TryGetValue(type.AssemblyQualifiedName, out MethodInfo f3method))
|
|
|
|
|
toString = (string)f3method.Invoke(value, new object[] { "F3" });
|
|
|
|
|
else
|
|
|
|
|
toString = (string)toStringMethods[type.AssemblyQualifiedName].Invoke(value, new object[0]);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
toString = ex.ReflectionExToString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string _ = null;
|
|
|
|
|
toString = ReflectionProvider.Instance.ProcessTypeFullNameInString(type, toString, ref _);
|
2021-05-01 20:55:27 +10:00
|
|
|
|
|
|
|
|
|
return toString;
|
|
|
|
|
}
|
2021-04-16 21:07:45 +10:00
|
|
|
|
}
|
|
|
|
|
}
|