2
Copyright (C) 2009-2012 Jeroen Frijters
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.
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:
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.
25
using System.Collections.Generic;
26
using System.Diagnostics;
29
namespace IKVM.Reflection
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>
36
private readonly string ns;
37
private readonly string name;
39
internal TypeName(string ns, string name)
43
throw new ArgumentNullException("name");
54
internal string Namespace
59
public static bool operator ==(TypeName o1, TypeName o2)
61
return o1.ns == o2.ns && o1.name == o2.name;
64
public static bool operator !=(TypeName o1, TypeName o2)
66
return o1.ns != o2.ns || o1.name != o2.name;
69
public override int GetHashCode()
71
return ns == null ? name.GetHashCode() : ns.GetHashCode() * 37 + name.GetHashCode();
74
public override bool Equals(object obj)
76
TypeName? other = obj as TypeName?;
77
return other != null && other.Value == this;
80
public override string ToString()
82
return ns == null ? name : ns + "." + name;
85
bool IEquatable<TypeName>.Equals(TypeName other)
90
internal TypeName ToLowerInvariant()
92
return new TypeName(ns == null ? null : ns.ToLowerInvariant(), name.ToLowerInvariant());
95
internal static TypeName Split(string name)
97
int dot = name.LastIndexOf('.');
100
return new TypeName(null, name);
104
return new TypeName(name.Substring(0, dot), name.Substring(dot + 1));
109
struct TypeNameParser
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;
121
internal static string Escape(string name)
127
StringBuilder sb = null;
128
for (int pos = 0; pos < name.Length; pos++)
142
sb = new StringBuilder(name, 0, pos, name.Length + 3);
144
sb.Append("\\").Append(c);
154
return sb != null ? sb.ToString() : name;
157
internal static string Unescape(string name)
159
int pos = name.IndexOf('\\');
164
StringBuilder sb = new StringBuilder(name, 0, pos, name.Length - 1);
165
for (; pos < name.Length; pos++)
174
return sb.ToString();
177
internal static TypeNameParser Parse(string typeName, bool throwOnError)
181
Parser parser = new Parser(typeName);
182
return new TypeNameParser(ref parser, true);
188
Parser parser = new Parser(typeName);
189
return new TypeNameParser(ref parser, true);
191
catch (ArgumentException)
193
return new TypeNameParser();
198
private TypeNameParser(ref Parser parser, bool withAssemblyName)
200
bool genericParameter = parser.pos != 0;
201
name = parser.NextNamePart();
203
parser.ParseNested(ref nested);
204
genericParameters = null;
205
parser.ParseGenericParameters(ref genericParameters);
207
parser.ParseModifiers(ref modifiers);
209
if (withAssemblyName)
211
parser.ParseAssemblyName(genericParameter, ref assemblyName);
217
get { return name == null; }
220
internal string FirstNamePart
225
internal string AssemblyName
227
get { return assemblyName; }
230
private struct Parser
232
private readonly string typeName;
235
internal Parser(string typeName)
237
this.typeName = typeName;
241
private void Check(bool condition)
245
throw new ArgumentException("Invalid type name '" + typeName + "'");
249
private void Consume(char c)
251
Check(pos < typeName.Length && typeName[pos++] == c);
254
private bool TryConsume(char c)
256
if (pos < typeName.Length && typeName[pos] == c)
267
internal string NextNamePart()
271
for (; pos < typeName.Length; pos++)
273
char c = typeName[pos];
277
Check(pos < typeName.Length && SpecialChars.IndexOf(typeName[pos]) != -1);
279
else if (SpecialChars.IndexOf(c) != -1)
284
Check(pos - start != 0);
285
if (start == 0 && pos == typeName.Length)
291
return typeName.Substring(start, pos - start);
295
internal void ParseNested(ref string[] nested)
297
while (TryConsume('+'))
299
Add(ref nested, NextNamePart());
303
internal void ParseGenericParameters(ref TypeNameParser[] genericParameters)
309
if (TryConsume(']') || TryConsume('*') || TryConsume(','))
311
// it's not a generic parameter list, but an array instead
320
Add(ref genericParameters, new TypeNameParser(ref this, true));
325
Add(ref genericParameters, new TypeNameParser(ref this, false));
328
while (TryConsume(','));
334
internal void ParseModifiers(ref short[] modifiers)
336
while (pos < typeName.Length)
338
switch (typeName[pos])
342
Add(ref modifiers, POINTER);
346
Add(ref modifiers, BYREF);
350
Add(ref modifiers, ParseArray());
360
internal void ParseAssemblyName(bool genericParameter, ref string assemblyName)
362
if (pos < typeName.Length)
364
if (typeName[pos] == ']' && genericParameter)
372
if (genericParameter)
375
while (pos < typeName.Length)
377
char c = typeName[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++] == ']');
393
Check(pos < typeName.Length && typeName[pos] == ']');
394
assemblyName = typeName.Substring(start, pos - start).Replace("\\]", "]");
398
// only when an assembly name is used in a generic type parameter, will it be escaped
399
assemblyName = typeName.Substring(pos);
401
Check(assemblyName.Length != 0);
406
Check(!genericParameter);
410
private short ParseArray()
413
Check(pos < typeName.Length);
414
char c = typeName[pos];
428
while (TryConsume(','))
430
Check(rank < short.MaxValue);
438
private void SkipWhiteSpace()
440
while (pos < typeName.Length && Char.IsWhiteSpace(typeName[pos]))
446
private static void Add<T>(ref T[] array, T elem)
450
array = new T[] { elem };
453
Array.Resize(ref array, array.Length + 1);
454
array[array.Length - 1] = elem;
458
internal Type GetType(Universe universe, Assembly context, bool throwOnError, string originalName, bool resolve, bool ignoreCase)
460
Debug.Assert(!resolve || !ignoreCase);
461
TypeName name = TypeName.Split(this.name);
463
if (assemblyName != null)
465
Assembly asm = universe.Load(assemblyName, context, throwOnError);
472
type = asm.ResolveType(name);
476
type = asm.FindTypeIgnoreCase(name.ToLowerInvariant());
480
type = asm.FindType(name);
483
else if (context == null)
487
type = universe.Mscorlib.ResolveType(name);
491
type = universe.Mscorlib.FindTypeIgnoreCase(name.ToLowerInvariant());
495
type = universe.Mscorlib.FindType(name);
502
name = name.ToLowerInvariant();
503
type = context.FindTypeIgnoreCase(name);
507
type = context.FindType(name);
509
if (type == null && context != universe.Mscorlib)
513
type = universe.Mscorlib.FindTypeIgnoreCase(name);
517
type = universe.Mscorlib.FindType(name);
520
if (type == null && resolve)
522
if (universe.Mscorlib.__IsMissing && !context.__IsMissing)
524
type = universe.Mscorlib.ResolveType(name);
528
type = context.ResolveType(name);
532
return Expand(type, context, throwOnError, originalName, resolve, ignoreCase);
535
internal Type Expand(Type type, Assembly context, bool throwOnError, string originalName, bool resolve, bool ignoreCase)
537
Debug.Assert(!resolve || !ignoreCase);
542
throw new TypeLoadException(originalName);
549
foreach (string nest in nested)
552
TypeName name = TypeName.Split(TypeNameParser.Unescape(nest));
554
? outer.FindNestedTypeIgnoreCase(name.ToLowerInvariant())
555
: outer.FindNestedType(name);
560
type = outer.Module.universe.GetMissingTypeOrThrow(outer.Module, outer, name);
562
else if (throwOnError)
564
throw new TypeLoadException(originalName);
573
if (genericParameters != null)
575
Type[] typeArgs = new Type[genericParameters.Length];
576
for (int i = 0; i < typeArgs.Length; i++)
578
typeArgs[i] = genericParameters[i].GetType(type.Assembly.universe, context, throwOnError, originalName, resolve, ignoreCase);
579
if (typeArgs[i] == null)
584
type = type.MakeGenericType(typeArgs);
586
if (modifiers != null)
588
foreach (short modifier in modifiers)
593
type = type.MakeArrayType();
596
type = type.MakeByRefType();
599
type = type.MakePointerType();
602
type = type.MakeArrayType(modifier);