[System.Private.CoreLib] Remove unused members and further cleanups
[mono-project.git] / mcs / class / corlib / System / TypeSpec.cs
blob3391c1fd343473df42276ec1c926a7dd3dcaa35d
1 //
2 // System.Type.cs
3 //
4 // Author:
5 // Rodrigo Kumpera <kumpera@gmail.com>
6 //
7 //
8 // Copyright (C) 2010 Novell, Inc (http://www.novell.com)
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
17 //
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 //
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 using System.Collections.Generic;
31 using System.IO;
32 using System.Reflection;
33 using System.Threading;
35 namespace System {
36 internal interface ModifierSpec {
37 Type Resolve (Type type);
38 Text.StringBuilder Append (Text.StringBuilder sb);
40 internal class ArraySpec : ModifierSpec
42 // dimensions == 1 and bound, or dimensions > 1 and !bound
43 int dimensions;
44 bool bound;
46 internal ArraySpec (int dimensions, bool bound)
48 this.dimensions = dimensions;
49 this.bound = bound;
52 public Type Resolve (Type type)
54 if (bound)
55 return type.MakeArrayType (1);
56 else if (dimensions == 1)
57 return type.MakeArrayType ();
58 return type.MakeArrayType (dimensions);
61 public Text.StringBuilder Append (Text.StringBuilder sb)
63 if (bound)
64 return sb.Append ("[*]");
65 return sb.Append ('[')
66 .Append (',', dimensions - 1)
67 .Append (']');
70 public override string ToString ()
72 return Append (new Text.StringBuilder ()).ToString ();
75 public int Rank {
76 get {
77 return dimensions;
81 public bool IsBound {
82 get {
83 return bound;
88 internal class PointerSpec : ModifierSpec
90 int pointer_level;
92 internal PointerSpec (int pointer_level) {
93 this.pointer_level = pointer_level;
96 public Type Resolve (Type type) {
97 for (int i = 0; i < pointer_level; ++i)
98 type = type.MakePointerType ();
99 return type;
102 public Text.StringBuilder Append (Text.StringBuilder sb)
104 return sb.Append ('*', pointer_level);
107 public override string ToString () {
108 return Append (new Text.StringBuilder ()).ToString ();
113 internal class TypeSpec
115 TypeIdentifier name;
116 string assembly_name;
117 List<TypeIdentifier> nested;
118 List<TypeSpec> generic_params;
119 List<ModifierSpec> modifier_spec;
120 bool is_byref;
122 string display_fullname; // cache
124 internal bool HasModifiers {
125 get { return modifier_spec != null; }
128 internal bool IsNested {
129 get { return nested != null && nested.Count > 0; }
132 internal bool IsByRef {
133 get { return is_byref; }
136 internal TypeName Name {
137 get { return name; }
140 internal IEnumerable<TypeName> Nested {
141 get {
142 if (nested != null)
143 return nested;
144 else
145 return Array.Empty<TypeName> ();
149 internal IEnumerable<ModifierSpec> Modifiers {
150 get {
151 if (modifier_spec != null)
152 return modifier_spec;
153 else
154 return Array.Empty<ModifierSpec> ();
158 [Flags]
159 internal enum DisplayNameFormat {
160 Default = 0x0,
161 WANT_ASSEMBLY = 0x1,
162 NO_MODIFIERS = 0x2,
164 #if DEBUG
165 public override string ToString () {
166 return GetDisplayFullName (DisplayNameFormat.WANT_ASSEMBLY);
168 #endif
170 string GetDisplayFullName (DisplayNameFormat flags)
172 bool wantAssembly = (flags & DisplayNameFormat.WANT_ASSEMBLY) != 0;
173 bool wantModifiers = (flags & DisplayNameFormat.NO_MODIFIERS) == 0;
174 var sb = new Text.StringBuilder(name.DisplayName);
175 if (nested != null) {
176 foreach (var n in nested)
177 sb.Append ('+').Append (n.DisplayName);
180 if (generic_params != null) {
181 sb.Append ('[');
182 for (int i = 0; i < generic_params.Count; ++i) {
183 if (i > 0)
184 sb.Append (", ");
185 if (generic_params [i].assembly_name != null)
186 sb.Append ('[').Append (generic_params [i].DisplayFullName).Append (']');
187 else
188 sb.Append (generic_params [i].DisplayFullName);
190 sb.Append (']');
193 if (wantModifiers)
194 GetModifierString (sb);
196 if (assembly_name != null && wantAssembly)
197 sb.Append (", ").Append (assembly_name);
199 return sb.ToString();
202 internal string ModifierString ()
204 return GetModifierString (new Text.StringBuilder ()).ToString ();
207 private Text.StringBuilder GetModifierString (Text.StringBuilder sb)
209 if (modifier_spec != null) {
210 foreach (var md in modifier_spec)
211 md.Append (sb);
214 if (is_byref)
215 sb.Append ('&');
217 return sb;
220 internal string DisplayFullName {
221 get {
222 if (display_fullname == null)
223 display_fullname = GetDisplayFullName (DisplayNameFormat.Default);
224 return display_fullname;
228 internal static TypeSpec Parse (string typeName)
230 int pos = 0;
231 if (typeName == null)
232 throw new ArgumentNullException ("typeName");
234 TypeSpec res = Parse (typeName, ref pos, false, true);
235 if (pos < typeName.Length)
236 throw new ArgumentException ("Count not parse the whole type name", "typeName");
237 return res;
240 internal static string EscapeDisplayName(string internalName)
242 // initial capacity = length of internalName.
243 // Maybe we won't have to escape anything.
244 var res = new Text.StringBuilder (internalName.Length);
245 foreach (char c in internalName)
247 switch (c) {
248 case '+':
249 case ',':
250 case '[':
251 case ']':
252 case '*':
253 case '&':
254 case '\\':
255 res.Append ('\\').Append (c);
256 break;
257 default:
258 res.Append (c);
259 break;
262 return res.ToString ();
265 internal static string UnescapeInternalName(string displayName)
267 var res = new Text.StringBuilder (displayName.Length);
268 for (int i = 0; i < displayName.Length; ++i)
270 char c = displayName[i];
271 if (c == '\\')
272 if (++i < displayName.Length)
273 c = displayName[i];
274 res.Append (c);
276 return res.ToString ();
279 internal static bool NeedsEscaping (string internalName)
281 foreach (char c in internalName)
283 switch (c) {
284 case ',':
285 case '+':
286 case '*':
287 case '&':
288 case '[':
289 case ']':
290 case '\\':
291 return true;
292 default:
293 break;
296 return false;
299 internal Type Resolve (Func<AssemblyName,Assembly> assemblyResolver, Func<Assembly,string,bool,Type> typeResolver, bool throwOnError, bool ignoreCase, ref StackCrawlMark stackMark)
301 Assembly asm = null;
302 if (assemblyResolver == null && typeResolver == null)
303 return RuntimeType.GetType (DisplayFullName, throwOnError, ignoreCase, false, ref stackMark);
305 if (assembly_name != null) {
306 if (assemblyResolver != null)
307 asm = assemblyResolver (new AssemblyName (assembly_name));
308 else
309 asm = Assembly.Load (assembly_name);
311 if (asm == null) {
312 if (throwOnError)
313 throw new FileNotFoundException ("Could not resolve assembly '" + assembly_name + "'");
314 return null;
318 Type type = null;
319 if (typeResolver != null)
320 type = typeResolver (asm, name.DisplayName, ignoreCase);
321 else
322 type = asm.GetType (name.DisplayName, false, ignoreCase);
323 if (type == null) {
324 if (throwOnError)
325 throw new TypeLoadException ("Could not resolve type '" + name + "'");
326 return null;
329 if (nested != null) {
330 foreach (var n in nested) {
331 var tmp = type.GetNestedType (n.DisplayName, BindingFlags.Public | BindingFlags.NonPublic);
332 if (tmp == null) {
333 if (throwOnError)
334 throw new TypeLoadException ("Could not resolve type '" + n + "'");
335 return null;
337 type = tmp;
341 if (generic_params != null) {
342 Type[] args = new Type [generic_params.Count];
343 for (int i = 0; i < args.Length; ++i) {
344 var tmp = generic_params [i].Resolve (assemblyResolver, typeResolver, throwOnError, ignoreCase, ref stackMark);
345 if (tmp == null) {
346 if (throwOnError)
347 throw new TypeLoadException ("Could not resolve type '" + generic_params [i].name + "'");
348 return null;
350 args [i] = tmp;
352 type = type.MakeGenericType (args);
355 if (modifier_spec != null) {
356 foreach (var md in modifier_spec)
357 type = md.Resolve (type);
360 if (is_byref)
361 type = type.MakeByRefType ();
363 return type;
366 void AddName (string type_name)
368 if (name == null) {
369 name = ParsedTypeIdentifier(type_name);
370 } else {
371 if (nested == null)
372 nested = new List <TypeIdentifier> ();
373 nested.Add (ParsedTypeIdentifier(type_name));
377 void AddModifier (ModifierSpec md)
379 if (modifier_spec == null)
380 modifier_spec = new List<ModifierSpec> ();
381 modifier_spec.Add (md);
384 static void SkipSpace (string name, ref int pos)
386 int p = pos;
387 while (p < name.Length && Char.IsWhiteSpace (name [p]))
388 ++p;
389 pos = p;
392 static void BoundCheck (int idx, string s)
394 if (idx >= s.Length)
395 throw new ArgumentException ("Invalid generic arguments spec", "typeName");
398 static TypeIdentifier ParsedTypeIdentifier (string displayName)
400 return TypeIdentifiers.FromDisplay(displayName);
403 static TypeSpec Parse (string name, ref int p, bool is_recurse, bool allow_aqn)
405 // Invariants:
406 // - On exit p, is updated to pos the current unconsumed character.
408 // - The callee peeks at but does not consume delimiters following
409 // recurisve parse (so for a recursive call like the args of "Foo[P,Q]"
410 // we'll return with p either on ',' or on ']'. If the name was aqn'd
411 // "Foo[[P,assmblystuff],Q]" on return p with be on the ']' just
412 // after the "assmblystuff")
414 // - If allow_aqn is True, assembly qualification is optional.
415 // If allow_aqn is False, assembly qualification is prohibited.
416 int pos = p;
417 int name_start;
418 bool in_modifiers = false;
419 TypeSpec data = new TypeSpec ();
421 SkipSpace (name, ref pos);
423 name_start = pos;
425 for (; pos < name.Length; ++pos) {
426 switch (name [pos]) {
427 case '+':
428 data.AddName (name.Substring (name_start, pos - name_start));
429 name_start = pos + 1;
430 break;
431 case ',':
432 case ']':
433 data.AddName (name.Substring (name_start, pos - name_start));
434 name_start = pos + 1;
435 in_modifiers = true;
436 if (is_recurse && !allow_aqn) {
437 p = pos;
438 return data;
440 break;
441 case '&':
442 case '*':
443 case '[':
444 if (name [pos] != '[' && is_recurse)
445 throw new ArgumentException ("Generic argument can't be byref or pointer type", "typeName");
446 data.AddName (name.Substring (name_start, pos - name_start));
447 name_start = pos + 1;
448 in_modifiers = true;
449 break;
450 case '\\':
451 pos++;
452 break;
454 if (in_modifiers)
455 break;
458 if (name_start < pos)
459 data.AddName (name.Substring (name_start, pos - name_start));
460 else if (name_start == pos)
461 data.AddName (String.Empty);
463 if (in_modifiers) {
464 for (; pos < name.Length; ++pos) {
466 switch (name [pos]) {
467 case '&':
468 if (data.is_byref)
469 throw new ArgumentException ("Can't have a byref of a byref", "typeName");
471 data.is_byref = true;
472 break;
473 case '*':
474 if (data.is_byref)
475 throw new ArgumentException ("Can't have a pointer to a byref type", "typeName");
476 // take subsequent '*'s too
477 int pointer_level = 1;
478 while (pos+1 < name.Length && name[pos+1] == '*') {
479 ++pos;
480 ++pointer_level;
482 data.AddModifier (new PointerSpec(pointer_level));
483 break;
484 case ',':
485 if (is_recurse && allow_aqn) {
486 int end = pos;
487 while (end < name.Length && name [end] != ']')
488 ++end;
489 if (end >= name.Length)
490 throw new ArgumentException ("Unmatched ']' while parsing generic argument assembly name");
491 data.assembly_name = name.Substring (pos + 1, end - pos - 1).Trim ();
492 p = end;
493 return data;
495 if (is_recurse) {
496 p = pos;
497 return data;
499 if (allow_aqn) {
500 data.assembly_name = name.Substring (pos + 1).Trim ();
501 pos = name.Length;
503 break;
504 case '[':
505 if (data.is_byref)
506 throw new ArgumentException ("Byref qualifier must be the last one of a type", "typeName");
507 ++pos;
508 if (pos >= name.Length)
509 throw new ArgumentException ("Invalid array/generic spec", "typeName");
510 SkipSpace (name, ref pos);
512 if (name [pos] != ',' && name [pos] != '*' && name [pos] != ']') {//generic args
513 List<TypeSpec> args = new List <TypeSpec> ();
514 if (data.HasModifiers)
515 throw new ArgumentException ("generic args after array spec or pointer type", "typeName");
517 while (pos < name.Length) {
518 SkipSpace (name, ref pos);
519 bool aqn = name [pos] == '[';
520 if (aqn)
521 ++pos; //skip '[' to the start of the type
522 args.Add (Parse (name, ref pos, true, aqn));
523 BoundCheck (pos, name);
524 if (aqn) {
525 if (name [pos] == ']')
526 ++pos;
527 else
528 throw new ArgumentException ("Unclosed assembly-qualified type name at " + name[pos], "typeName");
529 BoundCheck (pos, name);
532 if (name [pos] == ']')
533 break;
534 if (name [pos] == ',')
535 ++pos; // skip ',' to the start of the next arg
536 else
537 throw new ArgumentException ("Invalid generic arguments separator " + name [pos], "typeName");
540 if (pos >= name.Length || name [pos] != ']')
541 throw new ArgumentException ("Error parsing generic params spec", "typeName");
542 data.generic_params = args;
543 } else { //array spec
544 int dimensions = 1;
545 bool bound = false;
546 while (pos < name.Length && name [pos] != ']') {
547 if (name [pos] == '*') {
548 if (bound)
549 throw new ArgumentException ("Array spec cannot have 2 bound dimensions", "typeName");
550 bound = true;
552 else if (name [pos] != ',')
553 throw new ArgumentException ("Invalid character in array spec " + name [pos], "typeName");
554 else
555 ++dimensions;
557 ++pos;
558 SkipSpace (name, ref pos);
560 if (pos >= name.Length || name [pos] != ']')
561 throw new ArgumentException ("Error parsing array spec", "typeName");
562 if (dimensions > 1 && bound)
563 throw new ArgumentException ("Invalid array spec, multi-dimensional array cannot be bound", "typeName");
564 data.AddModifier (new ArraySpec (dimensions, bound));
567 break;
568 case ']':
569 if (is_recurse) {
570 p = pos;
571 return data;
573 throw new ArgumentException ("Unmatched ']'", "typeName");
574 default:
575 throw new ArgumentException ("Bad type def, can't handle '" + name [pos]+"'" + " at " + pos, "typeName");
580 p = pos;
581 return data;
584 internal TypeName TypeNameWithoutModifiers ()
586 return new TypeSpecTypeName (this, false);
589 internal TypeName TypeName {
590 get { return new TypeSpecTypeName (this, true); }
593 private class TypeSpecTypeName : TypeNames.ATypeName, TypeName {
594 TypeSpec ts;
595 bool want_modifiers;
597 internal TypeSpecTypeName (TypeSpec ts, bool wantModifiers)
599 this.ts = ts;
600 this.want_modifiers = wantModifiers;
603 public override string DisplayName {
604 get {
605 if (want_modifiers)
606 return ts.DisplayFullName;
607 else
608 return ts.GetDisplayFullName (DisplayNameFormat.NO_MODIFIERS);
612 public override TypeName NestedName (TypeIdentifier innerName)
614 return TypeNames.FromDisplay(DisplayName + "+" + innerName.DisplayName);