Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions RuntimeUnityEditor.Core/RuntimeUnityEditor.Core.projitems
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@
<Compile Include="$(MSBuildThisFileDirectory)Windows\Inspector\Inspector.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Windows\Inspector\Inspector.InspectorTab.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Windows\Inspector\InspectorHelpObject.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Windows\Inspector\MemberCollector.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Windows\Inspector\IL2CPP\MemberCollector.IL2CPP.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Windows\Inspector\ToStringConverter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Windows\Inspector\VariableFieldDrawer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Windows\ObjectTree\ObjectTreeViewer.cs" />
Expand All @@ -106,4 +108,7 @@
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Utils\UI\guisharp-box.png" />
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Utils\UI\guisharp-window.png" />
</ItemGroup>
<ItemGroup>
<Folder Include="$(MSBuildThisFileDirectory)Windows\Inspector\IL2CPP\" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ public string TypeName()
}

private bool? _canEnter;
private readonly GUIContent _nameContent;
private protected readonly GUIContent _nameContent;

/// <inheritdoc />
public virtual bool CanEnterValue()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public MethodCacheEntry(object instance, MethodInfo methodInfo, Type owner)

ParameterString = GetParameterPreviewString(methodInfo);

_content = new GUIContent(_name,null, methodInfo.GetFancyDescription());
_nameContent = new GUIContent(_name,null, methodInfo.GetFancyDescription());
}

internal static string GetParameterPreviewString(MethodBase methodInfo)
Expand Down Expand Up @@ -65,7 +65,7 @@ internal static string GetParameterPreviewString(MethodBase methodInfo)

private readonly string _name;
private readonly string _returnTypeName;
private readonly GUIContent _content;
private protected readonly GUIContent _nameContent;

/// <summary>
/// Name of the method.
Expand All @@ -78,7 +78,7 @@ internal static string GetParameterPreviewString(MethodBase methodInfo)
public string TypeName() => _returnTypeName;

/// <inheritdoc />
public GUIContent GetNameContent() => _content;
public GUIContent GetNameContent() => _nameContent;

/// <summary>
/// Not supported for methods.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
#if IL2CPP
using HarmonyLib;
using RuntimeUnityEditor.Core.Inspector.Entries;
using RuntimeUnityEditor.Core.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace RuntimeUnityEditor.Core.Inspector.IL2CPP;

