diff --git a/src/UI/Utility/SignatureHighlighter.cs b/src/UI/Utility/SignatureHighlighter.cs
index 77eba82..37d4319 100644
--- a/src/UI/Utility/SignatureHighlighter.cs
+++ b/src/UI/Utility/SignatureHighlighter.cs
@@ -1,6 +1,8 @@
using System;
+using System.Collections.Generic;
using System.Linq;
using System.Reflection;
+using System.Text;
using UnityEngine;
using UnityExplorer.Core.Runtime;
@@ -29,7 +31,7 @@ namespace UnityExplorer.UI.Utility
public static string CONST_VAR = "#92c470";
- internal static readonly Color s_silver = new Color(0.66f, 0.66f, 0.66f);
+ public static string NAMESPACE = "#a8a8a8";
internal static string GetClassColor(Type type)
{
@@ -43,72 +45,73 @@ namespace UnityExplorer.UI.Utility
return CLASS_INSTANCE;
}
+ private static readonly StringBuilder syntaxBuilder = new StringBuilder(8192);
+
public static string ParseFullSyntax(Type type, bool includeNamespace, MemberInfo memberInfo = null)
{
if (type == null)
throw new ArgumentNullException("type");
- //type = ReflectionProvider.Instance.GetDeobfuscatedType(type);
-
- string ret = "";
+ syntaxBuilder.Clear();
if (type.IsGenericParameter || (type.HasElementType && type.GetElementType().IsGenericParameter))
{
- ret = $"{type.Name}";
+ syntaxBuilder.Append($"{type.Name}");
}
else
{
if (includeNamespace && !string.IsNullOrEmpty(type.Namespace))
- ret += $"{type.Namespace}.";
+ syntaxBuilder.Append($"{type.Namespace}.");
var declaring = type.DeclaringType;
while (declaring != null)
{
- ret += HighlightTypeName(declaring) + ".";
+ syntaxBuilder.Append(HighlightTypeName(declaring) + ".");
declaring = declaring.DeclaringType;
}
- ret += HighlightTypeName(type);
+ syntaxBuilder.Append(HighlightTypeName(type));
}
if (memberInfo != null)
{
- ret += ".";
+ syntaxBuilder.Append('.');
- string memberColor = GetMemberInfoColor(memberInfo, out bool isStatic);
- string memberHighlight = $"{memberInfo.Name}";
+ string memColor = GetMemberInfoColor(memberInfo, out bool isStatic);
if (isStatic)
- memberHighlight = $"{memberHighlight}";
+ syntaxBuilder.Append("");
- ret += memberHighlight;
+ syntaxBuilder.Append($"{memberInfo.Name}");
+
+ if (isStatic)
+ syntaxBuilder.Append("");
- // generic method args
if (memberInfo is MethodInfo method)
- {
- var gArgs = method.GetGenericArguments();
- ret += ParseGenericArgs(gArgs, true);
- }
+ syntaxBuilder.Append(ParseGenericArgs(method.GetGenericArguments(), true));
}
- return ret;
+ return syntaxBuilder.ToString();
}
+ private static readonly Dictionary typeToRichType = new Dictionary();
+
public static string HighlightTypeName(Type type)
{
- //type = RuntimeProvider.Instance.Reflection.GetDeobfuscatedType(type);
+ if (typeToRichType.ContainsKey(type.AssemblyQualifiedName))
+ return typeToRichType[type.AssemblyQualifiedName];
var typeName = type.Name;
- var gArgs = type.GetGenericArguments();
+ var args = type.GetGenericArguments();
- if (gArgs.Length > 0)
+ if (args.Length > 0)
{
// remove the `N from the end of the type name
// this could actually be >9 in some cases, so get the length of the length string and use that.
// eg, if it was "List`15", we would remove the ending 3 chars
- int suffixLen = 1 + gArgs.Length.ToString().Length;
+ int suffixLen = 1 + args.Length.ToString().Length;
// make sure the typename actually has expected "`N" format.
if (typeName[typeName.Length - suffixLen] == '`')
@@ -120,72 +123,78 @@ namespace UnityExplorer.UI.Utility
typeName = $"{typeName}";
// parse the generic args, if any
- if (gArgs.Length > 0)
- typeName += ParseGenericArgs(gArgs);
+ if (args.Length > 0)
+ typeName += ParseGenericArgs(args);
+
+ typeToRichType.Add(type.AssemblyQualifiedName, typeName);
return typeName;
}
- public static string ParseGenericArgs(Type[] gArgs, bool allGeneric = false)
- {
- if (gArgs.Length < 1)
- return "";
+ private static readonly StringBuilder genericBuilder = new StringBuilder(4096);
- var args = "<";
- for (int i = 0; i < gArgs.Length; i++)
+ public static string ParseGenericArgs(Type[] args, bool isGenericParams = false)
+ {
+ if (args.Length < 1)
+ return string.Empty;
+
+ genericBuilder.Clear();
+ genericBuilder.Append('<');
+
+ for (int i = 0; i < args.Length; i++)
{
if (i > 0)
- args += ", ";
+ genericBuilder.Append(',');
- var arg = gArgs[i];
-
- if (allGeneric)
+ if (isGenericParams)
{
- args += $"{arg.Name}";
+ genericBuilder.Append($"{args[i].Name}");
continue;
}
// using HighlightTypeName makes it recursive, so we can parse nested generic args.
- args += HighlightTypeName(arg);
+ genericBuilder.Append(HighlightTypeName(args[i]));
}
- return args + ">";
+
+ genericBuilder.Append('>');
+ return genericBuilder.ToString();
}
public static string GetMemberInfoColor(MemberInfo memberInfo, out bool isStatic)
{
- string memberColor = "";
isStatic = false;
if (memberInfo is FieldInfo fi)
{
if (fi.IsStatic)
{
isStatic = true;
- memberColor = FIELD_STATIC;
+ return FIELD_STATIC;
}
else
- memberColor = FIELD_INSTANCE;
+ return FIELD_INSTANCE;
}
else if (memberInfo is MethodInfo mi)
{
if (mi.IsStatic)
{
isStatic = true;
- memberColor = METHOD_STATIC;
+ return METHOD_STATIC;
}
else
- memberColor = METHOD_INSTANCE;
+ return METHOD_INSTANCE;
}
else if (memberInfo is PropertyInfo pi)
{
if (pi.GetAccessors(true)[0].IsStatic)
{
isStatic = true;
- memberColor = PROP_STATIC;
+ return PROP_STATIC;
}
else
- memberColor = PROP_INSTANCE;
+ return PROP_INSTANCE;
}
- return memberColor;
+
+ throw new NotImplementedException(memberInfo.GetType().Name + " is not supported");
}
}
}
diff --git a/src/UI/Utility/ToStringUtility.cs b/src/UI/Utility/ToStringUtility.cs
index 8113f4a..4bcd8c9 100644
--- a/src/UI/Utility/ToStringUtility.cs
+++ b/src/UI/Utility/ToStringUtility.cs
@@ -14,40 +14,49 @@ namespace UnityExplorer.UI.Utility
internal static Dictionary toStringMethods = new Dictionary();
internal static Dictionary toStringFormattedMethods = new Dictionary();
+ // string allocs
+ private static readonly StringBuilder _stringBuilder = new StringBuilder(16384);
+ private const string unknownString = "";
+ private const string nullString = "[null]";
+ private const string destroyedString = "[Destroyed]";
+
public static string ToString(object value, Type fallbackType, bool includeNamespace = true, bool includeName = true)
{
if (value == null && fallbackType == null)
- return "";
+ return unknownString;
Type type = value?.GetActualType() ?? fallbackType;
- var richType = SignatureHighlighter.ParseFullSyntax(type, includeNamespace);
+ // todo SB this too
+ string richType = SignatureHighlighter.ParseFullSyntax(type, includeNamespace);
if (!includeName)
return richType;
+ _stringBuilder.Clear();
+
if (value.IsNullOrDestroyed())
{
if (value == null)
- return $"[null] ({richType})";
- else
- return $"[Destroyed] ({richType})";
+ {
+ _stringBuilder.Append(nullString);
+ AppendRichType(_stringBuilder, richType);
+ return _stringBuilder.ToString();
+ }
+ else // destroyed unity object
+ {
+ _stringBuilder.Append(destroyedString);
+ AppendRichType(_stringBuilder, richType);
+ return _stringBuilder.ToString();
+ }
}
- // value = value.TryCast(type);
-
- string label;
-
- // Two dirty fixes for TextAsset and EventSystem, which can have very long ToString results.
- if (value is TextAsset textAsset)
- {
- label = $"{textAsset.name} ({richType})";
+ if (value is UnityEngine.Object obj)
+ {
+ _stringBuilder.Append(obj.name);
+ AppendRichType(_stringBuilder, richType);
}
- else if (value is EventSystem es)
- {
- label = $"{es.name} ({richType})";
- }
- else // For everything else...
+ else
{
if (!toStringMethods.ContainsKey(type))
{
@@ -75,35 +84,64 @@ namespace UnityExplorer.UI.Utility
else
toString = (string)stdMethod.Invoke(value, new object[0]);
- toString = toString ?? "";
-
- string typeName = type.FullName;
- if (typeName.StartsWith("Il2CppSystem."))
- typeName = typeName.Substring(6, typeName.Length - 6);
-
- toString = ReflectionProvider.Instance.ProcessTypeFullNameInString(type, toString, ref typeName);
-
- // If the ToString is just the type name, use our syntax highlighted type name instead.
- if (toString == typeName)
+ if (toString == type.FullName || toString == $"Il2Cpp{type.FullName}")
{
- label = richType;
+ // the ToString was just the default object.ToString(), use our
+ // syntax highlighted type name instead.
+ _stringBuilder.Append(richType);
}
- else // Otherwise, parse the result and put our highlighted name in.
+ else // the ToString contains some actual implementation, use that value.
{
if (toString.Length > 200)
- toString = toString.Substring(0, 200) + "...";
-
- label = toString;
-
- var unityType = $"({type.FullName})";
- if (value is UnityEngine.Object && label.Contains(unityType))
- label = label.Replace(unityType, $"({richType})");
+ _stringBuilder.Append(toString.Substring(0, 200));
else
- label += $" ({richType})";
+ _stringBuilder.Append(toString);
+
+ AppendRichType(_stringBuilder, richType);
}
+
+ ////string toString;
+
+ //
+ //toString = toString ?? "";
+ //
+ //string typeName = type.FullName;
+ //if (typeName.StartsWith("Il2CppSystem."))
+ // typeName = typeName.Substring(6, typeName.Length - 6);
+ //
+ //toString = ReflectionProvider.Instance.ProcessTypeFullNameInString(type, toString, ref typeName);
+ //
+ //// If the ToString is just the type name, use our syntax highlighted type name instead.
+ //if (toString == typeName)
+ //{
+ // label = richType;
+ //}
+ //else // Otherwise, parse the result and put our highlighted name in.
+ //{
+ // if (toString.Length > 200)
+ // toString = toString.Substring(0, 200) + "...";
+ //
+ // label = toString;
+ //
+ // var unityType = $"({type.FullName})";
+ // if (value is UnityEngine.Object && label.Contains(unityType))
+ // label = label.Replace(unityType, $"({richType})");
+ // else
+ // label += $" ({richType})";
+ //}
}
- return label;
+ return _stringBuilder.ToString();
+ }
+
+ // Just a little optimization, append chars directly instead of allocating every time
+ // we want to do this.
+ private static void AppendRichType(StringBuilder sb, string richType)
+ {
+ sb.Append(' ');
+ sb.Append('(');
+ sb.Append(richType);
+ sb.Append(')');
}
}
}