Skip to content
This repository was archived by the owner on May 9, 2023. It is now read-only.

Commit e44ff9e

Browse files
committed
Add support for Constructors in Reflection Inspector
- Added CacheConstructor : CacheMember - Changed default scope to "All" from "Instance" when inspecting an instance - Bumped UniverseLib
1 parent b5c69fc commit e44ff9e

File tree

9 files changed

+161
-62
lines changed

9 files changed

+161
-62
lines changed
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Reflection;
5+
using System.Text;
6+
using UnityExplorer.Inspectors;
7+
using UniverseLib.Utility;
8+
9+
namespace UnityExplorer.CacheObject
10+
{
11+
public class CacheConstructor : CacheMember
12+
{
13+
public ConstructorInfo CtorInfo { get; }
14+
15+
public override Type DeclaringType => CtorInfo.DeclaringType;
16+
public override bool IsStatic => true;
17+
public override bool ShouldAutoEvaluate => false;
18+
public override bool CanWrite => false;
19+
20+
public CacheConstructor(ConstructorInfo ci)
21+
{
22+
this.CtorInfo = ci;
23+
}
24+
25+
public override void SetInspectorOwner(ReflectionInspector inspector, MemberInfo member)
26+
{
27+
base.SetInspectorOwner(inspector, member);
28+
29+
Arguments = CtorInfo.GetParameters();
30+
if (CtorInfo.DeclaringType.IsGenericTypeDefinition)
31+
GenericArguments = CtorInfo.DeclaringType.GetGenericArguments();
32+
}
33+
34+
protected override object TryEvaluate()
35+
{
36+
try
37+
{
38+
Type returnType = DeclaringType;
39+
40+
if (returnType.IsGenericTypeDefinition)
41+
returnType = DeclaringType.MakeGenericType(Evaluator.TryParseGenericArguments());
42+
43+
object ret;
44+
if (HasArguments)
45+
ret = Activator.CreateInstance(returnType, Evaluator.TryParseArguments());
46+
else
47+
ret = Activator.CreateInstance(returnType, ArgumentUtility.EmptyArgs);
48+
49+
HadException = false;
50+
LastException = null;
51+
return ret;
52+
}
53+
catch (Exception ex)
54+
{
55+
HadException = true;
56+
LastException = ex;
57+
return null;
58+
}
59+
}
60+
61+
protected override void TrySetValue(object value) => throw new NotImplementedException("You can't set a constructor");
62+
}
63+
}

src/CacheObject/CacheField.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ public class CacheField : CacheMember
1717

1818
public override bool ShouldAutoEvaluate => true;
1919

