5 // Rodrigo Kumpera <kumpera@gmail.com>
8 // Copyright (C) 2010 Novell, Inc (http://www.novell.com)
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:
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
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
;
32 using System
.Reflection
;
33 using System
.Threading
;
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
46 internal ArraySpec (int dimensions
, bool bound
)
48 this.dimensions
= dimensions
;
52 public Type
Resolve (Type type
)
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
)
64 return sb
.Append ("[*]");
65 return sb
.Append ('[')
66 .Append (',', dimensions
- 1)
70 public override string ToString ()
72 return Append (new Text
.StringBuilder ()).ToString ();
88 internal class PointerSpec
: ModifierSpec
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 ();
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
116 string assembly_name
;
117 List
<TypeIdentifier
> nested
;
118 List
<TypeSpec
> generic_params
;
119 List
<ModifierSpec
> modifier_spec
;
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
{
140 internal IEnumerable
<TypeName
> Nested
{
145 return Array
.Empty
<TypeName
> ();
149 internal IEnumerable
<ModifierSpec
> Modifiers
{
151 if (modifier_spec
!= null)
152 return modifier_spec
;
154 return Array
.Empty
<ModifierSpec
> ();
159 internal enum DisplayNameFormat
{
165 public override string ToString () {
166 return GetDisplayFullName (DisplayNameFormat
.WANT_ASSEMBLY
);
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) {
182 for (int i
= 0; i
< generic_params
.Count
; ++i
) {
185 if (generic_params
[i
].assembly_name
!= null)
186 sb
.Append ('[').Append (generic_params
[i
].DisplayFullName
).Append (']');
188 sb
.Append (generic_params
[i
].DisplayFullName
);
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
)
220 internal string DisplayFullName
{
222 if (display_fullname
== null)
223 display_fullname
= GetDisplayFullName (DisplayNameFormat
.Default
);
224 return display_fullname
;
228 internal static TypeSpec
Parse (string typeName
)
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");
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
)
255 res
.Append ('\\').Append (c
);
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
];
272 if (++i
< displayName
.Length
)
276 return res
.ToString ();
279 internal static bool NeedsEscaping (string internalName
)
281 foreach (char c
in internalName
)
299 internal Type
Resolve (Func
<AssemblyName
,Assembly
> assemblyResolver
, Func
<Assembly
,string,bool,Type
> typeResolver
, bool throwOnError
, bool ignoreCase
, ref StackCrawlMark stackMark
)
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
));
309 asm
= Assembly
.Load (assembly_name
);
313 throw new FileNotFoundException ("Could not resolve assembly '" + assembly_name
+ "'");
319 if (typeResolver
!= null)
320 type
= typeResolver (asm
, name
.DisplayName
, ignoreCase
);
322 type
= asm
.GetType (name
.DisplayName
, false, ignoreCase
);
325 throw new TypeLoadException ("Could not resolve type '" + name
+ "'");
329 if (nested
!= null) {
330 foreach (var n
in nested
) {
331 var tmp
= type
.GetNestedType (n
.DisplayName
, BindingFlags
.Public
| BindingFlags
.NonPublic
);
334 throw new TypeLoadException ("Could not resolve type '" + n
+ "'");
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
);
347 throw new TypeLoadException ("Could not resolve type '" + generic_params
[i
].name
+ "'");
352 type
= type
.MakeGenericType (args
);
355 if (modifier_spec
!= null) {
356 foreach (var md
in modifier_spec
)
357 type
= md
.Resolve (type
);
361 type
= type
.MakeByRefType ();
366 void AddName (string type_name
)
369 name
= ParsedTypeIdentifier(type_name
);
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
)
387 while (p
< name
.Length
&& Char
.IsWhiteSpace (name
[p
]))
392 static void BoundCheck (int idx
, string s
)
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
)
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.
418 bool in_modifiers
= false;
419 TypeSpec data
= new TypeSpec ();
421 SkipSpace (name
, ref pos
);
425 for (; pos
< name
.Length
; ++pos
) {
426 switch (name
[pos
]) {
428 data
.AddName (name
.Substring (name_start
, pos
- name_start
));
429 name_start
= pos
+ 1;
433 data
.AddName (name
.Substring (name_start
, pos
- name_start
));
434 name_start
= pos
+ 1;
436 if (is_recurse
&& !allow_aqn
) {
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;
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
);
464 for (; pos
< name
.Length
; ++pos
) {
466 switch (name
[pos
]) {
469 throw new ArgumentException ("Can't have a byref of a byref", "typeName");
471 data
.is_byref
= true;
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] == '*') {
482 data
.AddModifier (new PointerSpec(pointer_level
));
485 if (is_recurse
&& allow_aqn
) {
487 while (end
< name
.Length
&& name
[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 ();
500 data
.assembly_name
= name
.Substring (pos
+ 1).Trim ();
506 throw new ArgumentException ("Byref qualifier must be the last one of a type", "typeName");
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
] == '[';
521 ++pos
; //skip '[' to the start of the type
522 args
.Add (Parse (name
, ref pos
, true, aqn
));
523 BoundCheck (pos
, name
);
525 if (name
[pos
] == ']')
528 throw new ArgumentException ("Unclosed assembly-qualified type name at " + name
[pos
], "typeName");
529 BoundCheck (pos
, name
);
532 if (name
[pos
] == ']')
534 if (name
[pos
] == ',')
535 ++pos
; // skip ',' to the start of the next arg
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
546 while (pos
< name
.Length
&& name
[pos
] != ']') {
547 if (name
[pos
] == '*') {
549 throw new ArgumentException ("Array spec cannot have 2 bound dimensions", "typeName");
552 else if (name
[pos
] != ',')
553 throw new ArgumentException ("Invalid character in array spec " + name
[pos
], "typeName");
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
));
573 throw new ArgumentException ("Unmatched ']'", "typeName");
575 throw new ArgumentException ("Bad type def, can't handle '" + name
[pos
]+"'" + " at " + pos
, "typeName");
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
{
597 internal TypeSpecTypeName (TypeSpec ts
, bool wantModifiers
)
600 this.want_modifiers
= wantModifiers
;
603 public override string DisplayName
{
606 return ts
.DisplayFullName
;
608 return ts
.GetDisplayFullName (DisplayNameFormat
.NO_MODIFIERS
);
612 public override TypeName
NestedName (TypeIdentifier innerName
)
614 return TypeNames
.FromDisplay(DisplayName
+ "+" + innerName
.DisplayName
);