Cleanup redundant code
[mono.git] / mcs / class / corlib / System / TypeSpec.cs
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.
28 //
29
30 using System.Collections.Generic;
31 using System.IO;
32 using System.Reflection;
33
34 #if NET_4_0
35 namespace System {
36         internal class ArraySpec
37         {
38                 int dimensions;
39                 bool bound;
40
41                 internal ArraySpec (int dimensions, bool bound)
42                 {
43                         this.dimensions = dimensions;
44                         this.bound = bound;
45                 }
46
47                 internal Type Resolve (Type type)
48                 {
49                         if (bound)
50                                 return type.MakeArrayType (1);
51                         else if (dimensions == 1)
52                                 return type.MakeArrayType ();
53                         return type.MakeArrayType (dimensions);
54                 }
55
56 #if DEBUG
57                 public override string ToString ()
58                 {
59                         if (bound)
60                                 return "[*]";
61                         string str = "[";
62                         for (int i = 1; i < dimensions; ++i)
63                                 str += ",";
64                         return str + "]";
65                 }
66 #endif
67         }
68
69         internal class TypeSpec
70         {
71                 string name, assembly_name;
72                 List<string> nested;
73                 List<TypeSpec> generic_params;
74                 List<ArraySpec> array_spec;
75                 int pointer_level;
76                 bool is_byref;
77
78                 bool IsArray {
79                         get { return array_spec != null; }
80                 }
81
82 #if DEBUG
83                 public override string ToString () {
84                         string str = name;
85                         if (nested != null) {
86                                 foreach (var n in nested)
87                                         str += "+" + n;
88                         }
89
90                         if (generic_params != null) {
91                                 str += "[";
92                                 for (int i = 0; i < generic_params.Count; ++i) {
93                                         if (i > 0)
94                                                 str += ", ";
95                                         if (generic_params [i].assembly_name != null)
96                                                 str += "[" + generic_params [i] + "]";
97                                         else
98                                                 str += generic_params [i];
99                                 }
100                                 str += "]";
101                         }
102
103                         if (array_spec != null) {
104                                 foreach (var ar in array_spec)
105                                         str += ar;
106                         }
107
108                         for (int i = 0; i < pointer_level; ++i)
109                                 str += "*";
110
111                         if (is_byref)
112                                 str += "&";
113
114                         if (assembly_name != null)
115                                 str += ", " + assembly_name;
116
117                         return str;
118                 }
119 #endif
120
121                 internal static TypeSpec Parse (string typeName)
122                 {
123                         int pos = 0;
124                         if (typeName == null)
125                                 throw new ArgumentNullException ("typeName");
126
127                         TypeSpec res = Parse (typeName, ref pos, false, false);
128                         if (pos < typeName.Length)
129                                 throw new ArgumentException ("Count not parse the whole type name", "typeName");
130                         return res;
131                 }
132
133                 internal Type Resolve (Func<AssemblyName,Assembly> assemblyResolver, Func<Assembly,string,bool,Type> typeResolver, bool throwOnError, bool ignoreCase)
134                 {
135                         Assembly asm = null;
136                         if (assemblyResolver == null && typeResolver == null)
137                                 return Type.GetType (name, throwOnError, ignoreCase);
138
139                         if (assembly_name != null) {
140                                 if (assemblyResolver != null)
141                                         asm = assemblyResolver (new AssemblyName (assembly_name));
142                                 else
143                                         asm = Assembly.Load (assembly_name);
144
145                                 if (asm == null) {
146                                         if (throwOnError)
147                                                 throw new FileNotFoundException ("Could not resolve assembly '" + assembly_name + "'");
148                                         return null;
149                                 }
150                         }
151
152                         Type type = null;
153                         if (typeResolver != null)
154                                 type = typeResolver (asm, name, ignoreCase);
155                         else
156                                 type = asm.GetType (name, false, ignoreCase);
157                         if (type == null) {
158                                 if (throwOnError)
159                                         throw new TypeLoadException ("Could not resolve type '" + name + "'");
160                                 return null;
161                         }
162
163                         if (nested != null) {
164                                 foreach (var n in nested) {
165                                         var tmp = type.GetNestedType (n, BindingFlags.Public | BindingFlags.NonPublic);
166                                         if (tmp == null) {
167                                                 if (throwOnError)
168                                                         throw new TypeLoadException ("Could not resolve type '" + n + "'");
169                                                 return null;
170                                         }
171                                         type = tmp;
172                                 }
173                         }
174
175                         if (generic_params != null) {
176                                 Type[] args = new Type [generic_params.Count];
177                                 for (int i = 0; i < args.Length; ++i) {
178                                         var tmp = generic_params [i].Resolve (assemblyResolver, typeResolver, throwOnError, ignoreCase);
179                                         if (tmp == null) {
180                                                 if (throwOnError)
181                                                         throw new TypeLoadException ("Could not resolve type '" + generic_params [i].name + "'");
182                                                 return null;
183                                         }
184                                         args [i] = tmp;
185                                 }
186                                 type = type.MakeGenericType (args);
187                         }
188
189                         if (array_spec != null) {
190                                 foreach (var arr in array_spec)
191                                         type = arr.Resolve (type);
192                         }
193
194                         for (int i = 0; i < pointer_level; ++i)
195                                 type = type.MakePointerType ();
196
197                         if (is_byref)
198                                 type = type.MakeByRefType ();
199
200                         return type;
201                 }
202
203                 void AddName (string type_name)
204                 {
205                         if (name == null) {
206                                 name = type_name;
207                         } else {
208                                 if (nested == null)
209                                         nested = new List <string> ();
210                                 nested.Add (type_name);
211                         }
212                 }
213
214                 void AddArray (ArraySpec array)
215                 {
216                         if (array_spec == null)
217                                 array_spec = new List<ArraySpec> ();
218                         array_spec.Add (array);
219                 }
220
221                 static void SkipSpace (string name, ref int pos)
222                 {
223                         int p = pos;
224                         while (p < name.Length && Char.IsWhiteSpace (name [p]))
225                                 ++p;
226                         pos = p;
227                 }
228
229                 static TypeSpec Parse (string name, ref int p, bool is_recurse, bool allow_aqn)
230                 {
231                         int pos = p;
232                         int name_start;
233                         bool in_modifiers = false;
234                         TypeSpec data = new TypeSpec ();
235
236                         SkipSpace (name, ref pos);
237
238                         name_start = pos;
239
240                         for (; pos < name.Length; ++pos) {
241                                 switch (name [pos]) {
242                                 case '+':
243                                         data.AddName (name.Substring (name_start, pos - name_start));
244                                         name_start = pos + 1;
245                                         break;
246                                 case ',':
247                                 case ']':
248                                         data.AddName (name.Substring (name_start, pos - name_start));
249                                         name_start = pos + 1;
250                                         in_modifiers = true;
251                                         if (is_recurse && !allow_aqn) {
252                                                 p = pos;
253                                                 return data;
254                                         }
255                                         break;
256                                 case '&':
257                                 case '*':
258                                 case '[':
259                                         if (name [pos] != '[' && is_recurse)
260                                                 throw new ArgumentException ("Generic argument can't be byref or pointer type", "typeName");
261                                         data.AddName (name.Substring (name_start, pos - name_start));
262                                         name_start = pos + 1;
263                                         in_modifiers = true;
264                                         break;
265                                 }
266                                 if (in_modifiers)
267                                         break;
268                         }
269
270                         if (name_start < pos)
271                                 data.AddName (name.Substring (name_start, pos - name_start));           
272
273                         if (in_modifiers) {
274                                 for (; pos < name.Length; ++pos) {
275
276                                         switch (name [pos]) {
277                                         case '&':
278                                                 if (data.is_byref)
279                                                         throw new ArgumentException ("Can't have a byref of a byref", "typeName");
280
281                                                 data.is_byref = true;
282                                                 break;
283                                         case '*':
284                                                 if (data.is_byref)
285                                                         throw new ArgumentException ("Can't have a pointer to a byref type", "typeName");
286                                                 ++data.pointer_level;
287                                                 break;
288                                         case ',':
289                                                 if (is_recurse) {
290                                                         int end = pos;
291                                                         while (end < name.Length && name [end] != ']')
292                                                                 ++end;
293                                                         if (end >= name.Length)
294                                                                 throw new ArgumentException ("Unmatched ']' while parsing generic argument assembly name");
295                                                         data.assembly_name = name.Substring (pos + 1, end - pos - 1).Trim ();
296                                                         p = end + 1;
297                                                         return data;                                            
298                                                 }
299                                                 data.assembly_name = name.Substring (pos + 1).Trim ();
300                                                 pos = name.Length;
301                                                 break;
302                                         case '[':
303                                                 if (data.is_byref)
304                                                         throw new ArgumentException ("Byref qualifier must be the last one of a type", "typeName");
305                                                 ++pos;
306                                                 if (pos >= name.Length)
307                                                                 throw new ArgumentException ("Invalid array/generic spec", "typeName");
308                                                 SkipSpace (name, ref pos);
309
310                                                 if (name [pos] != ',' && name [pos] != '*' && name [pos]  != ']') {//generic args
311                                                         List<TypeSpec> args = new List <TypeSpec> ();
312                                                         if (data.IsArray)
313                                                                 throw new ArgumentException ("generic args after array spec", "typeName");
314
315                                                         while (pos < name.Length) {
316                                                                 SkipSpace (name, ref pos);
317                                                                 bool aqn = name [pos] == '[';
318                                                                 if (aqn)
319                                                                         ++pos; //skip '[' to the start of the type
320                                                                 args.Add (Parse (name, ref pos, true, aqn));
321                                                                 if (pos >= name.Length)
322                                                                         throw new ArgumentException ("Invalid generic arguments spec", "typeName");
323
324                                                                 if (name [pos] == ']')
325                                                                                 break;
326                                                                 if (name [pos] == ',')
327                                                                         ++pos; // skip ',' to the start of the next arg
328                                                                 else
329                                                                         throw new ArgumentException ("Invalid generic arguments separator " + name [pos], "typeName");
330                         
331                                                         }
332                                                         if (pos >= name.Length || name [pos] != ']')
333                                                                 throw new ArgumentException ("Error parsing generic params spec", "typeName");
334                                                         data.generic_params = args;
335                                                 } else { //array spec
336                                                         int dimensions = 1;
337                                                         bool bound = false;
338                                                         while (pos < name.Length && name [pos] != ']') {
339                                                                 if (name [pos] == '*') {
340                                                                         if (bound)
341                                                                                 throw new ArgumentException ("Array spec cannot have 2 bound dimensions", "typeName");
342                                                                         bound = true;
343                                                                 }
344                                                                 else if (name [pos] != ',')
345                                                                         throw new ArgumentException ("Invalid character in array spec " + name [pos], "typeName");
346                                                                 else
347                                                                         ++dimensions;
348
349                                                                 ++pos;
350                                                                 SkipSpace (name, ref pos);
351                                                         }
352                                                         if (name [pos] != ']')
353                                                                 throw new ArgumentException ("Error parsing array spec", "typeName");
354                                                         if (dimensions > 1 && bound)
355                                                                 throw new ArgumentException ("Invalid array spec, multi-dimensional array cannot be bound", "typeName");
356                                                         data.AddArray (new ArraySpec (dimensions, bound));
357                                                 }
358
359                                                 break;
360                                         case ']':
361                                                 if (is_recurse) {
362                                                         p = pos + 1;
363                                                         return data;
364                                                 }
365                                                 throw new ArgumentException ("Unmatched ']'", "typeName");
366                                         default:
367                                                 throw new ArgumentException ("Bad type def, can't handle '" + name [pos]+"'" + " at " + pos, "typeName");
368                                         }
369                                 }
370                         }
371
372                         p = pos;
373                         return data;
374                 }
375         }
376 }
377 #endif
378