A few important fixes

* Reflection on Il2CppSystem-namespace instances has been fixed
* Type/Value Syntax highlighting generalized and improved globally
* Scene changes now refresh the scene-picker dropdown
* probably other minor stuff too
This commit is contained in:
sinaioutlander
2020-11-13 18:46:36 +11:00
parent dc449d4a1e
commit bc113e9093
21 changed files with 450 additions and 395 deletions

View File

@ -152,7 +152,7 @@ namespace UnityExplorer.Inspectors.Reflection
private string GetRichTextName()
{
return m_richTextName = UISyntaxHighlight.GetHighlight(MemInfo.DeclaringType, false, MemInfo);
return m_richTextName = UISyntaxHighlight.ParseFullSyntax(MemInfo.DeclaringType, false, MemInfo);
}
#if CPP
@ -179,12 +179,7 @@ namespace UnityExplorer.Inspectors.Reflection
if (!typeof(Il2CppSystem.Object).IsAssignableFrom(type))
return true;
var ptr = (IntPtr)typeof(Il2CppClassPointerStore<>)
.MakeGenericType(type)
.GetField("NativeClassPtr")
.GetValue(null);
if (ptr == IntPtr.Zero)
if (!ReflectionHelpers.Il2CppTypeNotNull(type, out IntPtr ptr))
return false;
return Il2CppSystem.Type.internal_from_handle(IL2CPP.il2cpp_class_get_type(ptr)) is Il2CppSystem.Type;
@ -207,7 +202,7 @@ namespace UnityExplorer.Inspectors.Reflection
var textGen = m_memLabelText.cachedTextGeneratorForLayout;
float preferredWidth = textGen.GetPreferredWidth(RichTextName, textGenSettings);
float max = scrollRect.rect.width * 0.5f;
float max = scrollRect.rect.width * 0.4f;
if (preferredWidth > max) preferredWidth = max;
@ -245,6 +240,10 @@ namespace UnityExplorer.Inspectors.Reflection
topGroup.childControlHeight = true;
topGroup.childControlWidth = true;
topGroup.spacing = 10;
topGroup.padding.left = 3;
topGroup.padding.right = 3;
topGroup.padding.top = 0;
topGroup.padding.bottom = 0;
// left group
@ -295,6 +294,8 @@ namespace UnityExplorer.Inspectors.Reflection
rightGroup.childControlHeight = true;
rightGroup.childControlWidth = true;
rightGroup.spacing = 4;
rightGroup.padding.top = 2;
rightGroup.padding.bottom = 2;
// evaluate button
if (this is CacheMethod || HasParameters)

View File

