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