Extensions.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using Mono.CecilX;
  5. namespace Mirror.Weaver
  6. {
  7. public static class Extensions
  8. {
  9. public static bool Is(this TypeReference td, Type type) =>
  10. type.IsGenericType
  11. ? td.GetElementType().FullName == type.FullName
  12. : td.FullName == type.FullName;
  13. public static bool Is<T>(this TypeReference td) => Is(td, typeof(T));
  14. public static bool IsDerivedFrom<T>(this TypeReference tr) => IsDerivedFrom(tr, typeof(T));
  15. public static bool IsDerivedFrom(this TypeReference tr, Type baseClass)
  16. {
  17. TypeDefinition td = tr.Resolve();
  18. if (!td.IsClass)
  19. return false;
  20. // are ANY parent classes of baseClass?
  21. TypeReference parent = td.BaseType;
  22. if (parent == null)
  23. return false;
  24. if (parent.Is(baseClass))
  25. return true;
  26. if (parent.CanBeResolved())
  27. return IsDerivedFrom(parent.Resolve(), baseClass);
  28. return false;
  29. }
  30. public static TypeReference GetEnumUnderlyingType(this TypeDefinition td)
  31. {
  32. foreach (FieldDefinition field in td.Fields)
  33. {
  34. if (!field.IsStatic)
  35. return field.FieldType;
  36. }
  37. throw new ArgumentException($"Invalid enum {td.FullName}");
  38. }
  39. public static bool ImplementsInterface<TInterface>(this TypeDefinition td)
  40. {
  41. TypeDefinition typedef = td;
  42. while (typedef != null)
  43. {
  44. if (typedef.Interfaces.Any(iface => iface.InterfaceType.Is<TInterface>()))
  45. return true;
  46. try
  47. {
  48. TypeReference parent = typedef.BaseType;
  49. typedef = parent?.Resolve();
  50. }
  51. catch (AssemblyResolutionException)
  52. {
  53. // this can happen for plugins.
  54. //Console.WriteLine("AssemblyResolutionException: "+ ex.ToString());
  55. break;
  56. }
  57. }
  58. return false;
  59. }
  60. public static bool IsMultidimensionalArray(this TypeReference tr) =>
  61. tr is ArrayType arrayType && arrayType.Rank > 1;
  62. // Does type use netId as backing field
  63. public static bool IsNetworkIdentityField(this TypeReference tr) =>
  64. tr.Is<UnityEngine.GameObject>() ||
  65. tr.Is<NetworkIdentity>() ||
  66. tr.IsDerivedFrom<NetworkBehaviour>();
  67. public static bool CanBeResolved(this TypeReference parent)
  68. {
  69. while (parent != null)
  70. {
  71. if (parent.Scope.Name == "Windows")
  72. {
  73. return false;
  74. }
  75. if (parent.Scope.Name == "mscorlib")
  76. {
  77. TypeDefinition resolved = parent.Resolve();
  78. return resolved != null;
  79. }
  80. try
  81. {
  82. parent = parent.Resolve().BaseType;
  83. }
  84. catch
  85. {
  86. return false;
  87. }
  88. }
  89. return true;
  90. }
  91. // Makes T => Variable and imports function
  92. public static MethodReference MakeGeneric(this MethodReference generic, ModuleDefinition module, TypeReference variableReference)
  93. {
  94. GenericInstanceMethod instance = new GenericInstanceMethod(generic);
  95. instance.GenericArguments.Add(variableReference);
  96. MethodReference readFunc = module.ImportReference(instance);
  97. return readFunc;
  98. }
  99. // Given a method of a generic class such as ArraySegment`T.get_Count,
  100. // and a generic instance such as ArraySegment`int
  101. // Creates a reference to the specialized method ArraySegment`int`.get_Count
  102. // Note that calling ArraySegment`T.get_Count directly gives an invalid IL error
  103. public static MethodReference MakeHostInstanceGeneric(this MethodReference self, ModuleDefinition module, GenericInstanceType instanceType)
  104. {
  105. MethodReference reference = new MethodReference(self.Name, self.ReturnType, instanceType)
  106. {
  107. CallingConvention = self.CallingConvention,
  108. HasThis = self.HasThis,
  109. ExplicitThis = self.ExplicitThis
  110. };
  111. foreach (ParameterDefinition parameter in self.Parameters)
  112. reference.Parameters.Add(new ParameterDefinition(parameter.ParameterType));
  113. foreach (GenericParameter generic_parameter in self.GenericParameters)
  114. reference.GenericParameters.Add(new GenericParameter(generic_parameter.Name, reference));
  115. return module.ImportReference(reference);
  116. }
  117. // needed for NetworkBehaviour<T> support
  118. // https://github.com/vis2k/Mirror/pull/3073/
  119. public static FieldReference MakeHostInstanceGeneric(this FieldReference self)
  120. {
  121. var declaringType = new GenericInstanceType(self.DeclaringType);
  122. foreach (var parameter in self.DeclaringType.GenericParameters)
  123. {
  124. declaringType.GenericArguments.Add(parameter);
  125. }
  126. return new FieldReference(self.Name, self.FieldType, declaringType);
  127. }
  128. // Given a field of a generic class such as Writer<T>.write,
  129. // and a generic instance such as ArraySegment`int
  130. // Creates a reference to the specialized method ArraySegment`int`.get_Count
  131. // Note that calling ArraySegment`T.get_Count directly gives an invalid IL error
  132. public static FieldReference SpecializeField(this FieldReference self, ModuleDefinition module, GenericInstanceType instanceType)
  133. {
  134. FieldReference reference = new FieldReference(self.Name, self.FieldType, instanceType);
  135. return module.ImportReference(reference);
  136. }
  137. public static CustomAttribute GetCustomAttribute<TAttribute>(this ICustomAttributeProvider method)
  138. {
  139. return method.CustomAttributes.FirstOrDefault(ca => ca.AttributeType.Is<TAttribute>());
  140. }
  141. public static bool HasCustomAttribute<TAttribute>(this ICustomAttributeProvider attributeProvider)
  142. {
  143. return attributeProvider.CustomAttributes.Any(attr => attr.AttributeType.Is<TAttribute>());
  144. }
  145. public static T GetField<T>(this CustomAttribute ca, string field, T defaultValue)
  146. {
  147. foreach (CustomAttributeNamedArgument customField in ca.Fields)
  148. if (customField.Name == field)
  149. return (T)customField.Argument.Value;
  150. return defaultValue;
  151. }
  152. public static MethodDefinition GetMethod(this TypeDefinition td, string methodName)
  153. {
  154. return td.Methods.FirstOrDefault(method => method.Name == methodName);
  155. }
  156. public static List<MethodDefinition> GetMethods(this TypeDefinition td, string methodName)
  157. {
  158. return td.Methods.Where(method => method.Name == methodName).ToList();
  159. }
  160. public static MethodDefinition GetMethodInBaseType(this TypeDefinition td, string methodName)
  161. {
  162. TypeDefinition typedef = td;
  163. while (typedef != null)
  164. {
  165. foreach (MethodDefinition md in typedef.Methods)
  166. {
  167. if (md.Name == methodName)
  168. return md;
  169. }
  170. try
  171. {
  172. TypeReference parent = typedef.BaseType;
  173. typedef = parent?.Resolve();
  174. }
  175. catch (AssemblyResolutionException)
  176. {
  177. // this can happen for plugins.
  178. break;
  179. }
  180. }
  181. return null;
  182. }
  183. // Finds public fields in type and base type
  184. public static IEnumerable<FieldDefinition> FindAllPublicFields(this TypeReference variable)
  185. {
  186. return FindAllPublicFields(variable.Resolve());
  187. }
  188. // Finds public fields in type and base type
  189. public static IEnumerable<FieldDefinition> FindAllPublicFields(this TypeDefinition typeDefinition)
  190. {
  191. while (typeDefinition != null)
  192. {
  193. foreach (FieldDefinition field in typeDefinition.Fields)
  194. {
  195. if (field.IsStatic || field.IsPrivate)
  196. continue;
  197. if (field.IsNotSerialized)
  198. continue;
  199. yield return field;
  200. }
  201. try
  202. {
  203. typeDefinition = typeDefinition.BaseType?.Resolve();
  204. }
  205. catch (AssemblyResolutionException)
  206. {
  207. break;
  208. }
  209. }
  210. }
  211. public static bool ContainsClass(this ModuleDefinition module, string nameSpace, string className) =>
  212. module.GetTypes().Any(td => td.Namespace == nameSpace &&
  213. td.Name == className);
  214. public static AssemblyNameReference FindReference(this ModuleDefinition module, string referenceName)
  215. {
  216. foreach (AssemblyNameReference reference in module.AssemblyReferences)
  217. {
  218. if (reference.Name == referenceName)
  219. return reference;
  220. }
  221. return null;
  222. }
  223. // Takes generic arguments from child class and applies them to parent reference, if possible
  224. // eg makes `Base<T>` in Child<int> : Base<int> have `int` instead of `T`
  225. // Originally by James-Frowen under MIT
  226. // https://github.com/MirageNet/Mirage/commit/cf91e1d54796866d2cf87f8e919bb5c681977e45
  227. public static TypeReference ApplyGenericParameters(this TypeReference parentReference,
  228. TypeReference childReference)
  229. {
  230. // If the parent is not generic, we got nothing to apply
  231. if (!parentReference.IsGenericInstance)
  232. return parentReference;
  233. GenericInstanceType parentGeneric = (GenericInstanceType)parentReference;
  234. // make new type so we can replace the args on it
  235. // resolve it so we have non-generic instance (eg just instance with <T> instead of <int>)
  236. // if we don't cecil will make it double generic (eg INVALID IL)
  237. GenericInstanceType generic = new GenericInstanceType(parentReference.Resolve());
  238. foreach (TypeReference arg in parentGeneric.GenericArguments)
  239. generic.GenericArguments.Add(arg);
  240. for (int i = 0; i < generic.GenericArguments.Count; i++)
  241. {
  242. // if arg is not generic
  243. // eg List<int> would be int so not generic.
  244. // But List<T> would be T so is generic
  245. if (!generic.GenericArguments[i].IsGenericParameter)
  246. continue;
  247. // get the generic name, eg T
  248. string name = generic.GenericArguments[i].Name;
  249. // find what type T is, eg turn it into `int` if `List<int>`
  250. TypeReference arg = FindMatchingGenericArgument(childReference, name);
  251. // import just to be safe
  252. TypeReference imported = parentReference.Module.ImportReference(arg);
  253. // set arg on generic, parent ref will be Base<int> instead of just Base<T>
  254. generic.GenericArguments[i] = imported;
  255. }
  256. return generic;
  257. }
  258. // Finds the type reference for a generic parameter with the provided name in the child reference
  259. // Originally by James-Frowen under MIT
  260. // https://github.com/MirageNet/Mirage/commit/cf91e1d54796866d2cf87f8e919bb5c681977e45
  261. static TypeReference FindMatchingGenericArgument(TypeReference childReference, string paramName)
  262. {
  263. TypeDefinition def = childReference.Resolve();
  264. // child class must be generic if we are in this part of the code
  265. // eg Child<T> : Base<T> <--- child must have generic if Base has T
  266. // vs Child : Base<int> <--- wont be here if Base has int (we check if T exists before calling this)
  267. if (!def.HasGenericParameters)
  268. throw new InvalidOperationException(
  269. "Base class had generic parameters, but could not find them in child class");
  270. // go through parameters in child class, and find the generic that matches the name
  271. for (int i = 0; i < def.GenericParameters.Count; i++)
  272. {
  273. GenericParameter param = def.GenericParameters[i];
  274. if (param.Name == paramName)
  275. {
  276. GenericInstanceType generic = (GenericInstanceType)childReference;
  277. // return generic arg with same index
  278. return generic.GenericArguments[i];
  279. }
  280. }
  281. // this should never happen, if it does it means that this code is bugged
  282. throw new InvalidOperationException("Did not find matching generic");
  283. }
  284. }
  285. }