Add support for Constructors in Reflection Inspector

- Added CacheConstructor : CacheMember
- Changed default scope to "All" from "Instance" when inspecting an instance
- Bumped UniverseLib
This commit is contained in:
Sinai 2022-03-14 05:20:43 +11:00
parent b5c69fc1ea
commit e44ff9e207
9 changed files with 161 additions and 62 deletions

View File

@ -0,0 +1,63 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityExplorer.Inspectors;
using UniverseLib.Utility;
namespace UnityExplorer.CacheObject
{
public class CacheConstructor : CacheMember
{
public ConstructorInfo CtorInfo { get; }
public override Type DeclaringType => CtorInfo.DeclaringType;
public override bool IsStatic => true;
public override bool ShouldAutoEvaluate => false;
public override bool CanWrite => false;
public CacheConstructor(ConstructorInfo ci)
{
this.CtorInfo = ci;
}
public override void SetInspectorOwner(ReflectionInspector inspector, MemberInfo member)
{
base.SetInspectorOwner(inspector, member);
Arguments = CtorInfo.GetParameters();
if (CtorInfo.DeclaringType.IsGenericTypeDefinition)
GenericArguments = CtorInfo.DeclaringType.GetGenericArguments();
}
protected override object TryEvaluate()
{
try
{
Type returnType = DeclaringType;
if (returnType.IsGenericTypeDefinition)
returnType = DeclaringType.MakeGenericType(Evaluator.TryParseGenericArguments());
object ret;
if (HasArguments)
ret = Activator.CreateInstance(returnType, Evaluator.TryParseArguments());
else
ret = Activator.CreateInstance(returnType, ArgumentUtility.EmptyArgs);
HadException = false;
LastException = null;
return ret;
}
catch (Exception ex)
{
HadException = true;
LastException = ex;
return null;
}
}
protected override void TrySetValue(object value) => throw new NotImplementedException("You can't set a constructor");
}
}

View File

