diff --git a/src/CacheObject/CacheMember.cs b/src/CacheObject/CacheMember.cs index e8d04ba..850b787 100644 --- a/src/CacheObject/CacheMember.cs +++ b/src/CacheObject/CacheMember.cs @@ -169,186 +169,5 @@ namespace UnityExplorer.CacheObject } } } - - #region Cache Member Util - - public static List GetCacheMembers(object inspectorTarget, Type type, ReflectionInspector inspector) - { - //var list = new List(); - HashSet cachedSigs = new(); - List props = new(); - List fields = new(); - List ctors = new(); - List methods = new(); - - var types = ReflectionUtility.GetAllBaseTypes(type); - - var flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static; - 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. - foreach (var ctor in type.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) - 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); - } - } - - foreach (var declaringType in types) - { - var target = inspectorTarget; - if (!inspector.StaticOnly) - target = target.TryCast(declaringType); - - foreach (var prop in declaringType.GetProperties(flags)) - if (prop.DeclaringType == declaringType) - TryCacheMember(prop, props, cachedSigs, declaringType, inspector); - - foreach (var field in declaringType.GetFields(flags)) - if (field.DeclaringType == declaringType) - TryCacheMember(field, fields, cachedSigs, declaringType, inspector); - - foreach (var method in declaringType.GetMethods(flags)) - if (method.DeclaringType == declaringType) - TryCacheMember(method, methods, cachedSigs, declaringType, inspector); - - } - - var sorted = new List(); - 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; - } - - private static void TryCacheMember(MemberInfo member, IList list, HashSet cachedSigs, - Type declaringType, ReflectionInspector inspector, bool ignorePropertyMethodInfos = true) - { - try - { - if (UERuntimeHelper.IsBlacklisted(member)) - return; - - var sig = GetSig(member); - - // ExplorerCore.Log($"Trying to cache member {sig}... ({member.MemberType})"); - - CacheMember cached; - Type returnType; - switch (member.MemberType) - { - case MemberTypes.Constructor: - { - var ci = member as ConstructorInfo; - sig += GetArgumentString(ci.GetParameters()); - if (cachedSigs.Contains(sig)) - return; - cached = new CacheConstructor(ci); - returnType = ci.DeclaringType; - } - break; - - case MemberTypes.Method: - { - var mi = member as MethodInfo; - if (ignorePropertyMethodInfos - && (mi.Name.StartsWith("get_") || mi.Name.StartsWith("set_"))) - return; - - sig += GetArgumentString(mi.GetParameters()); - if (cachedSigs.Contains(sig)) - return; - - cached = new CacheMethod(mi); - returnType = mi.ReturnType; - break; - } - - case MemberTypes.Property: - { - var pi = member as PropertyInfo; - - if (!pi.CanRead && pi.CanWrite) - { - // write-only property, cache the set method instead. - var setMethod = pi.GetSetMethod(true); - if (setMethod != null) - TryCacheMember(setMethod, list, cachedSigs, declaringType, inspector, false); - return; - } - - sig += GetArgumentString(pi.GetIndexParameters()); - if (cachedSigs.Contains(sig)) - return; - - cached = new CacheProperty(pi); - returnType = pi.PropertyType; - break; - } - - case MemberTypes.Field: - { - var fi = member as FieldInfo; - cached = new CacheField(fi); - returnType = fi.FieldType; - break; - } - - default: return; - } - - cachedSigs.Add(sig); - - cached.SetFallbackType(returnType); - cached.SetInspectorOwner(inspector, member); - - list.Add(cached); - } - catch (Exception e) - { - ExplorerCore.LogWarning($"Exception caching member {member.DeclaringType.FullName}.{member.Name}!"); - ExplorerCore.Log(e.ToString()); - } - } - - internal static string GetSig(MemberInfo member) - => $"{member.DeclaringType.Name}.{member.Name}"; - - internal static string GetArgumentString(ParameterInfo[] args) - { - var sb = new StringBuilder(); - sb.Append(' '); - sb.Append('('); - foreach (var param in args) - { - sb.Append(param.ParameterType.Name); - sb.Append(' '); - sb.Append(param.Name); - sb.Append(','); - sb.Append(' '); - } - sb.Append(')'); - return sb.ToString(); - } - - #endregion - - } } diff --git a/src/CacheObject/CacheMemberFactory.cs b/src/CacheObject/CacheMemberFactory.cs new file mode 100644 index 0000000..924e24e --- /dev/null +++ b/src/CacheObject/CacheMemberFactory.cs @@ -0,0 +1,195 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using UnityExplorer.Inspectors; +using UnityExplorer.Runtime; +using UniverseLib; +using HarmonyLib; +using HarmonyLib.Tools; + +namespace UnityExplorer.CacheObject +{ + public static class CacheMemberFactory + { + public static List GetCacheMembers(object inspectorTarget, Type type, ReflectionInspector inspector) + { + //var list = new List(); + HashSet cachedSigs = new(); + List props = new(); + List fields = new(); + List ctors = new(); + List methods = new(); + + var types = ReflectionUtility.GetAllBaseTypes(type); + + var flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static; + 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. + foreach (var ctor in type.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) + 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); + } + } + + foreach (var declaringType in types) + { + var target = inspectorTarget; + if (!inspector.StaticOnly) + target = target.TryCast(declaringType); + + foreach (var prop in declaringType.GetProperties(flags)) + if (prop.DeclaringType == declaringType) + TryCacheMember(prop, props, cachedSigs, declaringType, inspector); + + foreach (var field in declaringType.GetFields(flags)) + if (field.DeclaringType == declaringType) + TryCacheMember(field, fields, cachedSigs, declaringType, inspector); + + foreach (var method in declaringType.GetMethods(flags)) + if (method.DeclaringType == declaringType) + TryCacheMember(method, methods, cachedSigs, declaringType, inspector); + + } + + var sorted = new List(); + 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; + } + + static void TryCacheMember( + MemberInfo member, + IList list, + HashSet cachedSigs, + Type declaringType, + ReflectionInspector inspector, + bool ignorePropertyMethodInfos = true) + { + try + { + if (UERuntimeHelper.IsBlacklisted(member)) + return; + + string sig = member switch + { + MethodBase mb => mb.FullDescription(), + 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: + { + var ci = member as ConstructorInfo; + cached = new CacheConstructor(ci); + returnType = ci.DeclaringType; + } + break; + + case MemberTypes.Method: + { + var mi = member as MethodInfo; + if (ignorePropertyMethodInfos + && (mi.Name.StartsWith("get_") || mi.Name.StartsWith("set_"))) + return; + + cached = new CacheMethod(mi); + returnType = mi.ReturnType; + break; + } + + case MemberTypes.Property: + { + var pi = member as PropertyInfo; + + if (!pi.CanRead && pi.CanWrite) + { + // write-only property, cache the set method instead. + var setMethod = pi.GetSetMethod(true); + if (setMethod != null) + TryCacheMember(setMethod, list, cachedSigs, declaringType, inspector, false); + return; + } + + cached = new CacheProperty(pi); + returnType = pi.PropertyType; + break; + } + + case MemberTypes.Field: + { + var fi = member as FieldInfo; + cached = new CacheField(fi); + returnType = fi.FieldType; + break; + } + + default: + throw new NotImplementedException(); + } + + cachedSigs.Add(sig); + + cached.SetFallbackType(returnType); + cached.SetInspectorOwner(inspector, member); + + list.Add(cached); + } + catch (Exception e) + { + ExplorerCore.LogWarning($"Exception caching member {member.DeclaringType.FullName}.{member.Name}!"); + ExplorerCore.Log(e.ToString()); + } + } + + //internal static string GetSig(MemberInfo member) => $"{member.DeclaringType.Name}.{member.Name}"; + // + //internal static string GetArgumentString(ParameterInfo[] args) + //{ + // var sb = new StringBuilder(); + // sb.Append(' '); + // sb.Append('('); + // foreach (var param in args) + // { + // sb.Append(param.ParameterType.Name); + // sb.Append(' '); + // sb.Append(param.Name); + // sb.Append(','); + // sb.Append(' '); + // } + // sb.Append(')'); + // return sb.ToString(); + //} + } +}