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

Commit b154cbf

Browse files
committed
Add support for generic methods, improved non-generic dictionary output
1 parent db91968 commit b154cbf

File tree

6 files changed

+150
-26
lines changed

6 files changed

+150
-26
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,9 @@ CppExplorer has two main inspector modes: <b>GameObject Inspector</b>, and <b>Re
9999
* Filter by name, type, etc.
100100
* For GameObjects and Transforms you can filter which scene they are found in too.
101101

102-
### C# REPL console
102+
### C# console
103103

104-
* A simple C# REPL console, allows you to execute a method body on the fly.
104+
* A simple C# console, allows you to execute a method body on the fly.
105105

106106
### Inspect-under-mouse
107107

src/CachedObjects/CacheObjectBase.cs

Lines changed: 68 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ public abstract class CacheObjectBase
2020
public Type DeclaringType { get; set; }
2121
public object DeclaringInstance { get; set; }
2222

23-
public bool HasParameters => m_arguments != null && m_arguments.Length > 0;
23+
public virtual bool HasParameters => m_arguments != null && m_arguments.Length > 0;
24+
2425
public bool m_evaluated = false;
2526
public bool m_isEvaluating;
2627
public ParameterInfo[] m_arguments = new ParameterInfo[0];
@@ -394,26 +395,54 @@ public void Draw(Rect window, float labelWidth = 215f)
394395

395396
if (m_isEvaluating)
396397
{
397-
for (int i = 0; i < m_arguments.Length; i++)
398+
if (cm != null && cm.GenericArgs.Length > 0)
398399
{
399-
var name = m_arguments[i].Name;
400-
var input = m_argumentInput[i];
401-
var type = m_arguments[i].ParameterType.Name;
400+
GUILayout.Label($"<b><color=orange>Generic Arguments:</color></b>", null);
402401

403-
var label = $"<color={UIStyles.Syntax.Class_Instance}>{type}</color> ";
404-
label += $"<color={UIStyles.Syntax.Local}>{name}</color>";
405-
if (m_arguments[i].HasDefaultValue)
402+
for (int i = 0; i < cm.GenericArgs.Length; i++)
406403
{
407-
label = $"<i>[{label} = {m_arguments[i].DefaultValue ?? "null"}]</i>";
408-
}
404+
var type = cm.GenericConstraints[i]?.FullName ?? "None";
405+
var input = cm.GenericArgInput[i];
406+
var label = $"<color={UIStyles.Syntax.Class_Instance}>{type}</color>";
409407

410-
GUILayout.BeginHorizontal(null);
408+
GUILayout.BeginHorizontal(null);
411409

412-
GUILayout.Label(i.ToString(), new GUILayoutOption[] { GUILayout.Width(20) });
413-
m_argumentInput[i] = GUILayout.TextField(input, new GUILayoutOption[] { GUILayout.Width(150) });
414-
GUILayout.Label(label, null);
410+
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
411+
GUILayout.Label($"<color={UIStyles.Syntax.StructGreen}>{cm.GenericArgs[i].Name}</color>", new GUILayoutOption[] { GUILayout.Width(15) });
412+
cm.GenericArgInput[i] = GUILayout.TextField(input, new GUILayoutOption[] { GUILayout.Width(150) });
413+
GUI.skin.label.alignment = TextAnchor.MiddleLeft;
414+
GUILayout.Label(label, null);
415415

416-
GUILayout.EndHorizontal();
416+
GUILayout.EndHorizontal();
417+
}
418+
}
419+
420+
if (m_arguments.Length > 0)
421+
{
422+
GUILayout.Label($"<b><color=orange>Arguments:</color></b>", null);
423+
for (int i = 0; i < m_arguments.Length; i++)
424+
{
425+
var name = m_arguments[i].Name;
426+
var input = m_argumentInput[i];
427+
var type = m_arguments[i].ParameterType.Name;
428+
429+
var label = $"<color={UIStyles.Syntax.Class_Instance}>{type}</color> ";
430+
label += $"<color={UIStyles.Syntax.Local}>{name}</color>";
431+
if (m_arguments[i].HasDefaultValue)
432+
{
433+
label = $"<i>[{label} = {m_arguments[i].DefaultValue ?? "null"}]</i>";
434+
}
435+
436+
GUILayout.BeginHorizontal(null);
437+
438+
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
439+
GUILayout.Label(i.ToString(), new GUILayoutOption[] { GUILayout.Width(15) });
440+
m_argumentInput[i] = GUILayout.TextField(input, new GUILayoutOption[] { GUILayout.Width(150) });
441+
GUI.skin.label.alignment = TextAnchor.MiddleLeft;
442+
GUILayout.Label(label, null);
443+
444+
GUILayout.EndHorizontal();
445+
}
417446
}
418447

