~ubuntu-branches/ubuntu/trusty/monodevelop/trusty-proposed

« back to all changes in this revision

Viewing changes to external/ikvm/reflect/TypeNameParser.cs

  • Committer: Package Import Robot
  • Author(s): Jo Shields
  • Date: 2013-05-12 09:46:03 UTC
  • mto: This revision was merged to the branch mainline in revision 29.
  • Revision ID: package-import@ubuntu.com-20130512094603-mad323bzcxvmcam0
Tags: upstream-4.0.5+dfsg
ImportĀ upstreamĀ versionĀ 4.0.5+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
  Copyright (C) 2009-2012 Jeroen Frijters
 
3
 
 
4
  This software is provided 'as-is', without any express or implied
 
5
  warranty.  In no event will the authors be held liable for any damages
 
6
  arising from the use of this software.
 
7
 
 
8
  Permission is granted to anyone to use this software for any purpose,
 
9
  including commercial applications, and to alter it and redistribute it
 
10
  freely, subject to the following restrictions:
 
11
 
 
12
  1. The origin of this software must not be misrepresented; you must not
 
13
     claim that you wrote the original software. If you use this software
 
14
     in a product, an acknowledgment in the product documentation would be
 
15
     appreciated but is not required.
 
16
  2. Altered source versions must be plainly marked as such, and must not be
 
17
     misrepresented as being the original software.
 
18
  3. This notice may not be removed or altered from any source distribution.
 
19
 
 
20
  Jeroen Frijters
 
21
  jeroen@frijters.net
 
22
  
 
23
*/
 
24
using System;
 
25
using System.Collections.Generic;
 
26
using System.Diagnostics;
 
27
using System.Text;
 
28
 
 
29
namespace IKVM.Reflection
 