@ -17,6 +17,11 @@ namespace UnityExplorer.CacheObject
public override bool ShouldAutoEvaluate => true; public override bool ShouldAutoEvaluate => true;
public CacheField(FieldInfo fi)
{
this.FieldInfo = fi;
}
public override void SetInspectorOwner(ReflectionInspector inspector, MemberInfo member) public override void SetInspectorOwner(ReflectionInspector inspector, MemberInfo member)
{ {
base.SetInspectorOwner(inspector, member); base.SetInspectorOwner(inspector, member);

View File

@ -14,6 +14,8 @@ using UniverseLib.UI;
using UnityExplorer.UI.Widgets; using UnityExplorer.UI.Widgets;
using UniverseLib.Utility; using UniverseLib.Utility;
using UniverseLib.UI.ObjectPool; using UniverseLib.UI.ObjectPool;
using System.Collections;
using HarmonyLib;
namespace UnityExplorer.CacheObject namespace UnityExplorer.CacheObject
{ {
@ -34,9 +36,12 @@ namespace UnityExplorer.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 = this is CacheMethod this.NameLabelText = this switch
? SignatureHighlighter.HighlightMethod(member as MethodInfo) {
: SignatureHighlighter.Parse(member.DeclaringType, false, member); CacheMethod => SignatureHighlighter.HighlightMethod(member as MethodInfo),
CacheConstructor => SignatureHighlighter.HighlightConstructor(member as ConstructorInfo),
_ => SignatureHighlighter.Parse(member.DeclaringType, false, member),
};
this.NameForFiltering = SignatureHighlighter.RemoveHighlighting(NameLabelText); this.NameForFiltering = SignatureHighlighter.RemoveHighlighting(NameLabelText);
this.NameLabelTextRaw = NameForFiltering; this.NameLabelTextRaw = NameForFiltering;
@ -167,56 +172,61 @@ namespace UnityExplorer.CacheObject
#region Cache Member Util #region Cache Member Util
public static List<CacheMember> GetCacheMembers(object inspectorTarget, Type _type, ReflectionInspector _inspector) public static List<CacheMember> GetCacheMembers(object inspectorTarget, Type type, ReflectionInspector inspector)
{ {
var list = new List<CacheMember>(); //var list = new List<CacheMember>();
var cachedSigs = new HashSet<string>(); HashSet<string> cachedSigs = new();
List<CacheMember> props = new();
List<CacheMember> fields = new();
List<CacheMember> ctors = new();
List<CacheMember> methods = new();
var types = ReflectionUtility.GetAllBaseTypes(_type); var types = ReflectionUtility.GetAllBaseTypes(type);
var flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static; var flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static;
if (!_inspector.StaticOnly) if (!inspector.StaticOnly)
flags |= BindingFlags.Instance; flags |= BindingFlags.Instance;
var infos = new List<MemberInfo>(); // 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);
foreach (var declaringType in types) foreach (var declaringType in types)
{ {
var target = inspectorTarget; var target = inspectorTarget;
if (!_inspector.StaticOnly) if (!inspector.StaticOnly)
target = target.TryCast(declaringType); target = target.TryCast(declaringType);
infos.Clear(); foreach (var prop in declaringType.GetProperties(flags))
infos.AddRange(declaringType.GetProperties(flags)); if (prop.DeclaringType == declaringType)
infos.AddRange(declaringType.GetFields(flags)); TryCacheMember(prop, props, cachedSigs, declaringType, inspector);
infos.AddRange(declaringType.GetMethods(flags));
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);
foreach (var member in infos)
{
if (member.DeclaringType != declaringType)
continue;
TryCacheMember(member, list, cachedSigs, declaringType, _inspector);
}
} }
var typeList = types.ToList();
var sorted = new List<CacheMember>(); var sorted = new List<CacheMember>();
sorted.AddRange(list.Where(it => it is CacheProperty) sorted.AddRange(props.OrderBy(it => Array.IndexOf(types, it.DeclaringType))
.OrderBy(it => typeList.IndexOf(it.DeclaringType)) .ThenBy(it => it.NameForFiltering));
.ThenBy(it => it.NameForFiltering)); sorted.AddRange(fields.OrderBy(it => Array.IndexOf(types, it.DeclaringType))
sorted.AddRange(list.Where(it => it is CacheField) .ThenBy(it => it.NameForFiltering));
.OrderBy(it => typeList.IndexOf(it.DeclaringType)) sorted.AddRange(ctors.OrderBy(it => Array.IndexOf(types, it.DeclaringType))
.ThenBy(it => it.NameForFiltering)); .ThenBy(it => it.NameForFiltering));
sorted.AddRange(list.Where(it => it is CacheMethod) sorted.AddRange(methods.OrderBy(it => Array.IndexOf(types, it.DeclaringType))
.OrderBy(it => typeList.IndexOf(it.DeclaringType)) .ThenBy(it => it.NameForFiltering));
.ThenBy(it => it.NameForFiltering));
return sorted; return sorted;
} }
private static void TryCacheMember(MemberInfo member, List<CacheMember> list, HashSet<string> cachedSigs, private static void TryCacheMember(MemberInfo member, IList list, HashSet<string> cachedSigs,
Type declaringType, ReflectionInspector _inspector, bool ignorePropertyMethodInfos = true) Type declaringType, ReflectionInspector inspector, bool ignorePropertyMethodInfos = true)
{ {
try try
{ {
@ -231,6 +241,17 @@ namespace UnityExplorer.CacheObject
Type returnType; Type returnType;
switch (member.MemberType) 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: case MemberTypes.Method:
{ {
var mi = member as MethodInfo; var mi = member as MethodInfo;
@ -238,15 +259,11 @@ namespace UnityExplorer.CacheObject
&& (mi.Name.StartsWith("get_") || mi.Name.StartsWith("set_"))) && (mi.Name.StartsWith("get_") || mi.Name.StartsWith("set_")))
return; return;
//var args = mi.GetParameters();
//if (!CanParseArgs(args))
// return;
sig += GetArgumentString(mi.GetParameters()); sig += GetArgumentString(mi.GetParameters());
if (cachedSigs.Contains(sig)) if (cachedSigs.Contains(sig))
return; return;
cached = new CacheMethod() { MethodInfo = mi }; cached = new CacheMethod(mi);
returnType = mi.ReturnType; returnType = mi.ReturnType;
break; break;
} }
@ -255,16 +272,12 @@ namespace UnityExplorer.CacheObject
{ {
var pi = member as PropertyInfo; var pi = member as PropertyInfo;
//var args = pi.GetIndexParameters();
//if (!CanParseArgs(args))
// return;
if (!pi.CanRead && pi.CanWrite) if (!pi.CanRead && pi.CanWrite)
{ {
// write-only property, cache the set method instead. // write-only property, cache the set method instead.
var setMethod = pi.GetSetMethod(true); var setMethod = pi.GetSetMethod(true);
if (setMethod != null) if (setMethod != null)
TryCacheMember(setMethod, list, cachedSigs, declaringType, _inspector, false); TryCacheMember(setMethod, list, cachedSigs, declaringType, inspector, false);
return; return;
} }
@ -272,7 +285,7 @@ namespace UnityExplorer.CacheObject
if (cachedSigs.Contains(sig)) if (cachedSigs.Contains(sig))
return; return;
cached = new CacheProperty() { PropertyInfo = pi }; cached = new CacheProperty(pi);
returnType = pi.PropertyType; returnType = pi.PropertyType;
break; break;
} }
@ -280,7 +293,7 @@ namespace UnityExplorer.CacheObject
case MemberTypes.Field: case MemberTypes.Field:
{ {
var fi = member as FieldInfo; var fi = member as FieldInfo;
cached = new CacheField() { FieldInfo = fi }; cached = new CacheField(fi);
returnType = fi.FieldType; returnType = fi.FieldType;
break; break;
} }
@ -291,7 +304,7 @@ namespace UnityExplorer.CacheObject
cachedSigs.Add(sig); cachedSigs.Add(sig);
cached.SetFallbackType(returnType); cached.SetFallbackType(returnType);
cached.SetInspectorOwner(_inspector, member); cached.SetInspectorOwner(inspector, member);
list.Add(cached); list.Add(cached);
} }

View File

@ -11,13 +11,18 @@ namespace UnityExplorer.CacheObject
{ {
public class CacheMethod : CacheMember public class CacheMethod : CacheMember
{ {
public MethodInfo MethodInfo { get; internal set; } public MethodInfo MethodInfo { get; }
public override Type DeclaringType => MethodInfo.DeclaringType; public override Type DeclaringType => MethodInfo.DeclaringType;
public override bool CanWrite => false; public override bool CanWrite => false;
public override bool IsStatic => MethodInfo.IsStatic; public override bool IsStatic => MethodInfo.IsStatic;
public override bool ShouldAutoEvaluate => false; public override bool ShouldAutoEvaluate => false;
public CacheMethod (MethodInfo mi)
{
this.MethodInfo = mi;
}
public override void SetInspectorOwner(ReflectionInspector inspector, MemberInfo member) public override void SetInspectorOwner(ReflectionInspector inspector, MemberInfo member)
{ {
base.SetInspectorOwner(inspector, member); base.SetInspectorOwner(inspector, member);

View File

@ -19,6 +19,11 @@ namespace UnityExplorer.CacheObject
public override bool ShouldAutoEvaluate => !HasArguments; public override bool ShouldAutoEvaluate => !HasArguments;
public CacheProperty(PropertyInfo pi)
{
this.PropertyInfo = pi;
}
public override void SetInspectorOwner(ReflectionInspector inspector, MemberInfo member) public override void SetInspectorOwner(ReflectionInspector inspector, MemberInfo member)
{ {
base.SetInspectorOwner(inspector, member); base.SetInspectorOwner(inspector, member);

View File

@ -46,8 +46,9 @@ namespace UnityExplorer.Inspectors
None = 0, None = 0,
Property = 1, Property = 1,
Field = 2, Field = 2,
Method = 4, Constructor = 4,
All = 7 Method = 8,
All = Property | Field | Method | Constructor,
} }
// UI // UI
@ -153,7 +154,7 @@ namespace UnityExplorer.Inspectors
this.filterInputField.Text = ""; this.filterInputField.Text = "";
SetFilter("", StaticOnly ? BindingFlags.Static : BindingFlags.Instance); SetFilter("", StaticOnly ? BindingFlags.Static : BindingFlags.Default);
scopeFilterButtons[BindingFlags.Default].Component.gameObject.SetActive(!StaticOnly); scopeFilterButtons[BindingFlags.Default].Component.gameObject.SetActive(!StaticOnly);
scopeFilterButtons[BindingFlags.Instance].Component.gameObject.SetActive(!StaticOnly); scopeFilterButtons[BindingFlags.Instance].Component.gameObject.SetActive(!StaticOnly);
@ -462,6 +463,7 @@ namespace UnityExplorer.Inspectors
AddMemberTypeToggle(rowObj, MemberTypes.Property, 90); AddMemberTypeToggle(rowObj, MemberTypes.Property, 90);
AddMemberTypeToggle(rowObj, MemberTypes.Field, 70); AddMemberTypeToggle(rowObj, MemberTypes.Field, 70);
AddMemberTypeToggle(rowObj, MemberTypes.Method, 90); AddMemberTypeToggle(rowObj, MemberTypes.Method, 90);
AddMemberTypeToggle(rowObj, MemberTypes.Constructor, 110);
} }
private void AddScopeFilterButton(GameObject parent, BindingFlags flags, bool setAsActive = false) private void AddScopeFilterButton(GameObject parent, BindingFlags flags, bool setAsActive = false)
@ -480,25 +482,26 @@ namespace UnityExplorer.Inspectors
{ {
var toggleObj = UIFactory.CreateToggle(parent, "Toggle_" + type, out Toggle toggle, out Text toggleText); var toggleObj = UIFactory.CreateToggle(parent, "Toggle_" + type, out Toggle toggle, out Text toggleText);
UIFactory.SetLayoutElement(toggleObj, minHeight: 25, minWidth: width); UIFactory.SetLayoutElement(toggleObj, minHeight: 25, minWidth: width);
var color = type switch string color = type switch
{ {
MemberTypes.Method => SignatureHighlighter.METHOD_INSTANCE, MemberTypes.Method => SignatureHighlighter.METHOD_INSTANCE,
MemberTypes.Field => SignatureHighlighter.FIELD_INSTANCE, MemberTypes.Field => SignatureHighlighter.FIELD_INSTANCE,
MemberTypes.Property => SignatureHighlighter.PROP_INSTANCE, MemberTypes.Property => SignatureHighlighter.PROP_INSTANCE,
MemberTypes.Constructor => SignatureHighlighter.CLASS_INSTANCE,
_ => throw new NotImplementedException() _ => throw new NotImplementedException()
}; };
toggleText.text = $"<color={color}>{type}</color>"; toggleText.text = $"<color={color}>{type}</color>";
toggle.graphic.TryCast<Image>().color = color.ToColor() * 0.65f; toggle.graphic.TryCast<Image>().color = color.ToColor() * 0.65f;
MemberFlags flag; MemberFlags flag = type switch
switch (type)
{ {
case MemberTypes.Method: flag = MemberFlags.Method; break; MemberTypes.Method => MemberFlags.Method,
case MemberTypes.Property: flag = MemberFlags.Property; break; MemberTypes.Property => MemberFlags.Property,
case MemberTypes.Field: flag = MemberFlags.Field; break; MemberTypes.Field => MemberFlags.Field,
default: return; MemberTypes.Constructor => MemberFlags.Constructor,
} _ => throw new NotImplementedException()
};
toggle.onValueChanged.AddListener((bool val) => { OnMemberTypeToggled(flag, val); }); toggle.onValueChanged.AddListener((bool val) => { OnMemberTypeToggled(flag, val); });

View File

@ -12,6 +12,7 @@ using UniverseLib.UI;
using UniverseLib; using UniverseLib;
using UnityExplorer.CacheObject; using UnityExplorer.CacheObject;
using UniverseLib.UI.ObjectPool; using UniverseLib.UI.ObjectPool;
using UniverseLib.Utility;
namespace UnityExplorer.UI.Widgets namespace UnityExplorer.UI.Widgets
{ {
@ -76,6 +77,9 @@ namespace UnityExplorer.UI.Widgets
public object[] TryParseArguments() public object[] TryParseArguments()
{ {
if (!parameters.Any())
return ArgumentUtility.EmptyArgs;
object[] outArgs = new object[parameters.Length]; object[] outArgs = new object[parameters.Length];
for (int i = 0; i < parameters.Length; i++) for (int i = 0; i < parameters.Length; i++)

View File

@ -175,13 +175,13 @@
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
<Reference Include="UniverseLib.Mono"> <Reference Include="UniverseLib.Mono">
<HintPath>packages\UniverseLib.1.2.15\lib\net35\UniverseLib.Mono.dll</HintPath> <HintPath>packages\UniverseLib.1.2.16\lib\net35\UniverseLib.Mono.dll</HintPath>
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<!-- Il2Cpp refs --> <!-- Il2Cpp refs -->
<ItemGroup Condition="'$(IsCpp)'=='true'"> <ItemGroup Condition="'$(IsCpp)'=='true'">
<Reference Include="UniverseLib.IL2CPP"> <Reference Include="UniverseLib.IL2CPP">
<HintPath>packages\UniverseLib.1.2.15\lib\net472\UniverseLib.IL2CPP.dll</HintPath> <HintPath>packages\UniverseLib.1.2.16\lib\net472\UniverseLib.IL2CPP.dll</HintPath>
</Reference> </Reference>
<Reference Include="UnhollowerBaseLib, Version=0.4.22.0, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="UnhollowerBaseLib, Version=0.4.22.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>packages\Il2CppAssemblyUnhollower.BaseLib.0.4.22\lib\net472\UnhollowerBaseLib.dll</HintPath> <HintPath>packages\Il2CppAssemblyUnhollower.BaseLib.0.4.22\lib\net472\UnhollowerBaseLib.dll</HintPath>
@ -225,6 +225,7 @@
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="CacheObject\CacheConstructor.cs" />
<Compile Include="Hooks\HookCell.cs" /> <Compile Include="Hooks\HookCell.cs" />
<Compile Include="Hooks\HookInstance.cs" /> <Compile Include="Hooks\HookInstance.cs" />
<Compile Include="Hooks\HookManager.cs" /> <Compile Include="Hooks\HookManager.cs" />

View File

@ -6,6 +6,6 @@
<package id="ILRepack.Lib.MSBuild.Task" version="2.0.18.2" targetFramework="net35" /> <package id="ILRepack.Lib.MSBuild.Task" version="2.0.18.2" targetFramework="net35" />
<package id="Mono.Cecil" version="0.10.4" targetFramework="net35" /> <package id="Mono.Cecil" version="0.10.4" targetFramework="net35" />
<package id="Samboy063.Tomlet" version="3.1.3" targetFramework="net472" /> <package id="Samboy063.Tomlet" version="3.1.3" targetFramework="net472" />
<package id="UniverseLib" version="1.2.15" targetFramework="net35" /> <package id="UniverseLib" version="1.2.16" targetFramework="net35" />
<package id="UniverseLib.Analyzers" version="1.0.3" targetFramework="net35" developmentDependency="true" /> <package id="UniverseLib.Analyzers" version="1.0.3" targetFramework="net35" developmentDependency="true" />
</packages> </packages>