419448
GUILayout.BeginHorizontal(null);
@@ -436,7 +465,12 @@ public void Draw(Rect window, float labelWidth = 215f)
436465
}
437466
else
438467
{
439-
if (GUILayout.Button($"Evaluate ({m_arguments.Length} params)", new GUILayoutOption[] { GUILayout.Width(150) }))
468+
var lbl = $"Evaluate (";
469+
int args = m_arguments.Length;
470+
if (cm != null) args += cm.GenericArgs.Length;
471+
lbl += args + " params)";
472+
473+
if (GUILayout.Button(lbl, new GUILayoutOption[] { GUILayout.Width(150) }))
440474
{
441475
m_isEvaluating = true;
442476
}
@@ -527,6 +561,24 @@ private string GetRichTextName()
527561
m_richTextName += $"<color={memberColor}>{MemInfo.Name}</color>";
528562
if (isStatic) m_richTextName += "</i>";
529563

564+
// generic method args
565+
if (this is CacheMethod cm && cm.GenericArgs.Length > 0)
566+
{
567+
m_richTextName += "<";
568+
569+
var args = "";
570+
for (int i = 0; i < cm.GenericArgs.Length; i++)
571+
{
572+
if (args != "") args += ", ";
573+
args += $"<color={UIStyles.Syntax.StructGreen}>{cm.GenericArgs[i].Name}</color>";
574+
}
575+
m_richTextName += args;
576+
577+
m_richTextName += ">";
578+
}
579+
580+
// Method / Property arguments
581+
530582
//if (m_arguments.Length > 0 || this is CacheMethod)
531583
//{
532584
// m_richTextName += "(";

src/CachedObjects/Object/CacheDictionary.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,14 +133,16 @@ public override void UpdateValue()
133133
var keys = new List<CacheObjectBase>();
134134
foreach (var key in IDict.Keys)
135135
{
136-
var cache = GetCacheObject(key, TypeOfKeys);
136+
Type t = ReflectionHelpers.GetActualType(key) ?? TypeOfKeys;
137+
var cache = GetCacheObject(key, t);
137138
keys.Add(cache);
138139
}
139140

140141
var values = new List<CacheObjectBase>();
141142
foreach (var val in IDict.Values)
142143
{
143-
var cache = GetCacheObject(val, TypeOfValues);
144+
Type t = ReflectionHelpers.GetActualType(val) ?? TypeOfValues;
145+
var cache = GetCacheObject(val, t);
144146
values.Add(cache);
145147
}
146148

src/CachedObjects/Other/CacheMethod.cs

Lines changed: 61 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,34 @@ public class CacheMethod : CacheObjectBase
1313
{
1414
private CacheObjectBase m_cachedReturnValue;
1515

16+
public override bool HasParameters => base.HasParameters || GenericArgs.Length > 0;
17+
18+
public Type[] GenericArgs { get; private set; }
19+
public Type[] GenericConstraints { get; private set; }
20+
21+
public string[] GenericArgInput = new string[0];
22+
1623
public static bool CanEvaluate(MethodInfo mi)
1724
{
18-
// TODO generic args
19-
if (mi.GetGenericArguments().Length > 0)
20-
{
21-
return false;
22-
}
23-
2425
// primitive and string args supported
2526
return CanProcessArgs(mi.GetParameters());
2627
}
2728

29+
public override void Init()
30+
{
31+
var mi = (MemInfo as MethodInfo);
32+
GenericArgs = mi.GetGenericArguments();
33+
34+
GenericConstraints = GenericArgs.Select(x => x.GetGenericParameterConstraints()
35+
.FirstOrDefault())
36+
.ToArray();
37+
38+
GenericArgInput = new string[GenericArgs.Length];
39+
40+
ValueType = mi.ReturnType;
41+
ValueTypeName = ValueType.FullName;
42+
}
43+
2844
public override void UpdateValue()
2945
{
3046
//base.UpdateValue();
@@ -37,6 +53,45 @@ public void Evaluate()
3753
var mi = MemInfo as MethodInfo;
3854
object ret = null;
3955

56+
// Parse generic arguments
57+
if (GenericArgs.Length > 0)
58+
{
59+
var list = new List<Type>();
60+
for (int i = 0; i < GenericArgs.Length; i++)
61+
{
62+
var input = GenericArgInput[i];
63+
if (ReflectionHelpers.GetTypeByName(input) is Type t)
64+
{
65+
if (GenericConstraints[i] == null)
66+
{
67+
list.Add(t);
68+
}
69+
else
70+
{
71+
if (GenericConstraints[i].IsAssignableFrom(t))
72+
{
73+
list.Add(t);
74+
}
75+
else
76+
{
77+
MelonLogger.Log($"Generic argument #{i} '{input}', is not assignable from the generic constraint!");
78+
return;
79+
}
80+
}
81+
}
82+
else
83+
{
84+
MelonLogger.Log($"Generic argument #{i}, could not get any type by the name of '{input}'!" +
85+
$" Make sure you use the full name, including the NameSpace.");
86+
return;
87+
}
88+
}
89+
90+
// make into a generic with type list
91+
mi = mi.MakeGenericMethod(list.ToArray());
92+
}
93+
94+
// Parse arguments
4095
if (!HasParameters)
4196
{
4297
ret = mi.Invoke(mi.IsStatic ? null : DeclaringInstance, new object[0]);

src/Menu/UIStyles.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ public class Syntax
2626
public const string Class_Instance = "#2df7b2";
2727

2828
public const string Local = "#a6e9e9";
29+
30+
public const string StructGreen = "#b8d7a3";
2931
}
3032

3133
public static Color LightGreen = new Color(Color.green.r - 0.3f, Color.green.g - 0.3f, Color.green.b - 0.3f);

src/Tests/TestClass.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Text;
66
using System.Threading.Tasks;
77
using MelonLoader;
8+
using Mono.CSharp.Linq;
89
using UnityEngine;
910

1011
namespace Explorer.Tests
@@ -26,6 +27,18 @@ public TestClass()
2627
public static int StaticField = 5;
2728
public int NonStaticField;
2829

30+
// test a generic method
31+
public static string TestGeneric<C, T>(string arg0) where C : Component
32+
{
33+
return "C: " + typeof(C).FullName + ", T: " + typeof(T).FullName + ", arg0: " + arg0;
34+
}
35+
36+
//// this type of generic is not supported, due to requiring a non-primitive argument.
37+
//public static T TestDifferentGeneric<T>(T obj) where T : Component
38+
//{
39+
// return obj;
40+
//}
41+
2942
// test a non-generic dictionary
3043

3144
public Hashtable TestNonGenericDict()

0 commit comments

Comments
 (0)