2 Copyright (C) 2009 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
32 private const string SpecialChars = "\\+,[]*&";
33 private const short SZARRAY = -1;
34 private const short BYREF = -2;
35 private const short POINTER = -3;
36 private readonly string name;
37 private readonly string[] nested;
38 private readonly string assemblyName;
39 private readonly short[] modifiers;
40 private readonly TypeNameParser[] genericParameters;
42 internal static string Escape(string name)
48 StringBuilder sb = null;
49 for (int pos = 0; pos < name.Length; pos++)
52 if (SpecialChars.IndexOf(c) != -1)
56 sb = new StringBuilder(name, 0, pos, name.Length + 3);
58 sb.Append('\\').Append(c);
65 return sb != null ? sb.ToString() : name;
68 internal static string Unescape(string name)
70 int pos = name.IndexOf('\\');
75 StringBuilder sb = new StringBuilder(name, 0, pos, name.Length - 1);
76 for (; pos < name.Length; pos++)
88 internal static TypeNameParser Parse(string typeName, bool throwOnError)
92 Parser parser = new Parser(typeName);
93 return new TypeNameParser(ref parser, true);
99 Parser parser = new Parser(typeName);
100 return new TypeNameParser(ref parser, true);
102 catch (ArgumentException)
104 return new TypeNameParser();
109 private TypeNameParser(ref Parser parser, bool withAssemblyName)
111 bool genericParameter = parser.pos != 0;
112 name = parser.NextNamePart();
114 parser.ParseNested(ref nested);
115 genericParameters = null;
116 parser.ParseGenericParameters(ref genericParameters);
118 parser.ParseModifiers(ref modifiers);
120 if (withAssemblyName)
122 parser.ParseAssemblyName(genericParameter, ref assemblyName);
128 get { return name == null; }
131 internal string FirstNamePart
136 internal string AssemblyName
138 get { return assemblyName; }
141 private struct Parser
143 private readonly string typeName;
146 internal Parser(string typeName)
148 this.typeName = typeName;
152 private void Check(bool condition)
156 throw new ArgumentException("Invalid type name '" + typeName + "'");
160 private void Consume(char c)
162 Check(pos < typeName.Length && typeName[pos++] == c);
165 private bool TryConsume(char c)
167 if (pos < typeName.Length && typeName[pos] == c)
178 internal string NextNamePart()
182 for (; pos < typeName.Length; pos++)
184 char c = typeName[pos];
188 Check(pos < typeName.Length && SpecialChars.IndexOf(typeName[pos]) != -1);
190 else if (SpecialChars.IndexOf(c) != -1)
195 Check(pos - start != 0);
196 if (start == 0 && pos == typeName.Length)
202 return typeName.Substring(start, pos - start);
206 internal void ParseNested(ref string[] nested)
208 while (TryConsume('+'))
210 Add(ref nested, NextNamePart());
214 internal void ParseGenericParameters(ref TypeNameParser[] genericParameters)
220 if (TryConsume(']') || TryConsume('*') || TryConsume(','))
222 // it's not a generic parameter list, but an array instead
231 Add(ref genericParameters, new TypeNameParser(ref this, true));
236 Add(ref genericParameters, new TypeNameParser(ref this, false));
239 while (TryConsume(','));
245 internal void ParseModifiers(ref short[] modifiers)
247 while (pos < typeName.Length)
249 switch (typeName[pos])
253 Add(ref modifiers, POINTER);
257 Add(ref modifiers, BYREF);
261 Add(ref modifiers, ParseArray());
271 internal void ParseAssemblyName(bool genericParameter, ref string assemblyName)
273 if (pos < typeName.Length)
275 if (typeName[pos] == ']' && genericParameter)
283 if (genericParameter)
286 while (pos < typeName.Length)
288 char c = typeName[pos];
292 // a backslash itself is not legal in an assembly name, so we don't need to check for an escaped backslash
293 Check(pos < typeName.Length && typeName[pos++] == ']');
304 Check(pos < typeName.Length && typeName[pos] == ']');
305 assemblyName = typeName.Substring(start, pos - start).Replace("\\]", "]");
309 // only when an assembly name is used in a generic type parameter, will it be escaped
310 assemblyName = typeName.Substring(pos);
312 Check(assemblyName.Length != 0);
317 Check(!genericParameter);
321 private short ParseArray()
324 Check(pos < typeName.Length);
325 char c = typeName[pos];
339 while (TryConsume(','))
341 Check(rank < short.MaxValue);
349 private void SkipWhiteSpace()
351 while (pos < typeName.Length && Char.IsWhiteSpace(typeName[pos]))
357 private static void Add<T>(ref T[] array, T elem)
361 array = new T[] { elem };
364 Array.Resize(ref array, array.Length + 1);
365 array[array.Length - 1] = elem;
369 internal Type GetType(Universe universe, Assembly context, bool throwOnError, string originalName)
372 if (assemblyName != null)
374 Assembly asm = universe.Load(assemblyName, context, throwOnError);
379 type = asm.GetTypeImpl(name);
381 else if (context == null)
383 type = universe.Mscorlib.GetTypeImpl(name);
387 type = context.GetTypeImpl(name);
388 if (type == null && context != universe.Mscorlib)
390 type = universe.Mscorlib.GetTypeImpl(name);
393 return Expand(type, context, throwOnError, originalName);
396 internal Type Expand(Type type, Assembly context, bool throwOnError, string originalName)
402 throw new TypeLoadException(originalName);
408 foreach (string nest in nested)
410 type = type.GetNestedTypeCorrectly(TypeNameParser.Unescape(nest));
415 throw new TypeLoadException(originalName);
421 if (genericParameters != null)
423 Type[] typeArgs = new Type[genericParameters.Length];
424 for (int i = 0; i < typeArgs.Length; i++)
426 typeArgs[i] = genericParameters[i].GetType(type.Assembly.universe, context, throwOnError, originalName);
427 if (typeArgs[i] == null)
432 type = type.MakeGenericType(typeArgs);
434 if (modifiers != null)
436 foreach (short modifier in modifiers)
441 type = type.MakeArrayType();
444 type = type.MakeByRefType();
447 type = type.MakePointerType();
450 type = type.MakeArrayType(modifier);