mirror of
https://github.com/GrahamKracker/UnityExplorer.git
synced 2025-07-16 00:07:52 +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())
|
||||
CacheTypes(asm);
|
||||
|
||||
allTypeNames.Sort();
|
||||
|
||||
AppDomain.CurrentDomain.AssemblyLoad += AssemblyLoaded;
|
||||
}
|
||||
|
||||
private static readonly Dictionary<string, Type> allCachedTypes = new Dictionary<string, Type>();
|
||||
|
||||
private static void CacheTypes(Assembly asm)
|
||||
{
|
||||
foreach (var type in asm.TryGetTypes())
|
||||
{
|
||||
if (allCachedTypes.ContainsKey(type.FullName))
|
||||
continue;
|
||||
|
||||
if (type.FullName.ContainsIgnoreCase("PrivateImplementationDetails"))
|
||||
continue;
|
||||
|
||||
allCachedTypes.Add(type.FullName, type);
|
||||
}
|
||||
}
|
||||
/// <summary>Key: Type.FullName</summary>
|
||||
public static readonly SortedDictionary<string, Type> AllTypes = new SortedDictionary<string, Type>(StringComparer.OrdinalIgnoreCase);
|
||||
private static readonly List<string> allTypeNames = new List<string>();
|
||||
|
||||
private static void AssemblyLoaded(object sender, AssemblyLoadEventArgs args)
|
||||
{
|
||||
if (args.LoadedAssembly == null)
|
||||
return;
|
||||
|
||||
s_cachedTypeInheritance.Clear();
|
||||
s_cachedGenericParameterInheritance.Clear();
|
||||
|
||||
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;
|
||||
|
||||
@ -156,7 +168,9 @@ namespace UnityExplorer
|
||||
/// <returns>The Type if found, otherwise null.</returns>
|
||||
public static Type GetTypeByName(string fullName)
|
||||
{
|
||||
allCachedTypes.TryGetValue(fullName, out Type type);
|
||||
|
||||
|
||||
AllTypes.TryGetValue(fullName, out Type 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_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>
|
||||
/// 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.
|
||||
@ -208,38 +237,94 @@ namespace UnityExplorer
|
||||
/// <returns>All implementations of the type in the current AppDomain.</returns>
|
||||
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))
|
||||
{
|
||||
var set = new HashSet<Type>();
|
||||
|
||||
if (!baseType.IsAbstract && !baseType.IsInterface)
|
||||
set.Add(baseType);
|
||||
|
||||
var keys = allCachedTypes.Keys.ToArray();
|
||||
for (int i = 0; i < keys.Length; i++)
|
||||
for (int i = 0; i < allTypeNames.Count; i++)
|
||||
{
|
||||
var type = allCachedTypes[keys[i]];
|
||||
var type = AllTypes[allTypeNames[i]];
|
||||
//type = ReflectionProvider.Instance.GetDeobfuscatedType(type);
|
||||
try
|
||||
{
|
||||
if ((type.IsAbstract && type.IsSealed) // ignore static classes
|
||||
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.IsAssignableFrom(type) && !set.Contains(type))
|
||||
set.Add(type);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
//set.
|
||||
|
||||
s_cachedTypeInheritance.Add(key, set);
|
||||
}
|
||||
|
||||
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>
|
||||
/// Safely get all valid Types inside an Assembly.
|
||||
/// </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)
|
||||
{
|
||||
if (castTo == typeof(Il2CppSystem.String))
|
||||
@ -82,10 +87,12 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
|
||||
{
|
||||
try
|
||||
{
|
||||
var cppType = Il2CppType.From(type);
|
||||
var monoType = GetMonoType(cppType);
|
||||
if (monoType != null)
|
||||
return monoType;
|
||||
if (Il2CppToMonoType.ContainsKey(type.AssemblyQualifiedName))
|
||||
return Il2CppToMonoType[type.AssemblyQualifiedName];
|
||||
//var cppType = Il2CppType.From(type);
|
||||
//var monoType = GetMonoType(cppType);
|
||||
//if (monoType != null)
|
||||
// return monoType;
|
||||
}
|
||||
catch { }
|
||||
|
||||
@ -116,7 +123,7 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
|
||||
|
||||
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.
|
||||
if (cppObject is CppType)
|
||||
@ -131,6 +138,9 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
|
||||
IntPtr classPtr = il2cpp_object_get_class(cppObject.Pointer);
|
||||
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);
|
||||
if (typeByName != null)
|
||||
return typeByName;
|
||||
@ -142,9 +152,9 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
|
||||
return getType;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
catch //(Exception ex)
|
||||
{
|
||||
ExplorerCore.LogWarning("Exception in GetActualType: " + ex);
|
||||
//ExplorerCore.LogWarning("Exception in GetActualType: " + ex);
|
||||
}
|
||||
|
||||
return type;
|
||||
@ -176,7 +186,7 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
|
||||
{
|
||||
var cppType = Il2CppType.From(type);
|
||||
|
||||
if (!Il2CppToMonoType.ContainsKey(cppType.FullName))
|
||||
if (!Il2CppToMonoType.ContainsKey(cppType.AssemblyQualifiedName))
|
||||
{
|
||||
Il2CppToMonoType.Add(cppType.AssemblyQualifiedName, type);
|
||||
s_deobfuscatedTypeNames.Add(cppType.FullName, type.FullName);
|
||||
@ -234,12 +244,22 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
|
||||
return null;
|
||||
|
||||
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))
|
||||
return cppObj.ToString();
|
||||
|
||||
return Activator.CreateInstance(castTo, cppObj.Pointer);
|
||||
try
|
||||
{
|
||||
return Activator.CreateInstance(castTo, cppObj.Pointer);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -32,6 +32,8 @@ namespace UnityExplorer.Core.Runtime
|
||||
|
||||
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 virtual string UnboxString(object value) => (string)value;
|
||||
|
Reference in New Issue
Block a user