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

Commit b062924

Browse files
committed
Add support for writing to IList<T>'s which don't implement IList
1 parent 5aef8dd commit b062924

File tree

3 files changed

+93
-4
lines changed

3 files changed

+93
-4
lines changed

src/Core/Tests/TestClass.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,40 @@
1414

1515
namespace UnityExplorer.Tests
1616
{
17+
public class TestIndexer : IList<int>
18+
{
19+
private readonly List<int> list = new List<int>() { 1,2,3,4,5 };
20+
21+
public int Count => list.Count;
22+
public bool IsReadOnly => false;
23+
24+
int IList<int>.this[int index]
25+
{
26+
get => list[index];
27+
set => list[index] = value;
28+
}
29+
30+
public int IndexOf(int item) => list.IndexOf(item);
31+
public bool Contains(int item) => list.Contains(item);
32+
33+
public void Add(int item) => list.Add(item);
34+
public void Insert(int index, int item) => list.Insert(index, item);
35+
36+
public bool Remove(int item) => list.Remove(item);
37+
public void RemoveAt(int index) => list.RemoveAt(index);
38+
39+
public void Clear() => list.Clear();
40+
41+
public void CopyTo(int[] array, int arrayIndex) => list.CopyTo(array, arrayIndex);
42+
43+
public IEnumerator<int> GetEnumerator() => list.GetEnumerator();
44+
IEnumerator IEnumerable.GetEnumerator() => list.GetEnumerator();
45+
}
46+
1747
public static class TestClass
1848
{
49+
public static readonly TestIndexer AAAAATest = new TestIndexer();
50+
1951
public static void ATestMethod(string s, float f, Vector3 vector, DateTime date, Quaternion quater, bool b, CameraClearFlags enumvalue)
2052
{
2153
ExplorerCore.Log($"{s}, {f}, {vector.ToString()}, {date}, {quater.eulerAngles.ToString()}, {b}, {enumvalue}");

src/ExplorerCore.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ namespace UnityExplorer
1919
public static class ExplorerCore
2020
{
2121
public const string NAME = "UnityExplorer";
22-
public const string VERSION = "4.0.0";
22+
public const string VERSION = "4.0.1";
2323
public const string AUTHOR = "Sinai";
2424
public const string GUID = "com.sinai.unityexplorer";
2525

src/UI/IValues/InteractiveList.cs

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections;
33
using System.Collections.Generic;
44
using System.Linq;
5+
using System.Reflection;
56
using UnityEngine;
67
using UnityEngine.UI;
78
using UnityExplorer.UI.CacheObject;
@@ -19,11 +20,14 @@ public class InteractiveList : InteractiveValue, ICellPoolDataSource<CacheListEn
1920
object ICacheObjectController.Target => this.CurrentOwner.Value;
2021
public Type TargetType { get; private set; }
2122

22-
public override bool CanWrite => base.CanWrite && RefIList != null && !RefIList.IsReadOnly;
23+
public override bool CanWrite => base.CanWrite && ((RefIList != null && !RefIList.IsReadOnly) || IsWritableGenericIList);
2324

2425
public Type EntryType;
2526
public IList RefIList;
2627

28+
private bool IsWritableGenericIList;
29+
private PropertyInfo genericIndexer;
30+
2731
public int ItemCount => values.Count;
2832
private readonly List<object> values = new List<object>();
2933
private readonly List<CacheListEntry> cachedEntries = new List<CacheListEntry>();
@@ -92,6 +96,12 @@ private void CacheEntries(object value)
9296
{
9397
RefIList = value as IList;
9498

99+
// Check if the type implements IList<T> but not IList (ie. Il2CppArrayBase)
100+
if (RefIList == null)
101+
CheckGenericIList(value);
102+
else
103+
IsWritableGenericIList = false;
104+
95105
values.Clear();
96106
int idx = 0;
97107

@@ -141,14 +151,61 @@ private void CacheEntries(object value)
141151
}
142152
}
143153

154+
private void CheckGenericIList(object value)
155+
{
156+
try
157+
{
158+
var type = value.GetType();
159+
if (type.GetInterfaces().Any(it => it.IsGenericType && it.GetGenericTypeDefinition() == typeof(IList<>)))
160+
IsWritableGenericIList = !(bool)type.GetProperty("IsReadOnly").GetValue(value, null);
161+
else
162+
IsWritableGenericIList = false;
163+
164+
if (IsWritableGenericIList)
165+
{
166+
// Find the "this[int index]" property.
167+
// It might be a private implementation.
168+
foreach (var prop in type.GetProperties(ReflectionUtility.FLAGS))
169+
{
170+
if ((prop.Name == "Item"
171+
|| (prop.Name.StartsWith("System.Collections.Generic.IList<") && prop.Name.EndsWith(">.Item")))
172+
&& prop.GetIndexParameters() is ParameterInfo[] parameters
173+
&& parameters.Length == 1
174+
&& parameters[0].ParameterType == typeof(int))
175+
{
176+
genericIndexer = prop;
177+
break;
178+
}
179+
}
180+
181+
if (genericIndexer == null)
182+
{
183+
ExplorerCore.LogWarning($"Failed to find indexer property for IList<T> type '{type.FullName}'!");
184+
IsWritableGenericIList = false;
185+
}
186+
}
187+
}
188+
catch (Exception ex)
189+
{
190+
ExplorerCore.LogWarning($"Exception processing IEnumerable for IList<T> check: {ex.ReflectionExToString()}");
191+
IsWritableGenericIList = false;
192+
}
193+
}
194+
144195
// Setting the value of an index to the list
145196

146197
public void TrySetValueToIndex(object value, int index)
147198
{
148199
try
149200
{
150-
//value = value.TryCast(this.EntryType);
151-
RefIList[index] = value;
201+
if (!IsWritableGenericIList)
202+
{
203+
RefIList[index] = value;
204+
}
205+
else
206+
{
207+
genericIndexer.SetValue(CurrentOwner.Value, value, new object[] { index });
208+
}
152209

153210
var entry = cachedEntries[index];
154211
entry.SetValueFromSource(value);

0 commit comments

Comments
 (0)