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);