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