mirror of https://github.com/icsharpcode/ILSpy.git
				
				
			
			You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							427 lines
						
					
					
						
							16 KiB
						
					
					
				
			
		
		
	
	
							427 lines
						
					
					
						
							16 KiB
						
					
					
				// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team | 
						|
//  | 
						|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this | 
						|
// software and associated documentation files (the "Software"), to deal in the Software | 
						|
// without restriction, including without limitation the rights to use, copy, modify, merge, | 
						|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons | 
						|
// to whom the Software is furnished to do so, subject to the following conditions: | 
						|
//  | 
						|
// The above copyright notice and this permission notice shall be included in all copies or | 
						|
// substantial portions of the Software. | 
						|
//  | 
						|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | 
						|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR | 
						|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE | 
						|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR | 
						|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | 
						|
// DEALINGS IN THE SOFTWARE. | 
						|
 | 
						|
using System; | 
						|
using System.Collections.Generic; | 
						|
using ICSharpCode.Decompiler.TypeSystem.Implementation; | 
						|
 | 
						|
namespace ICSharpCode.Decompiler.TypeSystem | 
						|
{ | 
						|
	/// <summary> | 
						|
	/// Static helper methods for reflection names. | 
						|
	/// </summary> | 
						|
	public static class ReflectionHelper | 
						|
	{ | 
						|
		/// <summary> | 
						|
		/// A reflection class used to represent <c>null</c>. | 
						|
		/// </summary> | 
						|
		public sealed class Null {} | 
						|
		 | 
						|
		/// <summary> | 
						|
		/// A reflection class used to represent <c>dynamic</c>. | 
						|
		/// </summary> | 
						|
		public sealed class Dynamic {} | 
						|
		 | 
						|
		/// <summary> | 
						|
		/// A reflection class used to represent an unbound type argument. | 
						|
		/// </summary> | 
						|
		public sealed class UnboundTypeArgument {} | 
						|
		 | 
						|
		#region ICompilation.FindType | 
						|
		/// <summary> | 
						|
		/// Retrieves the specified type in this compilation. | 
						|
		/// Returns <see cref="SpecialType.UnknownType"/> if the type cannot be found in this compilation. | 
						|
		/// </summary> | 
						|
		/// <remarks> | 
						|
		/// This method cannot be used with open types; all type parameters will be substituted | 
						|
		/// with <see cref="SpecialType.UnknownType"/>. | 
						|
		/// </remarks> | 
						|
		public static IType FindType(this ICompilation compilation, Type type) | 
						|
		{ | 
						|
			return type.ToTypeReference().Resolve(compilation.TypeResolveContext); | 
						|
		} | 
						|
		#endregion | 
						|
		 | 
						|
		#region Type.ToTypeReference() | 
						|
		/// <summary> | 
						|
		/// Creates a reference to the specified type. | 
						|
		/// </summary> | 
						|
		/// <param name="type">The type to be converted.</param> | 
						|
		/// <returns>Returns the type reference.</returns> | 
						|
		/// <remarks> | 
						|
		/// If the type is open (contains type parameters '`0' or '``0'), | 
						|
		/// an <see cref="ITypeResolveContext"/> with the appropriate CurrentTypeDefinition/CurrentMember is required | 
						|
		/// to resolve the type reference. | 
						|
		/// For closed types, the root type resolve context for the compilation is sufficient. | 
						|
		/// </remarks> | 
						|
		public static ITypeReference ToTypeReference(this Type type) | 
						|
		{ | 
						|
			if (type == null) | 
						|
				return SpecialType.UnknownType; | 
						|
			if (type.IsGenericType && !type.IsGenericTypeDefinition) { | 
						|
				ITypeReference def = ToTypeReference(type.GetGenericTypeDefinition()); | 
						|
				Type[] arguments = type.GetGenericArguments(); | 
						|
				ITypeReference[] args = new ITypeReference[arguments.Length]; | 
						|
				bool allUnbound = true; | 
						|
				for (int i = 0; i < arguments.Length; i++) { | 
						|
					args[i] = ToTypeReference(arguments[i]); | 
						|
					allUnbound &= args[i].Equals(SpecialType.UnboundTypeArgument); | 
						|
				} | 
						|
				if (allUnbound) | 
						|
					return def; | 
						|
				else | 
						|
					return new ParameterizedTypeReference(def, args); | 
						|
			} else if (type.IsArray) { | 
						|
				return new ArrayTypeReference(ToTypeReference(type.GetElementType()), type.GetArrayRank()); | 
						|
			} else if (type.IsPointer) { | 
						|
				return new PointerTypeReference(ToTypeReference(type.GetElementType())); | 
						|
			} else if (type.IsByRef) { | 
						|
				return new ByReferenceTypeReference(ToTypeReference(type.GetElementType())); | 
						|
			} else if (type.IsGenericParameter) { | 
						|
				if (type.DeclaringMethod != null) { | 
						|
					return TypeParameterReference.Create(SymbolKind.Method, type.GenericParameterPosition); | 
						|
				} else { | 
						|
					return TypeParameterReference.Create(SymbolKind.TypeDefinition, type.GenericParameterPosition); | 
						|
				} | 
						|
			} else if (type.DeclaringType != null) { | 
						|
				if (type == typeof(Dynamic)) | 
						|
					return SpecialType.Dynamic; | 
						|
				else if (type == typeof(Null)) | 
						|
					return SpecialType.NullType; | 
						|
				else if (type == typeof(UnboundTypeArgument)) | 
						|
					return SpecialType.UnboundTypeArgument; | 
						|
				ITypeReference baseTypeRef = ToTypeReference(type.DeclaringType); | 
						|
				int typeParameterCount; | 
						|
				string name = SplitTypeParameterCountFromReflectionName(type.Name, out typeParameterCount); | 
						|
				return new NestedTypeReference(baseTypeRef, name, typeParameterCount); | 
						|
			} else { | 
						|
				IAssemblyReference assemblyReference = new DefaultAssemblyReference(type.Assembly.FullName); | 
						|
				int typeParameterCount; | 
						|
				string name = SplitTypeParameterCountFromReflectionName(type.Name, out typeParameterCount); | 
						|
				return new GetClassTypeReference(assemblyReference, type.Namespace, name, typeParameterCount); | 
						|
			} | 
						|
		} | 
						|
		#endregion | 
						|
		 | 
						|
		#region SplitTypeParameterCountFromReflectionName | 
						|
		/// <summary> | 
						|
		/// Removes the ` with type parameter count from the reflection name. | 
						|
		/// </summary> | 
						|
		/// <remarks>Do not use this method with the full name of inner classes.</remarks> | 
						|
		public static string SplitTypeParameterCountFromReflectionName(string reflectionName) | 
						|
		{ | 
						|
			int pos = reflectionName.LastIndexOf('`'); | 
						|
			if (pos < 0) { | 
						|
				return reflectionName; | 
						|
			} else { | 
						|
				return reflectionName.Substring(0, pos); | 
						|
			} | 
						|
		} | 
						|
		 | 
						|
		/// <summary> | 
						|
		/// Removes the ` with type parameter count from the reflection name. | 
						|
		/// </summary> | 
						|
		/// <remarks>Do not use this method with the full name of inner classes.</remarks> | 
						|
		public static string SplitTypeParameterCountFromReflectionName(string reflectionName, out int typeParameterCount) | 
						|
		{ | 
						|
			int pos = reflectionName.LastIndexOf('`'); | 
						|
			if (pos < 0) { | 
						|
				typeParameterCount = 0; | 
						|
				return reflectionName; | 
						|
			} else { | 
						|
				string typeCount = reflectionName.Substring(pos + 1); | 
						|
				if (int.TryParse(typeCount, out typeParameterCount)) | 
						|
					return reflectionName.Substring(0, pos); | 
						|
				else | 
						|
					return reflectionName; | 
						|
			} | 
						|
		} | 
						|
		#endregion | 
						|
		 | 
						|
		#region TypeCode support | 
						|
		/// <summary> | 
						|
		/// Retrieves a built-in type using the specified type code. | 
						|
		/// </summary> | 
						|
		public static IType FindType(this ICompilation compilation, TypeCode typeCode) | 
						|
		{ | 
						|
			return compilation.FindType((KnownTypeCode)typeCode); | 
						|
		} | 
						|
		 | 
						|
		/// <summary> | 
						|
		/// Creates a reference to the specified type. | 
						|
		/// </summary> | 
						|
		/// <param name="typeCode">The type to be converted.</param> | 
						|
		/// <returns>Returns the type reference.</returns> | 
						|
		public static ITypeReference ToTypeReference(this TypeCode typeCode) | 
						|
		{ | 
						|
			return KnownTypeReference.Get((KnownTypeCode)typeCode); | 
						|
		} | 
						|
		 | 
						|
		/// <summary> | 
						|
		/// Gets the type code for the specified type, or TypeCode.Empty if none of the other type codes match. | 
						|
		/// </summary> | 
						|
		public static TypeCode GetTypeCode(this IType type) | 
						|
		{ | 
						|
			ITypeDefinition def = type as ITypeDefinition; | 
						|
			if (def != null) { | 
						|
				KnownTypeCode typeCode = def.KnownTypeCode; | 
						|
				if (typeCode <= KnownTypeCode.String && typeCode != KnownTypeCode.Void) | 
						|
					return (TypeCode)typeCode; | 
						|
				else | 
						|
					return TypeCode.Empty; | 
						|
			} | 
						|
			return TypeCode.Empty; | 
						|
		} | 
						|
		#endregion | 
						|
		 | 
						|
		#region ParseReflectionName | 
						|
		/// <summary> | 
						|
		/// Parses a reflection name into a type reference. | 
						|
		/// </summary> | 
						|
		/// <param name="reflectionTypeName">The reflection name of the type.</param> | 
						|
		/// <returns>A type reference that represents the reflection name.</returns> | 
						|
		/// <exception cref="ReflectionNameParseException">The syntax of the reflection type name is invalid</exception> | 
						|
		/// <remarks> | 
						|
		/// If the type is open (contains type parameters '`0' or '``0'), | 
						|
		/// an <see cref="ITypeResolveContext"/> with the appropriate CurrentTypeDefinition/CurrentMember is required | 
						|
		/// to resolve the reference to the ITypeParameter. | 
						|
		/// For looking up closed, assembly qualified type names, the root type resolve context for the compilation | 
						|
		/// is sufficient. | 
						|
		/// When looking up a type name that isn't assembly qualified, the type reference will look in | 
						|
		/// <see cref="ITypeResolveContext.CurrentAssembly"/> first, and if the type is not found there, | 
						|
		/// it will look in all other assemblies of the compilation. | 
						|
		/// </remarks> | 
						|
		/// <seealso cref="FullTypeName(string)"/> | 
						|
		public static ITypeReference ParseReflectionName(string reflectionTypeName) | 
						|
		{ | 
						|
			if (reflectionTypeName == null) | 
						|
				throw new ArgumentNullException("reflectionTypeName"); | 
						|
			int pos = 0; | 
						|
			ITypeReference r = ParseReflectionName(reflectionTypeName, ref pos); | 
						|
			if (pos < reflectionTypeName.Length) | 
						|
				throw new ReflectionNameParseException(pos, "Expected end of type name"); | 
						|
			return r; | 
						|
		} | 
						|
		 | 
						|
		static bool IsReflectionNameSpecialCharacter(char c) | 
						|
		{ | 
						|
			switch (c) { | 
						|
				case '+': | 
						|
				case '`': | 
						|
				case '[': | 
						|
				case ']': | 
						|
				case ',': | 
						|
				case '*': | 
						|
				case '&': | 
						|
					return true; | 
						|
				default: | 
						|
					return false; | 
						|
			} | 
						|
		} | 
						|
		 | 
						|
		/// <summary> | 
						|
		/// Parses the reflection name starting at pos. | 
						|
		/// If local is true, only parses local type names, not assembly qualified type names. | 
						|
		/// </summary> | 
						|
		static ITypeReference ParseReflectionName(string reflectionTypeName, ref int pos, bool local=false) | 
						|
		{ | 
						|
			if (pos == reflectionTypeName.Length) | 
						|
				throw new ReflectionNameParseException(pos, "Unexpected end"); | 
						|
			ITypeReference reference; | 
						|
			if (reflectionTypeName[pos] == '`') { | 
						|
				// type parameter reference | 
						|
				pos++; | 
						|
				if (pos == reflectionTypeName.Length) | 
						|
					throw new ReflectionNameParseException(pos, "Unexpected end"); | 
						|
				if (reflectionTypeName[pos] == '`') { | 
						|
					// method type parameter reference | 
						|
					pos++; | 
						|
					int index = ReadTypeParameterCount(reflectionTypeName, ref pos); | 
						|
					reference = TypeParameterReference.Create(SymbolKind.Method, index); | 
						|
				} else { | 
						|
					// class type parameter reference | 
						|
					int index = ReadTypeParameterCount(reflectionTypeName, ref pos); | 
						|
					reference = TypeParameterReference.Create(SymbolKind.TypeDefinition, index); | 
						|
				} | 
						|
			} else { | 
						|
				// not a type parameter reference: read the actual type name | 
						|
				string typeName = ReadTypeName(reflectionTypeName, ref pos, out int tpc); | 
						|
				string assemblyName = local ? null : SkipAheadAndReadAssemblyName(reflectionTypeName, pos); | 
						|
				reference = CreateGetClassTypeReference(assemblyName, typeName, tpc); | 
						|
			} | 
						|
			// read type suffixes | 
						|
			while (pos < reflectionTypeName.Length) { | 
						|
				switch (reflectionTypeName[pos++]) { | 
						|
					case '+': | 
						|
						int tpc; | 
						|
						string typeName = ReadTypeName(reflectionTypeName, ref pos, out tpc); | 
						|
						reference = new NestedTypeReference(reference, typeName, tpc); | 
						|
						break; | 
						|
					case '*': | 
						|
						reference = new PointerTypeReference(reference); | 
						|
						break; | 
						|
					case '&': | 
						|
						reference = new ByReferenceTypeReference(reference); | 
						|
						break; | 
						|
					case '[': | 
						|
						// this might be an array or a generic type | 
						|
						if (pos == reflectionTypeName.Length) | 
						|
							throw new ReflectionNameParseException(pos, "Unexpected end"); | 
						|
						if (reflectionTypeName[pos] != ']' && reflectionTypeName[pos] != ',') { | 
						|
							// it's a generic type | 
						|
							List<ITypeReference> typeArguments = new List<ITypeReference>(); | 
						|
							bool first = true; | 
						|
							while (first || pos < reflectionTypeName.Length && reflectionTypeName[pos] == ',') { | 
						|
								if (first) { | 
						|
									first = false; | 
						|
								} else { | 
						|
									pos++; // skip ',' | 
						|
								} | 
						|
								if (pos < reflectionTypeName.Length && reflectionTypeName[pos] == '[') { | 
						|
									// non-local type names are enclosed in another set of [] | 
						|
									pos++; | 
						|
 | 
						|
									typeArguments.Add(ParseReflectionName(reflectionTypeName, ref pos)); | 
						|
 | 
						|
									if (pos < reflectionTypeName.Length && reflectionTypeName[pos] == ']') | 
						|
										pos++; | 
						|
									else | 
						|
										throw new ReflectionNameParseException(pos, "Expected end of type argument"); | 
						|
								} else { | 
						|
									// local type names occur directly in the outer [] | 
						|
									typeArguments.Add(ParseReflectionName(reflectionTypeName, ref pos, local: true)); | 
						|
								} | 
						|
							} | 
						|
							 | 
						|
							if (pos < reflectionTypeName.Length && reflectionTypeName[pos] == ']') { | 
						|
								pos++; | 
						|
								reference = new ParameterizedTypeReference(reference, typeArguments); | 
						|
							} else { | 
						|
								throw new ReflectionNameParseException(pos, "Expected end of generic type"); | 
						|
							} | 
						|
						} else { | 
						|
							// it's an array | 
						|
							int dimensions = 1; | 
						|
							while (pos < reflectionTypeName.Length && reflectionTypeName[pos] == ',') { | 
						|
								dimensions++; | 
						|
								pos++; | 
						|
							} | 
						|
							if (pos < reflectionTypeName.Length && reflectionTypeName[pos] == ']') { | 
						|
								pos++; // end of array | 
						|
								reference = new ArrayTypeReference(reference, dimensions); | 
						|
							} else { | 
						|
								throw new ReflectionNameParseException(pos, "Invalid array modifier"); | 
						|
							} | 
						|
						} | 
						|
						break; | 
						|
					case ',' when !local: | 
						|
						// assembly qualified name, ignore everything up to the end/next ']' | 
						|
						while (pos < reflectionTypeName.Length && reflectionTypeName[pos] != ']') | 
						|
							pos++; | 
						|
						break; | 
						|
					default: | 
						|
						pos--; // reset pos to the character we couldn't read | 
						|
						if (reflectionTypeName[pos] == ']' || reflectionTypeName[pos] == ',') | 
						|
							return reference; // return from a nested generic | 
						|
						else | 
						|
							throw new ReflectionNameParseException(pos, "Unexpected character: '" + reflectionTypeName[pos] + "'"); | 
						|
				} | 
						|
			} | 
						|
			return reference; | 
						|
		} | 
						|
		 | 
						|
		static ITypeReference CreateGetClassTypeReference(string assemblyName, string typeName, int tpc) | 
						|
		{ | 
						|
			IAssemblyReference assemblyReference; | 
						|
			if (assemblyName != null) { | 
						|
				assemblyReference = new DefaultAssemblyReference(assemblyName); | 
						|
			} else { | 
						|
				assemblyReference = null; | 
						|
			} | 
						|
			int pos = typeName.LastIndexOf('.'); | 
						|
			if (pos < 0) | 
						|
				return new GetClassTypeReference(assemblyReference, string.Empty, typeName, tpc); | 
						|
			else | 
						|
				return new GetClassTypeReference(assemblyReference, typeName.Substring(0, pos), typeName.Substring(pos + 1), tpc); | 
						|
		} | 
						|
		 | 
						|
		static string SkipAheadAndReadAssemblyName(string reflectionTypeName, int pos) | 
						|
		{ | 
						|
			int nestingLevel = 0; | 
						|
			while (pos < reflectionTypeName.Length) { | 
						|
				switch (reflectionTypeName[pos++]) { | 
						|
					case '[': | 
						|
						nestingLevel++; | 
						|
						break; | 
						|
					case ']': | 
						|
						if (nestingLevel == 0) | 
						|
							return null; | 
						|
						nestingLevel--; | 
						|
						break; | 
						|
					case ',': | 
						|
						if (nestingLevel == 0) { | 
						|
							// first skip the whitespace | 
						|
							while (pos < reflectionTypeName.Length && reflectionTypeName[pos] == ' ') | 
						|
								pos++; | 
						|
							// everything up to the end/next ']' is the assembly name | 
						|
							int endPos = pos; | 
						|
							while (endPos < reflectionTypeName.Length && reflectionTypeName[endPos] != ']') | 
						|
								endPos++; | 
						|
							return reflectionTypeName.Substring(pos, endPos - pos); | 
						|
						} | 
						|
						break; | 
						|
				} | 
						|
			} | 
						|
			return null; | 
						|
		} | 
						|
		 | 
						|
		static string ReadTypeName(string reflectionTypeName, ref int pos, out int tpc) | 
						|
		{ | 
						|
			int startPos = pos; | 
						|
			// skip the simple name portion: | 
						|
			while (pos < reflectionTypeName.Length && !IsReflectionNameSpecialCharacter(reflectionTypeName[pos])) | 
						|
				pos++; | 
						|
			if (pos == startPos) | 
						|
				throw new ReflectionNameParseException(pos, "Expected type name"); | 
						|
			string typeName = reflectionTypeName.Substring(startPos, pos - startPos); | 
						|
			if (pos < reflectionTypeName.Length && reflectionTypeName[pos] == '`') { | 
						|
				pos++; | 
						|
				tpc = ReadTypeParameterCount(reflectionTypeName, ref pos); | 
						|
			} else { | 
						|
				tpc = 0; | 
						|
			} | 
						|
			return typeName; | 
						|
		} | 
						|
		 | 
						|
		internal static int ReadTypeParameterCount(string reflectionTypeName, ref int pos) | 
						|
		{ | 
						|
			int startPos = pos; | 
						|
			while (pos < reflectionTypeName.Length) { | 
						|
				char c = reflectionTypeName[pos]; | 
						|
				if (c < '0' || c > '9') | 
						|
					break; | 
						|
				pos++; | 
						|
			} | 
						|
			int tpc; | 
						|
			if (!int.TryParse(reflectionTypeName.Substring(startPos, pos - startPos), out tpc)) | 
						|
				throw new ReflectionNameParseException(pos, "Expected type parameter count"); | 
						|
			return tpc; | 
						|
		} | 
						|
		#endregion | 
						|
	} | 
						|
}
 | 
						|
 |