20+
public CacheField(FieldInfo fi)
21+
{
22+
this.FieldInfo = fi;
23+
}
24+
2025
public override void SetInspectorOwner(ReflectionInspector inspector, MemberInfo member)
2126
{
2227
base.SetInspectorOwner(inspector, member);

src/CacheObject/CacheMember.cs

Lines changed: 60 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
using UnityExplorer.UI.Widgets;
1515
using UniverseLib.Utility;
1616
using UniverseLib.UI.ObjectPool;
17+
using System.Collections;
18+
using HarmonyLib;
1719

1820
namespace UnityExplorer.CacheObject
1921
{
@@ -34,9 +36,12 @@ public abstract class CacheMember : CacheObjectBase
3436
public virtual void SetInspectorOwner(ReflectionInspector inspector, MemberInfo member)
3537
{
3638
this.Owner = inspector;
37-
this.NameLabelText = this is CacheMethod
38-
? SignatureHighlighter.HighlightMethod(member as MethodInfo)
39-
: SignatureHighlighter.Parse(member.DeclaringType, false, member);
39+
this.NameLabelText = this switch
40+
{
41+
CacheMethod => SignatureHighlighter.HighlightMethod(member as MethodInfo),
42+
CacheConstructor => SignatureHighlighter.HighlightConstructor(member as ConstructorInfo),
43+
_ => SignatureHighlighter.Parse(member.DeclaringType, false, member),
44+
};
4045

4146
this.NameForFiltering = SignatureHighlighter.RemoveHighlighting(NameLabelText);
4247
this.NameLabelTextRaw = NameForFiltering;
@@ -167,56 +172,61 @@ public void OnEvaluateClicked()
167172

168173
#region Cache Member Util
169174

170-
public static List<CacheMember> GetCacheMembers(object inspectorTarget, Type _type, ReflectionInspector _inspector)
175+
public static List<CacheMember> GetCacheMembers(object inspectorTarget, Type type, ReflectionInspector inspector)
171176
{
172-
var list = new List<CacheMember>();
173-
var cachedSigs = new HashSet<string>();
177+
//var list = new List<CacheMember>();
178+
HashSet<string> cachedSigs = new();
179+
List<CacheMember> props = new();
180+
List<CacheMember> fields = new();
181+
List<CacheMember> ctors = new();
182+
List<CacheMember> methods = new();
174183

175-
var types = ReflectionUtility.GetAllBaseTypes(_type);
184+
var types = ReflectionUtility.GetAllBaseTypes(type);
176185

177186
var flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static;
178-
if (!_inspector.StaticOnly)
187+
if (!inspector.StaticOnly)
179188
flags |= BindingFlags.Instance;
180189

181-
var infos = new List<MemberInfo>();
190+
// Get non-static constructors of the main type.
191+
// There's no reason to get the static cctor, it will be invoked when we inspect the class.
192+
// Also no point getting ctors on inherited types.
193+
foreach (var ctor in type.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
194+
TryCacheMember(ctor, ctors, cachedSigs, type, inspector);
182195

183196
foreach (var declaringType in types)
184197
{
185198
var target = inspectorTarget;
186-
if (!_inspector.StaticOnly)
199+
if (!inspector.StaticOnly)
187200
target = target.TryCast(declaringType);
188201

189-
infos.Clear();
190-
infos.AddRange(declaringType.GetProperties(flags));
191-
infos.AddRange(declaringType.GetFields(flags));
192-
infos.AddRange(declaringType.GetMethods(flags));
202+
foreach (var prop in declaringType.GetProperties(flags))
203+
if (prop.DeclaringType == declaringType)
204+
TryCacheMember(prop, props, cachedSigs, declaringType, inspector);
193205

194-
foreach (var member in infos)
195-
{
196-
if (member.DeclaringType != declaringType)
197-
continue;
198-
TryCacheMember(member, list, cachedSigs, declaringType, _inspector);
199-
}
200-
}
206+
foreach (var field in declaringType.GetFields(flags))
207+
if (field.DeclaringType == declaringType)
208+
TryCacheMember(field, fields, cachedSigs, declaringType, inspector);
201209

202-
var typeList = types.ToList();
210+
foreach (var method in declaringType.GetMethods(flags))
211+
if (method.DeclaringType == declaringType)
212+
TryCacheMember(method, methods, cachedSigs, declaringType, inspector);
203213

204-
var sorted = new List<CacheMember>();
205-
sorted.AddRange(list.Where(it => it is CacheProperty)
206-
.OrderBy(it => typeList.IndexOf(it.DeclaringType))
207-
.ThenBy(it => it.NameForFiltering));
208-
sorted.AddRange(list.Where(it => it is CacheField)
209-
.OrderBy(it => typeList.IndexOf(it.DeclaringType))
210-
.ThenBy(it => it.NameForFiltering));
211-
sorted.AddRange(list.Where(it => it is CacheMethod)
212-
.OrderBy(it => typeList.IndexOf(it.DeclaringType))
213-
.ThenBy(it => it.NameForFiltering));
214+
}
214215

216+
var sorted = new List<CacheMember>();
217+
sorted.AddRange(props.OrderBy(it => Array.IndexOf(types, it.DeclaringType))
218+
.ThenBy(it => it.NameForFiltering));
219+
sorted.AddRange(fields.OrderBy(it => Array.IndexOf(types, it.DeclaringType))
220+
.ThenBy(it => it.NameForFiltering));
221+
sorted.AddRange(ctors.OrderBy(it => Array.IndexOf(types, it.DeclaringType))
222+
.ThenBy(it => it.NameForFiltering));
223+
sorted.AddRange(methods.OrderBy(it => Array.IndexOf(types, it.DeclaringType))
224+
.ThenBy(it => it.NameForFiltering));
215225
return sorted;
216226
}
217227

218-
private static void TryCacheMember(MemberInfo member, List<CacheMember> list, HashSet<string> cachedSigs,
219-
Type declaringType, ReflectionInspector _inspector, bool ignorePropertyMethodInfos = true)
228+
private static void TryCacheMember(MemberInfo member, IList list, HashSet<string> cachedSigs,
229+
Type declaringType, ReflectionInspector inspector, bool ignorePropertyMethodInfos = true)
220230
{
221231
try
222232
{
@@ -231,22 +241,29 @@ private static void TryCacheMember(MemberInfo member, List<CacheMember> list, Ha
231241
Type returnType;
232242
switch (member.MemberType)
233243
{
244+
case MemberTypes.Constructor:
245+
{
246+
var ci = member as ConstructorInfo;
247+
sig += GetArgumentString(ci.GetParameters());
248+
if (cachedSigs.Contains(sig))
249+
return;
250+
cached = new CacheConstructor(ci);
251+
returnType = ci.DeclaringType;
252+
}
253+
break;
254+
234255
case MemberTypes.Method:
235256
{
236257
var mi = member as MethodInfo;
237258
if (ignorePropertyMethodInfos
238259
&& (mi.Name.StartsWith("get_") || mi.Name.StartsWith("set_")))
239260
return;
240261

241-
//var args = mi.GetParameters();
242-
//if (!CanParseArgs(args))
243-
// return;
244-
245262
sig += GetArgumentString(mi.GetParameters());
246263
if (cachedSigs.Contains(sig))
247264
return;
248265

249-
cached = new CacheMethod() { MethodInfo = mi };
266+
cached = new CacheMethod(mi);
250267
returnType = mi.ReturnType;
251268
break;
252269
}
@@ -255,32 +272,28 @@ private static void TryCacheMember(MemberInfo member, List<CacheMember> list, Ha
255272
{
256273
var pi = member as PropertyInfo;
257274

258-
//var args = pi.GetIndexParameters();
259-
//if (!CanParseArgs(args))
260-
// return;
261-
262275
if (!pi.CanRead && pi.CanWrite)
263276
{
264277
// write-only property, cache the set method instead.
265278
var setMethod = pi.GetSetMethod(true);
266279
if (setMethod != null)
267-
TryCacheMember(setMethod, list, cachedSigs, declaringType, _inspector, false);
280+
TryCacheMember(setMethod, list, cachedSigs, declaringType, inspector, false);
268281
return;
269282
}
270283

271284
sig += GetArgumentString(pi.GetIndexParameters());
272285
if (cachedSigs.Contains(sig))
273286
return;
274287

275-
cached = new CacheProperty() { PropertyInfo = pi };
288+
cached = new CacheProperty(pi);
276289
returnType = pi.PropertyType;
277290
break;
278291
}
279292

280293
case MemberTypes.Field:
281294
{
282295
var fi = member as FieldInfo;
283-
cached = new CacheField() { FieldInfo = fi };
296+
cached = new CacheField(fi);
284297
returnType = fi.FieldType;
285298
break;
286299
}
@@ -291,7 +304,7 @@ private static void TryCacheMember(MemberInfo member, List<CacheMember> list, Ha
291304
cachedSigs.Add(sig);
292305

293306
cached.SetFallbackType(returnType);
294-
cached.SetInspectorOwner(_inspector, member);
307+
cached.SetInspectorOwner(inspector, member);
295308

296309
list.Add(cached);
297310
}

src/CacheObject/CacheMethod.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,18 @@ namespace UnityExplorer.CacheObject
1111
{
1212
public class CacheMethod : CacheMember
1313
{
14-
public MethodInfo MethodInfo { get; internal set; }
14+
public MethodInfo MethodInfo { get; }
1515
public override Type DeclaringType => MethodInfo.DeclaringType;
1616
public override bool CanWrite => false;
1717
public override bool IsStatic => MethodInfo.IsStatic;
1818

1919
public override bool ShouldAutoEvaluate => false;
2020

21+
public CacheMethod (MethodInfo mi)
22+
{
23+
this.MethodInfo = mi;
24+
}
25+
2126
public override void SetInspectorOwner(ReflectionInspector inspector, MemberInfo member)
2227
{
2328
base.SetInspectorOwner(inspector, member);

src/CacheObject/CacheProperty.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ public class CacheProperty : CacheMember
1919

2020
public override bool ShouldAutoEvaluate => !HasArguments;
2121

22+
public CacheProperty(PropertyInfo pi)
23+
{
24+
this.PropertyInfo = pi;
25+
}
26+
2227
public override void SetInspectorOwner(ReflectionInspector inspector, MemberInfo member)
2328
{
2429
base.SetInspectorOwner(inspector, member);

src/Inspectors/ReflectionInspector.cs

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,9 @@ private enum MemberFlags
4646
None = 0,
4747
Property = 1,
4848
Field = 2,
49-
Method = 4,
50-
All = 7
49+
Constructor = 4,
50+
Method = 8,
51+
All = Property | Field | Method | Constructor,
5152
}
5253

5354
// UI
@@ -153,7 +154,7 @@ private void SetTarget(object target)
153154

154155
this.filterInputField.Text = "";
155156

156-
SetFilter("", StaticOnly ? BindingFlags.Static : BindingFlags.Instance);
157+
SetFilter("", StaticOnly ? BindingFlags.Static : BindingFlags.Default);
157158
scopeFilterButtons[BindingFlags.Default].Component.gameObject.SetActive(!StaticOnly);
158159
scopeFilterButtons[BindingFlags.Instance].Component.gameObject.SetActive(!StaticOnly);
159160

@@ -462,6 +463,7 @@ private void ConstructSecondRow(GameObject parent)
462463
AddMemberTypeToggle(rowObj, MemberTypes.Property, 90);
463464
AddMemberTypeToggle(rowObj, MemberTypes.Field, 70);
464465
AddMemberTypeToggle(rowObj, MemberTypes.Method, 90);
466+
AddMemberTypeToggle(rowObj, MemberTypes.Constructor, 110);
465467
}
466468

467469
private void AddScopeFilterButton(GameObject parent, BindingFlags flags, bool setAsActive = false)
@@ -480,25 +482,26 @@ private void AddMemberTypeToggle(GameObject parent, MemberTypes type, int width)
480482
{
481483
var toggleObj = UIFactory.CreateToggle(parent, "Toggle_" + type, out Toggle toggle, out Text toggleText);
482484
UIFactory.SetLayoutElement(toggleObj, minHeight: 25, minWidth: width);
483-
var color = type switch
485+
string color = type switch
484486
{
485487
MemberTypes.Method => SignatureHighlighter.METHOD_INSTANCE,
486488
MemberTypes.Field => SignatureHighlighter.FIELD_INSTANCE,
487489
MemberTypes.Property => SignatureHighlighter.PROP_INSTANCE,
490+
MemberTypes.Constructor => SignatureHighlighter.CLASS_INSTANCE,
488491
_ => throw new NotImplementedException()
489492
};
490493
toggleText.text = $"<color={color}>{type}</color>";
491494

492495
toggle.graphic.TryCast<Image>().color = color.ToColor() * 0.65f;
493496

494-
MemberFlags flag;
495-
switch (type)
497+
MemberFlags flag = type switch
496498
{
497-
case MemberTypes.Method: flag = MemberFlags.Method; break;
498-
case MemberTypes.Property: flag = MemberFlags.Property; break;
499-
case MemberTypes.Field: flag = MemberFlags.Field; break;
500-
default: return;
501-
}
499+
MemberTypes.Method => MemberFlags.Method,
500+
MemberTypes.Property => MemberFlags.Property,
501+
MemberTypes.Field => MemberFlags.Field,
502+
MemberTypes.Constructor => MemberFlags.Constructor,
503+
_ => throw new NotImplementedException()
504+
};
502505

503506
toggle.onValueChanged.AddListener((bool val) => { OnMemberTypeToggled(flag, val); });
504507

src/UI/Widgets/EvaluateWidget/EvaluateWidget.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
using UniverseLib;
1313
using UnityExplorer.CacheObject;
1414
using UniverseLib.UI.ObjectPool;
15+
using UniverseLib.Utility;
1516

1617
namespace UnityExplorer.UI.Widgets
1718
{
@@ -76,6 +77,9 @@ public Type[] TryParseGenericArguments()
7677

7778
public object[] TryParseArguments()
7879
{
80+
if (!parameters.Any())
81+
return ArgumentUtility.EmptyArgs;
82+
7983
object[] outArgs = new object[parameters.Length];
8084

8185
for (int i = 0; i < parameters.Length; i++)

0 commit comments

Comments
 (0)