diff --git a/src/CacheObject/CacheConstructor.cs b/src/CacheObject/CacheConstructor.cs new file mode 100644 index 0000000..202158b --- /dev/null +++ b/src/CacheObject/CacheConstructor.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using UnityExplorer.Inspectors; +using UniverseLib.Utility; + +namespace UnityExplorer.CacheObject +{ + public class CacheConstructor : CacheMember + { + public ConstructorInfo CtorInfo { get; } + + public override Type DeclaringType => CtorInfo.DeclaringType; + public override bool IsStatic => true; + public override bool ShouldAutoEvaluate => false; + public override bool CanWrite => false; + + public CacheConstructor(ConstructorInfo ci) + { + this.CtorInfo = ci; + } + + public override void SetInspectorOwner(ReflectionInspector inspector, MemberInfo member) + { + base.SetInspectorOwner(inspector, member); + + Arguments = CtorInfo.GetParameters(); + if (CtorInfo.DeclaringType.IsGenericTypeDefinition) + GenericArguments = CtorInfo.DeclaringType.GetGenericArguments(); + } + + protected override object TryEvaluate() + { + try + { + Type returnType = DeclaringType; + + if (returnType.IsGenericTypeDefinition) + returnType = DeclaringType.MakeGenericType(Evaluator.TryParseGenericArguments()); + + object ret; + if (HasArguments) + ret = Activator.CreateInstance(returnType, Evaluator.TryParseArguments()); + else + ret = Activator.CreateInstance(returnType, ArgumentUtility.EmptyArgs); + + HadException = false; + LastException = null; + return ret; + } + catch (Exception ex) + { + HadException = true; + LastException = ex; + return null; + } + } + + protected override void TrySetValue(object value) => throw new NotImplementedException("You can't set a constructor"); + } +} diff --git a/src/CacheObject/CacheField.cs b/src/CacheObject/CacheField.cs index 05c6d92..e8b335c 100644 --- a/src/CacheObject/CacheField.cs +++ b/src/CacheObject/CacheField.cs @@ -17,6 +17,11 @@ namespace UnityExplorer.CacheObject public override bool ShouldAutoEvaluate => true; + public CacheField(FieldInfo fi) + { + this.FieldInfo = fi; + } + public override void SetInspectorOwner(ReflectionInspector inspector, MemberInfo member) { base.SetInspectorOwner(inspector, member); diff --git a/src/CacheObject/CacheMember.cs b/src/CacheObject/CacheMember.cs index 9d2fdb8..b33c445 100644 --- a/src/CacheObject/CacheMember.cs +++ b/src/CacheObject/CacheMember.cs @@ -14,6 +14,8 @@ using UniverseLib.UI; using UnityExplorer.UI.Widgets; using UniverseLib.Utility; using UniverseLib.UI.ObjectPool; +using System.Collections; +using HarmonyLib; namespace UnityExplorer.CacheObject { @@ -34,9 +36,12 @@ namespace UnityExplorer.CacheObject public virtual void SetInspectorOwner(ReflectionInspector inspector, MemberInfo member) { this.Owner = inspector; - this.NameLabelText = this is CacheMethod - ? SignatureHighlighter.HighlightMethod(member as MethodInfo) - : SignatureHighlighter.Parse(member.DeclaringType, false, member); + this.NameLabelText = this switch + { + CacheMethod => SignatureHighlighter.HighlightMethod(member as MethodInfo), + CacheConstructor => SignatureHighlighter.HighlightConstructor(member as ConstructorInfo), + _ => SignatureHighlighter.Parse(member.DeclaringType, false, member), + }; this.NameForFiltering = SignatureHighlighter.RemoveHighlighting(NameLabelText); this.NameLabelTextRaw = NameForFiltering; @@ -167,56 +172,61 @@ namespace UnityExplorer.CacheObject #region Cache Member Util - public static List GetCacheMembers(object inspectorTarget, Type _type, ReflectionInspector _inspector) + public static List GetCacheMembers(object inspectorTarget, Type type, ReflectionInspector inspector) { - var list = new List(); - var cachedSigs = new HashSet(); + //var list = new List(); + HashSet cachedSigs = new(); + List props = new(); + List fields = new(); + List ctors = new(); + List methods = new(); - var types = ReflectionUtility.GetAllBaseTypes(_type); + var types = ReflectionUtility.GetAllBaseTypes(type); var flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static; - if (!_inspector.StaticOnly) + if (!inspector.StaticOnly) flags |= BindingFlags.Instance; - var infos = new List(); + // Get non-static constructors of the main type. + // There's no reason to get the static cctor, it will be invoked when we inspect the class. + // Also no point getting ctors on inherited types. + foreach (var ctor in type.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) + TryCacheMember(ctor, ctors, cachedSigs, type, inspector); foreach (var declaringType in types) { var target = inspectorTarget; - if (!_inspector.StaticOnly) + 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 prop in declaringType.GetProperties(flags)) + if (prop.DeclaringType == declaringType) + TryCacheMember(prop, props, cachedSigs, declaringType, inspector); + + foreach (var field in declaringType.GetFields(flags)) + if (field.DeclaringType == declaringType) + TryCacheMember(field, fields, cachedSigs, declaringType, inspector); + + foreach (var method in declaringType.GetMethods(flags)) + if (method.DeclaringType == declaringType) + TryCacheMember(method, methods, cachedSigs, declaringType, inspector); - foreach (var member in infos) - { - if (member.DeclaringType != declaringType) - continue; - TryCacheMember(member, list, cachedSigs, declaringType, _inspector); - } } - var typeList = types.ToList(); - var sorted = new List(); - 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)); - + sorted.AddRange(props.OrderBy(it => Array.IndexOf(types, it.DeclaringType)) + .ThenBy(it => it.NameForFiltering)); + sorted.AddRange(fields.OrderBy(it => Array.IndexOf(types, it.DeclaringType)) + .ThenBy(it => it.NameForFiltering)); + sorted.AddRange(ctors.OrderBy(it => Array.IndexOf(types, it.DeclaringType)) + .ThenBy(it => it.NameForFiltering)); + sorted.AddRange(methods.OrderBy(it => Array.IndexOf(types, it.DeclaringType)) + .ThenBy(it => it.NameForFiltering)); return sorted; } - private static void TryCacheMember(MemberInfo member, List list, HashSet cachedSigs, - Type declaringType, ReflectionInspector _inspector, bool ignorePropertyMethodInfos = true) + private static void TryCacheMember(MemberInfo member, IList list, HashSet cachedSigs, + Type declaringType, ReflectionInspector inspector, bool ignorePropertyMethodInfos = true) { try { @@ -231,6 +241,17 @@ namespace UnityExplorer.CacheObject Type returnType; switch (member.MemberType) { + case MemberTypes.Constructor: + { + var ci = member as ConstructorInfo; + sig += GetArgumentString(ci.GetParameters()); + if (cachedSigs.Contains(sig)) + return; + cached = new CacheConstructor(ci); + returnType = ci.DeclaringType; + } + break; + case MemberTypes.Method: { var mi = member as MethodInfo; @@ -238,15 +259,11 @@ namespace UnityExplorer.CacheObject && (mi.Name.StartsWith("get_") || mi.Name.StartsWith("set_"))) return; - //var args = mi.GetParameters(); - //if (!CanParseArgs(args)) - // return; - sig += GetArgumentString(mi.GetParameters()); if (cachedSigs.Contains(sig)) return; - cached = new CacheMethod() { MethodInfo = mi }; + cached = new CacheMethod(mi); returnType = mi.ReturnType; break; } @@ -255,16 +272,12 @@ namespace UnityExplorer.CacheObject { var pi = member as PropertyInfo; - //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); + TryCacheMember(setMethod, list, cachedSigs, declaringType, inspector, false); return; } @@ -272,7 +285,7 @@ namespace UnityExplorer.CacheObject if (cachedSigs.Contains(sig)) return; - cached = new CacheProperty() { PropertyInfo = pi }; + cached = new CacheProperty(pi); returnType = pi.PropertyType; break; } @@ -280,7 +293,7 @@ namespace UnityExplorer.CacheObject case MemberTypes.Field: { var fi = member as FieldInfo; - cached = new CacheField() { FieldInfo = fi }; + cached = new CacheField(fi); returnType = fi.FieldType; break; } @@ -291,7 +304,7 @@ namespace UnityExplorer.CacheObject cachedSigs.Add(sig); cached.SetFallbackType(returnType); - cached.SetInspectorOwner(_inspector, member); + cached.SetInspectorOwner(inspector, member); list.Add(cached); } diff --git a/src/CacheObject/CacheMethod.cs b/src/CacheObject/CacheMethod.cs index a96a754..77c2165 100644 --- a/src/CacheObject/CacheMethod.cs +++ b/src/CacheObject/CacheMethod.cs @@ -11,13 +11,18 @@ namespace UnityExplorer.CacheObject { public class CacheMethod : CacheMember { - public MethodInfo MethodInfo { get; internal set; } + public MethodInfo MethodInfo { get; } public override Type DeclaringType => MethodInfo.DeclaringType; public override bool CanWrite => false; public override bool IsStatic => MethodInfo.IsStatic; public override bool ShouldAutoEvaluate => false; + public CacheMethod (MethodInfo mi) + { + this.MethodInfo = mi; + } + public override void SetInspectorOwner(ReflectionInspector inspector, MemberInfo member) { base.SetInspectorOwner(inspector, member); diff --git a/src/CacheObject/CacheProperty.cs b/src/CacheObject/CacheProperty.cs index fee7b9e..d4c9569 100644 --- a/src/CacheObject/CacheProperty.cs +++ b/src/CacheObject/CacheProperty.cs @@ -19,6 +19,11 @@ namespace UnityExplorer.CacheObject public override bool ShouldAutoEvaluate => !HasArguments; + public CacheProperty(PropertyInfo pi) + { + this.PropertyInfo = pi; + } + public override void SetInspectorOwner(ReflectionInspector inspector, MemberInfo member) { base.SetInspectorOwner(inspector, member); diff --git a/src/Inspectors/ReflectionInspector.cs b/src/Inspectors/ReflectionInspector.cs index 4326f78..c1ad44f 100644 --- a/src/Inspectors/ReflectionInspector.cs +++ b/src/Inspectors/ReflectionInspector.cs @@ -46,8 +46,9 @@ namespace UnityExplorer.Inspectors None = 0, Property = 1, Field = 2, - Method = 4, - All = 7 + Constructor = 4, + Method = 8, + All = Property | Field | Method | Constructor, } // UI @@ -153,7 +154,7 @@ namespace UnityExplorer.Inspectors this.filterInputField.Text = ""; - SetFilter("", StaticOnly ? BindingFlags.Static : BindingFlags.Instance); + SetFilter("", StaticOnly ? BindingFlags.Static : BindingFlags.Default); scopeFilterButtons[BindingFlags.Default].Component.gameObject.SetActive(!StaticOnly); scopeFilterButtons[BindingFlags.Instance].Component.gameObject.SetActive(!StaticOnly); @@ -462,6 +463,7 @@ namespace UnityExplorer.Inspectors AddMemberTypeToggle(rowObj, MemberTypes.Property, 90); AddMemberTypeToggle(rowObj, MemberTypes.Field, 70); AddMemberTypeToggle(rowObj, MemberTypes.Method, 90); + AddMemberTypeToggle(rowObj, MemberTypes.Constructor, 110); } private void AddScopeFilterButton(GameObject parent, BindingFlags flags, bool setAsActive = false) @@ -480,25 +482,26 @@ namespace UnityExplorer.Inspectors { var toggleObj = UIFactory.CreateToggle(parent, "Toggle_" + type, out Toggle toggle, out Text toggleText); UIFactory.SetLayoutElement(toggleObj, minHeight: 25, minWidth: width); - var color = type switch + string color = type switch { MemberTypes.Method => SignatureHighlighter.METHOD_INSTANCE, MemberTypes.Field => SignatureHighlighter.FIELD_INSTANCE, MemberTypes.Property => SignatureHighlighter.PROP_INSTANCE, + MemberTypes.Constructor => SignatureHighlighter.CLASS_INSTANCE, _ => throw new NotImplementedException() }; toggleText.text = $"{type}"; toggle.graphic.TryCast().color = color.ToColor() * 0.65f; - MemberFlags flag; - switch (type) + MemberFlags flag = type switch { - case MemberTypes.Method: flag = MemberFlags.Method; break; - case MemberTypes.Property: flag = MemberFlags.Property; break; - case MemberTypes.Field: flag = MemberFlags.Field; break; - default: return; - } + MemberTypes.Method => MemberFlags.Method, + MemberTypes.Property => MemberFlags.Property, + MemberTypes.Field => MemberFlags.Field, + MemberTypes.Constructor => MemberFlags.Constructor, + _ => throw new NotImplementedException() + }; toggle.onValueChanged.AddListener((bool val) => { OnMemberTypeToggled(flag, val); }); diff --git a/src/UI/Widgets/EvaluateWidget/EvaluateWidget.cs b/src/UI/Widgets/EvaluateWidget/EvaluateWidget.cs index 76ae3be..201ba22 100644 --- a/src/UI/Widgets/EvaluateWidget/EvaluateWidget.cs +++ b/src/UI/Widgets/EvaluateWidget/EvaluateWidget.cs @@ -12,6 +12,7 @@ using UniverseLib.UI; using UniverseLib; using UnityExplorer.CacheObject; using UniverseLib.UI.ObjectPool; +using UniverseLib.Utility; namespace UnityExplorer.UI.Widgets { @@ -76,6 +77,9 @@ namespace UnityExplorer.UI.Widgets public object[] TryParseArguments() { + if (!parameters.Any()) + return ArgumentUtility.EmptyArgs; + object[] outArgs = new object[parameters.Length]; for (int i = 0; i < parameters.Length; i++) diff --git a/src/UnityExplorer.csproj b/src/UnityExplorer.csproj index b69ee66..a4efe22 100644 --- a/src/UnityExplorer.csproj +++ b/src/UnityExplorer.csproj @@ -175,13 +175,13 @@ False - packages\UniverseLib.1.2.15\lib\net35\UniverseLib.Mono.dll + packages\UniverseLib.1.2.16\lib\net35\UniverseLib.Mono.dll - packages\UniverseLib.1.2.15\lib\net472\UniverseLib.IL2CPP.dll + packages\UniverseLib.1.2.16\lib\net472\UniverseLib.IL2CPP.dll packages\Il2CppAssemblyUnhollower.BaseLib.0.4.22\lib\net472\UnhollowerBaseLib.dll @@ -225,6 +225,7 @@ + diff --git a/src/packages.config b/src/packages.config index 35694b3..be19fc9 100644 --- a/src/packages.config +++ b/src/packages.config @@ -6,6 +6,6 @@ - + \ No newline at end of file