.NET Decompiler with support for PDB generation, ReadyToRun, Metadata (&more) - cross-platform!
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

// 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();
}
}
}