1 // 2 // TypeParser.cs 3 // 4 // Author: 5 // Jb Evain (jbevain@gmail.com) 6 // 7 // Copyright (c) 2008 - 2011 Jb Evain 8 // 9 // Permission is hereby granted, free of charge, to any person obtaining 10 // a copy of this software and associated documentation files (the 11 // "Software"), to deal in the Software without restriction, including 12 // without limitation the rights to use, copy, modify, merge, publish, 13 // distribute, sublicense, and/or sell copies of the Software, and to 14 // permit persons to whom the Software is furnished to do so, subject to 15 // the following conditions: 16 // 17 // The above copyright notice and this permission notice shall be 18 // included in all copies or substantial portions of the Software. 19 // 20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 27 // 28 29 using System; 30 using System.Text; 31 32 using Mono.Cecil.Metadata; 33 34 namespace Mono.Cecil { 35 36 class TypeParser { 37 38 class Type { 39 public const int Ptr = -1; 40 public const int ByRef = -2; 41 public const int SzArray = -3; 42 43 public string type_fullname; 44 public string [] nested_names; 45 public int arity; 46 public int [] specs; 47 public Type [] generic_arguments; 48 public string assembly; 49 } 50 51 readonly string fullname; 52 readonly int length; 53 54 int position; 55 TypeParser(string fullname)56 TypeParser (string fullname) 57 { 58 this.fullname = fullname; 59 this.length = fullname.Length; 60 } 61 ParseType(bool fq_name)62 Type ParseType (bool fq_name) 63 { 64 var type = new Type (); 65 type.type_fullname = ParsePart (); 66 67 type.nested_names = ParseNestedNames (); 68 69 if (TryGetArity (type)) 70 type.generic_arguments = ParseGenericArguments (type.arity); 71 72 type.specs = ParseSpecs (); 73 74 if (fq_name) 75 type.assembly = ParseAssemblyName (); 76 77 return type; 78 } 79 TryGetArity(Type type)80 static bool TryGetArity (Type type) 81 { 82 int arity = 0; 83 84 TryAddArity (type.type_fullname, ref arity); 85 86 var nested_names = type.nested_names; 87 if (!nested_names.IsNullOrEmpty ()) { 88 for (int i = 0; i < nested_names.Length; i++) 89 TryAddArity (nested_names [i], ref arity); 90 } 91 92 type.arity = arity; 93 return arity > 0; 94 } 95 TryGetArity(string name, out int arity)96 static bool TryGetArity (string name, out int arity) 97 { 98 arity = 0; 99 var index = name.LastIndexOf ('`'); 100 if (index == -1) 101 return false; 102 103 return ParseInt32 (name.Substring (index + 1), out arity); 104 } 105 ParseInt32(string value, out int result)106 static bool ParseInt32 (string value, out int result) 107 { 108 #if CF 109 try { 110 result = int.Parse (value); 111 return true; 112 } catch { 113 result = 0; 114 return false; 115 } 116 #else 117 return int.TryParse (value, out result); 118 #endif 119 } 120 TryAddArity(string name, ref int arity)121 static void TryAddArity (string name, ref int arity) 122 { 123 int type_arity; 124 if (!TryGetArity (name, out type_arity)) 125 return; 126 127 arity += type_arity; 128 } 129 ParsePart()130 string ParsePart () 131 { 132 int start = position; 133 while (position < length && !IsDelimiter (fullname [position])) 134 position++; 135 136 return fullname.Substring (start, position - start); 137 } 138 IsDelimiter(char chr)139 static bool IsDelimiter (char chr) 140 { 141 return "+,[]*&".IndexOf (chr) != -1; 142 } 143 TryParseWhiteSpace()144 void TryParseWhiteSpace () 145 { 146 while (position < length && Char.IsWhiteSpace (fullname [position])) 147 position++; 148 } 149 ParseNestedNames()150 string [] ParseNestedNames () 151 { 152 string [] nested_names = null; 153 while (TryParse ('+')) 154 Add (ref nested_names, ParsePart ()); 155 156 return nested_names; 157 } 158 TryParse(char chr)159 bool TryParse (char chr) 160 { 161 if (position < length && fullname [position] == chr) { 162 position++; 163 return true; 164 } 165 166 return false; 167 } 168 Add(ref T [] array, T item)169 static void Add<T> (ref T [] array, T item) 170 { 171 if (array == null) { 172 array = new [] { item }; 173 return; 174 } 175 176 array = array.Resize (array.Length + 1); 177 array [array.Length - 1] = item; 178 } 179 ParseSpecs()180 int [] ParseSpecs () 181 { 182 int [] specs = null; 183 184 while (position < length) { 185 switch (fullname [position]) { 186 case '*': 187 position++; 188 Add (ref specs, Type.Ptr); 189 break; 190 case '&': 191 position++; 192 Add (ref specs, Type.ByRef); 193 break; 194 case '[': 195 position++; 196 switch (fullname [position]) { 197 case ']': 198 position++; 199 Add (ref specs, Type.SzArray); 200 break; 201 case '*': 202 position++; 203 Add (ref specs, 1); 204 break; 205 default: 206 var rank = 1; 207 while (TryParse (',')) 208 rank++; 209 210 Add (ref specs, rank); 211 212 TryParse (']'); 213 break; 214 } 215 break; 216 default: 217 return specs; 218 } 219 } 220 221 return specs; 222 } 223 ParseGenericArguments(int arity)224 Type [] ParseGenericArguments (int arity) 225 { 226 Type [] generic_arguments = null; 227 228 if (position == length || fullname [position] != '[') 229 return generic_arguments; 230 231 TryParse ('['); 232 233 for (int i = 0; i < arity; i++) { 234 var fq_argument = TryParse ('['); 235 Add (ref generic_arguments, ParseType (fq_argument)); 236 if (fq_argument) 237 TryParse (']'); 238 239 TryParse (','); 240 TryParseWhiteSpace (); 241 } 242 243 TryParse (']'); 244 245 return generic_arguments; 246 } 247 ParseAssemblyName()248 string ParseAssemblyName () 249 { 250 if (!TryParse (',')) 251 return string.Empty; 252 253 TryParseWhiteSpace (); 254 255 var start = position; 256 while (position < length) { 257 var chr = fullname [position]; 258 if (chr == '[' || chr == ']') 259 break; 260 261 position++; 262 } 263 264 return fullname.Substring (start, position - start); 265 } 266 ParseType(ModuleDefinition module, string fullname)267 public static TypeReference ParseType (ModuleDefinition module, string fullname) 268 { 269 if (string.IsNullOrEmpty (fullname)) 270 return null; 271 272 var parser = new TypeParser (fullname); 273 return GetTypeReference (module, parser.ParseType (true)); 274 } 275 GetTypeReference(ModuleDefinition module, Type type_info)276 static TypeReference GetTypeReference (ModuleDefinition module, Type type_info) 277 { 278 TypeReference type; 279 if (!TryGetDefinition (module, type_info, out type)) 280 type = CreateReference (type_info, module, GetMetadataScope (module, type_info)); 281 282 return CreateSpecs (type, type_info); 283 } 284 CreateSpecs(TypeReference type, Type type_info)285 static TypeReference CreateSpecs (TypeReference type, Type type_info) 286 { 287 type = TryCreateGenericInstanceType (type, type_info); 288 289 var specs = type_info.specs; 290 if (specs.IsNullOrEmpty ()) 291 return type; 292 293 for (int i = 0; i < specs.Length; i++) { 294 switch (specs [i]) { 295 case Type.Ptr: 296 type = new PointerType (type); 297 break; 298 case Type.ByRef: 299 type = new ByReferenceType (type); 300 break; 301 case Type.SzArray: 302 type = new ArrayType (type); 303 break; 304 default: 305 var array = new ArrayType (type); 306 array.Dimensions.Clear (); 307 308 for (int j = 0; j < specs [i]; j++) 309 array.Dimensions.Add (new ArrayDimension ()); 310 311 type = array; 312 break; 313 } 314 } 315 316 return type; 317 } 318 TryCreateGenericInstanceType(TypeReference type, Type type_info)319 static TypeReference TryCreateGenericInstanceType (TypeReference type, Type type_info) 320 { 321 var generic_arguments = type_info.generic_arguments; 322 if (generic_arguments.IsNullOrEmpty ()) 323 return type; 324 325 var instance = new GenericInstanceType (type); 326 var instance_arguments = instance.GenericArguments; 327 328 for (int i = 0; i < generic_arguments.Length; i++) 329 instance_arguments.Add (GetTypeReference (type.Module, generic_arguments [i])); 330 331 return instance; 332 } 333 SplitFullName(string fullname, out string @namespace, out string name)334 public static void SplitFullName (string fullname, out string @namespace, out string name) 335 { 336 var last_dot = fullname.LastIndexOf ('.'); 337 338 if (last_dot == -1) { 339 @namespace = string.Empty; 340 name = fullname; 341 } else { 342 @namespace = fullname.Substring (0, last_dot); 343 name = fullname.Substring (last_dot + 1); 344 } 345 } 346 CreateReference(Type type_info, ModuleDefinition module, IMetadataScope scope)347 static TypeReference CreateReference (Type type_info, ModuleDefinition module, IMetadataScope scope) 348 { 349 string @namespace, name; 350 SplitFullName (type_info.type_fullname, out @namespace, out name); 351 352 var type = new TypeReference (@namespace, name, module, scope); 353 MetadataSystem.TryProcessPrimitiveTypeReference (type); 354 355 AdjustGenericParameters (type); 356 357 var nested_names = type_info.nested_names; 358 if (nested_names.IsNullOrEmpty ()) 359 return type; 360 361 for (int i = 0; i < nested_names.Length; i++) { 362 type = new TypeReference (string.Empty, nested_names [i], module, null) { 363 DeclaringType = type, 364 }; 365 366 AdjustGenericParameters (type); 367 } 368 369 return type; 370 } 371 AdjustGenericParameters(TypeReference type)372 static void AdjustGenericParameters (TypeReference type) 373 { 374 int arity; 375 if (!TryGetArity (type.Name, out arity)) 376 return; 377 378 for (int i = 0; i < arity; i++) 379 type.GenericParameters.Add (new GenericParameter (type)); 380 } 381 GetMetadataScope(ModuleDefinition module, Type type_info)382 static IMetadataScope GetMetadataScope (ModuleDefinition module, Type type_info) 383 { 384 if (string.IsNullOrEmpty (type_info.assembly)) 385 return module.TypeSystem.Corlib; 386 387 return MatchReference (module, AssemblyNameReference.Parse (type_info.assembly)); 388 } 389 MatchReference(ModuleDefinition module, AssemblyNameReference pattern)390 static AssemblyNameReference MatchReference (ModuleDefinition module, AssemblyNameReference pattern) 391 { 392 var references = module.AssemblyReferences; 393 394 for (int i = 0; i < references.Count; i++) { 395 var reference = references [i]; 396 if (reference.FullName == pattern.FullName) 397 return reference; 398 } 399 400 return pattern; 401 } 402 TryGetDefinition(ModuleDefinition module, Type type_info, out TypeReference type)403 static bool TryGetDefinition (ModuleDefinition module, Type type_info, out TypeReference type) 404 { 405 type = null; 406 if (!TryCurrentModule (module, type_info)) 407 return false; 408 409 var typedef = module.GetType (type_info.type_fullname); 410 if (typedef == null) 411 return false; 412 413 var nested_names = type_info.nested_names; 414 if (!nested_names.IsNullOrEmpty ()) { 415 for (int i = 0; i < nested_names.Length; i++) 416 typedef = typedef.GetNestedType (nested_names [i]); 417 } 418 419 type = typedef; 420 return true; 421 } 422 TryCurrentModule(ModuleDefinition module, Type type_info)423 static bool TryCurrentModule (ModuleDefinition module, Type type_info) 424 { 425 if (string.IsNullOrEmpty (type_info.assembly)) 426 return true; 427 428 if (module.assembly != null && module.assembly.Name.FullName == type_info.assembly) 429 return true; 430 431 return false; 432 } 433 ToParseable(TypeReference type)434 public static string ToParseable (TypeReference type) 435 { 436 if (type == null) 437 return null; 438 439 var name = new StringBuilder (); 440 AppendType (type, name, true, true); 441 return name.ToString (); 442 } 443 AppendType(TypeReference type, StringBuilder name, bool fq_name, bool top_level)444 static void AppendType (TypeReference type, StringBuilder name, bool fq_name, bool top_level) 445 { 446 var declaring_type = type.DeclaringType; 447 if (declaring_type != null) { 448 AppendType (declaring_type, name, false, top_level); 449 name.Append ('+'); 450 } 451 452 var @namespace = type.Namespace; 453 if (!string.IsNullOrEmpty (@namespace)) { 454 name.Append (@namespace); 455 name.Append ('.'); 456 } 457 458 name.Append (type.GetElementType ().Name); 459 460 if (!fq_name) 461 return; 462 463 if (type.IsTypeSpecification ()) 464 AppendTypeSpecification ((TypeSpecification) type, name); 465 466 if (RequiresFullyQualifiedName (type, top_level)) { 467 name.Append (", "); 468 name.Append (GetScopeFullName (type)); 469 } 470 } 471 GetScopeFullName(TypeReference type)472 static string GetScopeFullName (TypeReference type) 473 { 474 var scope = type.Scope; 475 switch (scope.MetadataScopeType) { 476 case MetadataScopeType.AssemblyNameReference: 477 return ((AssemblyNameReference) scope).FullName; 478 case MetadataScopeType.ModuleDefinition: 479 return ((ModuleDefinition) scope).Assembly.Name.FullName; 480 } 481 482 throw new ArgumentException (); 483 } 484 AppendTypeSpecification(TypeSpecification type, StringBuilder name)485 static void AppendTypeSpecification (TypeSpecification type, StringBuilder name) 486 { 487 if (type.ElementType.IsTypeSpecification ()) 488 AppendTypeSpecification ((TypeSpecification) type.ElementType, name); 489 490 switch (type.etype) { 491 case ElementType.Ptr: 492 name.Append ('*'); 493 break; 494 case ElementType.ByRef: 495 name.Append ('&'); 496 break; 497 case ElementType.SzArray: 498 case ElementType.Array: 499 var array = (ArrayType) type; 500 if (array.IsVector) { 501 name.Append ("[]"); 502 } else { 503 name.Append ('['); 504 for (int i = 1; i < array.Rank; i++) 505 name.Append (','); 506 name.Append (']'); 507 } 508 break; 509 case ElementType.GenericInst: 510 var instance = (GenericInstanceType) type; 511 var arguments = instance.GenericArguments; 512 513 name.Append ('['); 514 515 for (int i = 0; i < arguments.Count; i++) { 516 if (i > 0) 517 name.Append (','); 518 519 var argument = arguments [i]; 520 var requires_fqname = argument.Scope != argument.Module; 521 522 if (requires_fqname) 523 name.Append ('['); 524 525 AppendType (argument, name, true, false); 526 527 if (requires_fqname) 528 name.Append (']'); 529 } 530 531 name.Append (']'); 532 break; 533 default: 534 return; 535 } 536 } 537 RequiresFullyQualifiedName(TypeReference type, bool top_level)538 static bool RequiresFullyQualifiedName (TypeReference type, bool top_level) 539 { 540 if (type.Scope == type.Module) 541 return false; 542 543 if (type.Scope.Name == "mscorlib" && top_level) 544 return false; 545 546 return true; 547 } 548 } 549 } 550