From 5abfa3da674418803fcc7f376a71ac658b8a05b3 Mon Sep 17 00:00:00 2001 From: Sinai Date: Wed, 19 May 2021 19:24:33 +1000 Subject: [PATCH] Better EntryType checking for enumerables and dicts --- src/Core/Reflection/Il2CppReflection.cs | 62 +++++++++++++++++++++-- src/Core/Reflection/ReflectionUtility.cs | 56 ++++++++++++++++++++ src/Core/Runtime/Il2Cpp/Il2CppProvider.cs | 14 ++--- src/UI/IValues/InteractiveDictionary.cs | 20 ++------ src/UI/IValues/InteractiveList.cs | 7 +-- 5 files changed, 124 insertions(+), 35 deletions(-) diff --git a/src/Core/Reflection/Il2CppReflection.cs b/src/Core/Reflection/Il2CppReflection.cs index d372361..267d0e4 100644 --- a/src/Core/Reflection/Il2CppReflection.cs +++ b/src/Core/Reflection/Il2CppReflection.cs @@ -258,6 +258,18 @@ namespace UnityExplorer } } + private static bool IsAssignableFrom(Type thisType, Type fromType) + { + if (!Il2CppTypeNotNull(fromType, out IntPtr fromTypePtr) + || !Il2CppTypeNotNull(thisType, out IntPtr thisTypePtr)) + { + // one or both of the types are not Il2Cpp types, use normal check + return thisType.IsAssignableFrom(fromType); + } + + return il2cpp_class_is_assignable_from(thisTypePtr, fromTypePtr); + } + #endregion @@ -508,7 +520,7 @@ namespace UnityExplorer #endregion -#region Il2cpp reflection blacklist + #region Il2cpp reflection blacklist public override string DefaultReflectionBlacklist => string.Join(";", defaultIl2CppBlacklist); @@ -654,10 +666,54 @@ namespace UnityExplorer "UnityEngine.XR.InputDevice.SendHapticImpulse", }; -#endregion + #endregion -#region Temp il2cpp list/dictionary fixes + protected override bool Internal_TryGetEntryType(Type enumerableType, out Type type) + { + // Check for system types (not unhollowed) + if (base.Internal_TryGetEntryType(enumerableType, out type)) + return true; + + // Type is either an IL2CPP enumerable, or its not generic. + + if (type.IsGenericType) + { + // Temporary naive solution until IL2CPP interface support improves. + // This will work fine for most cases, but there are edge cases which would not work. + type = type.GetGenericArguments()[0]; + return true; + } + + // Unable to determine entry type + type = typeof(object); + return false; + } + + protected override bool Internal_TryGetEntryTypes(Type type, out Type keys, out Type values) + { + if (base.Internal_TryGetEntryTypes(type, out keys, out values)) + return true; + + // Type is either an IL2CPP dictionary, or its not generic. + if (type.IsGenericType) + { + // Naive solution until IL2CPP interfaces improve. + var args = type.GetGenericArguments(); + if (args.Length == 2) + { + keys = args[0]; + values = args[1]; + return true; + } + } + + keys = typeof(object); + values = typeof(object); + return false; + } + + #region Temp il2cpp list/dictionary fixes // Temp fix until Unhollower interface support improves diff --git a/src/Core/Reflection/ReflectionUtility.cs b/src/Core/Reflection/ReflectionUtility.cs index 61a4dd3..a86fb72 100644 --- a/src/Core/Reflection/ReflectionUtility.cs +++ b/src/Core/Reflection/ReflectionUtility.cs @@ -495,6 +495,39 @@ namespace UnityExplorer enumerator = (list as IEnumerable).GetEnumerator(); return true; } + + // TryGetEntryType + + public static bool TryGetEntryType(Type enumerableType, out Type type) + => Instance.Internal_TryGetEntryType(enumerableType, out type); + + protected virtual bool Internal_TryGetEntryType(Type enumerableType, out Type type) + { + // Check for arrays + if (enumerableType.IsArray) + { + type = enumerableType.GetElementType(); + return true; + } + + // Check for implementation of IEnumerable, IList or ICollection + foreach (var t in enumerableType.GetInterfaces()) + { + if (t.IsGenericType) + { + var typeDef = t.GetGenericTypeDefinition(); + if (typeDef == typeof(IEnumerable<>) || typeDef == typeof(IList<>) || typeDef == typeof(ICollection<>)) + { + type = t.GetGenericArguments()[0]; + return true; + } + } + } + + // Unable to determine any generic element type, just use object. + type = typeof(object); + return false; + } // IsDictionary @@ -524,5 +557,28 @@ namespace UnityExplorer yield return new DictionaryEntry(enumerator.Key, enumerator.Value); } } + + // TryGetEntryTypes + + public static bool TryGetEntryTypes(Type dictionaryType, out Type keys, out Type values) + => Instance.Internal_TryGetEntryTypes(dictionaryType, out keys, out values); + + protected virtual bool Internal_TryGetEntryTypes(Type dictionaryType, out Type keys, out Type values) + { + foreach (var t in dictionaryType.GetInterfaces()) + { + if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IDictionary<,>)) + { + var args = t.GetGenericArguments(); + keys = args[0]; + values = args[1]; + return true; + } + } + + keys = typeof(object); + values = typeof(object); + return false; + } } } diff --git a/src/Core/Runtime/Il2Cpp/Il2CppProvider.cs b/src/Core/Runtime/Il2Cpp/Il2CppProvider.cs index 34f88bc..06b1006 100644 --- a/src/Core/Runtime/Il2Cpp/Il2CppProvider.cs +++ b/src/Core/Runtime/Il2Cpp/Il2CppProvider.cs @@ -22,7 +22,6 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp public override void Initialize() { ExplorerCore.Context = RuntimeContext.IL2CPP; - //Reflection = new Il2CppReflection(); TextureUtil = new Il2CppTextureUtil(); } @@ -30,19 +29,12 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp { try { - //Application.add_logMessageReceived(new Action(ExplorerCore.Instance.OnUnityLog)); - - var logType = ReflectionUtility.GetTypeByName("UnityEngine.Application+LogCallback"); - var castMethod = logType.GetMethod("op_Implicit", new[] { typeof(Action) }); - var addMethod = typeof(Application).GetMethod("add_logMessageReceived", BF.Static | BF.Public, null, new[] { logType }, null); - addMethod.Invoke(null, new[] - { - castMethod.Invoke(null, new[] { new Action(Application_logMessageReceived) }) - }); + Application.add_logMessageReceived(new Action(Application_logMessageReceived)); } - catch + catch (Exception ex) { ExplorerCore.LogWarning("Exception setting up Unity log listener, make sure Unity libraries have been unstripped!"); + ExplorerCore.Log(ex); } } diff --git a/src/UI/IValues/InteractiveDictionary.cs b/src/UI/IValues/InteractiveDictionary.cs index e49161c..7c4de88 100644 --- a/src/UI/IValues/InteractiveDictionary.cs +++ b/src/UI/IValues/InteractiveDictionary.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Collections.Generic; using System.Linq; +using System.Reflection; using UnityEngine; using UnityEngine.UI; using UnityExplorer.UI.CacheObject; @@ -21,8 +22,8 @@ namespace UnityExplorer.UI.IValues public override bool CanWrite => base.CanWrite && RefIDictionary != null && !RefIDictionary.IsReadOnly; - public Type KeyType; - public Type ValueType; + public Type KeysType; + public Type ValuesType; public IDictionary RefIDictionary; public int ItemCount => cachedEntries.Count; @@ -75,24 +76,13 @@ namespace UnityExplorer.UI.IValues else { var type = value.GetActualType(); - - if (type.TryGetGenericArguments(out var args) && args.Length == 2) - { - KeyType = args[0]; - ValueType = args[1]; - } - else - { - KeyType = typeof(object); - ValueType = typeof(object); - } + ReflectionUtility.TryGetEntryTypes(type, out KeysType, out ValuesType); CacheEntries(value); TopLabel.text = $"[{cachedEntries.Count}] {SignatureHighlighter.Parse(type, false)}"; } - this.DictScrollPool.Refresh(true, false); } @@ -117,7 +107,7 @@ namespace UnityExplorer.UI.IValues else cache = cachedEntries[idx]; - cache.SetFallbackType(ValueType); + cache.SetFallbackType(ValuesType); cache.SetKey(dictEnumerator.Current.Key); cache.SetValueFromSource(dictEnumerator.Current.Value); diff --git a/src/UI/IValues/InteractiveList.cs b/src/UI/IValues/InteractiveList.cs index 6a1f6be..f14b38c 100644 --- a/src/UI/IValues/InteractiveList.cs +++ b/src/UI/IValues/InteractiveList.cs @@ -74,12 +74,7 @@ namespace UnityExplorer.UI.IValues else { var type = value.GetActualType(); - if (type.TryGetGenericArguments(out var args)) - EntryType = args[0]; - else if (type.HasElementType) - EntryType = type.GetElementType(); - else - EntryType = typeof(object); + ReflectionUtility.TryGetEntryType(type, out EntryType); CacheEntries(value);