2022-04-12 05:20:35 +10:00
|
|
|
|
using HarmonyLib;
|
|
|
|
|
using System;
|
2022-03-20 21:20:17 +11:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Reflection;
|
|
|
|
|
using UnityExplorer.Inspectors;
|
|
|
|
|
using UnityExplorer.Runtime;
|
|
|
|
|
using UniverseLib;
|
|
|
|
|
|
|
|
|
|
namespace UnityExplorer.CacheObject
|
|
|
|
|
{
|
|
|
|
|
public static class CacheMemberFactory
|
|
|
|
|
{
|
2022-03-29 22:36:17 +11:00
|
|
|
|
public static List<CacheMember> GetCacheMembers(Type type, ReflectionInspector inspector)
|
2022-03-20 21:20:17 +11:00
|
|
|
|
{
|
|
|
|
|
//var list = new List<CacheMember>();
|
|
|
|
|
HashSet<string> cachedSigs = new();
|
|
|
|
|
List<CacheMember> props = new();
|
|
|
|
|
List<CacheMember> fields = new();
|
|
|
|
|
List<CacheMember> ctors = new();
|
|
|
|
|
List<CacheMember> methods = new();
|
|
|
|
|
|
2022-04-12 00:17:06 +10:00
|
|
|
|
Type[] types = ReflectionUtility.GetAllBaseTypes(type);
|
2022-03-20 21:20:17 +11:00
|
|
|
|
|
2022-04-12 00:17:06 +10:00
|
|
|
|
BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static;
|
2022-03-20 21:20:17 +11:00
|
|
|
|
if (!inspector.StaticOnly)
|
|
|
|
|
flags |= BindingFlags.Instance;
|
|
|
|
|
|
|
|
|
|
if (!type.IsAbstract)
|
|
|
|
|
{
|
|
|
|
|
// Get non-static constructors of the main type.
|
|
|
|
|
// There's no reason to get the static cctor, it will be invoked when we inspect the class.
|
|
|
|
|
// Also no point getting ctors on inherited types.
|
2022-04-12 00:17:06 +10:00
|
|
|
|
foreach (ConstructorInfo ctor in type.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
|
2022-03-20 21:20:17 +11:00
|
|
|
|
TryCacheMember(ctor, ctors, cachedSigs, type, inspector);
|
|
|
|
|
|
|
|
|
|
// structs always have a parameterless constructor
|
|
|
|
|
if (type.IsValueType)
|
|
|
|
|
{
|
|
|
|
|
CacheConstructor cached = new(type);
|
|
|
|
|
cached.SetFallbackType(type);
|
|
|
|
|
cached.SetInspectorOwner(inspector, null);
|
|
|
|
|
ctors.Add(cached);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-12 00:17:06 +10:00
|
|
|
|
foreach (Type declaringType in types)
|
2022-03-20 21:20:17 +11:00
|
|
|
|
{
|
2022-04-12 00:17:06 +10:00
|
|
|
|
foreach (PropertyInfo prop in declaringType.GetProperties(flags))
|
2022-03-20 21:20:17 +11:00
|
|
|
|
if (prop.DeclaringType == declaringType)
|
|
|
|
|
TryCacheMember(prop, props, cachedSigs, declaringType, inspector);
|
|
|
|
|
|
2022-04-12 00:17:06 +10:00
|
|
|
|
foreach (FieldInfo field in declaringType.GetFields(flags))
|
2022-03-20 21:20:17 +11:00
|
|
|
|
if (field.DeclaringType == declaringType)
|
|
|
|
|
TryCacheMember(field, fields, cachedSigs, declaringType, inspector);
|
|
|
|
|
|
2022-04-12 00:17:06 +10:00
|
|
|
|
foreach (MethodInfo method in declaringType.GetMethods(flags))
|
2022-03-20 21:20:17 +11:00
|
|
|
|
if (method.DeclaringType == declaringType)
|
|
|
|
|
TryCacheMember(method, methods, cachedSigs, declaringType, inspector);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-12 00:17:06 +10:00
|
|
|
|
List<CacheMember> sorted = new();
|
2022-03-20 21:20:17 +11:00
|
|
|
|
sorted.AddRange(props.OrderBy(it => Array.IndexOf(types, it.DeclaringType))
|
|
|
|
|
.ThenBy(it => it.NameForFiltering));
|
|
|
|
|
sorted.AddRange(fields.OrderBy(it => Array.IndexOf(types, it.DeclaringType))
|
|
|
|
|
.ThenBy(it => it.NameForFiltering));
|
|
|
|
|
sorted.AddRange(ctors.OrderBy(it => Array.IndexOf(types, it.DeclaringType))
|
|
|
|
|
.ThenBy(it => it.NameForFiltering));
|
|
|
|
|
sorted.AddRange(methods.OrderBy(it => Array.IndexOf(types, it.DeclaringType))
|
|
|
|
|
.ThenBy(it => it.NameForFiltering));
|
|
|
|
|
return sorted;
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-12 05:20:35 +10:00
|
|
|
|
static void TryCacheMember<T>(MemberInfo member, List<T> list, HashSet<string> cachedSigs,
|
|
|
|
|
Type declaringType, ReflectionInspector inspector, bool ignorePropertyMethodInfos = true)
|
2022-03-29 22:36:17 +11:00
|
|
|
|
where T : CacheMember
|
2022-03-20 21:20:17 +11:00
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (UERuntimeHelper.IsBlacklisted(member))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
string sig = member switch
|
|
|
|
|
{
|
2022-04-12 00:17:06 +10:00
|
|
|
|
MethodBase mb => mb.FullDescription(), // (method or constructor)
|
2022-03-20 21:20:17 +11:00
|
|
|
|
PropertyInfo or FieldInfo => $"{member.DeclaringType.FullDescription()}.{member.Name}",
|
|
|
|
|
_ => throw new NotImplementedException(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (cachedSigs.Contains(sig))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// ExplorerCore.Log($"Trying to cache member {sig}... ({member.MemberType})");
|
|
|
|
|
|
|
|
|
|
CacheMember cached;
|
|
|
|
|
Type returnType;
|
|
|
|
|
|
|
|
|
|
switch (member.MemberType)
|
|
|
|
|
{
|
|
|
|
|
case MemberTypes.Constructor:
|
|
|
|
|
{
|
2022-04-12 00:17:06 +10:00
|
|
|
|
ConstructorInfo ci = member as ConstructorInfo;
|
2022-03-20 21:20:17 +11:00
|
|
|
|
cached = new CacheConstructor(ci);
|
|
|
|
|
returnType = ci.DeclaringType;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case MemberTypes.Method:
|
|
|
|
|
{
|
2022-04-12 00:17:06 +10:00
|
|
|
|
MethodInfo mi = member as MethodInfo;
|
2022-03-20 21:20:17 +11:00
|
|
|
|
if (ignorePropertyMethodInfos
|
|
|
|
|
&& (mi.Name.StartsWith("get_") || mi.Name.StartsWith("set_")))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
cached = new CacheMethod(mi);
|
|
|
|
|
returnType = mi.ReturnType;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case MemberTypes.Property:
|
|
|
|
|
{
|
2022-04-12 00:17:06 +10:00
|
|
|
|
PropertyInfo pi = member as PropertyInfo;
|
2022-03-20 21:20:17 +11:00
|
|
|
|
|
|
|
|
|
if (!pi.CanRead && pi.CanWrite)
|
|
|
|
|
{
|
|
|
|
|
// write-only property, cache the set method instead.
|
2022-04-12 00:17:06 +10:00
|
|
|
|
MethodInfo setMethod = pi.GetSetMethod(true);
|
2022-03-20 21:20:17 +11:00
|
|
|
|
if (setMethod != null)
|
|
|
|
|
TryCacheMember(setMethod, list, cachedSigs, declaringType, inspector, false);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cached = new CacheProperty(pi);
|
|
|
|
|
returnType = pi.PropertyType;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case MemberTypes.Field:
|
|
|
|
|
{
|
2022-04-12 00:17:06 +10:00
|
|
|
|
FieldInfo fi = member as FieldInfo;
|
2022-03-20 21:20:17 +11:00
|
|
|
|
cached = new CacheField(fi);
|
|
|
|
|
returnType = fi.FieldType;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
throw new NotImplementedException();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cachedSigs.Add(sig);
|
|
|
|
|
|
|
|
|
|
cached.SetFallbackType(returnType);
|
|
|
|
|
cached.SetInspectorOwner(inspector, member);
|
|
|
|
|
|
2022-03-29 22:36:17 +11:00
|
|
|
|
list.Add((T)cached);
|
2022-03-20 21:20:17 +11:00
|
|
|
|
}
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
|
|
|
|
ExplorerCore.LogWarning($"Exception caching member {member.DeclaringType.FullName}.{member.Name}!");
|
2022-03-29 22:36:17 +11:00
|
|
|
|
ExplorerCore.Log(e);
|
2022-03-20 21:20:17 +11:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|