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++)
125 if (SpecialChars.IndexOf(c) != -1)
129 sb = new StringBuilder(name, 0, pos, name.Length + 3);
131 sb.Append('\\').Append(c);
138 return sb != null ? sb.ToString() : name;
141 internal static string Unescape(string name)
143 int pos = name.IndexOf('\\');
148 StringBuilder sb = new StringBuilder(name, 0, pos, name.Length - 1);
149 for (; pos < name.Length; pos++)
158 return sb.ToString();
161 internal static TypeNameParser Parse(string typeName, bool throwOnError)
165 Parser parser = new Parser(typeName);
166 return new TypeNameParser(ref parser, true);
172 Parser parser = new Parser(typeName);
173 return new TypeNameParser(ref parser, true);
175 catch (ArgumentException)
177 return new TypeNameParser();
182 private TypeNameParser(ref Parser parser, bool withAssemblyName)
184 bool genericParameter = parser.pos != 0;
185 name = parser.NextNamePart();
187 parser.ParseNested(ref nested);
188 genericParameters = null;
189 parser.ParseGenericParameters(ref genericParameters);
191 parser.ParseModifiers(ref modifiers);
193 if (withAssemblyName)
195 parser.ParseAssemblyName(genericParameter, ref assemblyName);
201 get { return name == null; }
204 internal string FirstNamePart
209 internal string AssemblyName
211 get { return assemblyName; }
214 private struct Parser
216 private readonly string typeName;
219 internal Parser(string typeName)
221 this.typeName = typeName;
225 private void Check(bool condition)
229 throw new ArgumentException("Invalid type name '" + typeName + "'");
233 private void Consume(char c)
235 Check(pos < typeName.Length && typeName[pos++] == c);
238 private bool TryConsume(char c)
240 if (pos < typeName.Length && typeName[pos] == c)
251 internal string NextNamePart()
255 for (; pos < typeName.Length; pos++)
257 char c = typeName[pos];
261 Check(pos < typeName.Length && SpecialChars.IndexOf(typeName[pos]) != -1);
263 else if (SpecialChars.IndexOf(c) != -1)
268 Check(pos - start != 0);
269 if (start == 0 && pos == typeName.Length)
275 return typeName.Substring(start, pos - start);
279 internal void ParseNested(ref string[] nested)
281 while (TryConsume('+'))
283 Add(ref nested, NextNamePart());
287 internal void ParseGenericParameters(ref TypeNameParser[] genericParameters)
293 if (TryConsume(']') || TryConsume('*') || TryConsume(','))
295 // it's not a generic parameter list, but an array instead
304 Add(ref genericParameters, new TypeNameParser(ref this, true));
309 Add(ref genericParameters, new TypeNameParser(ref this, false));
312 while (TryConsume(','));
318 internal void ParseModifiers(ref short[] modifiers)
320 while (pos < typeName.Length)
322 switch (typeName[pos])
326 Add(ref modifiers, POINTER);
330 Add(ref modifiers, BYREF);
334 Add(ref modifiers, ParseArray());
344 internal void ParseAssemblyName(bool genericParameter, ref string assemblyName)
346 if (pos < typeName.Length)
348 if (typeName[pos] == ']' && genericParameter)
356 if (genericParameter)
359 while (pos < typeName.Length)
361 char c = typeName[pos];
365 // a backslash itself is not legal in an assembly name, so we don't need to check for an escaped backslash
366 Check(pos < typeName.Length && typeName[pos++] == ']');
377 Check(pos < typeName.Length && typeName[pos] == ']');
378 assemblyName = typeName.Substring(start, pos - start).Replace("\\]", "]");
382 // only when an assembly name is used in a generic type parameter, will it be escaped
383 assemblyName = typeName.Substring(pos);
385 Check(assemblyName.Length != 0);
390 Check(!genericParameter);
394 private short ParseArray()
397 Check(pos < typeName.Length);
398 char c = typeName[pos];
412 while (TryConsume(','))
414 Check(rank < short.MaxValue);
422 private void SkipWhiteSpace()
424 while (pos < typeName.Length && Char.IsWhiteSpace(typeName[pos]))
430 private static void Add<T>(ref T[] array, T elem)
434 array = new T[] { elem };
437 Array.Resize(ref array, array.Length + 1);
438 array[array.Length - 1] = elem;
442 internal Type GetType(Universe universe, Assembly context, bool throwOnError, string originalName)
445 if (assemblyName != null)
447 Assembly asm = universe.Load(assemblyName, context, throwOnError);
452 type = asm.GetTypeImpl(name);
454 else if (context == null)
456 type = universe.Mscorlib.GetTypeImpl(name);
460 type = context.GetTypeImpl(name);
461 if (type == null && context != universe.Mscorlib)
463 type = universe.Mscorlib.GetTypeImpl(name);
466 return Expand(type, context, throwOnError, originalName);
469 internal Type Expand(Type type, Assembly context, bool throwOnError, string originalName)
475 throw new TypeLoadException(originalName);
481 foreach (string nest in nested)
483 type = type.FindNestedType(TypeName.Split(TypeNameParser.Unescape(nest)));
488 throw new TypeLoadException(originalName);
494 if (genericParameters != null)
496 Type[] typeArgs = new Type[genericParameters.Length];
497 for (int i = 0; i < typeArgs.Length; i++)
499 typeArgs[i] = genericParameters[i].GetType(type.Assembly.universe, context, throwOnError, originalName);
500 if (typeArgs[i] == null)
505 type = type.MakeGenericType(typeArgs);
507 if (modifiers != null)
509 foreach (short modifier in modifiers)
514 type = type.MakeArrayType();
517 type = type.MakeByRefType();
520 type = type.MakePointerType();
523 type = type.MakeArrayType(modifier);