mirror of
https://github.com/sinai-dev/UnityExplorer.git
synced 2025-06-23 17:02:36 +08:00
Fix some issues in IL2CPP, improve type cache efficiency, reduce alloc
This commit is contained in:
@ -17,36 +17,48 @@ namespace UnityExplorer
|
|||||||
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
|
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
|
||||||
CacheTypes(asm);
|
CacheTypes(asm);
|
||||||
|
|
||||||
|
allTypeNames.Sort();
|
||||||
|
|
||||||
AppDomain.CurrentDomain.AssemblyLoad += AssemblyLoaded;
|
AppDomain.CurrentDomain.AssemblyLoad += AssemblyLoaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly Dictionary<string, Type> allCachedTypes = new Dictionary<string, Type>();
|
/// <summary>Key: Type.FullName</summary>
|
||||||
|
public static readonly SortedDictionary<string, Type> AllTypes = new SortedDictionary<string, Type>(StringComparer.OrdinalIgnoreCase);
|
||||||
private static void CacheTypes(Assembly asm)
|
private static readonly List<string> allTypeNames = new List<string>();
|
||||||
{
|
|
||||||
foreach (var type in asm.TryGetTypes())
|
|
||||||
{
|
|
||||||
if (allCachedTypes.ContainsKey(type.FullName))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (type.FullName.ContainsIgnoreCase("PrivateImplementationDetails"))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
allCachedTypes.Add(type.FullName, type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void AssemblyLoaded(object sender, AssemblyLoadEventArgs args)
|
private static void AssemblyLoaded(object sender, AssemblyLoadEventArgs args)
|
||||||
{
|
{
|
||||||
if (args.LoadedAssembly == null)
|
if (args.LoadedAssembly == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
s_cachedTypeInheritance.Clear();
|
|
||||||
s_cachedGenericParameterInheritance.Clear();
|
|
||||||
|
|
||||||
CacheTypes(args.LoadedAssembly);
|
CacheTypes(args.LoadedAssembly);
|
||||||
|
allTypeNames.Sort();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void CacheTypes(Assembly asm)
|
||||||
|
{
|
||||||
|
foreach (var type in asm.TryGetTypes())
|
||||||
|
{
|
||||||
|
if (AllTypes.ContainsKey(type.FullName))
|
||||||
|
AllTypes[type.FullName] = type;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AllTypes.Add(type.FullName, type);
|
||||||
|
allTypeNames.Add(type.FullName);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var key in s_cachedTypeInheritance.Keys)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var baseType = AllTypes[key];
|
||||||
|
if (baseType.IsAssignableFrom(type) && !s_cachedTypeInheritance[key].Contains(type))
|
||||||
|
s_cachedTypeInheritance[key].Add(type);
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public const BF AllFlags = BF.Public | BF.Instance | BF.NonPublic | BF.Static;
|
public const BF AllFlags = BF.Public | BF.Instance | BF.NonPublic | BF.Static;
|
||||||
|
|
||||||
@ -156,7 +168,9 @@ namespace UnityExplorer
|
|||||||
/// <returns>The Type if found, otherwise null.</returns>
|
/// <returns>The Type if found, otherwise null.</returns>
|
||||||
public static Type GetTypeByName(string fullName)
|
public static Type GetTypeByName(string fullName)
|
||||||
{
|
{
|
||||||
allCachedTypes.TryGetValue(fullName, out Type type);
|
|
||||||
|
|
||||||
|
AllTypes.TryGetValue(fullName, out Type type);
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,6 +214,21 @@ namespace UnityExplorer
|
|||||||
internal static readonly Dictionary<string, HashSet<Type>> s_cachedTypeInheritance = new Dictionary<string, HashSet<Type>>();
|
internal static readonly Dictionary<string, HashSet<Type>> s_cachedTypeInheritance = new Dictionary<string, HashSet<Type>>();
|
||||||
internal static readonly Dictionary<string, HashSet<Type>> s_cachedGenericParameterInheritance = new Dictionary<string, HashSet<Type>>();
|
internal static readonly Dictionary<string, HashSet<Type>> s_cachedGenericParameterInheritance = new Dictionary<string, HashSet<Type>>();
|
||||||
|
|
||||||
|
public static string GetImplementationKey(Type type)
|
||||||
|
{
|
||||||
|
if (!type.IsGenericParameter)
|
||||||
|
return type.FullName;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
sb.Append(type.GenericParameterAttributes)
|
||||||
|
.Append('|');
|
||||||
|
foreach (var c in type.GetGenericParameterConstraints())
|
||||||
|
sb.Append(c.FullName).Append(',');
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get all non-abstract implementations of the provided type (include itself, if not abstract) in the current AppDomain.
|
/// Get all non-abstract implementations of the provided type (include itself, if not abstract) in the current AppDomain.
|
||||||
/// Also works for generic parameters by analyzing the constraints.
|
/// Also works for generic parameters by analyzing the constraints.
|
||||||
@ -208,38 +237,94 @@ namespace UnityExplorer
|
|||||||
/// <returns>All implementations of the type in the current AppDomain.</returns>
|
/// <returns>All implementations of the type in the current AppDomain.</returns>
|
||||||
public static HashSet<Type> GetImplementationsOf(this Type baseType, bool allowAbstract, bool allowGeneric)
|
public static HashSet<Type> GetImplementationsOf(this Type baseType, bool allowAbstract, bool allowGeneric)
|
||||||
{
|
{
|
||||||
var key = baseType.AssemblyQualifiedName;
|
var key = GetImplementationKey(baseType); //baseType.FullName;
|
||||||
|
|
||||||
|
if (!baseType.IsGenericParameter)
|
||||||
|
return GetImplementations(key, baseType, allowAbstract, allowGeneric);
|
||||||
|
else
|
||||||
|
return GetGenericParameterImplementations(key, baseType, allowAbstract, allowGeneric);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static HashSet<Type> GetImplementations(string key, Type baseType, bool allowAbstract, bool allowGeneric)
|
||||||
|
{
|
||||||
if (!s_cachedTypeInheritance.ContainsKey(key))
|
if (!s_cachedTypeInheritance.ContainsKey(key))
|
||||||
{
|
{
|
||||||
var set = new HashSet<Type>();
|
var set = new HashSet<Type>();
|
||||||
|
for (int i = 0; i < allTypeNames.Count; i++)
|
||||||
if (!baseType.IsAbstract && !baseType.IsInterface)
|
|
||||||
set.Add(baseType);
|
|
||||||
|
|
||||||
var keys = allCachedTypes.Keys.ToArray();
|
|
||||||
for (int i = 0; i < keys.Length; i++)
|
|
||||||
{
|
{
|
||||||
var type = allCachedTypes[keys[i]];
|
var type = AllTypes[allTypeNames[i]];
|
||||||
|
//type = ReflectionProvider.Instance.GetDeobfuscatedType(type);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if ((type.IsAbstract && type.IsSealed) // ignore static classes
|
if (set.Contains(type)
|
||||||
|
|| (type.IsAbstract && type.IsSealed) // ignore static classes
|
||||||
|| (!allowAbstract && type.IsAbstract)
|
|| (!allowAbstract && type.IsAbstract)
|
||||||
|| (!allowGeneric && (type.IsGenericType || type.IsGenericTypeDefinition)))
|
|| (!allowGeneric && (type.IsGenericType || type.IsGenericTypeDefinition)))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (type.FullName.Contains("PrivateImplementationDetails")
|
||||||
|
|| type.FullName.Contains("DisplayClass")
|
||||||
|
|| type.FullName.Contains('<'))
|
||||||
|
continue;
|
||||||
|
|
||||||
if (baseType.IsAssignableFrom(type) && !set.Contains(type))
|
if (baseType.IsAssignableFrom(type) && !set.Contains(type))
|
||||||
set.Add(type);
|
set.Add(type);
|
||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//set.
|
||||||
|
|
||||||
s_cachedTypeInheritance.Add(key, set);
|
s_cachedTypeInheritance.Add(key, set);
|
||||||
}
|
}
|
||||||
|
|
||||||
return s_cachedTypeInheritance[key];
|
return s_cachedTypeInheritance[key];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static HashSet<Type> GetGenericParameterImplementations(string key, Type baseType, bool allowAbstract, bool allowGeneric)
|
||||||
|
{
|
||||||
|
if (!s_cachedGenericParameterInheritance.ContainsKey(key))
|
||||||
|
{
|
||||||
|
var set = new HashSet<Type>();
|
||||||
|
|
||||||
|
for (int i = 0; i < allTypeNames.Count; i++)
|
||||||
|
{
|
||||||
|
var type = AllTypes[allTypeNames[i]];
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (set.Contains(type)
|
||||||
|
|| (type.IsAbstract && type.IsSealed) // ignore static classes
|
||||||
|
|| (!allowAbstract && type.IsAbstract)
|
||||||
|
|| (!allowGeneric && (type.IsGenericType || type.IsGenericTypeDefinition)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (type.FullName.Contains("PrivateImplementationDetails")
|
||||||
|
|| type.FullName.Contains("DisplayClass")
|
||||||
|
|| type.FullName.Contains('<'))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (baseType.GenericParameterAttributes.HasFlag(GenericParameterAttributes.NotNullableValueTypeConstraint)
|
||||||
|
&& type.IsClass)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (baseType.GenericParameterAttributes.HasFlag(GenericParameterAttributes.ReferenceTypeConstraint)
|
||||||
|
&& type.IsValueType)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (baseType.GetGenericParameterConstraints().Any(it => !it.IsAssignableFrom(type)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
set.Add(type);
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
|
||||||
|
s_cachedGenericParameterInheritance.Add(key, set);
|
||||||
|
}
|
||||||
|
|
||||||
|
return s_cachedGenericParameterInheritance[key];
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Safely get all valid Types inside an Assembly.
|
/// Safely get all valid Types inside an Assembly.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -53,6 +53,11 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override bool IsString(object obj)
|
||||||
|
{
|
||||||
|
return obj is string || obj is Il2CppSystem.String;
|
||||||
|
}
|
||||||
|
|
||||||
public override void BoxStringToType(ref object value, Type castTo)
|
public override void BoxStringToType(ref object value, Type castTo)
|
||||||
{
|
{
|
||||||
if (castTo == typeof(Il2CppSystem.String))
|
if (castTo == typeof(Il2CppSystem.String))
|
||||||
@ -82,10 +87,12 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var cppType = Il2CppType.From(type);
|
if (Il2CppToMonoType.ContainsKey(type.AssemblyQualifiedName))
|
||||||
var monoType = GetMonoType(cppType);
|
return Il2CppToMonoType[type.AssemblyQualifiedName];
|
||||||
if (monoType != null)
|
//var cppType = Il2CppType.From(type);
|
||||||
return monoType;
|
//var monoType = GetMonoType(cppType);
|
||||||
|
//if (monoType != null)
|
||||||
|
// return monoType;
|
||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
|
|
||||||
@ -116,7 +123,7 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if ((Il2CppSystem.Object)obj is Il2CppSystem.Object cppObject)
|
if (obj is Il2CppSystem.Object cppObject)
|
||||||
{
|
{
|
||||||
// weird specific case - if the object is an Il2CppSystem.Type, then return so manually.
|
// weird specific case - if the object is an Il2CppSystem.Type, then return so manually.
|
||||||
if (cppObject is CppType)
|
if (cppObject is CppType)
|
||||||
@ -131,6 +138,9 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
|
|||||||
IntPtr classPtr = il2cpp_object_get_class(cppObject.Pointer);
|
IntPtr classPtr = il2cpp_object_get_class(cppObject.Pointer);
|
||||||
if (RuntimeSpecificsStore.IsInjected(classPtr))
|
if (RuntimeSpecificsStore.IsInjected(classPtr))
|
||||||
{
|
{
|
||||||
|
// Note: This will fail on injected subclasses.
|
||||||
|
// - {Namespace}.{Class}.{Subclass} would be {Namespace}.{Subclass} when injected.
|
||||||
|
// Not sure on solution yet.
|
||||||
var typeByName = ReflectionUtility.GetTypeByName(cppType.FullName);
|
var typeByName = ReflectionUtility.GetTypeByName(cppType.FullName);
|
||||||
if (typeByName != null)
|
if (typeByName != null)
|
||||||
return typeByName;
|
return typeByName;
|
||||||
@ -142,9 +152,9 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
|
|||||||
return getType;
|
return getType;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch //(Exception ex)
|
||||||
{
|
{
|
||||||
ExplorerCore.LogWarning("Exception in GetActualType: " + ex);
|
//ExplorerCore.LogWarning("Exception in GetActualType: " + ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
return type;
|
return type;
|
||||||
@ -176,7 +186,7 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
|
|||||||
{
|
{
|
||||||
var cppType = Il2CppType.From(type);
|
var cppType = Il2CppType.From(type);
|
||||||
|
|
||||||
if (!Il2CppToMonoType.ContainsKey(cppType.FullName))
|
if (!Il2CppToMonoType.ContainsKey(cppType.AssemblyQualifiedName))
|
||||||
{
|
{
|
||||||
Il2CppToMonoType.Add(cppType.AssemblyQualifiedName, type);
|
Il2CppToMonoType.Add(cppType.AssemblyQualifiedName, type);
|
||||||
s_deobfuscatedTypeNames.Add(cppType.FullName, type.FullName);
|
s_deobfuscatedTypeNames.Add(cppType.FullName, type.FullName);
|
||||||
@ -234,13 +244,23 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
if (RuntimeSpecificsStore.IsInjected(castToPtr))
|
if (RuntimeSpecificsStore.IsInjected(castToPtr))
|
||||||
return UnhollowerBaseLib.Runtime.ClassInjectorBase.GetMonoObjectFromIl2CppPointer(cppObj.Pointer);
|
{
|
||||||
|
var injectedObj = UnhollowerBaseLib.Runtime.ClassInjectorBase.GetMonoObjectFromIl2CppPointer(cppObj.Pointer);
|
||||||
|
return injectedObj ?? obj;
|
||||||
|
}
|
||||||
|
|
||||||
if (castTo == typeof(string))
|
if (castTo == typeof(string))
|
||||||
return cppObj.ToString();
|
return cppObj.ToString();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
return Activator.CreateInstance(castTo, cppObj.Pointer);
|
return Activator.CreateInstance(castTo, cppObj.Pointer);
|
||||||
}
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the Il2Cpp Class Pointer for the provided Mono (Unhollowed) Type.
|
/// Get the Il2Cpp Class Pointer for the provided Mono (Unhollowed) Type.
|
||||||
|
@ -32,6 +32,8 @@ namespace UnityExplorer.Core.Runtime
|
|||||||
|
|
||||||
public abstract bool LoadModule(string module);
|
public abstract bool LoadModule(string module);
|
||||||
|
|
||||||
|
public virtual bool IsString(object obj) => obj is string;
|
||||||
|
|
||||||
public abstract void BoxStringToType(ref object _string, Type castTo);
|
public abstract void BoxStringToType(ref object _string, Type castTo);
|
||||||
|
|
||||||
public virtual string UnboxString(object value) => (string)value;
|
public virtual string UnboxString(object value) => (string)value;
|
||||||
|
@ -26,7 +26,7 @@ namespace UnityExplorer.UI.CacheObject
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var ret = FieldInfo.GetValue(this.Owner.Target.TryCast(this.DeclaringType));
|
var ret = FieldInfo.GetValue(DeclaringInstance);
|
||||||
HadException = false;
|
HadException = false;
|
||||||
LastException = null;
|
LastException = null;
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -42,7 +42,7 @@ namespace UnityExplorer.UI.CacheObject
|
|||||||
{
|
{
|
||||||
KeyInputWanted = true;
|
KeyInputWanted = true;
|
||||||
KeyInputText = key.ToString();
|
KeyInputText = key.ToString();
|
||||||
KeyInputTypeText = SignatureHighlighter.ParseType(type, false);
|
KeyInputTypeText = SignatureHighlighter.Parse(type, false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -18,6 +18,8 @@ namespace UnityExplorer.UI.CacheObject
|
|||||||
|
|
||||||
public abstract Type DeclaringType { get; }
|
public abstract Type DeclaringType { get; }
|
||||||
public string NameForFiltering { get; protected set; }
|
public string NameForFiltering { get; protected set; }
|
||||||
|
public object DeclaringInstance => IsStatic ? null : (m_declaringInstance ?? (m_declaringInstance = Owner.Target.TryCast(DeclaringType)));
|
||||||
|
private object m_declaringInstance;
|
||||||
|
|
||||||
public abstract bool IsStatic { get; }
|
public abstract bool IsStatic { get; }
|
||||||
public override bool HasArguments => Arguments?.Length > 0 || GenericArguments.Length > 0;
|
public override bool HasArguments => Arguments?.Length > 0 || GenericArguments.Length > 0;
|
||||||
@ -29,7 +31,7 @@ namespace UnityExplorer.UI.CacheObject
|
|||||||
public virtual void SetInspectorOwner(ReflectionInspector inspector, MemberInfo member)
|
public virtual void SetInspectorOwner(ReflectionInspector inspector, MemberInfo member)
|
||||||
{
|
{
|
||||||
this.Owner = inspector;
|
this.Owner = inspector;
|
||||||
this.NameLabelText = SignatureHighlighter.ParseFullSyntax(member.DeclaringType, false, member);
|
this.NameLabelText = SignatureHighlighter.Parse(member.DeclaringType, false, member);
|
||||||
this.NameForFiltering = $"{member.DeclaringType.Name}.{member.Name}";
|
this.NameForFiltering = $"{member.DeclaringType.Name}.{member.Name}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,12 +34,10 @@ namespace UnityExplorer.UI.CacheObject
|
|||||||
if (methodInfo.IsGenericMethod)
|
if (methodInfo.IsGenericMethod)
|
||||||
methodInfo = MethodInfo.MakeGenericMethod(Evaluator.TryParseGenericArguments());
|
methodInfo = MethodInfo.MakeGenericMethod(Evaluator.TryParseGenericArguments());
|
||||||
|
|
||||||
var target = MethodInfo.IsStatic ? null : Owner.Target.TryCast(DeclaringType);
|
|
||||||
|
|
||||||
if (Arguments.Length > 0)
|
if (Arguments.Length > 0)
|
||||||
return methodInfo.Invoke(target, Evaluator.TryParseArguments());
|
return methodInfo.Invoke(DeclaringInstance, Evaluator.TryParseArguments());
|
||||||
|
|
||||||
var ret = methodInfo.Invoke(target, new object[0]);
|
var ret = methodInfo.Invoke(DeclaringInstance, new object[0]);
|
||||||
|
|
||||||
HadException = false;
|
HadException = false;
|
||||||
LastException = null;
|
LastException = null;
|
||||||
|
@ -5,6 +5,7 @@ using System.Reflection;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.UI;
|
using UnityEngine.UI;
|
||||||
|
using UnityExplorer.Core.Runtime;
|
||||||
using UnityExplorer.UI.CacheObject.Views;
|
using UnityExplorer.UI.CacheObject.Views;
|
||||||
using UnityExplorer.UI.IValues;
|
using UnityExplorer.UI.IValues;
|
||||||
using UnityExplorer.UI.ObjectPool;
|
using UnityExplorer.UI.ObjectPool;
|
||||||
@ -94,6 +95,8 @@ namespace UnityExplorer.UI.CacheObject
|
|||||||
|
|
||||||
// Updating and applying values
|
// Updating and applying values
|
||||||
|
|
||||||
|
public abstract void SetUserValue(object value);
|
||||||
|
|
||||||
public virtual void SetValueFromSource(object value)
|
public virtual void SetValueFromSource(object value)
|
||||||
{
|
{
|
||||||
this.Value = value;
|
this.Value = value;
|
||||||
@ -118,8 +121,6 @@ namespace UnityExplorer.UI.CacheObject
|
|||||||
this.IValue.SetValue(Value);
|
this.IValue.SetValue(Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void SetUserValue(object value);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Process the CacheMember state when the value has been evaluated (or re-evaluated)
|
/// Process the CacheMember state when the value has been evaluated (or re-evaluated)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -137,7 +138,7 @@ namespace UnityExplorer.UI.CacheObject
|
|||||||
State = ValueState.Boolean;
|
State = ValueState.Boolean;
|
||||||
else if (type.IsPrimitive || type == typeof(decimal))
|
else if (type.IsPrimitive || type == typeof(decimal))
|
||||||
State = ValueState.Number;
|
State = ValueState.Number;
|
||||||
else if (type == typeof(string))
|
else if (ReflectionProvider.Instance.IsString(Value))
|
||||||
State = ValueState.String;
|
State = ValueState.String;
|
||||||
else if (type.IsEnum)
|
else if (type.IsEnum)
|
||||||
State = ValueState.Enum;
|
State = ValueState.Enum;
|
||||||
@ -162,14 +163,14 @@ namespace UnityExplorer.UI.CacheObject
|
|||||||
switch (State)
|
switch (State)
|
||||||
{
|
{
|
||||||
case ValueState.NotEvaluated:
|
case ValueState.NotEvaluated:
|
||||||
label = $"<i>{NOT_YET_EVAL} ({SignatureHighlighter.ParseType(FallbackType, true)})</i>"; break;
|
label = $"<i>{NOT_YET_EVAL} ({SignatureHighlighter.Parse(FallbackType, true)})</i>"; break;
|
||||||
case ValueState.Exception:
|
case ValueState.Exception:
|
||||||
label = $"<i><color=red>{ReflectionUtility.ReflectionExToString(LastException)}</color></i>"; break;
|
label = $"<i><color=red>{ReflectionUtility.ReflectionExToString(LastException)}</color></i>"; break;
|
||||||
case ValueState.Boolean:
|
case ValueState.Boolean:
|
||||||
case ValueState.Number:
|
case ValueState.Number:
|
||||||
label = null; break;
|
label = null; break;
|
||||||
case ValueState.String:
|
case ValueState.String:
|
||||||
string s = Value as string;
|
string s = ReflectionProvider.Instance.UnboxString(Value);
|
||||||
if (s.Length > 200)
|
if (s.Length > 200)
|
||||||
s = $"{s.Substring(0, 200)}...";
|
s = $"{s.Substring(0, 200)}...";
|
||||||
label = $"\"{s}\""; break;
|
label = $"\"{s}\""; break;
|
||||||
@ -252,7 +253,7 @@ namespace UnityExplorer.UI.CacheObject
|
|||||||
|
|
||||||
cell.TypeLabel.gameObject.SetActive(args.typeLabelActive);
|
cell.TypeLabel.gameObject.SetActive(args.typeLabelActive);
|
||||||
if (args.typeLabelActive)
|
if (args.typeLabelActive)
|
||||||
cell.TypeLabel.text = SignatureHighlighter.ParseType(Value.GetActualType(), false);
|
cell.TypeLabel.text = SignatureHighlighter.Parse(Value.GetActualType(), false);
|
||||||
|
|
||||||
cell.Toggle.gameObject.SetActive(args.toggleActive);
|
cell.Toggle.gameObject.SetActive(args.toggleActive);
|
||||||
if (args.toggleActive)
|
if (args.toggleActive)
|
||||||
|
@ -28,12 +28,10 @@ namespace UnityExplorer.UI.CacheObject
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var target = IsStatic ? null : Owner.Target.TryCast(DeclaringType);
|
|
||||||
|
|
||||||
if (HasArguments)
|
if (HasArguments)
|
||||||
return PropertyInfo.GetValue(target, this.Evaluator.TryParseArguments());
|
return PropertyInfo.GetValue(DeclaringInstance, this.Evaluator.TryParseArguments());
|
||||||
|
|
||||||
var ret = PropertyInfo.GetValue(target, null);
|
var ret = PropertyInfo.GetValue(DeclaringInstance, null);
|
||||||
HadException = false;
|
HadException = false;
|
||||||
LastException = null;
|
LastException = null;
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -38,7 +38,7 @@ namespace UnityExplorer.UI.CacheObject.Views
|
|||||||
|
|
||||||
Image = root.AddComponent<Image>();
|
Image = root.AddComponent<Image>();
|
||||||
|
|
||||||
this.NameLayout.minWidth = 40;
|
this.NameLayout.minWidth = 55;
|
||||||
this.NameLayout.flexibleWidth = 50;
|
this.NameLayout.flexibleWidth = 50;
|
||||||
this.NameLayout.minHeight = 30;
|
this.NameLayout.minHeight = 30;
|
||||||
this.NameLayout.flexibleHeight = 0;
|
this.NameLayout.flexibleHeight = 0;
|
||||||
|
@ -31,6 +31,7 @@ namespace UnityExplorer.UI.CacheObject.Views
|
|||||||
private GameObject genericArgHolder;
|
private GameObject genericArgHolder;
|
||||||
private readonly List<GameObject> genericArgRows = new List<GameObject>();
|
private readonly List<GameObject> genericArgRows = new List<GameObject>();
|
||||||
private readonly List<Text> genericArgLabels = new List<Text>();
|
private readonly List<Text> genericArgLabels = new List<Text>();
|
||||||
|
private readonly List<TypeCompleter> genericAutocompleters = new List<TypeCompleter>();
|
||||||
|
|
||||||
private readonly List<InputField> inputFieldCache = new List<InputField>();
|
private readonly List<InputField> inputFieldCache = new List<InputField>();
|
||||||
|
|
||||||
@ -153,9 +154,12 @@ namespace UnityExplorer.UI.CacheObject.Views
|
|||||||
|
|
||||||
genericArgRows[i].SetActive(true);
|
genericArgRows[i].SetActive(true);
|
||||||
|
|
||||||
var constraints = arg.GetGenericParameterConstraints();
|
var autoCompleter = genericAutocompleters[i];
|
||||||
|
autoCompleter.BaseType = arg;
|
||||||
|
autoCompleter.CacheTypes();
|
||||||
|
|
||||||
// TODO show "class" constraints as they dont show up, "struct" does effectively.
|
var constraints = arg.GetGenericParameterConstraints();
|
||||||
|
autoCompleter.GenericConstraints = constraints;
|
||||||
|
|
||||||
var sb = new StringBuilder($"<color={SignatureHighlighter.CONST}>{arg.Name}</color>");
|
var sb = new StringBuilder($"<color={SignatureHighlighter.CONST}>{arg.Name}</color>");
|
||||||
|
|
||||||
@ -164,7 +168,7 @@ namespace UnityExplorer.UI.CacheObject.Views
|
|||||||
if (j == 0) sb.Append(' ').Append('(');
|
if (j == 0) sb.Append(' ').Append('(');
|
||||||
else sb.Append(',').Append(' ');
|
else sb.Append(',').Append(' ');
|
||||||
|
|
||||||
sb.Append(SignatureHighlighter.ParseType(constraints[j]));
|
sb.Append(SignatureHighlighter.Parse(constraints[j], false));
|
||||||
|
|
||||||
if (j + 1 == constraints.Length)
|
if (j + 1 == constraints.Length)
|
||||||
sb.Append(')');
|
sb.Append(')');
|
||||||
@ -194,19 +198,19 @@ namespace UnityExplorer.UI.CacheObject.Views
|
|||||||
AddArgRow(i, false);
|
AddArgRow(i, false);
|
||||||
|
|
||||||
argRows[i].SetActive(true);
|
argRows[i].SetActive(true);
|
||||||
argLabels[i].text = $"{SignatureHighlighter.ParseType(arg.ParameterType)} <color={SignatureHighlighter.LOCAL_ARG}>{arg.Name}</color>";
|
argLabels[i].text = $"{SignatureHighlighter.Parse(arg.ParameterType, false)} <color={SignatureHighlighter.LOCAL_ARG}>{arg.Name}</color>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddArgRow(int index, bool generic)
|
private void AddArgRow(int index, bool generic)
|
||||||
{
|
{
|
||||||
if (!generic)
|
if (!generic)
|
||||||
AddArgRow(index, argHolder, argRows, argLabels, argumentInput);//, false);
|
AddArgRow(index, argHolder, argRows, argLabels, argumentInput, false);
|
||||||
else
|
else
|
||||||
AddArgRow(index, genericArgHolder, genericArgRows, genericArgLabels, genericInput);//, true);
|
AddArgRow(index, genericArgHolder, genericArgRows, genericArgLabels, genericInput, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddArgRow(int index, GameObject parent, List<GameObject> objectList, List<Text> labelList, string[] inputArray)//, bool autocomplete)
|
private void AddArgRow(int index, GameObject parent, List<GameObject> objectList, List<Text> labelList, string[] inputArray, bool autocomplete)
|
||||||
{
|
{
|
||||||
var horiGroup = UIFactory.CreateUIObject("ArgRow_" + index, parent);
|
var horiGroup = UIFactory.CreateUIObject("ArgRow_" + index, parent);
|
||||||
UIFactory.SetLayoutElement(horiGroup, minHeight: 25, flexibleHeight: 50, minWidth: 50, flexibleWidth: 9999);
|
UIFactory.SetLayoutElement(horiGroup, minHeight: 25, flexibleHeight: 50, minWidth: 50, flexibleWidth: 9999);
|
||||||
@ -225,6 +229,9 @@ namespace UnityExplorer.UI.CacheObject.Views
|
|||||||
inputObj.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
inputObj.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
||||||
inputField.onValueChanged.AddListener((string val) => { inputArray[index] = val; });
|
inputField.onValueChanged.AddListener((string val) => { inputArray[index] = val; });
|
||||||
inputFieldCache.Add(inputField);
|
inputFieldCache.Add(inputField);
|
||||||
|
|
||||||
|
if (autocomplete)
|
||||||
|
genericAutocompleters.Add(new TypeCompleter(null, inputField));
|
||||||
}
|
}
|
||||||
|
|
||||||
public GameObject CreateContent(GameObject parent)
|
public GameObject CreateContent(GameObject parent)
|
||||||
|
@ -86,7 +86,7 @@ namespace UnityExplorer.UI.IValues
|
|||||||
|
|
||||||
CacheEntries(value);
|
CacheEntries(value);
|
||||||
|
|
||||||
TopLabel.text = $"[{cachedEntries.Count}] {SignatureHighlighter.ParseType(type, false)}";
|
TopLabel.text = $"[{cachedEntries.Count}] {SignatureHighlighter.Parse(type, false)}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ namespace UnityExplorer.UI.IValues
|
|||||||
|
|
||||||
CacheEntries(value);
|
CacheEntries(value);
|
||||||
|
|
||||||
TopLabel.text = $"[{cachedEntries.Count}] {SignatureHighlighter.ParseType(type, false)}";
|
TopLabel.text = $"[{cachedEntries.Count}] {SignatureHighlighter.Parse(type, false)}";
|
||||||
}
|
}
|
||||||
|
|
||||||
//this.ScrollPoolLayout.minHeight = Math.Min(400f, 35f * values.Count);
|
//this.ScrollPoolLayout.minHeight = Math.Min(400f, 35f * values.Count);
|
||||||
|
@ -160,7 +160,7 @@ namespace UnityExplorer.UI.Inspectors
|
|||||||
|
|
||||||
if (!compToStringCache.ContainsKey(type.AssemblyQualifiedName))
|
if (!compToStringCache.ContainsKey(type.AssemblyQualifiedName))
|
||||||
{
|
{
|
||||||
compToStringCache.Add(type.AssemblyQualifiedName, SignatureHighlighter.ParseType(type, true));
|
compToStringCache.Add(type.AssemblyQualifiedName, SignatureHighlighter.Parse(type, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
cell.Button.ButtonText.text = compToStringCache[type.AssemblyQualifiedName];
|
cell.Button.ButtonText.text = compToStringCache[type.AssemblyQualifiedName];
|
||||||
|
@ -128,8 +128,8 @@ namespace UnityExplorer.UI.Inspectors
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Setup main labels and tab text
|
// Setup main labels and tab text
|
||||||
Tab.TabText.text = $"{prefix} {SignatureHighlighter.ParseType(TargetType)}";
|
Tab.TabText.text = $"{prefix} {SignatureHighlighter.Parse(TargetType, false)}";
|
||||||
NameText.text = SignatureHighlighter.ParseFullSyntax(TargetType, true);
|
NameText.text = SignatureHighlighter.Parse(TargetType, true);
|
||||||
|
|
||||||
string asmText;
|
string asmText;
|
||||||
if (TargetType.Assembly != null && !string.IsNullOrEmpty(TargetType.Assembly.Location))
|
if (TargetType.Assembly != null && !string.IsNullOrEmpty(TargetType.Assembly.Location))
|
||||||
@ -230,6 +230,7 @@ namespace UnityExplorer.UI.Inspectors
|
|||||||
|
|
||||||
private void UpdateDisplayedMembers()// bool onlyAutoUpdate)
|
private void UpdateDisplayedMembers()// bool onlyAutoUpdate)
|
||||||
{
|
{
|
||||||
|
ExplorerCore.Log("Updating values...");
|
||||||
bool shouldRefresh = false;
|
bool shouldRefresh = false;
|
||||||
foreach (var cell in MemberScrollPool.CellPool)
|
foreach (var cell in MemberScrollPool.CellPool)
|
||||||
{
|
{
|
||||||
@ -238,6 +239,7 @@ namespace UnityExplorer.UI.Inspectors
|
|||||||
var member = cell.MemberOccupant;
|
var member = cell.MemberOccupant;
|
||||||
if (member.ShouldAutoEvaluate) // && (!onlyAutoUpdate || member.AutoUpdateWanted))
|
if (member.ShouldAutoEvaluate) // && (!onlyAutoUpdate || member.AutoUpdateWanted))
|
||||||
{
|
{
|
||||||
|
ExplorerCore.Log("Evaluating cell " + cell.MemberOccupant.NameForFiltering);
|
||||||
shouldRefresh = true;
|
shouldRefresh = true;
|
||||||
member.Evaluate();
|
member.Evaluate();
|
||||||
member.SetDataToCell(member.CellView);
|
member.SetDataToCell(member.CellView);
|
||||||
|
@ -79,9 +79,9 @@ namespace UnityExplorer.UI.Panels
|
|||||||
lastCheckedTypeInput = desiredTypeInput;
|
lastCheckedTypeInput = desiredTypeInput;
|
||||||
|
|
||||||
//var type = ReflectionUtility.GetTypeByName(desiredTypeInput);
|
//var type = ReflectionUtility.GetTypeByName(desiredTypeInput);
|
||||||
if (typeAutocompleter.AllTypes.TryGetValue(desiredTypeInput, out var cachedType))
|
if (ReflectionUtility.AllTypes.TryGetValue(desiredTypeInput, out var cachedType))
|
||||||
{
|
{
|
||||||
var type = cachedType.Type;
|
var type = cachedType;
|
||||||
lastTypeCanHaveGO = typeof(Component).IsAssignableFrom(type) || type == typeof(GameObject);
|
lastTypeCanHaveGO = typeof(Component).IsAssignableFrom(type) || type == typeof(GameObject);
|
||||||
sceneFilterRow.SetActive(lastTypeCanHaveGO);
|
sceneFilterRow.SetActive(lastTypeCanHaveGO);
|
||||||
childFilterRow.SetActive(lastTypeCanHaveGO);
|
childFilterRow.SetActive(lastTypeCanHaveGO);
|
||||||
@ -134,7 +134,7 @@ namespace UnityExplorer.UI.Panels
|
|||||||
{
|
{
|
||||||
string text;
|
string text;
|
||||||
if (m_context == SearchContext.StaticClass)
|
if (m_context == SearchContext.StaticClass)
|
||||||
text = SignatureHighlighter.ParseType(currentResults[index] as Type, true, true);
|
text = SignatureHighlighter.Parse(currentResults[index] as Type, true);
|
||||||
else
|
else
|
||||||
text = ToStringUtility.ToStringWithType(currentResults[index], currentResults[index]?.GetActualType());
|
text = ToStringUtility.ToStringWithType(currentResults[index], currentResults[index]?.GetActualType());
|
||||||
|
|
||||||
|
@ -108,6 +108,8 @@ namespace UnityExplorer.UI
|
|||||||
if (!ShowMenu)
|
if (!ShowMenu)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
gcLabel.text = "GC : " + (GC.GetTotalMemory(false) / 1024 / 1024) + "MB";
|
||||||
|
|
||||||
if (InputManager.GetKeyDown(ConfigManager.Force_Unlock_Keybind.Value))
|
if (InputManager.GetKeyDown(ConfigManager.Force_Unlock_Keybind.Value))
|
||||||
CursorUnlocker.Unlock = !CursorUnlocker.Unlock;
|
CursorUnlocker.Unlock = !CursorUnlocker.Unlock;
|
||||||
|
|
||||||
@ -221,6 +223,8 @@ namespace UnityExplorer.UI
|
|||||||
//private static float lastTimeSpeed;
|
//private static float lastTimeSpeed;
|
||||||
//private static bool pausing;
|
//private static bool pausing;
|
||||||
|
|
||||||
|
private static Text gcLabel;
|
||||||
|
|
||||||
private static void CreateTopNavBar()
|
private static void CreateTopNavBar()
|
||||||
{
|
{
|
||||||
var navbarPanel = UIFactory.CreateUIObject("MainNavbar", CanvasRoot);
|
var navbarPanel = UIFactory.CreateUIObject("MainNavbar", CanvasRoot);
|
||||||
@ -238,6 +242,11 @@ namespace UnityExplorer.UI
|
|||||||
var title = UIFactory.CreateLabel(navbarPanel, "Title", titleTxt, TextAnchor.MiddleLeft, default, true, 18);
|
var title = UIFactory.CreateLabel(navbarPanel, "Title", titleTxt, TextAnchor.MiddleLeft, default, true, 18);
|
||||||
UIFactory.SetLayoutElement(title.gameObject, minWidth: 240, flexibleWidth: 0);
|
UIFactory.SetLayoutElement(title.gameObject, minWidth: 240, flexibleWidth: 0);
|
||||||
|
|
||||||
|
// temp debug
|
||||||
|
|
||||||
|
gcLabel = UIFactory.CreateLabel(navbarPanel, "GCLabel", "GC: ", TextAnchor.MiddleLeft);
|
||||||
|
UIFactory.SetLayoutElement(gcLabel.gameObject, minWidth: 150, minHeight: 25, flexibleWidth: 0);
|
||||||
|
|
||||||
// TODO something nicer for this, maybe a 'Tools' dropout below the main navbar with a few helpers like this.
|
// TODO something nicer for this, maybe a 'Tools' dropout below the main navbar with a few helpers like this.
|
||||||
|
|
||||||
//var btn = UIFactory.CreateButton(navbarPanel, "Button", "pause", new Color(0.2f, 0.2f, 0.2f));
|
//var btn = UIFactory.CreateButton(navbarPanel, "Button", "pause", new Color(0.2f, 0.2f, 0.2f));
|
||||||
|
@ -68,7 +68,7 @@ namespace UnityExplorer.UI.Utility
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string ParseFullSyntax(Type type, bool includeNamespace, MemberInfo memberInfo = null)
|
public static string Parse(Type type, bool includeNamespace, MemberInfo memberInfo = null)
|
||||||
{
|
{
|
||||||
if (type == null)
|
if (type == null)
|
||||||
throw new ArgumentNullException("type");
|
throw new ArgumentNullException("type");
|
||||||
@ -136,30 +136,30 @@ namespace UnityExplorer.UI.Utility
|
|||||||
return syntaxBuilder.ToString();
|
return syntaxBuilder.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string ParseType(Type type, bool includeNamespace = false, bool includeDllName = false)
|
//public static string ParseType(Type type, bool includeNamespace = false, bool includeDllName = false)
|
||||||
{
|
//{
|
||||||
var sb = new StringBuilder();
|
// var sb = new StringBuilder();
|
||||||
|
//
|
||||||
bool isGeneric = type.IsGenericParameter || (type.HasElementType && type.GetElementType().IsGenericParameter);
|
// bool isGeneric = type.IsGenericParameter || (type.HasElementType && type.GetElementType().IsGenericParameter);
|
||||||
|
//
|
||||||
if (!isGeneric && includeNamespace && GetNamespace(type, out string ns))
|
// if (!isGeneric && includeNamespace && GetNamespace(type, out string ns))
|
||||||
//sb.Append($"<color={NAMESPACE}>{ns}{CLOSE_COLOR}.");
|
// //sb.Append($"<color={NAMESPACE}>{ns}{CLOSE_COLOR}.");
|
||||||
sb.Append(OPEN_COLOR).Append(NAMESPACE).Append('>').Append(ns).Append(CLOSE_COLOR).Append('.');
|
// sb.Append(OPEN_COLOR).Append(NAMESPACE).Append('>').Append(ns).Append(CLOSE_COLOR).Append('.');
|
||||||
|
//
|
||||||
sb.Append(HighlightType(type));
|
// sb.Append(HighlightType(type));
|
||||||
|
//
|
||||||
if (includeDllName)
|
// if (includeDllName)
|
||||||
{
|
// {
|
||||||
if (!string.IsNullOrEmpty(type.Assembly.Location))
|
// if (!string.IsNullOrEmpty(type.Assembly.Location))
|
||||||
//sb.Append($" ({Path.GetFileName(type.Assembly.Location)})");
|
// //sb.Append($" ({Path.GetFileName(type.Assembly.Location)})");
|
||||||
sb.Append(' ').Append('(').Append(Path.GetFileName(type.Assembly.Location)).Append(')');
|
// sb.Append(' ').Append('(').Append(Path.GetFileName(type.Assembly.Location)).Append(')');
|
||||||
else
|
// else
|
||||||
//sb.Append($" ({type.Assembly.GetName().Name})");
|
// //sb.Append($" ({type.Assembly.GetName().Name})");
|
||||||
sb.Append(' ').Append('(').Append(type.Assembly.GetName().Name).Append(')');
|
// sb.Append(' ').Append('(').Append(type.Assembly.GetName().Name).Append(')');
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
return sb.ToString();
|
// return sb.ToString();
|
||||||
}
|
//}
|
||||||
|
|
||||||
private static readonly Dictionary<string, string> typeToRichType = new Dictionary<string, string>();
|
private static readonly Dictionary<string, string> typeToRichType = new Dictionary<string, string>();
|
||||||
|
|
||||||
@ -182,6 +182,7 @@ namespace UnityExplorer.UI.Utility
|
|||||||
private static string HighlightType(Type type)
|
private static string HighlightType(Type type)
|
||||||
{
|
{
|
||||||
string key = type.ToString();
|
string key = type.ToString();
|
||||||
|
|
||||||
if (typeToRichType.ContainsKey(key))
|
if (typeToRichType.ContainsKey(key))
|
||||||
return typeToRichType[key];
|
return typeToRichType[key];
|
||||||
|
|
||||||
@ -265,7 +266,7 @@ namespace UnityExplorer.UI.Utility
|
|||||||
}
|
}
|
||||||
|
|
||||||
//ret += ParseType(args[i]);
|
//ret += ParseType(args[i]);
|
||||||
sb.Append(ParseType(args[i]));
|
sb.Append(HighlightType(args[i]));
|
||||||
}
|
}
|
||||||
|
|
||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
|
@ -30,7 +30,7 @@ namespace UnityExplorer.UI.Utility
|
|||||||
|
|
||||||
Type type = value?.GetActualType() ?? fallbackType;
|
Type type = value?.GetActualType() ?? fallbackType;
|
||||||
|
|
||||||
string richType = SignatureHighlighter.ParseFullSyntax(type, includeNamespace);
|
string richType = SignatureHighlighter.Parse(type, includeNamespace);
|
||||||
|
|
||||||
_stringBuilder.Clear();
|
_stringBuilder.Clear();
|
||||||
|
|
||||||
|
@ -8,17 +8,13 @@ using UnityEngine.UI;
|
|||||||
using UnityExplorer.Core.Runtime;
|
using UnityExplorer.Core.Runtime;
|
||||||
using UnityExplorer.UI.Models;
|
using UnityExplorer.UI.Models;
|
||||||
using UnityExplorer.UI.Panels;
|
using UnityExplorer.UI.Panels;
|
||||||
|
using UnityExplorer.UI.Utility;
|
||||||
|
|
||||||
namespace UnityExplorer.UI.Widgets.AutoComplete
|
namespace UnityExplorer.UI.Widgets.AutoComplete
|
||||||
{
|
{
|
||||||
public class TypeCompleter : ISuggestionProvider
|
public class TypeCompleter : ISuggestionProvider
|
||||||
{
|
{
|
||||||
public class CachedType
|
internal static readonly Dictionary<string, string> sharedTypeToLabel = new Dictionary<string, string>(4096);
|
||||||
{
|
|
||||||
public Type Type;
|
|
||||||
public string FullNameValue;
|
|
||||||
public string DisplayName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public event Action<Suggestion> SuggestionClicked;
|
public event Action<Suggestion> SuggestionClicked;
|
||||||
|
|
||||||
@ -31,10 +27,7 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
|
|||||||
private readonly List<Suggestion> suggestions = new List<Suggestion>();
|
private readonly List<Suggestion> suggestions = new List<Suggestion>();
|
||||||
private float timeOfLastCheck;
|
private float timeOfLastCheck;
|
||||||
|
|
||||||
public Dictionary<string, CachedType> AllTypes = new Dictionary<string, CachedType>();
|
private HashSet<Type> allowedTypes;
|
||||||
|
|
||||||
// cached type trees from all autocompleters
|
|
||||||
private static readonly Dictionary<string, Dictionary<string, CachedType>> typeCache = new Dictionary<string, Dictionary<string, CachedType>>();
|
|
||||||
|
|
||||||
public TypeCompleter(Type baseType, InputField inputField)
|
public TypeCompleter(Type baseType, InputField inputField)
|
||||||
{
|
{
|
||||||
@ -47,6 +40,11 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
|
|||||||
CacheTypes();
|
CacheTypes();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void CacheTypes()
|
||||||
|
{
|
||||||
|
allowedTypes = ReflectionUtility.GetImplementationsOf(BaseType, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
public void OnSuggestionClicked(Suggestion suggestion)
|
public void OnSuggestionClicked(Suggestion suggestion)
|
||||||
{
|
{
|
||||||
timeOfLastCheck = Time.realtimeSinceStartup;
|
timeOfLastCheck = Time.realtimeSinceStartup;
|
||||||
@ -84,61 +82,29 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
|
|||||||
{
|
{
|
||||||
suggestions.Clear();
|
suggestions.Clear();
|
||||||
|
|
||||||
var added = new HashSet<string>();
|
if (BaseType == null)
|
||||||
|
{
|
||||||
|
ExplorerCore.LogWarning("Autocompleter Base type is null!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Check for exact match first
|
// Check for exact match first
|
||||||
if (AllTypes.TryGetValue(value, out CachedType cache))
|
if (ReflectionUtility.AllTypes.TryGetValue(value, out Type t) && allowedTypes.Contains(t))
|
||||||
AddSuggestion(cache);
|
AddSuggestion(t);
|
||||||
|
|
||||||
foreach (var entry in AllTypes.Values)
|
foreach (var entry in allowedTypes)
|
||||||
|
{
|
||||||
|
if (entry.FullName.ContainsIgnoreCase(value))
|
||||||
AddSuggestion(entry);
|
AddSuggestion(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void AddSuggestion(CachedType entry)
|
void AddSuggestion(Type type)
|
||||||
{
|
{
|
||||||
if (entry.FullNameValue == null)
|
if (!sharedTypeToLabel.ContainsKey(type.FullName))
|
||||||
entry.FullNameValue = ReflectionProvider.Instance.GetDeobfuscatedType(entry.Type).FullName;
|
sharedTypeToLabel.Add(type.FullName, SignatureHighlighter.Parse(type, true));
|
||||||
|
|
||||||
if (added.Contains(entry.FullNameValue))
|
suggestions.Add(new Suggestion(sharedTypeToLabel[type.FullName], type.FullName));
|
||||||
return;
|
|
||||||
added.Add(entry.FullNameValue);
|
|
||||||
|
|
||||||
if (entry.DisplayName == null)
|
|
||||||
entry.DisplayName = Utility.SignatureHighlighter.ParseFullSyntax(entry.Type, true);
|
|
||||||
|
|
||||||
suggestions.Add(new Suggestion(entry.DisplayName, entry.FullNameValue));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void CacheTypes()
|
|
||||||
{
|
|
||||||
var key = BaseType.AssemblyQualifiedName;
|
|
||||||
|
|
||||||
if (typeCache.ContainsKey(key))
|
|
||||||
{
|
|
||||||
AllTypes = typeCache[key];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
AllTypes = new Dictionary<string, CachedType>();
|
|
||||||
|
|
||||||
var list = ReflectionUtility.GetImplementationsOf(BaseType, true, false)
|
|
||||||
.Select(it => new CachedType()
|
|
||||||
{
|
|
||||||
Type = it,
|
|
||||||
FullNameValue = ReflectionProvider.Instance.GetDeobfuscatedType(it).FullName
|
|
||||||
})
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
list.Sort((CachedType a, CachedType b) => a.FullNameValue.CompareTo(b.FullNameValue));
|
|
||||||
|
|
||||||
foreach (var cache in list)
|
|
||||||
{
|
|
||||||
if (AllTypes.ContainsKey(cache.FullNameValue))
|
|
||||||
continue;
|
|
||||||
AllTypes.Add(cache.FullNameValue, cache);
|
|
||||||
}
|
|
||||||
|
|
||||||
typeCache.Add(key, AllTypes);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,19 @@ namespace UnityExplorer.UI.Widgets
|
|||||||
public float DefaultHeight => m_defaultHeight ?? (float)(m_defaultHeight = ScrollPool.PrototypeHeight);
|
public float DefaultHeight => m_defaultHeight ?? (float)(m_defaultHeight = ScrollPool.PrototypeHeight);
|
||||||
private float? m_defaultHeight;
|
private float? m_defaultHeight;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Lookup table for "which data index first appears at this position"<br/>
|
||||||
|
/// Index: DefaultHeight * index from top of data<br/>
|
||||||
|
/// Value: the first data index at this position<br/>
|
||||||
|
/// </summary>
|
||||||
|
private readonly List<int> rangeCache = new List<int>();
|
||||||
|
|
||||||
|
/// <summary>Same as GetRangeIndexOfPosition, except this rounds up to the next division if there was remainder from the previous cell.</summary>
|
||||||
|
private int GetRangeCeilingOfPosition(float position) => (int)Math.Ceiling((decimal)position / (decimal)DefaultHeight);
|
||||||
|
|
||||||
|
/// <summary>Get the first range (division of DefaultHeight) which the position appears in.</summary>
|
||||||
|
private int GetRangeFloorOfPosition(float position) => (int)Math.Floor((decimal)position / (decimal)DefaultHeight);
|
||||||
|
|
||||||
/// <summary>Get the data index at the specified position of the total height cache.</summary>
|
/// <summary>Get the data index at the specified position of the total height cache.</summary>
|
||||||
public int GetFirstDataIndexAtPosition(float desiredHeight) => GetFirstDataIndexAtPosition(desiredHeight, out _);
|
public int GetFirstDataIndexAtPosition(float desiredHeight) => GetFirstDataIndexAtPosition(desiredHeight, out _);
|
||||||
|
|
||||||
@ -81,19 +94,6 @@ namespace UnityExplorer.UI.Widgets
|
|||||||
return dataIndex;
|
return dataIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Lookup table for "which data index first appears at this position"<br/>
|
|
||||||
/// Index: DefaultHeight * index from top of data<br/>
|
|
||||||
/// Value: the first data index at this position<br/>
|
|
||||||
/// </summary>
|
|
||||||
private readonly List<int> rangeCache = new List<int>();
|
|
||||||
|
|
||||||
/// <summary>Get the first range (division of DefaultHeight) which the position appears in.</summary>
|
|
||||||
private int GetRangeFloorOfPosition(float position) => (int)Math.Floor((decimal)position / (decimal)DefaultHeight);
|
|
||||||
|
|
||||||
/// <summary>Same as GetRangeIndexOfPosition, except this rounds up to the next division if there was remainder from the previous cell.</summary>
|
|
||||||
private int GetRangeCeilingOfPosition(float position) => (int)Math.Ceiling((decimal)position / (decimal)DefaultHeight);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the spread of the height, starting from the start position.<br/><br/>
|
/// Get the spread of the height, starting from the start position.<br/><br/>
|
||||||
/// The "spread" begins at the start of the next interval of the DefaultHeight, then increases for
|
/// The "spread" begins at the start of the next interval of the DefaultHeight, then increases for
|
||||||
@ -116,6 +116,8 @@ namespace UnityExplorer.UI.Widgets
|
|||||||
/// <summary>Append a data index to the cache with the provided height value.</summary>
|
/// <summary>Append a data index to the cache with the provided height value.</summary>
|
||||||
public void Add(float value)
|
public void Add(float value)
|
||||||
{
|
{
|
||||||
|
value = (float)Math.Floor(value);
|
||||||
|
|
||||||
int spread = GetRangeSpread(totalHeight, value);
|
int spread = GetRangeSpread(totalHeight, value);
|
||||||
|
|
||||||
heightCache.Add(new DataViewInfo()
|
heightCache.Add(new DataViewInfo()
|
||||||
@ -150,6 +152,8 @@ namespace UnityExplorer.UI.Widgets
|
|||||||
/// <summary>Set a given data index with the specified value.</summary>
|
/// <summary>Set a given data index with the specified value.</summary>
|
||||||
public void SetIndex(int dataIndex, float height)
|
public void SetIndex(int dataIndex, float height)
|
||||||
{
|
{
|
||||||
|
height = (float)Math.Floor(height);
|
||||||
|
|
||||||
// If the index being set is beyond the DataSource item count, prune and return.
|
// If the index being set is beyond the DataSource item count, prune and return.
|
||||||
if (dataIndex >= ScrollPool.DataSource.ItemCount)
|
if (dataIndex >= ScrollPool.DataSource.ItemCount)
|
||||||
{
|
{
|
||||||
@ -189,20 +193,17 @@ namespace UnityExplorer.UI.Widgets
|
|||||||
int spread = GetRangeSpread(cache.startPosition, height);
|
int spread = GetRangeSpread(cache.startPosition, height);
|
||||||
|
|
||||||
// If the previous item in the range cache is not the previous data index, there is a gap.
|
// If the previous item in the range cache is not the previous data index, there is a gap.
|
||||||
if (dataIndex > 0 && rangeCache.Count > rangeIndex && rangeCache[rangeIndex - 1] != (dataIndex - 1))
|
if (dataIndex > 0 && rangeCache[rangeIndex - 1] != (dataIndex - 1))
|
||||||
{
|
{
|
||||||
// Recalculate start positions up to this index. The gap could be anywhere.
|
// Recalculate start positions up to this index. The gap could be anywhere before here.
|
||||||
RecalculateStartPositions(dataIndex);
|
RecalculateStartPositions(dataIndex + 1);
|
||||||
// Get the range index and spread again after rebuilding
|
// Get the range index and spread again after rebuilding
|
||||||
rangeIndex = GetRangeCeilingOfPosition(cache.startPosition);
|
rangeIndex = GetRangeCeilingOfPosition(cache.startPosition);
|
||||||
spread = GetRangeSpread(cache.startPosition, height);
|
spread = GetRangeSpread(cache.startPosition, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Should never happen
|
|
||||||
if (rangeCache.Count <= rangeIndex || rangeCache[rangeIndex] != dataIndex)
|
if (rangeCache.Count <= rangeIndex || rangeCache[rangeIndex] != dataIndex)
|
||||||
throw new Exception($"Trying to set range index but cache is corrupt after rebuild!\r\n" +
|
throw new Exception("ScrollPool data height cache is corrupt or invalid, rebuild failed!");
|
||||||
$"dataIndex: {dataIndex}, rangeIndex: {rangeIndex}, rangeCache.Count: {rangeCache.Count}, " +
|
|
||||||
$"startPos: {cache.startPosition}/{TotalHeight}");
|
|
||||||
|
|
||||||
if (spread != cache.normalizedSpread)
|
if (spread != cache.normalizedSpread)
|
||||||
{
|
{
|
||||||
@ -217,13 +218,21 @@ namespace UnityExplorer.UI.Widgets
|
|||||||
{
|
{
|
||||||
if (spreadDiff > 0)
|
if (spreadDiff > 0)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < spreadDiff; i++)
|
while (rangeCache[rangeIndex] == dataIndex && spreadDiff > 0)
|
||||||
|
{
|
||||||
rangeCache.Insert(rangeIndex, dataIndex);
|
rangeCache.Insert(rangeIndex, dataIndex);
|
||||||
|
spreadDiff--;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (int i = 0; i < -spreadDiff; i++)
|
while (rangeCache[rangeIndex] == dataIndex && spreadDiff < 0)
|
||||||
|
{
|
||||||
rangeCache.RemoveAt(rangeIndex);
|
rangeCache.RemoveAt(rangeIndex);
|
||||||
|
spreadDiff++;
|
||||||
|
}
|
||||||
|
//for (int i = 0; i < -spreadDiff; i++)
|
||||||
|
// rangeCache.RemoveAt(rangeIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -233,20 +242,37 @@ namespace UnityExplorer.UI.Widgets
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
DataViewInfo cache;
|
DataViewInfo cache;
|
||||||
DataViewInfo prev = heightCache[0];
|
DataViewInfo prev = null;
|
||||||
for (int i = 1; i <= toIndex && i < heightCache.Count; i++)
|
for (int i = 0; i <= toIndex && i < heightCache.Count; i++)
|
||||||
{
|
{
|
||||||
cache = heightCache[i];
|
cache = heightCache[i];
|
||||||
|
|
||||||
|
if (prev != null)
|
||||||
cache.startPosition = prev.startPosition + prev.height;
|
cache.startPosition = prev.startPosition + prev.height;
|
||||||
|
else
|
||||||
|
cache.startPosition = 0;
|
||||||
|
|
||||||
var prevSpread = cache.normalizedSpread;
|
var origSpread = cache.normalizedSpread;
|
||||||
cache.normalizedSpread = GetRangeSpread(cache.startPosition, cache.height);
|
cache.normalizedSpread = GetRangeSpread(cache.startPosition, cache.height);
|
||||||
if (cache.normalizedSpread != prevSpread)
|
if (cache.normalizedSpread != origSpread)
|
||||||
SetSpread(i, GetRangeCeilingOfPosition(cache.startPosition), cache.normalizedSpread - prevSpread);
|
SetSpread(i, GetRangeCeilingOfPosition(cache.startPosition), cache.normalizedSpread - origSpread);
|
||||||
|
|
||||||
prev = cache;
|
prev = cache;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//private void HardRebuildRanges()
|
||||||
|
//{
|
||||||
|
// var tempList = new List<float>();
|
||||||
|
// for (int i = 0; i < heightCache.Count; i++)
|
||||||
|
// tempList.Add(heightCache[i]);
|
||||||
|
//
|
||||||
|
// heightCache.Clear();
|
||||||
|
// rangeCache.Clear();
|
||||||
|
// totalHeight = 0;
|
||||||
|
//
|
||||||
|
// for (int i = 0; i < tempList.Count; i++)
|
||||||
|
// SetIndex(i, tempList[i]);
|
||||||
|
//}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user