using System; using System.Collections; using System.Collections.Generic; using System.Reflection; using Godot; namespace Rokojori.Tools; public static class ObjectGraphInspector { public class Stats { public int totalObjects; public int maxDepth; public Dictionary typeCounts = new(); public Dictionary memberInfos = new(); public override string ToString() { var info = "Total Objects:" + totalObjects + " MaxDepth: " + maxDepth; foreach ( var t in typeCounts ) { info += "\n"; info += t.Key + ": " + t.Value; } foreach ( var t in memberInfos ) { info += "\n"; info += t.Key + ": " + t.Value; } return info; } } public static Stats Inspect( object root, int maxDepth = 64 ) { var visited = new HashSet(ReferenceEqualityComparer.Instance); var stats = new Stats(); Walk( root, 0, maxDepth, visited, stats, "" ); stats.totalObjects = visited.Count; return stats; } static void Walk( object obj, int depth, int maxDepth, HashSet visited, Stats stats, string memberInfo ) { if ( obj == null ) { return; } var type = obj.GetType(); if ( type.IsValueType || obj is string ) { return; } if ( ! visited.Add(obj) ) { return; } stats.maxDepth = Math.Max( stats.maxDepth, depth ); stats.typeCounts.TryGetValue( type, out int count ); stats.typeCounts[ type ] = count + 1; var combinedInfo = memberInfo + "(" + type.Name + ")"; stats.memberInfos.TryGetValue( combinedInfo, out int memberCount ); stats.memberInfos[ combinedInfo ] = memberCount + 1; if ( depth >= maxDepth ) { return; } if ( obj is IEnumerable enumerable ) { foreach ( var item in enumerable ) { Walk( item, depth + 1, maxDepth, visited, stats, type.Name + "." + item ); } } var flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; foreach ( var field in type.GetFields( flags ) ) { if ( field.FieldType.IsValueType || field.FieldType == typeof(string)) { continue; } try { var value = field.GetValue( obj ); Walk( value, depth + 1, maxDepth, visited, stats, type.Name + "." + field.Name ); } catch { // Ignore reflection failures } } } class ReferenceEqualityComparer : IEqualityComparer { public static readonly ReferenceEqualityComparer Instance = new(); public new bool Equals(object x, object y) => ReferenceEquals(x, y); public int GetHashCode(object obj) => System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(obj); } }