30
{
 
31
        // this respresents a type name as in metadata:
 
32
        // - ns will be null for empty the namespace (never the empty string)
 
33
        // - the strings are not escaped
 
34
        struct TypeName : IEquatable<TypeName>
 
35
        {
 
36
                private readonly string ns;
 
37
                private readonly string name;
 
38
 
 
39
                internal TypeName(string ns, string name)
 
40
                {
 
41
                        if (name == null)
 
42
                        {
 
43
                                throw new ArgumentNullException("name");
 
44
                        }
 
45
                        this.ns = ns;
 
46
                        this.name = name;
 
47
                }
 
48
 
 
49
                internal string Name
 
50
                {
 
51
                        get { return name; }
 
52
                }
 
53
 
 
54
                internal string Namespace
 
55
                {
 
56
                        get { return ns; }
 
57
                }
 
58
 
 
59
                public static bool operator ==(TypeName o1, TypeName o2)
 
60
                {
 
61
                        return o1.ns == o2.ns && o1.name == o2.name;
 
62
                }
 
63
 
 
64
                public static bool operator !=(TypeName o1, TypeName o2)
 
65
                {
 
66
                        return o1.ns != o2.ns || o1.name != o2.name;
 
67
                }
 
68
 
 
69
                public override int GetHashCode()
 
70
                {
 
71
                        return ns == null ? name.GetHashCode() : ns.GetHashCode() * 37 + name.GetHashCode();
 
72
                }
 
73
 
 
74
                public override bool Equals(object obj)
 
75
                {
 
76
                        TypeName? other = obj as TypeName?;
 
77
                        return other != null && other.Value == this;
 
78
                }
 
79
 
 
80
                public override string ToString()
 
81
                {
 
82
                        return ns == null ? name : ns + "." + name;
 
83
                }
 
84
 
 
85
                bool IEquatable<TypeName>.Equals(TypeName other)
 
86
                {
 
87
                        return this == other;
 
88
                }
 
89
 
 
90
                internal TypeName ToLowerInvariant()
 
91
                {
 
92
                        return new TypeName(ns == null ? null : ns.ToLowerInvariant(), name.ToLowerInvariant());
 
93
                }
 
94
 
 
95
                internal static TypeName Split(string name)
 
96
                {
 
97
                        int dot = name.LastIndexOf('.');
 
98
                        if (dot == -1)
 
99
                        {
 
100
                                return new TypeName(null, name);
 
101
                        }
 
102
                        else
 
103
                        {
 
104
                                return new TypeName(name.Substring(0, dot), name.Substring(dot + 1));
 
105
                        }
 
106
                }
 
107
        }
 
108
 
 
109
        struct TypeNameParser
 
110
        {
 
111
                private const string SpecialChars = "\\+,[]*&";
 
112
                private const short SZARRAY = -1;
 
113
                private const short BYREF = -2;
 
114
                private const short POINTER = -3;
 
115
                private readonly string name;
 
116
                private readonly string[] nested;
 
117
                private readonly string assemblyName;
 
118
                private readonly short[] modifiers;
 
119
                private readonly TypeNameParser[] genericParameters;
 
120
 
 
121
                internal static string Escape(string name)
 
122
                {
 
123
                        if (name == null)
 
124
                        {
 
125
                                return null;
 
126
                        }
 
127
                        StringBuilder sb = null;
 
128
                        for (int pos = 0; pos < name.Length; pos++)
 
129
                        {
 
130
                                char c = name[pos];
 
131
                                switch (c)
 
132
                                {
 
133
                                        case '\\':
 
134
                                        case '+':
 
135
                                        case ',':
 
136
                                        case '[':
 
137
                                        case ']':
 
138
                                        case '*':
 
139
                                        case '&':
 
140
                                                if (sb == null)
 
141
                                                {
 
142
                                                        sb = new StringBuilder(name, 0, pos, name.Length + 3);
 
143
                                                }
 
144
                                                sb.Append("\\").Append(c);
 
145
                                                break;
 
146
                                        default:
 
147
                                                if (sb != null)
 
148
                                                {
 
149
                                                        sb.Append(c);
 
150
                                                }
 
151
                                                break;
 
152
                                }
 
153
                        }
 
154
                        return sb != null ? sb.ToString() : name;
 
155
                }
 
156
 
 
157
                internal static string Unescape(string name)
 
158
                {
 
159
                        int pos = name.IndexOf('\\');
 
160
                        if (pos == -1)
 
161
                        {
 
162
                                return name;
 
163
                        }
 
164
                        StringBuilder sb = new StringBuilder(name, 0, pos, name.Length - 1);
 
165
                        for (; pos < name.Length; pos++)
 
166
                        {
 
167
                                char c = name[pos];
 
168
                                if (c == '\\')
 
169
                                {
 
170
                                        c = name[++pos];
 
171
                                }
 
172
                                sb.Append(c);
 
173
                        }
 
174
                        return sb.ToString();
 
175
                }
 
176
 
 
177
                internal static TypeNameParser Parse(string typeName, bool throwOnError)
 
178
                {
 
179
                        if (throwOnError)
 
180
                        {
 
181
                                Parser parser = new Parser(typeName);
 
182
                                return new TypeNameParser(ref parser, true);
 
183
                        }
 
184
                        else
 
185
                        {
 
186
                                try
 
187
                                {
 
188
                                        Parser parser = new Parser(typeName);
 
189
                                        return new TypeNameParser(ref parser, true);
 
190
                                }
 
191
                                catch (ArgumentException)
 
192
                                {
 
193
                                        return new TypeNameParser();
 
194
                                }
 
195
                        }
 
196
                }
 
197
 
 
198
                private TypeNameParser(ref Parser parser, bool withAssemblyName)
 
199
                {
 
200
                        bool genericParameter = parser.pos != 0;
 
201
                        name = parser.NextNamePart();
 
202
                        nested = null;
 
203
                        parser.ParseNested(ref nested);
 
204
                        genericParameters = null;
 
205
                        parser.ParseGenericParameters(ref genericParameters);
 
206
                        modifiers = null;
 
207
                        parser.ParseModifiers(ref modifiers);
 
208
                        assemblyName = null;
 
209
                        if (withAssemblyName)
 
210
                        {
 
211
                                parser.ParseAssemblyName(genericParameter, ref assemblyName);
 
212
                        }
 
213
                }
 
214
 
 
215
                internal bool Error
 
216
                {
 
217
                        get { return name == null; }
 
218
                }
 
219
 
 
220
                internal string FirstNamePart
 
221
                {
 
222
                        get { return name; }
 
223
                }
 
224
 
 
225
                internal string AssemblyName
 
226
                {
 
227
                        get { return assemblyName; }
 
228
                }
 
229
 
 
230
                private struct Parser
 
231
                {
 
232
                        private readonly string typeName;
 
233
                        internal int pos;
 
234
 
 
235
                        internal Parser(string typeName)
 
236
                        {
 
237
                                this.typeName = typeName;
 
238
                                this.pos = 0;
 
239
                        }
 
240
 
 
241
                        private void Check(bool condition)
 
242
                        {
 
243
                                if (!condition)
 
244
                                {
 
245
                                        throw new ArgumentException("Invalid type name '" + typeName + "'");
 
246
                                }
 
247
                        }
 
248
 
 
249
                        private void Consume(char c)
 
250
                        {
 
251
                                Check(pos < typeName.Length && typeName[pos++] == c);
 
252
                        }
 
253
 
 
254
                        private bool TryConsume(char c)
 
255
                        {
 
256
                                if (pos < typeName.Length && typeName[pos] == c)
 
257
                                {
 
258
                                        pos++;
 
259
                                        return true;
 
260
                                }
 
261
                                else
 
262
                                {
 
263
                                        return false;
 
264
                                }
 
265
                        }
 
266
 
 
267
                        internal string NextNamePart()
 
268
                        {
 
269
                                SkipWhiteSpace();
 
270
                                int start = pos;
 
271
                                for (; pos < typeName.Length; pos++)
 
272
                                {
 
273
                                        char c = typeName[pos];
 
274
                                        if (c == '\\')
 
275
                                        {
 
276
                                                pos++;
 
277
                                                Check(pos < typeName.Length && SpecialChars.IndexOf(typeName[pos]) != -1);
 
278
                                        }
 
279
                                        else if (SpecialChars.IndexOf(c) != -1)
 
280
                                        {
 
281
                                                break;
 
282
                                        }
 
283
                                }
 
284
                                Check(pos - start != 0);
 
285
                                if (start == 0 && pos == typeName.Length)
 
286
                                {
 
287
                                        return typeName;
 
288
                                }
 
289
                                else
 
290
                                {
 
291
                                        return typeName.Substring(start, pos - start);
 
292
                                }
 
293
                        }
 
294
 
 
295
                        internal void ParseNested(ref string[] nested)
 
296
                        {
 
297
                                while (TryConsume('+'))
 
298
                                {
 
299
                                        Add(ref nested, NextNamePart());
 
300
                                }
 
301
                        }
 
302
 
 
303
                        internal void ParseGenericParameters(ref TypeNameParser[] genericParameters)
 
304
                        {
 
305
                                int saved = pos;
 
306
                                if (TryConsume('['))
 
307
                                {
 
308
                                        SkipWhiteSpace();
 
309
                                        if (TryConsume(']') || TryConsume('*') || TryConsume(','))
 
310
                                        {
 
311
                                                // it's not a generic parameter list, but an array instead
 
312
                                                pos = saved;
 
313
                                                return;
 
314
                                        }
 
315
                                        do
 
316
                                        {
 
317
                                                SkipWhiteSpace();
 
318
                                                if (TryConsume('['))
 
319
                                                {
 
320
                                                        Add(ref genericParameters, new TypeNameParser(ref this, true));
 
321
                                                        Consume(']');
 
322
                                                }
 
323
                                                else
 
324
                                                {
 
325
                                                        Add(ref genericParameters, new TypeNameParser(ref this, false));
 
326
                                                }
 
327
                                        }
 
328
                                        while (TryConsume(','));
 
329
                                        Consume(']');
 
330
                                        SkipWhiteSpace();
 
331
                                }
 
332
                        }
 
333
 
 
334
                        internal void ParseModifiers(ref short[] modifiers)
 
335
                        {
 
336
                                while (pos < typeName.Length)
 
337
                                {
 
338
                                        switch (typeName[pos])
 
339
                                        {
 
340
                                                case '*':
 
341
                                                        pos++;
 
342
                                                        Add(ref modifiers, POINTER);
 
343
                                                        break;
 
344
                                                case '&':
 
345
                                                        pos++;
 
346
                                                        Add(ref modifiers, BYREF);
 
347
                                                        break;
 
348
                                                case '[':
 
349
                                                        pos++;
 
350
                                                        Add(ref modifiers, ParseArray());
 
351
                                                        Consume(']');
 
352
                                                        break;
 
353
                                                default:
 
354
                                                        return;
 
355
                                        }
 
356
                                        SkipWhiteSpace();
 
357
                                }
 
358
                        }
 
359
 
 
360
                        internal void ParseAssemblyName(bool genericParameter, ref string assemblyName)
 
361
                        {
 
362
                                if (pos < typeName.Length)
 
363
                                {
 
364
                                        if (typeName[pos] == ']' && genericParameter)
 
365
                                        {
 
366
                                                // ok
 
367
                                        }
 
368
                                        else
 
369
                                        {
 
370
                                                Consume(',');
 
371
                                                SkipWhiteSpace();
 
372
                                                if (genericParameter)
 
373
                                                {
 
374
                                                        int start = pos;
 
375
                                                        while (pos < typeName.Length)
 
376
                                                        {
 
377
                                                                char c = typeName[pos];
 
378
                                                                if (c == '\\')
 
379
                                                                {
 
380
                                                                        pos++;
 
381
                                                                        // a backslash itself is not legal in an assembly name, so we don't need to check for an escaped backslash
 
382
                                                                        Check(pos < typeName.Length && typeName[pos++] == ']');
 
383
                                                                }
 
384
                                                                else if (c == ']')
 
385
                                                                {
 
386
                                                                        break;
 
387
                                                                }
 
388
                                                                else
 
389
                                                                {
 
390
                                                                        pos++;
 
391
                                                                }
 
392
                                                        }
 
393
                                                        Check(pos < typeName.Length && typeName[pos] == ']');
 
394
                                                        assemblyName = typeName.Substring(start, pos - start).Replace("\\]", "]");
 
395
                                                }
 
396
                                                else
 
397
                                                {
 
398
                                                        // only when an assembly name is used in a generic type parameter, will it be escaped
 
399
                                                        assemblyName = typeName.Substring(pos);
 
400
                                                }
 
401
                                                Check(assemblyName.Length != 0);
 
402
                                        }
 
403
                                }
 
404
                                else
 
405
                                {
 
406
                                        Check(!genericParameter);
 
407
                                }
 
408
                        }
 
409
 
 
410
                        private short ParseArray()
 
411
                        {
 
412
                                SkipWhiteSpace();
 
413
                                Check(pos < typeName.Length);
 
414
                                char c = typeName[pos];
 
415
                                if (c == ']')
 
416
                                {
 
417
                                        return SZARRAY;
 
418
                                }
 
419
                                else if (c == '*')
 
420
                                {
 
421
                                        pos++;
 
422
                                        SkipWhiteSpace();
 
423
                                        return 1;
 
424
                                }
 
425
                                else
 
426
                                {
 
427
                                        short rank = 1;
 
428
                                        while (TryConsume(','))
 
429
                                        {
 
430
                                                Check(rank < short.MaxValue);
 
431
                                                rank++;
 
432
                                                SkipWhiteSpace();
 
433
                                        }
 
434
                                        return rank;
 
435
                                }
 
436
                        }
 
437
 
 
438
                        private void SkipWhiteSpace()
 
439
                        {
 
440
                                while (pos < typeName.Length && Char.IsWhiteSpace(typeName[pos]))
 
441
                                {
 
442
                                        pos++;
 
443
                                }
 
444
                        }
 
445
 
 
446
                        private static void Add<T>(ref T[] array, T elem)
 
447
                        {
 
448
                                if (array == null)
 
449
                                {
 
450
                                        array = new T[] { elem };
 
451
                                        return;
 
452
                                }
 
453
                                Array.Resize(ref array, array.Length + 1);
 
454
                                array[array.Length - 1] = elem;
 
455
                        }
 
456
                }
 
457
 
 
458
                internal Type GetType(Universe universe, Assembly context, bool throwOnError, string originalName, bool resolve, bool ignoreCase)
 
459
                {
 
460
                        Debug.Assert(!resolve || !ignoreCase);
 
461
                        TypeName name = TypeName.Split(this.name);
 
462
                        Type type;
 
463
                        if (assemblyName != null)
 
464
                        {
 
465
                                Assembly asm = universe.Load(assemblyName, context, throwOnError);
 
466
                                if (asm == null)
 
467
                                {
 
468
                                        return null;
 
469
                                }
 
470
                                if (resolve)
 
471
                                {
 
472
                                        type = asm.ResolveType(name);
 
473
                                }
 
474
                                else if (ignoreCase)
 
475
                                {
 
476
                                        type = asm.FindTypeIgnoreCase(name.ToLowerInvariant());
 
477
                                }
 
478
                                else
 
479
                                {
 
480
                                        type = asm.FindType(name);
 
481
                                }
 
482
                        }
 
483
                        else if (context == null)
 
484
                        {
 
485
                                if (resolve)
 
486
                                {
 
487
                                        type = universe.Mscorlib.ResolveType(name);
 
488
                                }
 
489
                                else if (ignoreCase)
 
490
                                {
 
491
                                        type = universe.Mscorlib.FindTypeIgnoreCase(name.ToLowerInvariant());
 
492
                                }
 
493
                                else
 
494
                                {
 
495
                                        type = universe.Mscorlib.FindType(name);
 
496
                                }
 
497
                        }
 
498
                        else
 
499
                        {
 
500
                                if (ignoreCase)
 
501
                                {
 
502
                                        name = name.ToLowerInvariant();
 
503
                                        type = context.FindTypeIgnoreCase(name);
 
504
                                }
 
505
                                else
 
506
                                {
 
507
                                        type = context.FindType(name);
 
508
                                }
 
509
                                if (type == null && context != universe.Mscorlib)
 
510
                                {
 
511
                                        if (ignoreCase)
 
512
                                        {
 
513
                                                type = universe.Mscorlib.FindTypeIgnoreCase(name);
 
514
                                        }
 
515
                                        else
 
516
                                        {
 
517
                                                type = universe.Mscorlib.FindType(name);
 
518
                                        }
 
519
                                }
 
520
                                if (type == null && resolve)
 
521
                                {
 
522
                                        if (universe.Mscorlib.__IsMissing && !context.__IsMissing)
 
523
                                        {
 
524
                                                type = universe.Mscorlib.ResolveType(name);
 
525
                                        }
 
526
                                        else
 
527
                                        {
 
528
                                                type = context.ResolveType(name);
 
529
                                        }
 
530
                                }
 
531
                        }
 
532
                        return Expand(type, context, throwOnError, originalName, resolve, ignoreCase);
 
533
                }
 
534
 
 
535
                internal Type Expand(Type type, Assembly context, bool throwOnError, string originalName, bool resolve, bool ignoreCase)
 
536
                {
 
537
                        Debug.Assert(!resolve || !ignoreCase);
 
538
                        if (type == null)
 
539
                        {
 
540
                                if (throwOnError)
 
541
                                {
 
542
                                        throw new TypeLoadException(originalName);
 
543
                                }
 
544
                                return null;
 
545
                        }
 
546
                        if (nested != null)
 
547
                        {
 
548
                                Type outer;
 
549
                                foreach (string nest in nested)
 
550
                                {
 
551
                                        outer = type;
 
552
                                        TypeName name = TypeName.Split(TypeNameParser.Unescape(nest));
 
553
                                        type = ignoreCase
 
554
                                                ? outer.FindNestedTypeIgnoreCase(name.ToLowerInvariant())
 
555
                                                : outer.FindNestedType(name);
 
556
                                        if (type == null)
 
557
                                        {
 
558
                                                if (resolve)
 
559
                                                {
 
560
                                                        type = outer.Module.universe.GetMissingTypeOrThrow(outer.Module, outer, name);
 
561
                                                }
 
562
                                                else if (throwOnError)
 
563
                                                {
 
564
                                                        throw new TypeLoadException(originalName);
 
565
                                                }
 
566
                                                else
 
567
                                                {
 
568
                                                        return null;
 
569
                                                }
 
570
                                        }
 
571
                                }
 
572
                        }
 
573
                        if (genericParameters != null)
 
574
                        {
 
575
                                Type[] typeArgs = new Type[genericParameters.Length];
 
576
                                for (int i = 0; i < typeArgs.Length; i++)
 
577
                                {
 
578
                                        typeArgs[i] = genericParameters[i].GetType(type.Assembly.universe, context, throwOnError, originalName, resolve, ignoreCase);
 
579
                                        if (typeArgs[i] == null)
 
580
                                        {
 
581
                                                return null;
 
582
                                        }
 
583
                                }
 
584
                                type = type.MakeGenericType(typeArgs);
 
585
                        }
 
586
                        if (modifiers != null)
 
587
                        {
 
588
                                foreach (short modifier in modifiers)
 
589
                                {
 
590
                                        switch (modifier)
 
591
                                        {
 
592
                                                case SZARRAY:
 
593
                                                        type = type.MakeArrayType();
 
594
                                                        break;
 
595
                                                case BYREF:
 
596
                                                        type = type.MakeByRefType();
 
597
                                                        break;
 
598
                                                case POINTER:
 
599
                                                        type = type.MakePointerType();
 
600
                                                        break;
 
601
                                                default:
 
602
                                                        type = type.MakeArrayType(modifier);
 
603
                                                        break;
 
604
                                        }
 
605
                                }
 
606
                        }
 
607
                        return type;
 
608
                }
 
609
        }
 
610
}