UnityExplorer/src/CacheObject/CacheMember.cs

343 lines
12 KiB
C#
Raw Normal View History

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityEngine;
using UnityExplorer.Runtime;
using UnityExplorer.CacheObject.Views;
using UnityExplorer.Inspectors;
2021-12-02 18:35:46 +11:00
using UniverseLib.UI.Models;
using UnityExplorer.UI;
2021-12-02 18:35:46 +11:00
using UniverseLib;
using UniverseLib.UI;
using UnityExplorer.UI.Widgets;
namespace UnityExplorer.CacheObject
{
public abstract class CacheMember : CacheObjectBase
{
public abstract Type DeclaringType { get; }
2021-04-30 21:34:50 +10:00
public string NameForFiltering { get; protected set; }
public object DeclaringInstance => IsStatic ? null : (m_declaringInstance ?? (m_declaringInstance = Owner.Target.TryCast(DeclaringType)));
private object m_declaringInstance;
2021-05-05 21:27:09 +10:00
public abstract bool IsStatic { get; }
2021-05-04 20:10:46 +10:00
public override bool HasArguments => Arguments?.Length > 0 || GenericArguments.Length > 0;
public ParameterInfo[] Arguments { get; protected set; } = new ParameterInfo[0];
public Type[] GenericArguments { get; protected set; } = ArgumentUtility.EmptyTypes;
2021-05-04 20:10:46 +10:00
public EvaluateWidget Evaluator { get; protected set; }
public bool Evaluating => Evaluator != null && Evaluator.UIRoot.activeSelf;
2021-06-05 19:36:09 +10:00
2021-04-30 21:34:50 +10:00
public virtual void SetInspectorOwner(ReflectionInspector inspector, MemberInfo member)
{
this.Owner = inspector;
this.NameLabelText = SignatureHighlighter.Parse(member.DeclaringType, false, member);
2021-04-30 21:34:50 +10:00
this.NameForFiltering = $"{member.DeclaringType.Name}.{member.Name}";
this.NameLabelTextRaw = NameForFiltering;
}
2021-05-04 20:10:46 +10:00
public override void ReleasePooledObjects()
{
base.ReleasePooledObjects();
if (this.Evaluator != null)
{
this.Evaluator.OnReturnToPool();
Pool<EvaluateWidget>.Return(this.Evaluator);
this.Evaluator = null;
}
}
2021-05-05 21:27:09 +10:00
public override void UnlinkFromView()
2021-05-04 20:10:46 +10:00
{
if (this.Evaluator != null)
this.Evaluator.UIRoot.transform.SetParent(Pool<EvaluateWidget>.Instance.InactiveHolder.transform, false);
2021-05-05 21:27:09 +10:00
base.UnlinkFromView();
2021-05-04 20:10:46 +10:00
}
protected abstract object TryEvaluate();
protected abstract void TrySetValue(object value);
2021-09-06 15:51:40 +10:00
/// <summary>
/// Evaluate is called when first shown (if ShouldAutoEvaluate), or else when Evaluate button is clicked, or auto-updated.
/// </summary>
public void Evaluate()
2021-05-04 20:10:46 +10:00
{
2021-09-06 15:51:40 +10:00
SetValueFromSource(TryEvaluate());
2021-05-04 20:10:46 +10:00
}
/// <summary>
2021-09-06 15:51:40 +10:00
/// Called when user presses the Evaluate button.
/// </summary>
2021-09-06 15:51:40 +10:00
public void EvaluateAndSetCell()
{
2021-09-06 15:51:40 +10:00
Evaluate();
if (CellView != null)
SetDataToCell(CellView);
}
public override void TrySetUserValue(object value)
{
2021-04-30 21:34:50 +10:00
TrySetValue(value);
Evaluate();
}
protected override void SetValueState(CacheObjectCell cell, ValueStateArgs args)
{
base.SetValueState(cell, args);
}
2021-05-04 20:10:46 +10:00
private static readonly Color evalEnabledColor = new Color(0.15f, 0.25f, 0.15f);
private static readonly Color evalDisabledColor = new Color(0.15f, 0.15f, 0.15f);
2021-04-30 21:34:50 +10:00
protected override bool SetCellEvaluateState(CacheObjectCell objectcell)
{
2021-04-30 21:34:50 +10:00
var cell = objectcell as CacheMemberCell;
cell.EvaluateHolder.SetActive(!ShouldAutoEvaluate);
if (!ShouldAutoEvaluate)
{
2021-05-11 19:18:27 +10:00
cell.EvaluateButton.Component.gameObject.SetActive(true);
if (HasArguments)
2021-05-04 20:10:46 +10:00
{
if (!Evaluating)
cell.EvaluateButton.ButtonText.text = $"Evaluate ({Arguments.Length + GenericArguments.Length})";
else
{
cell.EvaluateButton.ButtonText.text = "Hide";
Evaluator.UIRoot.transform.SetParent(cell.EvaluateHolder.transform, false);
2021-05-11 19:18:27 +10:00
RuntimeProvider.Instance.SetColorBlock(cell.EvaluateButton.Component, evalEnabledColor, evalEnabledColor * 1.3f);
2021-05-04 20:10:46 +10:00
}
}
else
cell.EvaluateButton.ButtonText.text = "Evaluate";
2021-05-04 20:10:46 +10:00
if (!Evaluating)
2021-05-11 19:18:27 +10:00
RuntimeProvider.Instance.SetColorBlock(cell.EvaluateButton.Component, evalDisabledColor, evalDisabledColor * 1.3f);
}
if (State == ValueState.NotEvaluated && !ShouldAutoEvaluate)
{
SetValueState(cell, ValueStateArgs.Default);
cell.RefreshSubcontentButton();
2021-04-30 21:34:50 +10:00
return true;
}
if (State == ValueState.NotEvaluated)
Evaluate();
2021-04-30 21:34:50 +10:00
return false;
}
2021-05-04 20:10:46 +10:00
public void OnEvaluateClicked()
{
if (!HasArguments)
{
EvaluateAndSetCell();
}
else
{
if (Evaluator == null)
{
this.Evaluator = Pool<EvaluateWidget>.Borrow();
Evaluator.OnBorrowedFromPool(this);
Evaluator.UIRoot.transform.SetParent((CellView as CacheMemberCell).EvaluateHolder.transform, false);
SetCellEvaluateState(CellView);
}
else
{
if (Evaluator.UIRoot.activeSelf)
Evaluator.UIRoot.SetActive(false);
else
Evaluator.UIRoot.SetActive(true);
SetCellEvaluateState(CellView);
}
}
}
#region Cache Member Util
2022-01-01 21:40:47 +11:00
//public static bool CanParseArgs(ParameterInfo[] parameters)
//{
// foreach (var param in parameters)
// {
// var pType = param.ParameterType;
//
// if (pType.IsByRef && pType.HasElementType)
// pType = pType.GetElementType();
//
// if (pType != null && ParseUtility.CanParse(pType))
// continue;
// else
// return false;
// }
// return true;
//}
public static List<CacheMember> GetCacheMembers(object inspectorTarget, Type _type, ReflectionInspector _inspector)
{
var list = new List<CacheMember>();
var cachedSigs = new HashSet<string>();
var types = ReflectionUtility.GetAllBaseTypes(_type);
var flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static;
if (!_inspector.StaticOnly)
flags |= BindingFlags.Instance;
var infos = new List<MemberInfo>();
foreach (var declaringType in types)
{
var target = inspectorTarget;
if (!_inspector.StaticOnly)
target = target.TryCast(declaringType);
infos.Clear();
infos.AddRange(declaringType.GetProperties(flags));
infos.AddRange(declaringType.GetFields(flags));
infos.AddRange(declaringType.GetMethods(flags));
foreach (var member in infos)
{
if (member.DeclaringType != declaringType)
continue;
TryCacheMember(member, list, cachedSigs, declaringType, _inspector);
}
}
var typeList = types.ToList();
var sorted = new List<CacheMember>();
sorted.AddRange(list.Where(it => it is CacheProperty)
.OrderBy(it => typeList.IndexOf(it.DeclaringType))
.ThenBy(it => it.NameForFiltering));
sorted.AddRange(list.Where(it => it is CacheField)
.OrderBy(it => typeList.IndexOf(it.DeclaringType))
.ThenBy(it => it.NameForFiltering));
sorted.AddRange(list.Where(it => it is CacheMethod)
.OrderBy(it => typeList.IndexOf(it.DeclaringType))
.ThenBy(it => it.NameForFiltering));
return sorted;
}
2021-06-05 19:36:09 +10:00
private static void TryCacheMember(MemberInfo member, List<CacheMember> list, HashSet<string> cachedSigs,
Type declaringType, ReflectionInspector _inspector, bool ignorePropertyMethodInfos = true)
{
try
{
2021-12-02 18:35:46 +11:00
if (RuntimeHelper.IsBlacklisted(member))
return;
var sig = GetSig(member);
//ExplorerCore.Log($"Trying to cache member {sig}... ({member.MemberType})");
CacheMember cached;
Type returnType;
switch (member.MemberType)
{
case MemberTypes.Method:
{
var mi = member as MethodInfo;
2021-06-05 19:36:09 +10:00
if (ignorePropertyMethodInfos
&& (mi.Name.StartsWith("get_") || mi.Name.StartsWith("set_")))
return;
2022-01-01 21:40:47 +11:00
//var args = mi.GetParameters();
//if (!CanParseArgs(args))
// return;
2022-01-01 21:40:47 +11:00
sig += GetArgumentString(mi.GetParameters());
if (cachedSigs.Contains(sig))
return;
cached = new CacheMethod() { MethodInfo = mi };
returnType = mi.ReturnType;
break;
}
case MemberTypes.Property:
{
var pi = member as PropertyInfo;
2022-01-01 21:40:47 +11:00
//var args = pi.GetIndexParameters();
//if (!CanParseArgs(args))
// return;
if (!pi.CanRead && pi.CanWrite)
{
// write-only property, cache the set method instead.
var setMethod = pi.GetSetMethod(true);
if (setMethod != null)
TryCacheMember(setMethod, list, cachedSigs, declaringType, _inspector, false);
return;
}
2022-01-01 21:40:47 +11:00
sig += GetArgumentString(pi.GetIndexParameters());
if (cachedSigs.Contains(sig))
return;
cached = new CacheProperty() { PropertyInfo = pi };
returnType = pi.PropertyType;
break;
}
case MemberTypes.Field:
{
var fi = member as FieldInfo;
cached = new CacheField() { FieldInfo = fi };
returnType = fi.FieldType;
break;
}
default: return;
}
cachedSigs.Add(sig);
cached.SetFallbackType(returnType);
2021-04-30 21:34:50 +10:00
cached.SetInspectorOwner(_inspector, member);
list.Add(cached);
}
catch (Exception e)
{
ExplorerCore.LogWarning($"Exception caching member {member.DeclaringType.FullName}.{member.Name}!");
ExplorerCore.Log(e.ToString());
}
}
2021-06-05 19:36:09 +10:00
internal static string GetSig(MemberInfo member)
=> $"{member.DeclaringType.Name}.{member.Name}";
internal static string GetArgumentString(ParameterInfo[] args)
{
var sb = new StringBuilder();
sb.Append(' ');
sb.Append('(');
foreach (var param in args)
{
sb.Append(param.ParameterType.Name);
sb.Append(' ');
sb.Append(param.Name);
sb.Append(',');
sb.Append(' ');
}
sb.Append(')');
return sb.ToString();
}
#endregion
}
}