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.
		
		
		
		
		
			
		
			
				
					
					
						
							276 lines
						
					
					
						
							8.3 KiB
						
					
					
				
			
		
		
	
	
							276 lines
						
					
					
						
							8.3 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.Collections.Generic; | 
						|
using System.Text; | 
						|
 | 
						|
using ICSharpCode.Decompiler.TypeSystem.Implementation; | 
						|
 | 
						|
namespace ICSharpCode.Decompiler.TypeSystem | 
						|
{ | 
						|
	/// <summary> | 
						|
	/// Substitutes class and method type parameters. | 
						|
	/// </summary> | 
						|
	public class TypeParameterSubstitution : TypeVisitor | 
						|
	{ | 
						|
		/// <summary> | 
						|
		/// The identity function. | 
						|
		/// </summary> | 
						|
		public static readonly TypeParameterSubstitution Identity = new TypeParameterSubstitution(null, null); | 
						|
 | 
						|
		readonly IReadOnlyList<IType> classTypeArguments; | 
						|
		readonly IReadOnlyList<IType> methodTypeArguments; | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Creates a new type parameter substitution. | 
						|
		/// </summary> | 
						|
		/// <param name="classTypeArguments"> | 
						|
		/// The type arguments to substitute for class type parameters. | 
						|
		/// Pass <c>null</c> to keep class type parameters unmodified. | 
						|
		/// </param> | 
						|
		/// <param name="methodTypeArguments"> | 
						|
		/// The type arguments to substitute for method type parameters. | 
						|
		/// Pass <c>null</c> to keep method type parameters unmodified. | 
						|
		/// </param> | 
						|
		public TypeParameterSubstitution(IReadOnlyList<IType> classTypeArguments, IReadOnlyList<IType> methodTypeArguments) | 
						|
		{ | 
						|
			this.classTypeArguments = classTypeArguments; | 
						|
			this.methodTypeArguments = methodTypeArguments; | 
						|
		} | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Gets the list of class type arguments. | 
						|
		/// Returns <c>null</c> if this substitution keeps class type parameters unmodified. | 
						|
		/// </summary> | 
						|
		public IReadOnlyList<IType> ClassTypeArguments { | 
						|
			get { return classTypeArguments; } | 
						|
		} | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Gets the list of method type arguments. | 
						|
		/// Returns <c>null</c> if this substitution keeps method type parameters unmodified. | 
						|
		/// </summary> | 
						|
		public IReadOnlyList<IType> MethodTypeArguments { | 
						|
			get { return methodTypeArguments; } | 
						|
		} | 
						|
 | 
						|
		#region Compose | 
						|
		/// <summary> | 
						|
		/// Computes a single TypeParameterSubstitution so that for all types <c>t</c>: | 
						|
		/// <c>t.AcceptVisitor(Compose(g, f)) equals t.AcceptVisitor(f).AcceptVisitor(g)</c> | 
						|
		/// </summary> | 
						|
		/// <remarks>If you consider type parameter substitution to be a function, this is function composition.</remarks> | 
						|
		public static TypeParameterSubstitution Compose(TypeParameterSubstitution g, TypeParameterSubstitution f) | 
						|
		{ | 
						|
			if (g == null) | 
						|
				return f; | 
						|
			if (f == null || (f.classTypeArguments == null && f.methodTypeArguments == null)) | 
						|
				return g; | 
						|
			// The composition is a copy of 'f', with 'g' applied on the array elements. | 
						|
			// If 'f' has a null list (keeps type parameters unmodified), we have to treat it as | 
						|
			// the identity function, and thus use the list from 'g'. | 
						|
			var classTypeArguments = f.classTypeArguments != null ? GetComposedTypeArguments(f.classTypeArguments, g) : g.classTypeArguments; | 
						|
			var methodTypeArguments = f.methodTypeArguments != null ? GetComposedTypeArguments(f.methodTypeArguments, g) : g.methodTypeArguments; | 
						|
			return new TypeParameterSubstitution(classTypeArguments, methodTypeArguments); | 
						|
		} | 
						|
 | 
						|
		static IReadOnlyList<IType> GetComposedTypeArguments(IReadOnlyList<IType> input, TypeParameterSubstitution substitution) | 
						|
		{ | 
						|
			IType[] result = new IType[input.Count]; | 
						|
			for (int i = 0; i < result.Length; i++) | 
						|
			{ | 
						|
				result[i] = input[i].AcceptVisitor(substitution); | 
						|
			} | 
						|
			return result; | 
						|
		} | 
						|
		#endregion | 
						|
 | 
						|
		#region Equals and GetHashCode implementation | 
						|
		public bool Equals(TypeParameterSubstitution other, TypeVisitor normalization) | 
						|
		{ | 
						|
			if (other == null) | 
						|
				return false; | 
						|
			return TypeListEquals(classTypeArguments, other.classTypeArguments, normalization) | 
						|
				&& TypeListEquals(methodTypeArguments, other.methodTypeArguments, normalization); | 
						|
		} | 
						|
 | 
						|
		public override bool Equals(object obj) | 
						|
		{ | 
						|
			TypeParameterSubstitution other = obj as TypeParameterSubstitution; | 
						|
			if (other == null) | 
						|
				return false; | 
						|
			return TypeListEquals(classTypeArguments, other.classTypeArguments) | 
						|
				&& TypeListEquals(methodTypeArguments, other.methodTypeArguments); | 
						|
		} | 
						|
 | 
						|
		public override int GetHashCode() | 
						|
		{ | 
						|
			unchecked | 
						|
			{ | 
						|
				return 1124131 * TypeListHashCode(classTypeArguments) + 1821779 * TypeListHashCode(methodTypeArguments); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		static bool TypeListEquals(IReadOnlyList<IType> a, IReadOnlyList<IType> b) | 
						|
		{ | 
						|
			if (a == b) | 
						|
				return true; | 
						|
			if (a == null || b == null) | 
						|
				return false; | 
						|
			if (a.Count != b.Count) | 
						|
				return false; | 
						|
			for (int i = 0; i < a.Count; i++) | 
						|
			{ | 
						|
				if (!a[i].Equals(b[i])) | 
						|
					return false; | 
						|
			} | 
						|
			return true; | 
						|
		} | 
						|
 | 
						|
		static bool TypeListEquals(IReadOnlyList<IType> a, IReadOnlyList<IType> b, TypeVisitor normalization) | 
						|
		{ | 
						|
			if (a == b) | 
						|
				return true; | 
						|
			if (a == null || b == null) | 
						|
				return false; | 
						|
			if (a.Count != b.Count) | 
						|
				return false; | 
						|
			for (int i = 0; i < a.Count; i++) | 
						|
			{ | 
						|
				var an = a[i].AcceptVisitor(normalization); | 
						|
				var bn = b[i].AcceptVisitor(normalization); | 
						|
				if (!an.Equals(bn)) | 
						|
					return false; | 
						|
			} | 
						|
			return true; | 
						|
		} | 
						|
 | 
						|
		static int TypeListHashCode(IReadOnlyList<IType> obj) | 
						|
		{ | 
						|
			if (obj == null) | 
						|
				return 0; | 
						|
			unchecked | 
						|
			{ | 
						|
				int hashCode = 1; | 
						|
				foreach (var element in obj) | 
						|
				{ | 
						|
					hashCode *= 27; | 
						|
					hashCode += element.GetHashCode(); | 
						|
				} | 
						|
				return hashCode; | 
						|
			} | 
						|
		} | 
						|
		#endregion | 
						|
 | 
						|
		public override IType VisitTypeParameter(ITypeParameter type) | 
						|
		{ | 
						|
			int index = type.Index; | 
						|
			if (classTypeArguments != null && type.OwnerType == SymbolKind.TypeDefinition) | 
						|
			{ | 
						|
				if (index >= 0 && index < classTypeArguments.Count) | 
						|
					return classTypeArguments[index]; | 
						|
				else | 
						|
					return SpecialType.UnknownType; | 
						|
			} | 
						|
			else if (methodTypeArguments != null && type.OwnerType == SymbolKind.Method) | 
						|
			{ | 
						|
				if (index >= 0 && index < methodTypeArguments.Count) | 
						|
					return methodTypeArguments[index]; | 
						|
				else | 
						|
					return SpecialType.UnknownType; | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				return base.VisitTypeParameter(type); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		public override IType VisitNullabilityAnnotatedType(NullabilityAnnotatedType type) | 
						|
		{ | 
						|
			if (type is NullabilityAnnotatedTypeParameter tp) | 
						|
			{ | 
						|
				if (tp.Nullability == Nullability.Nullable) | 
						|
				{ | 
						|
					return VisitTypeParameter(tp).ChangeNullability(Nullability.Nullable); | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					// T! substituted with T=oblivious string should result in oblivious string | 
						|
					return VisitTypeParameter(tp); | 
						|
				} | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				return base.VisitNullabilityAnnotatedType(type); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		public override string ToString() | 
						|
		{ | 
						|
			StringBuilder b = new StringBuilder(); | 
						|
			b.Append('['); | 
						|
			bool first = true; | 
						|
			if (classTypeArguments != null) | 
						|
			{ | 
						|
				for (int i = 0; i < classTypeArguments.Count; i++) | 
						|
				{ | 
						|
					if (first) | 
						|
						first = false; | 
						|
					else | 
						|
						b.Append(", "); | 
						|
					b.Append('`'); | 
						|
					b.Append(i); | 
						|
					b.Append(" -> "); | 
						|
					b.Append(classTypeArguments[i].ReflectionName); | 
						|
				} | 
						|
				if (classTypeArguments.Count == 0) | 
						|
				{ | 
						|
					if (first) | 
						|
						first = false; | 
						|
					else | 
						|
						b.Append(", "); | 
						|
					b.Append("[]"); | 
						|
				} | 
						|
			} | 
						|
			if (methodTypeArguments != null) | 
						|
			{ | 
						|
				for (int i = 0; i < methodTypeArguments.Count; i++) | 
						|
				{ | 
						|
					if (first) | 
						|
						first = false; | 
						|
					else | 
						|
						b.Append(", "); | 
						|
					b.Append("``"); | 
						|
					b.Append(i); | 
						|
					b.Append(" -> "); | 
						|
					b.Append(methodTypeArguments[i].ReflectionName); | 
						|
				} | 
						|
				if (methodTypeArguments.Count == 0) | 
						|
				{ | 
						|
					if (first) | 
						|
						first = false; | 
						|
					else | 
						|
						b.Append(", "); | 
						|
					b.Append("[]"); | 
						|
				} | 
						|
			} | 
						|
			b.Append(']'); | 
						|
			return b.ToString(); | 
						|
		} | 
						|
	} | 
						|
}
 | 
						|
 |