public class IL2CPPCacheEntryHelper
{
private static readonly Dictionary<Type, Dictionary<MemberInfo, FieldInfo>> _ptrLookup = new();

public static Dictionary<MemberInfo, FieldInfo> GetPtrLookupTable(Type type)
{
// todo some way to clean up old entries?
if (_ptrLookup.TryGetValue(type, out var value))
return value;

value = new Dictionary<MemberInfo, FieldInfo>();

var staticFields = type.GetFields(BindingFlags.Static | BindingFlags.NonPublic).Where(x => x.IsInitOnly).ToList();

var fieldPtrs = staticFields.Where(x => x.Name.StartsWith("NativeFieldInfoPtr_"))
.Select(x => new { trimmed = x.Name.Substring("NativeFieldInfoPtr_".Length), ptrF = x });

var usedFields = new HashSet<MethodInfo>();

foreach (var fieldPtr in fieldPtrs)
{
var targetFieldName = fieldPtr.trimmed;
// Fields are props in il2cpp interop
var targetField = type.GetProperty(targetFieldName, AccessTools.all);
if (targetField != null)
{
value[targetField] = fieldPtr.ptrF;

var getMethod = targetField.GetGetMethod();
if (getMethod != null) usedFields.Add(getMethod);
var setMethod = targetField.GetSetMethod();
if (setMethod != null) usedFields.Add(setMethod);
}
}

foreach (var propertyInfo in type.GetAllProperties(true))
{
// It's a field
if (value.ContainsKey(propertyInfo))
continue;

var getMethod = propertyInfo.GetGetMethod(true);
if (getMethod != null && getMethod.GetMethodBody() != null)
{
var ptr = Il2CppInterop.Common.Il2CppInteropUtils.GetIl2CppMethodInfoPointerFieldForGeneratedMethod(getMethod);
if (ptr != null)
value[getMethod] = ptr;
}

var setMethod = propertyInfo.GetSetMethod();
if (setMethod != null && setMethod.GetMethodBody() != null)
{
var ptr = Il2CppInterop.Common.Il2CppInteropUtils.GetIl2CppMethodInfoPointerFieldForGeneratedMethod(setMethod);
if (ptr != null)
value[setMethod] = ptr;
}
}

foreach (var methodInfo in type.GetAllMethods(true))
{
if (value.ContainsKey(methodInfo) || usedFields.Contains(methodInfo))
continue;

if (methodInfo.GetMethodBody() != null)
{
var ptr = Il2CppInterop.Common.Il2CppInteropUtils.GetIl2CppMethodInfoPointerFieldForGeneratedMethod(methodInfo);
if (ptr != null)
value[methodInfo] = ptr;
}
}

_ptrLookup[type] = value;
return value;
}

public static bool TryGetIl2CppCacheEntry(object instance, Type type, EventInfo p, Dictionary<MemberInfo, FieldInfo> lookup, out ICacheEntry result)
{
FieldInfo ptrAdd = null;
FieldInfo ptrRaise = null;
FieldInfo ptrRemove = null;
var addMethod = p.GetAddMethod(true);
if (addMethod != null) lookup.TryGetValue(addMethod, out ptrAdd);
var raiseMethod = p.GetRaiseMethod(true);
if (raiseMethod != null) lookup.TryGetValue(raiseMethod, out ptrRaise);
var removeMethod = p.GetRemoveMethod(true);
if (removeMethod != null) lookup.TryGetValue(removeMethod, out ptrRemove);
if (ptrAdd != null || ptrRaise != null || ptrRemove != null)
{
result = new IL2CPPEventCacheEntry(instance, p, type, ptrAdd, ptrRaise, ptrRemove);
return true;
}

result = null;
return false;
}

public static bool TryGetIl2CppCacheEntry(object instance, Type type, PropertyInfo p, Dictionary<MemberInfo, FieldInfo> lookup, out ICacheEntry result)
{
if (lookup.TryGetValue(p, out var ptr))
{
result = new IL2CPPFieldCacheEntry(instance, p, type, ptr);
return true;
}

FieldInfo ptrGet = null;
FieldInfo ptrSet = null;
var getMethod = p.GetGetMethod(true);
if (getMethod != null) lookup.TryGetValue(getMethod, out ptrGet);
var setMethod = p.GetSetMethod(true);
if (setMethod != null) lookup.TryGetValue(setMethod, out ptrSet);
if (ptrGet != null || ptrSet != null)
{
result = new IL2CPPPropertyCacheEntry(instance, p, type, ptrGet, ptrSet);
return true;
}

result = null;
return false;
}

public static object SafeGetPtr(Type owner, FieldInfo ptrField)
{
if (ptrField == null) return "null";
if (owner.ContainsGenericParameters)
return "???";
try
{
return ptrField.GetValue(null);
}
catch
{
return "error";
}
}

internal static bool IsIl2CppCacheEntry(ICacheEntry entry)
{
return entry is IL2CPPFieldCacheEntry || entry is IL2CPPPropertyCacheEntry || entry is IL2CPPMethodCacheEntry || entry is IL2CPPEventCacheEntry;
}
}

