A game about forced loneliness, made by TACStudios
at master 301 lines 12 kB view raw
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&lt;float&gt;) 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}