2 Copyright (C) 2009-2011 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;
28 namespace IKVM.Reflection
30 // this respresents a type name as in metadata:
31 // - ns will be null for empty the namespace (never the empty string)
32 // - the strings are not escaped
33 struct TypeName : IEquatable<TypeName>
35 private readonly string ns;
36 private readonly string name;
38 internal TypeName(string ns, string name)
42 throw new ArgumentNullException("name");
53 internal string Namespace
58 public static bool operator ==(TypeName o1, TypeName o2)
60 return o1.ns == o2.ns && o1.name == o2.name;
63 public static bool operator !=(TypeName o1, TypeName o2)
65 return o1.ns != o2.ns || o1.name != o2.name;
68 public override int GetHashCode()
70 return ns == null ? name.GetHashCode() : ns.GetHashCode() * 37 + name.GetHashCode();
73 public override bool Equals(object obj)
75 TypeName? other = obj as TypeName?;
76 return other != null && other.Value == this;
79 public override string ToString()
81 return ns == null ? name : ns + "." + name;
84 bool IEquatable<TypeName>.Equals(TypeName other)
89 internal static TypeName Split(string name)
91 int dot = name.LastIndexOf('.');
94 return new TypeName(null, name);
98 return new TypeName(name.Substring(0, dot), name.Substring(dot + 1));
103 struct TypeNameParser
105 private const string SpecialChars = "\\+,[]*&";
106 private const short SZARRAY = -1;
107 private const short BYREF = -2;
108 private const short POINTER = -3;
109 private readonly string name;
110 private readonly string[] nested;
111 private readonly string assemblyName;
112 private readonly short[] modifiers;
113 private readonly TypeNameParser[] genericParameters;
115 internal static string Escape(string name)
121 StringBuilder sb = null;
122 for (int pos = 0; pos < name.Length; pos++)
136 sb = new StringBuilder(name, 0, pos, name.Length + 3);
138 sb.Append("\\").Append(c);
148 return sb != null ? sb.ToString() : name;
151 internal static string Unescape(string name)
153 int pos = name.IndexOf('\\');
158 StringBuilder sb = new StringBuilder(name, 0, pos, name.Length - 1);
159 for (; pos < name.Length; pos++)
168 return sb.ToString();
171 internal static TypeNameParser Parse(string typeName, bool throwOnError)
175 Parser parser = new Parser(typeName);
176 return new TypeNameParser(ref parser, true);
182 Parser parser = new Parser(typeName);
183 return new TypeNameParser(ref parser, true);
185 catch (ArgumentException)
187 return new TypeNameParser();
192 private TypeNameParser(ref Parser parser, bool withAssemblyName)
194 bool genericParameter = parser.pos != 0;
195 name = parser.NextNamePart();
197 parser.ParseNested(ref nested);
198 genericParameters = null;
199 parser.ParseGenericParameters(ref genericParameters);
201 parser.ParseModifiers(ref modifiers);
203 if (withAssemblyName)
205 parser.ParseAssemblyName(genericParameter, ref assemblyName);
211 get { return name == null; }
214 internal string FirstNamePart
219 internal string AssemblyName
221 get { return assemblyName; }
224 private struct Parser
226 private readonly string typeName;
229 internal Parser(string typeName)
231 this.typeName = typeName;
235 private void Check(bool condition)
239 throw new ArgumentException("Invalid type name '" + typeName + "'");
243 private void Consume(char c)
245 Check(pos < typeName.Length && typeName[pos++] == c);
248 private bool TryConsume(char c)
250 if (pos < typeName.Length && typeName[pos] == c)
261 internal string NextNamePart()
265 for (; pos < typeName.Length; pos++)
267 char c = typeName[pos];
271 Check(pos < typeName.Length && SpecialChars.IndexOf(typeName[pos]) != -1);
273 else if (SpecialChars.IndexOf(c) != -1)
278 Check(pos - start != 0);
279 if (start == 0 && pos == typeName.Length)
285 return typeName.Substring(start, pos - start);
289 internal void ParseNested(ref string[] nested)
291 while (TryConsume('+'))
293 Add(ref nested, NextNamePart());
297 internal void ParseGenericParameters(ref TypeNameParser[] genericParameters)
303 if (TryConsume(']') || TryConsume('*') || TryConsume(','))
305 // it's not a generic parameter list, but an array instead
314 Add(ref genericParameters, new TypeNameParser(ref this, true));
319 Add(ref genericParameters, new TypeNameParser(ref this, false));
322 while (TryConsume(','));
328 internal void ParseModifiers(ref short[] modifiers)
330 while (pos < typeName.Length)
332 switch (typeName[pos])
336 Add(ref modifiers, POINTER);
340 Add(ref modifiers, BYREF);
344 Add(ref modifiers, ParseArray());
354 internal void ParseAssemblyName(bool genericParameter, ref string assemblyName)
356 if (pos < typeName.Length)
358 if (typeName[pos] == ']' && genericParameter)
366 if (genericParameter)
369 while (pos < typeName.Length)
371 char c = typeName[pos];
375 // a backslash itself is not legal in an assembly name, so we don't need to check for an escaped backslash
376 Check(pos < typeName.Length && typeName[pos++] == ']');
387 Check(pos < typeName.Length && typeName[pos] == ']');
388 assemblyName = typeName.Substring(start, pos - start).Replace("\\]", "]");
392 // only when an assembly name is used in a generic type parameter, will it be escaped
393 assemblyName = typeName.Substring(pos);
395 Check(assemblyName.Length != 0);
400 Check(!genericParameter);
404 private short ParseArray()
407 Check(pos < typeName.Length);
408 char c = typeName[pos];
422 while (TryConsume(','))
424 Check(rank < short.MaxValue);
432 private void SkipWhiteSpace()
434 while (pos < typeName.Length && Char.IsWhiteSpace(typeName[pos]))
440 private static void Add<T>(ref T[] array, T elem)
444 array = new T[] { elem };
447 Array.Resize(ref array, array.Length + 1);
448 array[array.Length - 1] = elem;
452 internal Type GetType(Universe universe, Assembly context, bool throwOnError, string originalName, bool resolve)
454 TypeName name = TypeName.Split(this.name);
456 if (assemblyName != null)
458 Assembly asm = universe.Load(assemblyName, context, throwOnError);
465 type = asm.ResolveType(name);
469 type = asm.FindType(name);
472 else if (context == null)
476 type = universe.Mscorlib.ResolveType(name);
480 type = universe.Mscorlib.FindType(name);
485 type = context.FindType(name);
486 if (type == null && context != universe.Mscorlib)
488 type = universe.Mscorlib.FindType(name);
490 if (type == null && resolve)
492 if (universe.Mscorlib.__IsMissing && !context.__IsMissing)
494 type = universe.Mscorlib.ResolveType(name);
498 type = context.ResolveType(name);
502 return Expand(type, context, throwOnError, originalName, resolve);
505 internal Type Expand(Type type, Assembly context, bool throwOnError, string originalName, bool resolve)
511 throw new TypeLoadException(originalName);
518 foreach (string nest in nested)
521 TypeName name = TypeName.Split(TypeNameParser.Unescape(nest));
522 type = outer.FindNestedType(name);
527 type = outer.Module.universe.GetMissingTypeOrThrow(outer.Module, outer, name);
529 else if (throwOnError)
531 throw new TypeLoadException(originalName);
540 if (genericParameters != null)
542 Type[] typeArgs = new Type[genericParameters.Length];
543 for (int i = 0; i < typeArgs.Length; i++)
545 typeArgs[i] = genericParameters[i].GetType(type.Assembly.universe, context, throwOnError, originalName, resolve);
546 if (typeArgs[i] == null)
551 type = type.MakeGenericType(typeArgs);
553 if (modifiers != null)
555 foreach (short modifier in modifiers)
560 type = type.MakeArrayType();
563 type = type.MakeByRefType();
566 type = type.MakePointerType();
569 type = type.MakeArrayType(modifier);