/// <inheritdoc />
public class IL2CPPFieldCacheEntry : PropertyCacheEntry
{
public FieldInfo PtrField { get; }

/// <inheritdoc />
public IL2CPPFieldCacheEntry(object ins, PropertyInfo p, Type owner, FieldInfo ptrField) : base(ins, p, owner)
{
PtrField = ptrField;
_nameContent.tooltip = $"IL2CPP Field (ptr={IL2CPPCacheEntryHelper.SafeGetPtr(owner, ptrField)})\n\n{_nameContent.tooltip}";
}

/// <inheritdoc />
public IL2CPPFieldCacheEntry(object ins, PropertyInfo p, Type owner, FieldInfo ptrField, ICacheEntry parent) : base(ins, p, owner, parent)
{
PtrField = ptrField;
_nameContent.tooltip = $"IL2CPP Field (ptr={IL2CPPCacheEntryHelper.SafeGetPtr(owner, ptrField)})\n\n{_nameContent.tooltip}";
}
}

/// <inheritdoc />
public class IL2CPPPropertyCacheEntry : PropertyCacheEntry
{
public FieldInfo PtrFieldGet { get; }
public FieldInfo PtrFieldSet { get; }

/// <inheritdoc />
public IL2CPPPropertyCacheEntry(object ins, PropertyInfo p, Type owner, FieldInfo ptrFieldGet, FieldInfo ptrFieldSet) : base(ins, p, owner)
{
PtrFieldGet = ptrFieldGet;
PtrFieldSet = ptrFieldSet;
_nameContent.tooltip = $"IL2CPP Property (getPtr={IL2CPPCacheEntryHelper.SafeGetPtr(owner, ptrFieldGet)}, setPtr={IL2CPPCacheEntryHelper.SafeGetPtr(owner, ptrFieldSet)})\n\n{_nameContent.tooltip}";
}
/// <inheritdoc />
public IL2CPPPropertyCacheEntry(object ins, PropertyInfo p, Type owner, FieldInfo ptrFieldGet, FieldInfo ptrFieldSet, ICacheEntry parent) : base(ins, p, owner, parent)
{
PtrFieldGet = ptrFieldGet;
PtrFieldSet = ptrFieldSet;
_nameContent.tooltip = $"IL2CPP Property (getPtr={IL2CPPCacheEntryHelper.SafeGetPtr(owner, ptrFieldGet)}, setPtr={IL2CPPCacheEntryHelper.SafeGetPtr(owner, ptrFieldSet)})\n\n{_nameContent.tooltip}";
}
}

/// <inheritdoc />
public class IL2CPPMethodCacheEntry : MethodCacheEntry
{
public FieldInfo PtrField { get; }

/// <inheritdoc />
public IL2CPPMethodCacheEntry(object instance, MethodInfo methodInfo, Type owner, FieldInfo ptrField) : base(instance, methodInfo, owner)
{
PtrField = ptrField;
_nameContent.tooltip = $"IL2CPP Method (ptr={IL2CPPCacheEntryHelper.SafeGetPtr(owner, ptrField)})\n\n{_nameContent.tooltip}";
}
}

/// <inheritdoc />
/// TODO: This does nothing so far because events are not implemented in il2cpp interop (they show up as separate add/remove/raise methods). Maybe combine them back into events?
public class IL2CPPEventCacheEntry : EventCacheEntry
{
public FieldInfo PtrFieldAdd { get; }
public FieldInfo PtrFieldRaise { get; }
public FieldInfo PtrFieldRemove { get; }
/// <inheritdoc />
public IL2CPPEventCacheEntry(object ins, EventInfo e, Type owner, FieldInfo ptrFieldAdd, FieldInfo ptrFieldRaise, FieldInfo ptrFieldRemove) : base(ins, e, owner)
{
PtrFieldAdd = ptrFieldAdd;
PtrFieldRaise = ptrFieldRaise;
PtrFieldRemove = ptrFieldRemove;
_nameContent.tooltip = $"IL2CPP Event (addPtr={IL2CPPCacheEntryHelper.SafeGetPtr(owner, ptrFieldAdd)}, raisePtr={IL2CPPCacheEntryHelper.SafeGetPtr(owner, ptrFieldRaise)}, removePtr={IL2CPPCacheEntryHelper.SafeGetPtr(owner, ptrFieldRemove)})\n\n{_nameContent.tooltip}";
}
}

#endif
Loading