AccessTools.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Reflection;
  6. using System.Reflection.Emit;
  7. using System.Runtime.Serialization;
  8. namespace Harmony
  9. {
  10. public static class AccessTools
  11. {
  12. public static BindingFlags all = BindingFlags.Public
  13. | BindingFlags.NonPublic
  14. | BindingFlags.Instance
  15. | BindingFlags.Static
  16. | BindingFlags.GetField
  17. | BindingFlags.SetField
  18. | BindingFlags.GetProperty
  19. | BindingFlags.SetProperty;
  20. public static Type TypeByName(string name)
  21. {
  22. var type = Type.GetType(name, false);
  23. if (type == null)
  24. type = AppDomain.CurrentDomain.GetAssemblies()
  25. .SelectMany(x => x.GetTypes())
  26. .FirstOrDefault(x => x.FullName == name);
  27. if (type == null)
  28. type = AppDomain.CurrentDomain.GetAssemblies()
  29. .SelectMany(x => x.GetTypes())
  30. .FirstOrDefault(x => x.Name == name);
  31. return type;
  32. }
  33. public static T FindIncludingBaseTypes<T>(Type type, Func<Type, T> action)
  34. {
  35. while (true)
  36. {
  37. var result = action(type);
  38. if (result != null) return result;
  39. if (type == typeof(object)) return default(T);
  40. type = type.BaseType;
  41. }
  42. }
  43. public static T FindIncludingInnerTypes<T>(Type type, Func<Type, T> action)
  44. {
  45. var result = action(type);
  46. if (result != null) return result;
  47. foreach (var subType in type.GetNestedTypes(all))
  48. {
  49. result = FindIncludingInnerTypes(subType, action);
  50. if (result != null)
  51. break;
  52. }
  53. return result;
  54. }
  55. public static FieldInfo Field(Type type, string name)
  56. {
  57. if (type == null || name == null) return null;
  58. return FindIncludingBaseTypes(type, t => t.GetField(name, all));
  59. }
  60. public static FieldInfo Field(Type type, int idx)
  61. {
  62. return GetDeclaredFields(type).ElementAtOrDefault(idx);
  63. }
  64. public static PropertyInfo DeclaredProperty(Type type, string name)
  65. {
  66. if (type == null || name == null) return null;
  67. return type.GetProperty(name, all);
  68. }
  69. public static PropertyInfo Property(Type type, string name)
  70. {
  71. if (type == null || name == null) return null;
  72. return FindIncludingBaseTypes(type, t => t.GetProperty(name, all));
  73. }
  74. public static MethodInfo DeclaredMethod(Type type, string name, Type[] parameters = null, Type[] generics = null)
  75. {
  76. if (type == null || name == null) return null;
  77. MethodInfo result;
  78. var modifiers = new ParameterModifier[] { };
  79. if (parameters == null)
  80. result = type.GetMethod(name, all);
  81. else
  82. result = type.GetMethod(name, all, null, parameters, modifiers);
  83. if (result == null) return null;
  84. if (generics != null) result = result.MakeGenericMethod(generics);
  85. return result;
  86. }
  87. public static MethodInfo Method(Type type, string name, Type[] parameters = null, Type[] generics = null)
  88. {
  89. if (type == null || name == null) return null;
  90. MethodInfo result;
  91. var modifiers = new ParameterModifier[] { };
  92. if (parameters == null)
  93. {
  94. try
  95. {
  96. result = FindIncludingBaseTypes(type, t => t.GetMethod(name, all));
  97. }
  98. catch (AmbiguousMatchException)
  99. {
  100. result = FindIncludingBaseTypes(type, t => t.GetMethod(name, all, null, new Type[0], modifiers));
  101. }
  102. }
  103. else
  104. {
  105. result = FindIncludingBaseTypes(type, t => t.GetMethod(name, all, null, parameters, modifiers));
  106. }
  107. if (result == null) return null;
  108. if (generics != null) result = result.MakeGenericMethod(generics);
  109. return result;
  110. }
  111. public static MethodInfo Method(string typeColonMethodname, Type[] parameters = null, Type[] generics = null)
  112. {
  113. if (typeColonMethodname == null) return null;
  114. var parts = typeColonMethodname.Split(':');
  115. if (parts.Length != 2)
  116. throw new ArgumentException("Method must be specified as 'Namespace.Type1.Type2:MethodName", nameof(typeColonMethodname));
  117. var type = TypeByName(parts[0]);
  118. return Method(type, parts[1], parameters, generics);
  119. }
  120. public static List<string> GetMethodNames(Type type)
  121. {
  122. if (type == null) return new List<string>();
  123. return type.GetMethods(all).Select(m => m.Name).ToList();
  124. }
  125. public static List<string> GetMethodNames(object instance)
  126. {
  127. if (instance == null) return new List<string>();
  128. return GetMethodNames(instance.GetType());
  129. }
  130. public static ConstructorInfo DeclaredConstructor(Type type, Type[] parameters = null)
  131. {
  132. if (type == null) return null;
  133. if (parameters == null) parameters = new Type[0];
  134. return type.GetConstructor(all, null, parameters, new ParameterModifier[] { });
  135. }
  136. public static ConstructorInfo Constructor(Type type, Type[] parameters = null)
  137. {
  138. if (type == null) return null;
  139. if (parameters == null) parameters = new Type[0];
  140. return FindIncludingBaseTypes(type, t => t.GetConstructor(all, null, parameters, new ParameterModifier[] { }));
  141. }
  142. public static List<ConstructorInfo> GetDeclaredConstructors(Type type)
  143. {
  144. return type.GetConstructors(all).Where(method => method.DeclaringType == type).ToList();
  145. }
  146. public static List<MethodInfo> GetDeclaredMethods(Type type)
  147. {
  148. return type.GetMethods(all).Where(method => method.DeclaringType == type).ToList();
  149. }
  150. public static List<PropertyInfo> GetDeclaredProperties(Type type)
  151. {
  152. return type.GetProperties(all).Where(property => property.DeclaringType == type).ToList();
  153. }
  154. public static List<FieldInfo> GetDeclaredFields(Type type)
  155. {
  156. return type.GetFields(all).Where(field => field.DeclaringType == type).ToList();
  157. }
  158. public static Type GetReturnedType(MethodBase method)
  159. {
  160. var constructor = method as ConstructorInfo;
  161. if (constructor != null) return typeof(void);
  162. return ((MethodInfo)method).ReturnType;
  163. }
  164. public static Type Inner(Type type, string name)
  165. {
  166. if (type == null || name == null) return null;
  167. return FindIncludingBaseTypes(type, t => t.GetNestedType(name, all));
  168. }
  169. public static Type FirstInner(Type type, Func<Type, bool> predicate)
  170. {
  171. if (type == null || predicate == null) return null;
  172. return type.GetNestedTypes(all).FirstOrDefault(subType => predicate(subType));
  173. }
  174. public static MethodInfo FirstMethod(Type type, Func<MethodInfo, bool> predicate)
  175. {
  176. if (type == null || predicate == null) return null;
  177. return type.GetMethods(all).FirstOrDefault(method => predicate(method));
  178. }
  179. public static ConstructorInfo FirstConstructor(Type type, Func<ConstructorInfo, bool> predicate)
  180. {
  181. if (type == null || predicate == null) return null;
  182. return type.GetConstructors(all).FirstOrDefault(constructor => predicate(constructor));
  183. }
  184. public static PropertyInfo FirstProperty(Type type, Func<PropertyInfo, bool> predicate)
  185. {
  186. if (type == null || predicate == null) return null;
  187. return type.GetProperties(all).FirstOrDefault(property => predicate(property));
  188. }
  189. public static Type[] GetTypes(object[] parameters)
  190. {
  191. if (parameters == null) return new Type[0];
  192. return parameters.Select(p => p == null ? typeof(object) : p.GetType()).ToArray();
  193. }
  194. public static List<string> GetFieldNames(Type type)
  195. {
  196. if (type == null) return new List<string>();
  197. return type.GetFields(all).Select(f => f.Name).ToList();
  198. }
  199. public static List<string> GetFieldNames(object instance)
  200. {
  201. if (instance == null) return new List<string>();
  202. return GetFieldNames(instance.GetType());
  203. }
  204. public static List<string> GetPropertyNames(Type type)
  205. {
  206. if (type == null) return new List<string>();
  207. return type.GetProperties(all).Select(f => f.Name).ToList();
  208. }
  209. public static List<string> GetPropertyNames(object instance)
  210. {
  211. if (instance == null) return new List<string>();
  212. return GetPropertyNames(instance.GetType());
  213. }
  214. public delegate ref U FieldRef<T, U>(T obj);
  215. public static FieldRef<T, U> FieldRefAccess<T, U>(string fieldName)
  216. {
  217. const BindingFlags bf = BindingFlags.NonPublic |
  218. BindingFlags.Instance |
  219. BindingFlags.DeclaredOnly;
  220. var fi = typeof(T).GetField(fieldName, bf);
  221. if (fi == null)
  222. throw new MissingFieldException(typeof(T).Name, fieldName);
  223. var s_name = "__refget_" + typeof(T).Name + "_fi_" + fi.Name;
  224. // workaround for using ref-return with DynamicMethod:
  225. // a.) initialize with dummy return value
  226. var dm = new DynamicMethod(s_name, typeof(U), new[] { typeof(T) }, typeof(T), true);
  227. // b.) replace with desired 'ByRef' return value
  228. var trv = Traverse.Create(dm);
  229. trv.Field("returnType").SetValue(typeof(U).MakeByRefType());
  230. trv.Field("m_returnType").SetValue(typeof(U).MakeByRefType());
  231. var il = dm.GetILGenerator();
  232. il.Emit(OpCodes.Ldarg_0);
  233. il.Emit(OpCodes.Ldflda, fi);
  234. il.Emit(OpCodes.Ret);
  235. return (FieldRef<T, U>)dm.CreateDelegate(typeof(FieldRef<T, U>));
  236. }
  237. public static ref U FieldRefAccess<T, U>(T instance, string fieldName)
  238. {
  239. return ref FieldRefAccess<T, U>(fieldName)(instance);
  240. }
  241. public static void ThrowMissingMemberException(Type type, params string[] names)
  242. {
  243. var fields = string.Join(",", GetFieldNames(type).ToArray());
  244. var properties = string.Join(",", GetPropertyNames(type).ToArray());
  245. throw new MissingMemberException(string.Join(",", names) + "; available fields: " + fields + "; available properties: " + properties);
  246. }
  247. public static object GetDefaultValue(Type type)
  248. {
  249. if (type == null) return null;
  250. if (type == typeof(void)) return null;
  251. if (type.IsValueType)
  252. return Activator.CreateInstance(type);
  253. return null;
  254. }
  255. public static object CreateInstance(Type type)
  256. {
  257. if (type == null)
  258. throw new NullReferenceException("Cannot create instance for NULL type");
  259. var ctor = type.GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, CallingConventions.Any, new Type[0], null);
  260. if (ctor != null)
  261. return Activator.CreateInstance(type);
  262. return FormatterServices.GetUninitializedObject(type);
  263. }
  264. public static object MakeDeepCopy(object source, Type resultType, Func<string, Traverse, Traverse, object> processor = null, string pathRoot = "")
  265. {
  266. if (source == null)
  267. return null;
  268. var type = source.GetType();
  269. if (type.IsPrimitive)
  270. return source;
  271. if (type.IsEnum)
  272. return Enum.ToObject(resultType, (int)source);
  273. if (type.IsGenericType && resultType.IsGenericType)
  274. {
  275. var addOperation = FirstMethod(resultType, m => m.Name == "Add" && m.GetParameters().Count() == 1);
  276. if (addOperation != null)
  277. {
  278. var addableResult = Activator.CreateInstance(resultType);
  279. var addInvoker = MethodInvoker.GetHandler(addOperation);
  280. var newElementType = resultType.GetGenericArguments()[0];
  281. var i = 0;
  282. foreach (var element in source as IEnumerable)
  283. {
  284. var iStr = (i++).ToString();
  285. var path = pathRoot.Length > 0 ? pathRoot + "." + iStr : iStr;
  286. var newElement = MakeDeepCopy(element, newElementType, processor, path);
  287. addInvoker(addableResult, new object[] { newElement });
  288. }
  289. return addableResult;
  290. }
  291. // TODO: add dictionaries support
  292. // maybe use methods in Dictionary<KeyValuePair<TKey,TVal>>
  293. }
  294. if (type.IsArray && resultType.IsArray)
  295. {
  296. var elementType = resultType.GetElementType();
  297. var length = ((Array)source).Length;
  298. var arrayResult = Activator.CreateInstance(resultType, new object[] { length }) as object[];
  299. var originalArray = source as object[];
  300. for (var i = 0; i < length; i++)
  301. {
  302. var iStr = i.ToString();
  303. var path = pathRoot.Length > 0 ? pathRoot + "." + iStr : iStr;
  304. arrayResult[i] = MakeDeepCopy(originalArray[i], elementType, processor, path);
  305. }
  306. return arrayResult;
  307. }
  308. var ns = type.Namespace;
  309. if (ns == "System" || (ns?.StartsWith("System.") ?? false))
  310. return source;
  311. var result = CreateInstance(resultType);
  312. Traverse.IterateFields(source, result, (name, src, dst) =>
  313. {
  314. var path = pathRoot.Length > 0 ? pathRoot + "." + name : name;
  315. var value = processor != null ? processor(path, src, dst) : src.GetValue();
  316. dst.SetValue(MakeDeepCopy(value, dst.GetValueType(), processor, path));
  317. });
  318. return result;
  319. }
  320. public static void MakeDeepCopy<T>(object source, out T result, Func<string, Traverse, Traverse, object> processor = null, string pathRoot = "")
  321. {
  322. result = (T)MakeDeepCopy(source, typeof(T), processor, pathRoot);
  323. }
  324. public static bool IsStruct(Type type)
  325. {
  326. return type.IsValueType && !IsValue(type) && !IsVoid(type);
  327. }
  328. public static bool IsClass(Type type)
  329. {
  330. return !type.IsValueType;
  331. }
  332. public static bool IsValue(Type type)
  333. {
  334. return type.IsPrimitive || type.IsEnum;
  335. }
  336. public static bool IsVoid(Type type)
  337. {
  338. return type == typeof(void);
  339. }
  340. }
  341. }