PageRenderTime 41ms CodeModel.GetById 15ms app.highlight 21ms RepoModel.GetById 1ms app.codeStats 0ms

/Mono.Cecil/Mono.Cecil/TypeParser.cs

http://github.com/icsharpcode/ILSpy
C# | 555 lines | 418 code | 110 blank | 27 comment | 87 complexity | ffa7a02e0b7ce7d14ff0eff395881460 MD5 | raw file
  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
 29using System;
 30using System.Text;
 31
 32using Mono.Cecil.Metadata;
 33
 34namespace 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
 56		TypeParser (string fullname)
 57		{
 58			this.fullname = fullname;
 59			this.length = fullname.Length;
 60		}
 61
 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
 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
 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
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
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
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
139		static bool IsDelimiter (char chr)
140		{
141			return "+,[]*&".IndexOf (chr) != -1;
142		}
143
144		void TryParseWhiteSpace ()
145		{
146			while (position < length && Char.IsWhiteSpace (fullname [position]))
147				position++;
148		}
149
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
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
169		static void Add<T> (ref T [] array, T item)
170		{
171			if (array == null) {
172				array = new [] { item };
173				return;
174			}
175
176#if !CF
177			Array.Resize (ref array, array.Length + 1);
178#else
179			var copy = new T [array.Length + 1];
180			Array.Copy (array, copy, array.Length);
181			array = copy;
182#endif
183			array [array.Length - 1] = item;
184		}
185
186		int [] ParseSpecs ()
187		{
188			int [] specs = null;
189
190			while (position < length) {
191				switch (fullname [position]) {
192				case '*':
193					position++;
194					Add (ref specs, Type.Ptr);
195					break;
196				case '&':
197					position++;
198					Add (ref specs, Type.ByRef);
199					break;
200				case '[':
201					position++;
202					switch (fullname [position]) {
203					case ']':
204						position++;
205						Add (ref specs, Type.SzArray);
206						break;
207					case '*':
208						position++;
209						Add (ref specs, 1);
210						break;
211					default:
212						var rank = 1;
213						while (TryParse (','))
214							rank++;
215
216						Add (ref specs, rank);
217
218						TryParse (']');
219						break;
220					}
221					break;
222				default:
223					return specs;
224				}
225			}
226
227			return specs;
228		}
229
230		Type [] ParseGenericArguments (int arity)
231		{
232			Type [] generic_arguments = null;
233
234			if (position == length || fullname [position] != '[')
235				return generic_arguments;
236
237			TryParse ('[');
238
239			for (int i = 0; i < arity; i++) {
240				var fq_argument = TryParse ('[');
241				Add (ref generic_arguments, ParseType (fq_argument));
242				if (fq_argument)
243					TryParse (']');
244
245				TryParse (',');
246				TryParseWhiteSpace ();
247			}
248
249			TryParse (']');
250
251			return generic_arguments;
252		}
253
254		string ParseAssemblyName ()
255		{
256			if (!TryParse (','))
257				return string.Empty;
258
259			TryParseWhiteSpace ();
260
261			var start = position;
262			while (position < length) {
263				var chr = fullname [position];
264				if (chr == '[' || chr == ']')
265					break;
266
267				position++;
268			}
269
270			return fullname.Substring (start, position - start);
271		}
272
273		public static TypeReference ParseType (ModuleDefinition module, string fullname)
274		{
275			if (string.IsNullOrEmpty (fullname))
276				return null;
277
278			var parser = new TypeParser (fullname);
279			return GetTypeReference (module, parser.ParseType (true));
280		}
281
282		static TypeReference GetTypeReference (ModuleDefinition module, Type type_info)
283		{
284			TypeReference type;
285			if (!TryGetDefinition (module, type_info, out type))
286				type = CreateReference (type_info, module, GetMetadataScope (module, type_info));
287
288			return CreateSpecs (type, type_info);
289		}
290
291		static TypeReference CreateSpecs (TypeReference type, Type type_info)
292		{
293			type = TryCreateGenericInstanceType (type, type_info);
294
295			var specs = type_info.specs;
296			if (specs.IsNullOrEmpty ())
297				return type;
298
299			for (int i = 0; i < specs.Length; i++) {
300				switch (specs [i]) {
301				case Type.Ptr:
302					type = new PointerType (type);
303					break;
304				case Type.ByRef:
305					type = new ByReferenceType (type);
306					break;
307				case Type.SzArray:
308					type = new ArrayType (type);
309					break;
310				default:
311					var array = new ArrayType (type);
312					array.Dimensions.Clear ();
313
314					for (int j = 0; j < specs [i]; j++)
315						array.Dimensions.Add (new ArrayDimension ());
316
317					type = array;
318					break;
319				}
320			}
321
322			return type;
323		}
324
325		static TypeReference TryCreateGenericInstanceType (TypeReference type, Type type_info)
326		{
327			var generic_arguments = type_info.generic_arguments;
328			if (generic_arguments.IsNullOrEmpty ())
329				return type;
330
331			var instance = new GenericInstanceType (type);
332			var instance_arguments = instance.GenericArguments;
333
334			for (int i = 0; i < generic_arguments.Length; i++)
335				instance_arguments.Add (GetTypeReference (type.Module, generic_arguments [i]));
336
337			return instance;
338		}
339
340		public static void SplitFullName (string fullname, out string @namespace, out string name)
341		{
342			var last_dot = fullname.LastIndexOf ('.');
343
344			if (last_dot == -1) {
345				@namespace = string.Empty;
346				name = fullname;
347			} else {
348				@namespace = fullname.Substring (0, last_dot);
349				name = fullname.Substring (last_dot + 1);
350			}
351		}
352
353		static TypeReference CreateReference (Type type_info, ModuleDefinition module, IMetadataScope scope)
354		{
355			string @namespace, name;
356			SplitFullName (type_info.type_fullname, out @namespace, out name);
357
358			var type = new TypeReference (@namespace, name, module, scope);
359			MetadataSystem.TryProcessPrimitiveTypeReference (type);
360
361			AdjustGenericParameters (type);
362
363			var nested_names = type_info.nested_names;
364			if (nested_names.IsNullOrEmpty ())
365				return type;
366
367			for (int i = 0; i < nested_names.Length; i++) {
368				type = new TypeReference (string.Empty, nested_names [i], module, null) {
369					DeclaringType = type,
370				};
371
372				AdjustGenericParameters (type);
373			}
374
375			return type;
376		}
377
378		static void AdjustGenericParameters (TypeReference type)
379		{
380			int arity;
381			if (!TryGetArity (type.Name, out arity))
382				return;
383
384			for (int i = 0; i < arity; i++)
385				type.GenericParameters.Add (new GenericParameter (type));
386		}
387
388		static IMetadataScope GetMetadataScope (ModuleDefinition module, Type type_info)
389		{
390			if (string.IsNullOrEmpty (type_info.assembly))
391				return module.TypeSystem.Corlib;
392
393			return MatchReference (module, AssemblyNameReference.Parse (type_info.assembly));
394		}
395
396		static AssemblyNameReference MatchReference (ModuleDefinition module, AssemblyNameReference pattern)
397		{
398			var references = module.AssemblyReferences;
399
400			for (int i = 0; i < references.Count; i++) {
401				var reference = references [i];
402				if (reference.FullName == pattern.FullName)
403					return reference;
404			}
405
406			return pattern;
407		}
408
409		static bool TryGetDefinition (ModuleDefinition module, Type type_info, out TypeReference type)
410		{
411			type = null;
412			if (!TryCurrentModule (module, type_info))
413				return false;
414
415			var typedef = module.GetType (type_info.type_fullname);
416			if (typedef == null)
417				return false;
418
419			var nested_names = type_info.nested_names;
420			if (!nested_names.IsNullOrEmpty ()) {
421				for (int i = 0; i < nested_names.Length; i++)
422					typedef = typedef.GetNestedType (nested_names [i]);
423			}
424
425			type = typedef;
426			return true;
427		}
428
429		static bool TryCurrentModule (ModuleDefinition module, Type type_info)
430		{
431			if (string.IsNullOrEmpty (type_info.assembly))
432				return true;
433
434			if (module.assembly != null && module.assembly.Name.FullName == type_info.assembly)
435				return true;
436
437			return false;
438		}
439
440		public static string ToParseable (TypeReference type)
441		{
442			if (type == null)
443				return null;
444
445			var name = new StringBuilder ();
446			AppendType (type, name, true, true);
447			return name.ToString ();
448		}
449
450		static void AppendType (TypeReference type, StringBuilder name, bool fq_name, bool top_level)
451		{
452			var declaring_type = type.DeclaringType;
453			if (declaring_type != null) {
454				AppendType (declaring_type, name, false, top_level);
455				name.Append ('+');
456			}
457
458			var @namespace = type.Namespace;
459			if (!string.IsNullOrEmpty (@namespace)) {
460				name.Append (@namespace);
461				name.Append ('.');
462			}
463
464			name.Append (type.GetElementType ().Name);
465
466			if (!fq_name)
467				return;
468
469			if (type.IsTypeSpecification ())
470				AppendTypeSpecification ((TypeSpecification) type, name);
471
472			if (RequiresFullyQualifiedName (type, top_level)) {
473				name.Append (", ");
474				name.Append (GetScopeFullName (type));
475			}
476		}
477
478		static string GetScopeFullName (TypeReference type)
479		{
480			var scope = type.Scope;
481			switch (scope.MetadataScopeType) {
482			case MetadataScopeType.AssemblyNameReference:
483				return ((AssemblyNameReference) scope).FullName;
484			case MetadataScopeType.ModuleDefinition:
485				return ((ModuleDefinition) scope).Assembly.Name.FullName;
486			}
487
488			throw new ArgumentException ();
489		}
490
491		static void AppendTypeSpecification (TypeSpecification type, StringBuilder name)
492		{
493			if (type.ElementType.IsTypeSpecification ())
494				AppendTypeSpecification ((TypeSpecification) type.ElementType, name);
495
496			switch (type.etype) {
497			case ElementType.Ptr:
498				name.Append ('*');
499				break;
500			case ElementType.ByRef:
501				name.Append ('&');
502				break;
503			case ElementType.SzArray:
504			case ElementType.Array:
505				var array = (ArrayType) type;
506				if (array.IsVector) {
507					name.Append ("[]");
508				} else {
509					name.Append ('[');
510					for (int i = 1; i < array.Rank; i++)
511						name.Append (',');
512					name.Append (']');
513				}
514				break;
515			case ElementType.GenericInst:
516				var instance = (GenericInstanceType) type;
517				var arguments = instance.GenericArguments;
518
519				name.Append ('[');
520
521				for (int i = 0; i < arguments.Count; i++) {
522					if (i > 0)
523						name.Append (',');
524
525					var argument = arguments [i];
526					var requires_fqname = argument.Scope != argument.Module;
527
528					if (requires_fqname)
529						name.Append ('[');
530
531					AppendType (argument, name, true, false);
532
533					if (requires_fqname)
534						name.Append (']');
535				}
536
537				name.Append (']');
538				break;
539			default:
540				return;
541			}
542		}
543
544		static bool RequiresFullyQualifiedName (TypeReference type, bool top_level)
545		{
546			if (type.Scope == type.Module)
547				return false;
548
549			if (type.Scope.Name == "mscorlib" && top_level)
550				return false;
551
552			return true;
553		}
554	}
555}