PageRenderTime 62ms CodeModel.GetById 39ms RepoModel.GetById 0ms app.codeStats 1ms

/FunnyFoods2/Assets/FullInspector2/Core/fiRuntimeReflectionUtility.cs

https://gitlab.com/saneknovco/refregerator
C# | 345 lines | 211 code | 59 blank | 75 comment | 44 complexity | ddfd45405e8581f78661e138a67d75f6 MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Reflection;
  5. using FullSerializer;
  6. using FullSerializer.Internal;
  7. using UnityObject = UnityEngine.Object;
  8. namespace FullInspector.Internal {
  9. /// <summary>
  10. /// Some reflection utilities that can be AOT compiled (and are therefore available at runtime).
  11. /// </summary>
  12. public class fiRuntimeReflectionUtility {
  13. /// <summary>
  14. /// Invokes the given static method on the given type.
  15. /// </summary>
  16. /// <param name="type">The type to search for the method.</param>
  17. /// <param name="methodName">The name of the method.</param>
  18. /// <param name="parameters">The parameters to invoke the method with.</param>
  19. public static object InvokeStaticMethod(Type type, string methodName, object[] parameters) {
  20. try {
  21. return type.GetFlattenedMethod(methodName).Invoke(null, parameters);
  22. }
  23. catch { }
  24. return null;
  25. }
  26. public static object InvokeStaticMethod(string typeName, string methodName, object[] parameters) {
  27. return InvokeStaticMethod(TypeCache.FindType(typeName), methodName, parameters);
  28. }
  29. /// <summary>
  30. /// Invokes the given method on the given type.
  31. /// </summary>
  32. /// <param name="type">The type to find the method to invoke from.</param>
  33. /// <param name="methodName">The name of the method to invoke.</param>
  34. /// <param name="thisInstance">The "this" object in the method.</param>
  35. /// <param name="parameters">The parameters to invoke with.</param>
  36. public static void InvokeMethod(Type type, string methodName, object thisInstance, object[] parameters) {
  37. try {
  38. type.GetFlattenedMethod(methodName).Invoke(thisInstance, parameters);
  39. }
  40. catch { }
  41. }
  42. /// <summary>
  43. /// Reads the given field from the value. Note that only field reads are supported.
  44. /// </summary>
  45. /// <typeparam name="T">The type of value stored within the field that we are reading.</typeparam>
  46. /// <typeparam name="TContext">The type of context we are reading the field from.</typeparam>
  47. /// <param name="context">The value where we are reading the field from.</param>
  48. /// <param name="fieldName">The name of the field we are reading.</param>
  49. /// <returns>The read value, or a thrown exception on error.</returns>
  50. public static T ReadField<TContext, T>(TContext context, string fieldName) {
  51. var members = typeof(TContext).GetFlattenedMember(fieldName);
  52. if (members == null || members.Length == 0)
  53. throw new ArgumentException(typeof(TContext).CSharpName() + " does not contain a field named \"" + fieldName + "\"");
  54. if (members.Length > 1)
  55. throw new ArgumentException(typeof(TContext).CSharpName() + " has more than one field named \"" + fieldName + "\"");
  56. var fieldInfo = members[0] as FieldInfo;
  57. if (fieldInfo == null)
  58. throw new ArgumentException(typeof(TContext).CSharpName() + "." + fieldName + " is not a field");
  59. if (fieldInfo.FieldType != typeof(T))
  60. throw new ArgumentException(typeof(TContext).CSharpName() + "." + fieldName + " type is not compatable with " + typeof(T).CSharpName());
  61. return (T)fieldInfo.GetValue(context);
  62. }
  63. public static T ReadFields<TContext, T>(TContext context, params string[] fieldNames) {
  64. for (int i = 0; i < fieldNames.Length; ++i) {
  65. string fieldName = fieldNames[i];
  66. var members = typeof(TContext).GetFlattenedMember(fieldName);
  67. if (members == null || members.Length == 0) continue;
  68. if (members.Length > 1) continue;
  69. var fieldInfo = members[0] as FieldInfo;
  70. if (fieldInfo == null) continue;
  71. if (fieldInfo.FieldType != typeof(T)) continue;
  72. return (T)fieldInfo.GetValue(context);
  73. }
  74. throw new ArgumentException("Unable to read any of the following fields " + string.Join(", ", fieldNames) + " on " + context);
  75. }
  76. /// <summary>
  77. /// Returns a list of object instances from types in the assembly that implement the given
  78. /// type. This only constructs objects which have default constructors.
  79. /// </summary>
  80. public static IEnumerable<TInterface> GetAssemblyInstances<TInterface>() {
  81. return from assembly in GetUserDefinedEditorAssemblies()
  82. from type in assembly.GetTypes()
  83. where type.Resolve().IsGenericTypeDefinition == false
  84. where type.Resolve().IsAbstract == false
  85. where type.Resolve().IsInterface == false
  86. where typeof(TInterface).IsAssignableFrom(type)
  87. where type.GetDeclaredConstructor(fsPortableReflection.EmptyTypes) != null
  88. select (TInterface)Activator.CreateInstance(type);
  89. }
  90. /// <summary>
  91. /// Returns all types that derive from UnityEngine.Object that are usable during runtime.
  92. /// </summary>
  93. public static IEnumerable<Type> GetUnityObjectTypes() {
  94. return from assembly in GetRuntimeAssemblies()
  95. // GetExportedTypes() doesn't work for dynamic modules, so we jut use GetTypes()
  96. // instead and manually filter for public
  97. from type in assembly.GetTypes()
  98. where type.Resolve().IsVisible
  99. // Cannot be an open generic type
  100. where type.Resolve().IsGenericTypeDefinition == false
  101. where typeof(UnityObject).IsAssignableFrom(type)
  102. select type;
  103. }
  104. /// <summary>
  105. /// Returns the equivalent of assembly.GetName().Name, which does not work on WebPlayer.
  106. /// </summary>
  107. private static string GetName(Assembly assembly) {
  108. int index = assembly.FullName.IndexOf(",");
  109. if (index >= 0) {
  110. return assembly.FullName.Substring(0, index);
  111. }
  112. return assembly.FullName;
  113. }
  114. /// <summary>
  115. /// Return a guess of all assemblies that can be used at runtime.
  116. /// </summary>
  117. public static IEnumerable<Assembly> GetRuntimeAssemblies() {
  118. #if !UNITY_EDITOR && UNITY_METRO
  119. yield return typeof(Assembly).GetTypeInfo().Assembly;
  120. #else
  121. if (_cachedRuntimeAssemblies == null) {
  122. _cachedRuntimeAssemblies =
  123. (from assembly in AppDomain.CurrentDomain.GetAssemblies()
  124. // Unity exposes lots of assemblies that won't contain any behaviors that will
  125. // contain a MonoBehaviour or UnityObject reference... so we ignore them to speed
  126. // up reflection processing
  127. where IsBannedAssembly(assembly) == false
  128. where IsUnityEditorAssembly(assembly) == false
  129. // In the editor, even Assembly-CSharp, etc contain a reference to UnityEditor,
  130. // so we can't strip the assembly that way
  131. where GetName(assembly).Contains("-Editor") == false
  132. select assembly).ToList();
  133. fiLog.Blank();
  134. foreach (var assembly in _cachedRuntimeAssemblies) {
  135. fiLog.Log(typeof(fiRuntimeReflectionUtility), "GetRuntimeAssemblies - " + GetName(assembly));
  136. }
  137. fiLog.Blank();
  138. }
  139. return _cachedRuntimeAssemblies;
  140. #endif
  141. }
  142. private static List<Assembly> _cachedRuntimeAssemblies;
  143. /// <summary>
  144. /// Returns a guess of all user-defined assemblies that are available in the editor, but not
  145. /// necessarily in the runtime. This is a superset over GetRuntimeAssemblies().
  146. /// </summary>
  147. public static IEnumerable<Assembly> GetUserDefinedEditorAssemblies() {
  148. #if !UNITY_EDITOR && UNITY_METRO
  149. yield return typeof(Assembly).GetTypeInfo().Assembly;
  150. #else
  151. if (_cachedUserDefinedEditorAssemblies == null) {
  152. _cachedUserDefinedEditorAssemblies =
  153. (from assembly in AppDomain.CurrentDomain.GetAssemblies()
  154. where IsBannedAssembly(assembly) == false
  155. where IsUnityEditorAssembly(assembly) == false
  156. // bugfix: This does not necessarily work because Assembly-CSharp will is not guaranteed
  157. // to have a reference to UnityEditor if there are no conditional references to
  158. // UnityEditor
  159. //where ContainsAssemblyReference(assembly, typeof(EditorWindow).Assembly)
  160. select assembly).ToList();
  161. fiLog.Blank();
  162. foreach (var assembly in _cachedUserDefinedEditorAssemblies) {
  163. fiLog.Log(typeof(fiRuntimeReflectionUtility), "GetUserDefinedEditorAssemblies - " + GetName(assembly));
  164. }
  165. fiLog.Blank();
  166. }
  167. return _cachedUserDefinedEditorAssemblies;
  168. #endif
  169. }
  170. private static List<Assembly> _cachedUserDefinedEditorAssemblies;
  171. /// <summary>
  172. /// Gets all possible editor assemblies, including those defined by Unity. This is a superset over
  173. /// GetUserDefinedEditorAssemblies().
  174. /// </summary>
  175. public static IEnumerable<Assembly> GetAllEditorAssemblies() {
  176. #if !UNITY_EDITOR && UNITY_METRO
  177. yield return typeof(Assembly).GetTypeInfo().Assembly;
  178. #else
  179. if (_cachedAllEditorAssembles == null) {
  180. _cachedAllEditorAssembles =
  181. (from assembly in AppDomain.CurrentDomain.GetAssemblies()
  182. where IsBannedAssembly(assembly) == false
  183. // bugfix: This does not necessarily work because Assembly-CSharp will is not guaranteed
  184. // to have a reference to UnityEditor if there are no conditional references to
  185. // UnityEditor
  186. //where ContainsAssemblyReference(assembly, typeof(EditorWindow).Assembly)
  187. select assembly).ToList();
  188. fiLog.Blank();
  189. foreach (var assembly in _cachedAllEditorAssembles) {
  190. fiLog.Log(typeof(fiRuntimeReflectionUtility), "GetAllEditorAssemblies - " + GetName(assembly));
  191. }
  192. fiLog.Blank();
  193. }
  194. return _cachedAllEditorAssembles;
  195. #endif
  196. }
  197. private static List<Assembly> _cachedAllEditorAssembles;
  198. private static bool IsUnityEditorAssembly(Assembly assembly) {
  199. var allowableScripts = new string[] {
  200. "UnityEditor",
  201. "UnityEditor.UI",
  202. };
  203. return allowableScripts.Contains(GetName(assembly));
  204. }
  205. /// <summary>
  206. /// Returns true if the given assembly is likely to contain user-scripts or it is a core
  207. /// runtime assembly (ie, UnityEngine).
  208. /// </summary>
  209. /// <param name="name">The unqualified name of the assembly.</param>
  210. private static bool IsBannedAssembly(Assembly assembly) {
  211. var bannedScripts = new string[] {
  212. "AssetStoreTools",
  213. "AssetStoreToolsExtra",
  214. "UnityScript",
  215. "UnityScript.Lang",
  216. "Boo.Lang.Parser",
  217. "Boo.Lang",
  218. "Boo.Lang.Compiler",
  219. "mscorlib",
  220. "System.ComponentModel.DataAnnotations",
  221. "System.Xml.Linq",
  222. "ICSharpCode.NRefactory",
  223. "Mono.Cecil",
  224. "Mono.Cecil.Mdb",
  225. "Unity.DataContract",
  226. "Unity.IvyParser",
  227. "Unity.Locator",
  228. "Unity.PackageManager",
  229. "Unity.SerializationLogic",
  230. //"UnityEngine",
  231. "UnityEngine.UI",
  232. "UnityEditor.Android.Extensions",
  233. "UnityEditor.BB10.Extensions",
  234. "UnityEditor.Metro.Extensions",
  235. "UnityEditor.WP8.Extensions",
  236. "UnityEditor.iOS.Extensions",
  237. "UnityEditor.iOS.Extensions.Xcode",
  238. "UnityEditor.WindowsStandalone.Extensions",
  239. "UnityEditor.LinuxStandalone.Extensions",
  240. "UnityEditor.OSXStandalone.Extensions",
  241. "UnityEditor.WebGL.Extensions",
  242. "UnityEditor.Graphs",
  243. "protobuf-net",
  244. "Newtonsoft.Json",
  245. "System",
  246. "System.Configuration",
  247. "System.Xml",
  248. "System.Core",
  249. "Mono.Security",
  250. "I18N",
  251. "I18N.West",
  252. "nunit.core",
  253. "nunit.core.interfaces",
  254. "nunit.framework",
  255. "NSubstitute",
  256. "UnityVS.VersionSpecific",
  257. "SyntaxTree.VisualStudio.Unity.Bridge",
  258. "SyntaxTree.VisualStudio.Unity.Messaging",
  259. };
  260. return bannedScripts.Contains(GetName(assembly));
  261. }
  262. /// <summary>
  263. /// Returns all types in the current AppDomain that derive from the given baseType and are a
  264. /// class that is not an open generic type.
  265. /// </summary>
  266. public static IEnumerable<Type> AllSimpleTypesDerivingFrom(Type baseType) {
  267. return from assembly in GetRuntimeAssemblies()
  268. from type in assembly.GetTypes()
  269. where baseType.IsAssignableFrom(type)
  270. where type.Resolve().IsClass
  271. where type.Resolve().IsGenericTypeDefinition == false
  272. select type;
  273. }
  274. /// <summary>
  275. /// Returns all types in the current AppDomain that derive from the given baseType, are classes,
  276. /// are not generic, have a default constuctor, and are not abstract.
  277. /// </summary>
  278. public static IEnumerable<Type> AllSimpleCreatableTypesDerivingFrom(Type baseType) {
  279. return from type in AllSimpleTypesDerivingFrom(baseType)
  280. where type.Resolve().IsAbstract == false
  281. where type.Resolve().IsGenericType == false
  282. where type.GetDeclaredConstructor(fsPortableReflection.EmptyTypes) != null
  283. select type;
  284. }
  285. }
  286. }