@ -22,7 +22,7 @@ namespace UnityExplorer.Inspectors.Reflection
// might not need
public virtual bool HasSubContent => false;
public string RichTextValue => m_richValue ?? GetRichTextValue();
public string RichTextValue => m_richValue ?? GetLabelForValue();
internal string m_richValue;
internal string m_richValueType;
@ -45,39 +45,20 @@ namespace UnityExplorer.Inspectors.Reflection
return;
}
GetRichTextValue();
GetLabelForValue();
m_text.text = RichTextValue;
//if (Value == null)
// m_text.text = $"<color=red>null</color> {m_richValueType}";
//else
// m_text.text = RichTextValue;
bool shouldShowInspect = !InspectorBase.IsNullOrDestroyed(this.Value, true);
if (m_inspectButton.activeSelf != shouldShowInspect)
m_inspectButton.SetActive(shouldShowInspect);
}
private MethodInfo GetToStringMethod()
{
try
{
m_toStringMethod = ReflectionHelpers.GetActualType(Value).GetMethod("ToString", new Type[0])
?? typeof(object).GetMethod("ToString", new Type[0]);
// test invoke
m_toStringMethod.Invoke(Value, null);
}
catch
{
m_toStringMethod = typeof(object).GetMethod("ToString", new Type[0]);
}
return m_toStringMethod;
}
public string GetRichTextValue()
public string GetLabelForValue()
{
if (Value != null)
ValueType = Value.GetType();
m_richValueType = UISyntaxHighlight.GetHighlight(ValueType, true);
m_richValueType = UISyntaxHighlight.ParseFullSyntax(ValueType, true);
if (OwnerCacheObject is CacheMember cm && !cm.HasEvaluated)
return $"<i><color=grey>Not yet evaluated</color> ({m_richValueType})</i>";
@ -103,7 +84,11 @@ namespace UnityExplorer.Inspectors.Reflection
{
var toString = (string)ToStringMethod.Invoke(Value, null);
var temp = toString.Replace(ValueType.FullName, "").Trim();
var fullnametemp = ValueType.ToString();
if (fullnametemp.StartsWith("Il2CppSystem"))
fullnametemp = fullnametemp.Substring(6, fullnametemp.Length - 6);
var temp = toString.Replace(fullnametemp, "").Trim();
if (string.IsNullOrEmpty(temp))
{
@ -127,20 +112,78 @@ namespace UnityExplorer.Inspectors.Reflection
return m_richValue = label;
}
#region UI CONSTRUCTION
private MethodInfo GetToStringMethod()
{
try
{
m_toStringMethod = ReflectionHelpers.GetActualType(Value).GetMethod("ToString", new Type[0])
?? typeof(object).GetMethod("ToString", new Type[0]);
internal GameObject m_UIContent;
// test invoke
m_toStringMethod.Invoke(Value, null);
}
catch
{
m_toStringMethod = typeof(object).GetMethod("ToString", new Type[0]);
}
return m_toStringMethod;
}
#region UI CONSTRUCTION
internal GameObject m_mainContent;
internal GameObject m_inspectButton;
internal Text m_text;
internal GameObject m_subContentParent;
public virtual void ConstructUI(GameObject parent, GameObject subGroup)
{
m_UIContent = UIFactory.CreateLabel(parent, TextAnchor.MiddleLeft);
var mainLayout = m_UIContent.AddComponent<LayoutElement>();
mainLayout.minWidth = 200;
mainLayout.flexibleWidth = 5000;
m_mainContent = UIFactory.CreateHorizontalGroup(parent, new Color(1, 1, 1, 0));
var mainGroup = m_mainContent.GetComponent<HorizontalLayoutGroup>();
mainGroup.childForceExpandWidth = true;
mainGroup.childControlWidth = true;
mainGroup.childForceExpandHeight = false;
mainGroup.childControlHeight = true;
mainGroup.spacing = 4;
mainGroup.childAlignment = TextAnchor.UpperLeft;
var mainLayout = m_mainContent.AddComponent<LayoutElement>();
mainLayout.flexibleWidth = 9000;
mainLayout.minWidth = 175;
mainLayout.minHeight = 25;
m_text = m_UIContent.GetComponent<Text>();
mainLayout.flexibleHeight = 0;
// inspect button
m_inspectButton = UIFactory.CreateButton(m_mainContent, new Color(0.3f, 0.3f, 0.3f, 0.2f));
var inspectLayout = m_inspectButton.AddComponent<LayoutElement>();
inspectLayout.minWidth = 60;
inspectLayout.minHeight = 25;
inspectLayout.flexibleHeight = 0;
inspectLayout.flexibleWidth = 0;
var inspectText = m_inspectButton.GetComponentInChildren<Text>();
inspectText.text = "Inspect";
var inspectBtn = m_inspectButton.GetComponent<Button>();
#if CPP
inspectBtn.onClick.AddListener(new Action(OnInspectClicked));
#else
inspectBtn.onClick.AddListener(OnInspectClicked);
#endif
void OnInspectClicked()
{
if (!InspectorBase.IsNullOrDestroyed(this.Value))
InspectorManager.Instance.Inspect(this.Value);
}
m_inspectButton.SetActive(false);
// value label / tostring
var labelObj = UIFactory.CreateLabel(m_mainContent, TextAnchor.MiddleLeft);
m_text = labelObj.GetComponent<Text>();
var labelLayout = labelObj.AddComponent<LayoutElement>();
labelLayout.flexibleWidth = 9000;
labelLayout.minHeight = 25;
m_subContentParent = subGroup;
}

View File

@ -0,0 +1,455 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityExplorer.Helpers;
using UnityEngine;
using UnityExplorer.Inspectors.Reflection;
using UnityExplorer.UI.Shared;
using System.Reflection;
using UnityExplorer.UI;
using UnityEngine.UI;
using UnityExplorer.Config;
namespace UnityExplorer.Inspectors
{
// TODO:
// - Filters
// - Helper tools for Target object (for UnityEngine.Objects, Components, Textures, and maybe a general ToString helper)
public class ReflectionInspector : InspectorBase
{
#region STATIC
public static ReflectionInspector ActiveInstance { get; private set; }
static ReflectionInspector()
{
PanelDragger.OnFinishResize += OnContainerResized;
SceneExplorer.OnToggleShow += OnContainerResized;
}
private static void OnContainerResized()
{
if (ActiveInstance == null)
return;
ActiveInstance.m_widthUpdateWanted = true;
}
// Blacklists
private static readonly HashSet<string> s_typeAndMemberBlacklist = new HashSet<string>
{
#if CPP
// these cause a crash in IL2CPP
"Type.DeclaringMethod",
"Rigidbody2D.Cast",
"Collider2D.Cast",
"Collider2D.Raycast",
"Texture2D.SetPixelDataImpl",
#endif
};
private static readonly HashSet<string> s_methodStartsWithBlacklist = new HashSet<string>
{
// these are redundant
"get_",
"set_",
};
#endregion
#region INSTANCE
public override string TabLabel => m_targetTypeShortName;
public bool AutoUpdate { get; set; }
internal readonly Type m_targetType;
internal readonly string m_targetTypeShortName;
// all cached members of the target
internal CacheMember[] m_allMembers;
// filtered members based on current filters
internal CacheMember[] m_membersFiltered;
// actual shortlist of displayed members
internal readonly CacheMember[] m_displayedMembers = new CacheMember[ModConfig.Instance.Default_Page_Limit];
// UI members
private GameObject m_content;
public override GameObject Content
{
get => m_content;
set => m_content = value;
}
internal PageHandler m_pageHandler;
internal SliderScrollbar m_sliderScroller;
internal GameObject m_scrollContent;
internal RectTransform m_scrollContentRect;
internal bool m_widthUpdateWanted;
internal bool m_widthUpdateWaiting;
// Ctor
public ReflectionInspector(object target) : base(target)
{
if (this is StaticInspector)
m_targetType = target as Type;
else
m_targetType = ReflectionHelpers.GetActualType(target);
m_targetTypeShortName = m_targetType.Name;
ConstructUI();
CacheMembers(m_targetType);
RefreshDisplay();
}
// Methods
public override void SetActive()
{
base.SetActive();
ActiveInstance = this;
}
public override void SetInactive()
{
base.SetInactive();
ActiveInstance = null;
}
public override void Update()
{
base.Update();
if (AutoUpdate)
{
foreach (var member in m_displayedMembers)
{
member.UpdateValue();
}
}
if (m_widthUpdateWanted)
{
if (!m_widthUpdateWaiting)
m_widthUpdateWaiting = true;
else
{
UpdateWidths();
m_widthUpdateWaiting = false;
m_widthUpdateWanted = false;
}
}
}
public override void Destroy()
{
base.Destroy();
if (this.Content)
GameObject.Destroy(this.Content);
}
private void OnPageTurned()
{
RefreshDisplay();
}
public void RefreshDisplay()
{
// temp because not doing filtering yet
m_membersFiltered = m_allMembers;
var members = m_membersFiltered;
m_pageHandler.ListCount = members.Length;
// disable current members
for (int i = 0; i < m_displayedMembers.Length; i++)
{
var mem = m_displayedMembers[i];
if (mem != null)
mem.Disable();
else
break;
}
if (members.Length < 1)
return;
foreach (var itemIndex in m_pageHandler)
{
if (itemIndex >= members.Length)
break;
CacheMember member = members[itemIndex];
m_displayedMembers[itemIndex - m_pageHandler.StartIndex] = member;
member.Enable();
}
m_widthUpdateWanted = true;
}
internal void UpdateWidths()
{
float labelWidth = 125;
foreach (var cache in m_displayedMembers)
{
if (cache == null)
break;
var width = cache.GetMemberLabelWidth(m_scrollContentRect);
if (width > labelWidth)
labelWidth = width;
}
float valueWidth = m_scrollContentRect.rect.width - labelWidth - 20;
foreach (var cache in m_displayedMembers)
{
if (cache == null)
break;
cache.SetWidths(labelWidth, valueWidth);
}
}
public void CacheMembers(Type type)
{
var list = new List<CacheMember>();
var cachedSigs = new HashSet<string>();
var types = ReflectionHelpers.GetAllBaseTypes(type);
foreach (var declaringType in types)
{
MemberInfo[] infos;
try
{
infos = declaringType.GetMembers(ReflectionHelpers.CommonFlags);
}
catch
{
ExplorerCore.Log($"Exception getting members for type: {declaringType.FullName}");
continue;
}
var target = Target;
#if CPP
try
{
target = target.Il2CppCast(declaringType);
}
catch //(Exception e)
{
//ExplorerCore.LogWarning("Excepting casting " + target.GetType().FullName + " to " + declaringType.FullName);
}
#endif
foreach (var member in infos)
{
try
{
//ExplorerCore.Log($"Trying to cache member {sig}...");
//ExplorerCore.Log(member.DeclaringType.FullName + "." + member.Name);
// make sure member type is Field, Method or Property (4 / 8 / 16)
int m = (int)member.MemberType;
if (m < 4 || m > 16)
continue;
var pi = member as PropertyInfo;
var mi = member as MethodInfo;
if (this is StaticInspector)
{
if (member is FieldInfo fi && !fi.IsStatic) continue;
else if (pi != null && !pi.GetAccessors(true)[0].IsStatic) continue;
else if (mi != null && !mi.IsStatic) continue;
}
// check blacklisted members
var sig = $"{member.DeclaringType.Name}.{member.Name}";
if (s_typeAndMemberBlacklist.Any(it => sig.Contains(it)))
continue;
if (s_methodStartsWithBlacklist.Any(it => member.Name.StartsWith(it)))
continue;
if (mi != null)
{
AppendParams(mi.GetParameters());
}
else if (pi != null)
{
AppendParams(pi.GetIndexParameters());
}
void AppendParams(ParameterInfo[] _args)
{
sig += " (";
foreach (var param in _args)
{
sig += $"{param.ParameterType.Name} {param.Name}, ";
}
sig += ")";
}
if (cachedSigs.Contains(sig))
{
continue;
}
try
{
var cached = CacheFactory.GetCacheObject(member, target, m_scrollContent);
if (cached != null)
{
cachedSigs.Add(sig);
list.Add(cached);
}
}
catch (Exception e)
{
ExplorerCore.LogWarning($"Exception caching member {sig}!");
ExplorerCore.Log(e.ToString());
}
}
catch (Exception e)
{
ExplorerCore.LogWarning($"Exception caching member {member.DeclaringType.FullName}.{member.Name}!");
ExplorerCore.Log(e.ToString());
}
}
}
var sorted = new List<CacheMember>();
sorted.AddRange(list.Where(x => x is CacheMethod));
sorted.AddRange(list.Where(x => x is CacheProperty));
sorted.AddRange(list.Where(x => x is CacheField));
m_allMembers = sorted.ToArray();
// ExplorerCore.Log("Cached " + m_allMembers.Length + " members");
}
#region UI CONSTRUCTION
internal void ConstructUI()
{
var parent = InspectorManager.Instance.m_inspectorContent;
this.Content = UIFactory.CreateVerticalGroup(parent, new Color(0.15f, 0.15f, 0.15f));
var mainGroup = Content.GetComponent<VerticalLayoutGroup>();
mainGroup.childForceExpandHeight = false;
mainGroup.childForceExpandWidth = true;
mainGroup.childControlHeight = true;
mainGroup.childControlWidth = true;
mainGroup.spacing = 5;
mainGroup.padding.top = 4;
mainGroup.padding.left = 4;
mainGroup.padding.right = 4;
mainGroup.padding.bottom = 4;
ConstructTopArea();
ConstructFilterArea();
ConstructMemberList();
}
internal void ConstructTopArea()
{
var typeRowObj = UIFactory.CreateHorizontalGroup(Content, new Color(1, 1, 1, 0));
var typeRowGroup = typeRowObj.GetComponent<HorizontalLayoutGroup>();
typeRowGroup.childForceExpandWidth = true;
typeRowGroup.childForceExpandHeight = true;
typeRowGroup.childControlHeight = true;
typeRowGroup.childControlWidth = true;
var typeRowLayout = typeRowObj.AddComponent<LayoutElement>();
typeRowLayout.minHeight = 25;
typeRowLayout.flexibleHeight = 0;
typeRowLayout.minWidth = 200;
typeRowLayout.flexibleWidth = 5000;
var typeLabel = UIFactory.CreateLabel(typeRowObj, TextAnchor.MiddleLeft);
var typeLabelText = typeLabel.GetComponent<Text>();
typeLabelText.text = "Type:";
typeLabelText.horizontalOverflow = HorizontalWrapMode.Overflow;
var typeLabelTextLayout = typeLabel.AddComponent<LayoutElement>();
typeLabelTextLayout.minWidth = 60;
typeLabelTextLayout.flexibleWidth = 0;
typeLabelTextLayout.minHeight = 25;
var typeLabelInputObj = UIFactory.CreateInputField(typeRowObj);
var typeLabelInput = typeLabelInputObj.GetComponent<InputField>();
typeLabelInput.readOnly = true;
var typeLabelLayout = typeLabelInputObj.AddComponent<LayoutElement>();
typeLabelLayout.minWidth = 150;
typeLabelLayout.flexibleWidth = 5000;
typeLabelInput.text = UISyntaxHighlight.ParseFullSyntax(m_targetType, true);
// Helper tools
if (this is InstanceInspector instanceInspector)
{
instanceInspector.ConstructInstanceHelpers(Content);
}
}
internal void ConstructFilterArea()
{
var filterAreaObj = UIFactory.CreateVerticalGroup(Content, new Color(0.1f, 0.1f, 0.1f));
var filterLayout = filterAreaObj.AddComponent<LayoutElement>();
filterLayout.minHeight = 25;
var filterGroup = filterAreaObj.GetComponent<VerticalLayoutGroup>();
filterGroup.childForceExpandWidth = true;
filterGroup.childForceExpandHeight = false;
filterGroup.childControlWidth = true;
filterGroup.childControlHeight = true;
// name filter
// membertype filter
// Instance filters
if (this is InstanceInspector instanceInspector)
{
instanceInspector.ConstructInstanceFilters(filterAreaObj);
}
}
internal void ConstructMemberList()
{
var scrollobj = UIFactory.CreateScrollView(Content, out m_scrollContent, out m_sliderScroller, new Color(0.12f, 0.12f, 0.12f));
m_scrollContentRect = m_scrollContent.GetComponent<RectTransform>();
var scrollGroup = m_scrollContent.GetComponent<VerticalLayoutGroup>();
scrollGroup.spacing = 3;
scrollGroup.padding.left = 0;
scrollGroup.padding.right = 0;
m_pageHandler = new PageHandler(m_sliderScroller);
m_pageHandler.ConstructUI(Content);
m_pageHandler.OnPageChanged += OnPageTurned;
}
#endregion
#endregion
}
}