using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using BF = System.Reflection.BindingFlags; using UnityExplorer.Core.Runtime; namespace UnityExplorer { public static class ReflectionUtility { public const BF AllFlags = BF.Public | BF.Instance | BF.NonPublic | BF.Static; /// /// Helper for IL2CPP to get the underlying true Type (Unhollowed) of the object. /// /// The object to get the true Type for. /// The most accurate Type of the object which could be identified. public static Type GetActualType(this object obj) { if (obj == null) return null; return ReflectionProvider.Instance.GetActualType(obj); } /// /// Cast an object to its underlying Type. /// /// The object to cast /// The object, cast to the underlying Type if possible, otherwise the original object. public static object Cast(this object obj) => ReflectionProvider.Instance.Cast(obj, GetActualType(obj)); /// /// Cast an object to a Type, if possible. /// /// The object to cast /// The Type to cast to /// The object, cast to the Type provided if possible, otherwise the original object. public static object Cast(this object obj, Type castTo) => ReflectionProvider.Instance.Cast(obj, castTo); /// /// Check if the provided Type is assignable to IEnumerable. /// /// The Type to check /// True if the Type is assignable to IEnumerable, otherwise false. public static bool IsEnumerable(this Type t) => ReflectionProvider.Instance.IsAssignableFrom(typeof(IEnumerable), t); /// /// Check if the provided Type is assignable to IDictionary. /// /// The Type to check /// True if the Type is assignable to IDictionary, otherwise false. public static bool IsDictionary(this Type t) => ReflectionProvider.Instance.IsAssignableFrom(typeof(IDictionary), t); /// /// [INTERNAL] Used to load Unhollowed DLLs in IL2CPP. /// internal static bool LoadModule(string module) => ReflectionProvider.Instance.LoadModule(module); // cache for GetTypeByName internal static readonly Dictionary s_typesByName = new Dictionary(); /// /// Find a in the current AppDomain whose matches the provided . /// /// The you want to search for - case sensitive and full matches only. /// The Type if found, otherwise null. public static Type GetTypeByName(string fullName) { s_typesByName.TryGetValue(fullName, out Type ret); if (ret != null) return ret; foreach (var type in from asm in AppDomain.CurrentDomain.GetAssemblies() from type in asm.TryGetTypes() select type) { if (type.FullName == fullName) { ret = type; break; } } if (s_typesByName.ContainsKey(fullName)) s_typesByName[fullName] = ret; else s_typesByName.Add(fullName, ret); return ret; } // cache for GetBaseTypes internal static readonly Dictionary s_cachedTypeInheritance = new Dictionary(); /// /// Get all base types of the provided Type, including itself. /// public static Type[] GetAllBaseTypes(this object obj) => GetAllBaseTypes(GetActualType(obj)); /// /// Get all base types of the provided Type, including itself. /// public static Type[] GetAllBaseTypes(this Type type) { if (type == null) throw new ArgumentNullException("type"); var name = type.AssemblyQualifiedName; if (s_cachedTypeInheritance.TryGetValue(name, out Type[] ret)) return ret; List list = new List(); while (type != null) { list.Add(type); type = type.BaseType; } ret = list.ToArray(); s_cachedTypeInheritance.Add(name, ret); return ret; } /// /// Safely get all valid Types inside an Assembly. /// /// The Assembly to find Types in. /// All possible Types which could be retrieved from the Assembly, or an empty array. public static IEnumerable TryGetTypes(this Assembly asm) { try { return asm.GetTypes(); } catch (ReflectionTypeLoadException e) { try { return asm.GetExportedTypes(); } catch { return e.Types.Where(t => t != null); } } catch { return Enumerable.Empty(); } } internal static Dictionary> s_cachedFieldInfos = new Dictionary>(); public static FieldInfo GetFieldInfo(Type type, string fieldName) { if (!s_cachedFieldInfos.ContainsKey(type)) s_cachedFieldInfos.Add(type, new Dictionary()); if (!s_cachedFieldInfos[type].ContainsKey(fieldName)) s_cachedFieldInfos[type].Add(fieldName, type.GetField(fieldName, AllFlags)); return s_cachedFieldInfos[type][fieldName]; } internal static Dictionary> s_cachedPropInfos = new Dictionary>(); public static PropertyInfo GetPropertyInfo(Type type, string propertyName) { if (!s_cachedPropInfos.ContainsKey(type)) s_cachedPropInfos.Add(type, new Dictionary()); if (!s_cachedPropInfos[type].ContainsKey(propertyName)) s_cachedPropInfos[type].Add(propertyName, type.GetProperty(propertyName, AllFlags)); return s_cachedPropInfos[type][propertyName]; } /// /// Helper to display a simple "{ExceptionType}: {Message}" of the exception, and optionally use the inner-most exception. /// /// The Exception to convert to string. /// Should the inner-most Exception of the stack be used? If false, the Exception you provided will be used directly. /// The exception to string. public static string ReflectionExToString(this Exception e, bool innerMost = false) { if (innerMost) { while (e.InnerException != null) { #if CPP if (e.InnerException is System.Runtime.CompilerServices.RuntimeWrappedException) break; #endif e = e.InnerException; } } return $"{e.GetType()}: {e.Message}"; } } }