No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

GenericContext.cs 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. using System.Diagnostics;
  2. using Mono.Cecil;
  3. using Mono.Cecil.Rocks;
  4. namespace Burst.Compiler.IL.Syntax
  5. {
  6. /// <summary>
  7. /// A generic context contains a mapping between GenericParameter ({T}) and resolved TypeReference (int, float, MyStruct&lt;float&gt;)
  8. /// </summary>
  9. #if BURST_INTERNAL || BURST_COMPILER_SHARED
  10. public
  11. #else
  12. internal
  13. #endif
  14. readonly struct GenericContext
  15. {
  16. private readonly GenericInstanceType _typeContext;
  17. private readonly GenericInstanceMethod _methodContext;
  18. /// <summary>
  19. /// An empty <see cref="GenericContext"/>
  20. /// </summary>
  21. public static readonly GenericContext None = new GenericContext();
  22. /// <summary>
  23. /// Initializes a new instance of the <see cref="GenericContext"/> class.
  24. /// </summary>
  25. /// <param name="genericMethod">The generic method instance.</param>
  26. private GenericContext(GenericInstanceMethod genericMethod, GenericInstanceType genericType)
  27. {
  28. _methodContext = genericMethod;
  29. _typeContext = genericType;
  30. }
  31. /// <summary>
  32. /// Is there no generics in this context?
  33. /// </summary>
  34. /// <returns></returns>
  35. public bool IsEmpty()
  36. {
  37. return _typeContext == null && _methodContext == null;
  38. }
  39. /// <summary>
  40. /// Resolve the generics of the given <see cref="T:Mono.Cecil.MethodReference"/>
  41. /// </summary>
  42. /// <param name="unresolvedMethod"></param>
  43. /// <returns></returns>
  44. public MethodReference Resolve(MethodReference unresolvedMethod)
  45. {
  46. Debug.Assert(unresolvedMethod != null);
  47. // The following code was originally derived from IL2CPP.
  48. var resolvedMethod = unresolvedMethod;
  49. if (IsEmpty())
  50. {
  51. return resolvedMethod;
  52. }
  53. var declaringType = Resolve(unresolvedMethod.DeclaringType);
  54. if (unresolvedMethod is GenericInstanceMethod genericInstanceMethod)
  55. {
  56. resolvedMethod = new MethodReference(unresolvedMethod.Name, unresolvedMethod.ReturnType, declaringType);
  57. foreach (var p in unresolvedMethod.Parameters)
  58. {
  59. resolvedMethod.Parameters.Add(new ParameterDefinition(p.Name, p.Attributes, p.ParameterType));
  60. }
  61. foreach (var gp in genericInstanceMethod.ElementMethod.GenericParameters)
  62. {
  63. resolvedMethod.GenericParameters.Add(new GenericParameter(gp.Name, resolvedMethod));
  64. }
  65. resolvedMethod.HasThis = unresolvedMethod.HasThis;
  66. var m = new GenericInstanceMethod(resolvedMethod);
  67. foreach (var ga in genericInstanceMethod.GenericArguments)
  68. {
  69. m.GenericArguments.Add(Resolve(ga));
  70. }
  71. resolvedMethod = m;
  72. }
  73. else
  74. {
  75. if (unresolvedMethod.HasGenericParameters)
  76. {
  77. var newGenericInstanceMethod = new GenericInstanceMethod(unresolvedMethod);
  78. foreach (var gp in unresolvedMethod.GenericParameters)
  79. {
  80. newGenericInstanceMethod.GenericArguments.Add(Resolve(gp));
  81. }
  82. resolvedMethod = newGenericInstanceMethod;
  83. }
  84. else
  85. {
  86. resolvedMethod = new MethodReference(unresolvedMethod.Name, unresolvedMethod.ReturnType, declaringType);
  87. foreach (var p in unresolvedMethod.Parameters)
  88. {
  89. resolvedMethod.Parameters.Add(new ParameterDefinition(p.Name, p.Attributes, p.ParameterType));
  90. }
  91. resolvedMethod.HasThis = unresolvedMethod.HasThis;
  92. resolvedMethod.MetadataToken = unresolvedMethod.MetadataToken;
  93. }
  94. }
  95. return resolvedMethod;
  96. }
  97. /// <summary>
  98. /// Expands the specified <see cref="T:Mono.Cecil.TypeReference"/> if it is either a <see cref="T:Mono.Cecil.GenericParameter"/> or a partially expanded <see cref="T:Mono.Cecil.GenericInstanceType"/>
  99. /// </summary>
  100. /// <param name="typeReference">The type reference.</param>
  101. /// <returns>TypeReference.</returns>
  102. public TypeReference Resolve(TypeReference typeReference)
  103. {
  104. Debug.Assert(typeReference != null);
  105. if (IsEmpty())
  106. {
  107. return typeReference;
  108. }
  109. switch (typeReference)
  110. {
  111. case GenericParameter genericParam:
  112. Debug.Assert(genericParam.Owner != null);
  113. if (genericParam.Owner.GenericParameterType == GenericParameterType.Type)
  114. {
  115. Debug.Assert(_typeContext != null);
  116. return _typeContext.GenericArguments[genericParam.Position];
  117. }
  118. else
  119. {
  120. Debug.Assert(_methodContext != null);
  121. return _methodContext.GenericArguments[genericParam.Position];
  122. }
  123. case ArrayType arrayType:
  124. return new ArrayType(Resolve(arrayType.ElementType), arrayType.Rank);
  125. case PointerType pointerType:
  126. return Resolve(pointerType.ElementType).MakePointerType();
  127. case PinnedType pinnedType:
  128. return Resolve(pinnedType.ElementType).MakePointerType();
  129. case ByReferenceType byRefType:
  130. return Resolve(byRefType.ElementType).MakeByReferenceType();
  131. case RequiredModifierType requiredModType:
  132. return new RequiredModifierType(requiredModType.ModifierType, Resolve(requiredModType.ElementType));
  133. case OptionalModifierType optionalModType:
  134. return Resolve(optionalModType.ElementType);
  135. }
  136. if (ContainsGenericParameters(typeReference))
  137. {
  138. if (typeReference is GenericInstanceType partialGenericInstance)
  139. {
  140. // TODO: Ideally, we should cache this GenericInstanceType once it has been resolved
  141. var genericInstance = new GenericInstanceType(partialGenericInstance.ElementType);
  142. foreach (var genericArgument in partialGenericInstance.GenericArguments)
  143. {
  144. genericInstance.GenericArguments.Add(Resolve(genericArgument));
  145. }
  146. return genericInstance;
  147. }
  148. else
  149. {
  150. // Sometimes we can have a TypeDefinition with HasGenericParameters false, but GenericParameters.Count > 0
  151. var typeDefinition = typeReference as TypeDefinition;
  152. if (typeDefinition?.GenericParameters.Count > 0)
  153. {
  154. var genericInstance = new GenericInstanceType(typeDefinition);
  155. foreach (var genericArgument in typeDefinition.GenericParameters)
  156. {
  157. genericInstance.GenericArguments.Add(Resolve(genericArgument));
  158. }
  159. return genericInstance;
  160. }
  161. }
  162. }
  163. return typeReference;
  164. }
  165. /// <summary>
  166. /// If the given type is a reference or pointer type, the underlying type is returned
  167. /// </summary>
  168. /// <param name="typeReference"></param>
  169. /// <returns></returns>
  170. public static TypeReference GetTypeReferenceForPointerOrReference(TypeReference typeReference)
  171. {
  172. while (true)
  173. {
  174. switch (typeReference)
  175. {
  176. case PointerType pointerType:
  177. typeReference = pointerType.ElementType;
  178. break;
  179. case ByReferenceType byRefType:
  180. typeReference = byRefType.ElementType;
  181. break;
  182. default:
  183. return typeReference;
  184. }
  185. }
  186. }
  187. /// <summary>
  188. /// Create <see cref="GenericContext"/> from a <see cref="T:Mono.Cecil.TypeReference"/>
  189. /// </summary>
  190. /// <param name="typeReference"></param>
  191. /// <returns></returns>
  192. public static GenericContext From(TypeReference typeReference)
  193. {
  194. Debug.Assert(typeReference != null);
  195. if (typeReference is PinnedType pinnedType)
  196. {
  197. typeReference = pinnedType.ElementType;
  198. }
  199. typeReference = GetTypeReferenceForPointerOrReference(typeReference);
  200. if (typeReference is ArrayType arrayType)
  201. {
  202. typeReference = arrayType.ElementType;
  203. }
  204. return new GenericContext(null, typeReference as GenericInstanceType);
  205. }
  206. /// <summary>
  207. /// Create <see cref="GenericContext"/> from a <see cref="T:Mono.Cecil.MethodReference"/> and a <see cref="T:Mono.Cecil.TypeReference"/>
  208. /// </summary>
  209. /// <param name="methodReference"></param>
  210. /// <param name="typeReference"></param>
  211. /// <returns></returns>
  212. public static GenericContext From(MethodReference methodReference, TypeReference typeReference)
  213. {
  214. Debug.Assert(methodReference != null);
  215. Debug.Assert(typeReference != null);
  216. typeReference = GetTypeReferenceForPointerOrReference(typeReference);
  217. return new GenericContext(methodReference as GenericInstanceMethod, typeReference as GenericInstanceType);
  218. }
  219. /// <summary>
  220. /// Checks if the specified TypeReference contains generic parameters that need type expansion
  221. /// </summary>
  222. /// <param name="typeReference">The type reference.</param>
  223. /// <returns><c>true</c> if the specified TypeReference contains generic arguments that need type expansion, <c>false</c> otherwise.</returns>
  224. public static bool ContainsGenericParameters(TypeReference typeReference)
  225. {
  226. switch (typeReference)
  227. {
  228. case GenericParameter genericParam:
  229. return true;
  230. case ArrayType arrayType:
  231. return ContainsGenericParameters(arrayType.ElementType);
  232. case PointerType pointerType:
  233. return ContainsGenericParameters(pointerType.ElementType);
  234. case PinnedType pinnedType:
  235. return ContainsGenericParameters(pinnedType.ElementType);
  236. case ByReferenceType byRefType:
  237. return ContainsGenericParameters(byRefType.ElementType);
  238. case RequiredModifierType requiredModType:
  239. return ContainsGenericParameters(requiredModType.ModifierType);
  240. case OptionalModifierType optionalModType:
  241. return ContainsGenericParameters(optionalModType.ElementType);
  242. case GenericInstanceType partialGenericInstance:
  243. {
  244. foreach (var genericArgument in partialGenericInstance.GenericArguments)
  245. {
  246. if (ContainsGenericParameters(genericArgument))
  247. {
  248. return true;
  249. }
  250. }
  251. break;
  252. }
  253. case TypeDefinition typeDefinition:
  254. {
  255. // Sometimes we can have a TypeDefinition with HasGenericParameters false, but GenericParameters.Count > 0
  256. return typeDefinition.GenericParameters.Count > 0;
  257. }
  258. }
  259. return false;
  260. }
  261. }
  262. }