Free cookie consent management tool by TermsFeed Policy Generator

source: tags/3.3.11/HeuristicLab.ExtLibs/HeuristicLab.NRefactory/5.5.0/NRefactory-5.5.0/TypeSystem/ReflectionHelper.cs

Last change on this file was 11700, checked in by jkarder, 10 years ago

#2077: created branch and added first version

File size: 15.5 KB
Line 
1// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy of this
4// software and associated documentation files (the "Software"), to deal in the Software
5// without restriction, including without limitation the rights to use, copy, modify, merge,
6// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
7// to whom the Software is furnished to do so, subject to the following conditions:
8//
9// The above copyright notice and this permission notice shall be included in all copies or
10// substantial portions of the Software.
11//
12// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
13// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
15// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
17// DEALINGS IN THE SOFTWARE.
18
19using System;
20using System.Collections.Generic;
21using System.Text;
22using ICSharpCode.NRefactory.TypeSystem.Implementation;
23
24namespace ICSharpCode.NRefactory.TypeSystem
25{
26  /// <summary>
27  /// Static helper methods for reflection names.
28  /// </summary>
29  public static class ReflectionHelper
30  {
31    /// <summary>
32    /// A reflection class used to represent <c>null</c>.
33    /// </summary>
34    public sealed class Null {}
35   
36    /// <summary>
37    /// A reflection class used to represent <c>dynamic</c>.
38    /// </summary>
39    public sealed class Dynamic {}
40   
41    /// <summary>
42    /// A reflection class used to represent an unbound type argument.
43    /// </summary>
44    public sealed class UnboundTypeArgument {}
45   
46    #region ICompilation.FindType
47    /// <summary>
48    /// Retrieves the specified type in this compilation.
49    /// Returns <see cref="SpecialType.UnknownType"/> if the type cannot be found in this compilation.
50    /// </summary>
51    /// <remarks>
52    /// This method cannot be used with open types; all type parameters will be substituted
53    /// with <see cref="SpecialType.UnknownType"/>.
54    /// </remarks>
55    public static IType FindType(this ICompilation compilation, Type type)
56    {
57      return type.ToTypeReference().Resolve(compilation.TypeResolveContext);
58    }
59    #endregion
60   
61    #region Type.ToTypeReference()
62    /// <summary>
63    /// Creates a reference to the specified type.
64    /// </summary>
65    /// <param name="type">The type to be converted.</param>
66    /// <returns>Returns the type reference.</returns>
67    /// <remarks>
68    /// If the type is open (contains type parameters '`0' or '``0'),
69    /// an <see cref="ITypeResolveContext"/> with the appropriate CurrentTypeDefinition/CurrentMember is required
70    /// to resolve the type reference.
71    /// For closed types, the root type resolve context for the compilation is sufficient.
72    /// </remarks>
73    public static ITypeReference ToTypeReference(this Type type)
74    {
75      if (type == null)
76        return SpecialType.UnknownType;
77      if (type.IsGenericType && !type.IsGenericTypeDefinition) {
78        ITypeReference def = ToTypeReference(type.GetGenericTypeDefinition());
79        Type[] arguments = type.GetGenericArguments();
80        ITypeReference[] args = new ITypeReference[arguments.Length];
81        bool allUnbound = true;
82        for (int i = 0; i < arguments.Length; i++) {
83          args[i] = ToTypeReference(arguments[i]);
84          allUnbound &= args[i].Equals(SpecialType.UnboundTypeArgument);
85        }
86        if (allUnbound)
87          return def;
88        else
89          return new ParameterizedTypeReference(def, args);
90      } else if (type.IsArray) {
91        return new ArrayTypeReference(ToTypeReference(type.GetElementType()), type.GetArrayRank());
92      } else if (type.IsPointer) {
93        return new PointerTypeReference(ToTypeReference(type.GetElementType()));
94      } else if (type.IsByRef) {
95        return new ByReferenceTypeReference(ToTypeReference(type.GetElementType()));
96      } else if (type.IsGenericParameter) {
97        if (type.DeclaringMethod != null) {
98          return TypeParameterReference.Create(SymbolKind.Method, type.GenericParameterPosition);
99        } else {
100          return TypeParameterReference.Create(SymbolKind.TypeDefinition, type.GenericParameterPosition);
101        }
102      } else if (type.DeclaringType != null) {
103        if (type == typeof(Dynamic))
104          return SpecialType.Dynamic;
105        else if (type == typeof(Null))
106          return SpecialType.NullType;
107        else if (type == typeof(UnboundTypeArgument))
108          return SpecialType.UnboundTypeArgument;
109        ITypeReference baseTypeRef = ToTypeReference(type.DeclaringType);
110        int typeParameterCount;
111        string name = SplitTypeParameterCountFromReflectionName(type.Name, out typeParameterCount);
112        return new NestedTypeReference(baseTypeRef, name, typeParameterCount);
113      } else {
114        IAssemblyReference assemblyReference = new DefaultAssemblyReference(type.Assembly.FullName);
115        int typeParameterCount;
116        string name = SplitTypeParameterCountFromReflectionName(type.Name, out typeParameterCount);
117        return new GetClassTypeReference(assemblyReference, type.Namespace, name, typeParameterCount);
118      }
119    }
120    #endregion
121   
122    #region SplitTypeParameterCountFromReflectionName
123    /// <summary>
124    /// Removes the ` with type parameter count from the reflection name.
125    /// </summary>
126    /// <remarks>Do not use this method with the full name of inner classes.</remarks>
127    public static string SplitTypeParameterCountFromReflectionName(string reflectionName)
128    {
129      int pos = reflectionName.LastIndexOf('`');
130      if (pos < 0) {
131        return reflectionName;
132      } else {
133        return reflectionName.Substring(0, pos);
134      }
135    }
136   
137    /// <summary>
138    /// Removes the ` with type parameter count from the reflection name.
139    /// </summary>
140    /// <remarks>Do not use this method with the full name of inner classes.</remarks>
141    public static string SplitTypeParameterCountFromReflectionName(string reflectionName, out int typeParameterCount)
142    {
143      int pos = reflectionName.LastIndexOf('`');
144      if (pos < 0) {
145        typeParameterCount = 0;
146        return reflectionName;
147      } else {
148        string typeCount = reflectionName.Substring(pos + 1);
149        if (int.TryParse(typeCount, out typeParameterCount))
150          return reflectionName.Substring(0, pos);
151        else
152          return reflectionName;
153      }
154    }
155    #endregion
156   
157    #region TypeCode support
158    /// <summary>
159    /// Retrieves a built-in type using the specified type code.
160    /// </summary>
161    public static IType FindType(this ICompilation compilation, TypeCode typeCode)
162    {
163      return compilation.FindType((KnownTypeCode)typeCode);
164    }
165   
166    /// <summary>
167    /// Creates a reference to the specified type.
168    /// </summary>
169    /// <param name="typeCode">The type to be converted.</param>
170    /// <returns>Returns the type reference.</returns>
171    public static ITypeReference ToTypeReference(this TypeCode typeCode)
172    {
173      return KnownTypeReference.Get((KnownTypeCode)typeCode);
174    }
175   
176    /// <summary>
177    /// Gets the type code for the specified type, or TypeCode.Empty if none of the other type codes match.
178    /// </summary>
179    public static TypeCode GetTypeCode(IType type)
180    {
181      ITypeDefinition def = type as ITypeDefinition;
182      if (def != null) {
183        KnownTypeCode typeCode = def.KnownTypeCode;
184        if (typeCode <= KnownTypeCode.String && typeCode != KnownTypeCode.Void)
185          return (TypeCode)typeCode;
186        else
187          return TypeCode.Empty;
188      }
189      return TypeCode.Empty;
190    }
191    #endregion
192   
193    #region ParseReflectionName
194    /// <summary>
195    /// Parses a reflection name into a type reference.
196    /// </summary>
197    /// <param name="reflectionTypeName">The reflection name of the type.</param>
198    /// <returns>A type reference that represents the reflection name.</returns>
199    /// <exception cref="ReflectionNameParseException">The syntax of the reflection type name is invalid</exception>
200    /// <remarks>
201    /// If the type is open (contains type parameters '`0' or '``0'),
202    /// an <see cref="ITypeResolveContext"/> with the appropriate CurrentTypeDefinition/CurrentMember is required
203    /// to resolve the reference to the ITypeParameter.
204    /// For looking up closed, assembly qualified type names, the root type resolve context for the compilation
205    /// is sufficient.
206    /// When looking up a type name that isn't assembly qualified, the type reference will look in
207    /// <see cref="ITypeResolveContext.CurrentAssembly"/> first, and if the type is not found there,
208    /// it will look in all other assemblies of the compilation.
209    /// </remarks>
210    /// <seealso cref="FullTypeName(string)"/>
211    public static ITypeReference ParseReflectionName(string reflectionTypeName)
212    {
213      if (reflectionTypeName == null)
214        throw new ArgumentNullException("reflectionTypeName");
215      int pos = 0;
216      ITypeReference r = ParseReflectionName(reflectionTypeName, ref pos);
217      if (pos < reflectionTypeName.Length)
218        throw new ReflectionNameParseException(pos, "Expected end of type name");
219      return r;
220    }
221   
222    static bool IsReflectionNameSpecialCharacter(char c)
223    {
224      switch (c) {
225        case '+':
226        case '`':
227        case '[':
228        case ']':
229        case ',':
230        case '*':
231        case '&':
232          return true;
233        default:
234          return false;
235      }
236    }
237   
238    static ITypeReference ParseReflectionName(string reflectionTypeName, ref int pos)
239    {
240      if (pos == reflectionTypeName.Length)
241        throw new ReflectionNameParseException(pos, "Unexpected end");
242      ITypeReference reference;
243      if (reflectionTypeName[pos] == '`') {
244        // type parameter reference
245        pos++;
246        if (pos == reflectionTypeName.Length)
247          throw new ReflectionNameParseException(pos, "Unexpected end");
248        if (reflectionTypeName[pos] == '`') {
249          // method type parameter reference
250          pos++;
251          int index = ReadTypeParameterCount(reflectionTypeName, ref pos);
252          reference = TypeParameterReference.Create(SymbolKind.Method, index);
253        } else {
254          // class type parameter reference
255          int index = ReadTypeParameterCount(reflectionTypeName, ref pos);
256          reference = TypeParameterReference.Create(SymbolKind.TypeDefinition, index);
257        }
258      } else {
259        // not a type parameter reference: read the actual type name
260        int tpc;
261        string typeName = ReadTypeName(reflectionTypeName, ref pos, out tpc);
262        string assemblyName = SkipAheadAndReadAssemblyName(reflectionTypeName, pos);
263        reference = CreateGetClassTypeReference(assemblyName, typeName, tpc);
264      }
265      // read type suffixes
266      while (pos < reflectionTypeName.Length) {
267        switch (reflectionTypeName[pos++]) {
268          case '+':
269            int tpc;
270            string typeName = ReadTypeName(reflectionTypeName, ref pos, out tpc);
271            reference = new NestedTypeReference(reference, typeName, tpc);
272            break;
273          case '*':
274            reference = new PointerTypeReference(reference);
275            break;
276          case '&':
277            reference = new ByReferenceTypeReference(reference);
278            break;
279          case '[':
280            // this might be an array or a generic type
281            if (pos == reflectionTypeName.Length)
282              throw new ReflectionNameParseException(pos, "Unexpected end");
283            if (reflectionTypeName[pos] == '[') {
284              // it's a generic type
285              List<ITypeReference> typeArguments = new List<ITypeReference>();
286              pos++;
287              typeArguments.Add(ParseReflectionName(reflectionTypeName, ref pos));
288              if (pos < reflectionTypeName.Length && reflectionTypeName[pos] == ']')
289                pos++;
290              else
291                throw new ReflectionNameParseException(pos, "Expected end of type argument");
292             
293              while (pos < reflectionTypeName.Length && reflectionTypeName[pos] == ',') {
294                pos++;
295                if (pos < reflectionTypeName.Length && reflectionTypeName[pos] == '[')
296                  pos++;
297                else
298                  throw new ReflectionNameParseException(pos, "Expected another type argument");
299               
300                typeArguments.Add(ParseReflectionName(reflectionTypeName, ref pos));
301               
302                if (pos < reflectionTypeName.Length && reflectionTypeName[pos] == ']')
303                  pos++;
304                else
305                  throw new ReflectionNameParseException(pos, "Expected end of type argument");
306              }
307             
308              if (pos < reflectionTypeName.Length && reflectionTypeName[pos] == ']') {
309                pos++;
310                reference = new ParameterizedTypeReference(reference, typeArguments);
311              } else {
312                throw new ReflectionNameParseException(pos, "Expected end of generic type");
313              }
314            } else {
315              // it's an array
316              int dimensions = 1;
317              while (pos < reflectionTypeName.Length && reflectionTypeName[pos] == ',') {
318                dimensions++;
319                pos++;
320              }
321              if (pos < reflectionTypeName.Length && reflectionTypeName[pos] == ']') {
322                pos++; // end of array
323                reference = new ArrayTypeReference(reference, dimensions);
324              } else {
325                throw new ReflectionNameParseException(pos, "Invalid array modifier");
326              }
327            }
328            break;
329          case ',':
330            // assembly qualified name, ignore everything up to the end/next ']'
331            while (pos < reflectionTypeName.Length && reflectionTypeName[pos] != ']')
332              pos++;
333            break;
334          default:
335            pos--; // reset pos to the character we couldn't read
336            if (reflectionTypeName[pos] == ']')
337              return reference; // return from a nested generic
338            else
339              throw new ReflectionNameParseException(pos, "Unexpected character: '" + reflectionTypeName[pos] + "'");
340        }
341      }
342      return reference;
343    }
344   
345    static ITypeReference CreateGetClassTypeReference(string assemblyName, string typeName, int tpc)
346    {
347      IAssemblyReference assemblyReference;
348      if (assemblyName != null) {
349        assemblyReference = new DefaultAssemblyReference(assemblyName);
350      } else {
351        assemblyReference = null;
352      }
353      int pos = typeName.LastIndexOf('.');
354      if (pos < 0)
355        return new GetClassTypeReference(assemblyReference, string.Empty, typeName, tpc);
356      else
357        return new GetClassTypeReference(assemblyReference, typeName.Substring(0, pos), typeName.Substring(pos + 1), tpc);
358    }
359   
360    static string SkipAheadAndReadAssemblyName(string reflectionTypeName, int pos)
361    {
362      int nestingLevel = 0;
363      while (pos < reflectionTypeName.Length) {
364        switch (reflectionTypeName[pos++]) {
365          case '[':
366            nestingLevel++;
367            break;
368          case ']':
369            if (nestingLevel == 0)
370              return null;
371            nestingLevel--;
372            break;
373          case ',':
374            if (nestingLevel == 0) {
375              // first skip the whitespace
376              while (pos < reflectionTypeName.Length && reflectionTypeName[pos] == ' ')
377                pos++;
378              // everything up to the end/next ']' is the assembly name
379              int endPos = pos;
380              while (endPos < reflectionTypeName.Length && reflectionTypeName[endPos] != ']')
381                endPos++;
382              return reflectionTypeName.Substring(pos, endPos - pos);
383            }
384            break;
385        }
386      }
387      return null;
388    }
389   
390    static string ReadTypeName(string reflectionTypeName, ref int pos, out int tpc)
391    {
392      int startPos = pos;
393      // skip the simple name portion:
394      while (pos < reflectionTypeName.Length && !IsReflectionNameSpecialCharacter(reflectionTypeName[pos]))
395        pos++;
396      if (pos == startPos)
397        throw new ReflectionNameParseException(pos, "Expected type name");
398      string typeName = reflectionTypeName.Substring(startPos, pos - startPos);
399      if (pos < reflectionTypeName.Length && reflectionTypeName[pos] == '`') {
400        pos++;
401        tpc = ReadTypeParameterCount(reflectionTypeName, ref pos);
402      } else {
403        tpc = 0;
404      }
405      return typeName;
406    }
407   
408    internal static int ReadTypeParameterCount(string reflectionTypeName, ref int pos)
409    {
410      int startPos = pos;
411      while (pos < reflectionTypeName.Length) {
412        char c = reflectionTypeName[pos];
413        if (c < '0' || c > '9')
414          break;
415        pos++;
416      }
417      int tpc;
418      if (!int.TryParse(reflectionTypeName.Substring(startPos, pos - startPos), out tpc))
419        throw new ReflectionNameParseException(pos, "Expected type parameter count");
420      return tpc;
421    }
422    #endregion
423  }
424}
Note: See TracBrowser for help on using the repository browser.