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}";
}
}
}