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.
309 lines
11 KiB
309 lines
11 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 System.Diagnostics; |
|
using System.Linq; |
|
|
|
using ICSharpCode.Decompiler.Semantics; |
|
using ICSharpCode.Decompiler.TypeSystem; |
|
using ICSharpCode.Decompiler.Util; |
|
|
|
namespace ICSharpCode.Decompiler.CSharp.Resolver |
|
{ |
|
/// <summary> |
|
/// A method list that belongs to a declaring type. |
|
/// </summary> |
|
public class MethodListWithDeclaringType : List<IParameterizedMember> |
|
{ |
|
readonly IType declaringType; |
|
|
|
/// <summary> |
|
/// The declaring type. |
|
/// </summary> |
|
/// <remarks> |
|
/// Not all methods in this list necessarily have this as their declaring type. |
|
/// For example, this program: |
|
/// <code> |
|
/// class Base { |
|
/// public virtual void M() {} |
|
/// } |
|
/// class Derived : Base { |
|
/// public override void M() {} |
|
/// public void M(int i) {} |
|
/// } |
|
/// </code> |
|
/// results in two lists: |
|
/// <c>new MethodListWithDeclaringType(Base) { Derived.M() }</c>, |
|
/// <c>new MethodListWithDeclaringType(Derived) { Derived.M(int) }</c> |
|
/// </remarks> |
|
public IType DeclaringType { |
|
get { return declaringType; } |
|
} |
|
|
|
public MethodListWithDeclaringType(IType declaringType) |
|
{ |
|
this.declaringType = declaringType; |
|
} |
|
|
|
public MethodListWithDeclaringType(IType declaringType, IEnumerable<IParameterizedMember> methods) |
|
: base(methods) |
|
{ |
|
this.declaringType = declaringType; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Represents a group of methods. |
|
/// A method reference used to create a delegate is resolved to a MethodGroupResolveResult. |
|
/// The MethodGroupResolveResult has no type. |
|
/// To retrieve the delegate type or the chosen overload, look at the method group conversion. |
|
/// </summary> |
|
public class MethodGroupResolveResult : ResolveResult |
|
{ |
|
readonly IReadOnlyList<MethodListWithDeclaringType> methodLists; |
|
readonly IReadOnlyList<IType> typeArguments; |
|
readonly ResolveResult targetResult; |
|
readonly string methodName; |
|
|
|
public MethodGroupResolveResult(ResolveResult targetResult, string methodName, |
|
IReadOnlyList<MethodListWithDeclaringType> methods, IReadOnlyList<IType> typeArguments) |
|
: base(SpecialType.NoType) |
|
{ |
|
if (methods == null) |
|
throw new ArgumentNullException(nameof(methods)); |
|
this.targetResult = targetResult; |
|
this.methodName = methodName; |
|
this.methodLists = methods; |
|
this.typeArguments = typeArguments ?? EmptyList<IType>.Instance; |
|
} |
|
|
|
/// <summary> |
|
/// Gets the resolve result for the target object. |
|
/// </summary> |
|
public ResolveResult TargetResult { |
|
get { return targetResult; } |
|
} |
|
|
|
/// <summary> |
|
/// Gets the type of the reference to the target object. |
|
/// </summary> |
|
public IType TargetType { |
|
get { return targetResult != null ? targetResult.Type : SpecialType.UnknownType; } |
|
} |
|
|
|
/// <summary> |
|
/// Gets the name of the methods in this group. |
|
/// </summary> |
|
public string MethodName { |
|
get { return methodName; } |
|
} |
|
|
|
/// <summary> |
|
/// Gets the methods that were found. |
|
/// This list does not include extension methods. |
|
/// </summary> |
|
public IEnumerable<IMethod> Methods { |
|
get { return methodLists.SelectMany(m => m.Cast<IMethod>()); } |
|
} |
|
|
|
/// <summary> |
|
/// Gets the methods that were found, grouped by their declaring type. |
|
/// This list does not include extension methods. |
|
/// Base types come first in the list. |
|
/// </summary> |
|
public IEnumerable<MethodListWithDeclaringType> MethodsGroupedByDeclaringType { |
|
get { return methodLists; } |
|
} |
|
|
|
/// <summary> |
|
/// Gets the type arguments that were explicitly provided. |
|
/// </summary> |
|
public IReadOnlyList<IType> TypeArguments { |
|
get { return typeArguments; } |
|
} |
|
|
|
/// <summary> |
|
/// List of extension methods, used to avoid re-calculating it in ResolveInvocation() when it was already |
|
/// calculated by ResolveMemberAccess(). |
|
/// </summary> |
|
internal List<List<IMethod>> extensionMethods; |
|
|
|
// the resolver is used to fetch extension methods on demand |
|
internal CSharpResolver resolver; |
|
|
|
/// <summary> |
|
/// Gets all candidate extension methods. |
|
/// Note: this includes candidates that are not eligible due to an inapplicable |
|
/// this argument. |
|
/// The candidates will only be specialized if the type arguments were provided explicitly. |
|
/// </summary> |
|
/// <remarks> |
|
/// The results are stored in nested lists because they are grouped by using scope. |
|
/// That is, for "using SomeExtensions; namespace X { using MoreExtensions; ... }", |
|
/// the return value will be |
|
/// new List { |
|
/// new List { all extensions from MoreExtensions }, |
|
/// new List { all extensions from SomeExtensions } |
|
/// } |
|
/// </remarks> |
|
public IEnumerable<IEnumerable<IMethod>> GetExtensionMethods() |
|
{ |
|
if (resolver != null) |
|
{ |
|
Debug.Assert(extensionMethods == null); |
|
try |
|
{ |
|
extensionMethods = resolver.GetExtensionMethods(methodName, typeArguments); |
|
} |
|
finally |
|
{ |
|
resolver = null; |
|
} |
|
} |
|
return extensionMethods ?? Enumerable.Empty<IEnumerable<IMethod>>(); |
|
} |
|
|
|
/// <summary> |
|
/// Gets the eligible extension methods. |
|
/// </summary> |
|
/// <param name="substituteInferredTypes"> |
|
/// Specifies whether to produce a <c>SpecializedMethod</c> |
|
/// when type arguments could be inferred from <see cref="TargetType"/>. |
|
/// This setting is only used for inferred types and has no effect if the type parameters are |
|
/// specified explicitly. |
|
/// </param> |
|
/// <remarks> |
|
/// The results are stored in nested lists because they are grouped by using scope. |
|
/// That is, for "using SomeExtensions; namespace X { using MoreExtensions; ... }", |
|
/// the return value will be |
|
/// new List { |
|
/// new List { all extensions from MoreExtensions }, |
|
/// new List { all extensions from SomeExtensions } |
|
/// } |
|
/// </remarks> |
|
public IEnumerable<IEnumerable<IMethod>> GetEligibleExtensionMethods(bool substituteInferredTypes) |
|
{ |
|
var result = new List<List<IMethod>>(); |
|
foreach (var methodGroup in GetExtensionMethods()) |
|
{ |
|
var outputGroup = new List<IMethod>(); |
|
foreach (var method in methodGroup) |
|
{ |
|
if (CSharpResolver.IsEligibleExtensionMethod(this.TargetType, method, true, out IType[] inferredTypes)) |
|
{ |
|
if (substituteInferredTypes && inferredTypes != null) |
|
{ |
|
outputGroup.Add(method.Specialize(new TypeParameterSubstitution(null, inferredTypes))); |
|
} |
|
else |
|
{ |
|
outputGroup.Add(method); |
|
} |
|
} |
|
} |
|
if (outputGroup.Count > 0) |
|
result.Add(outputGroup); |
|
} |
|
return result; |
|
} |
|
|
|
public override string ToString() |
|
{ |
|
return string.Format("[{0} with {1} method(s)]", GetType().Name, this.Methods.Count()); |
|
} |
|
|
|
public OverloadResolution PerformOverloadResolution(ICompilation compilation, ResolveResult[] arguments, string[] argumentNames = null, |
|
bool allowExtensionMethods = true, |
|
bool allowExpandingParams = true, |
|
bool allowOptionalParameters = true, |
|
bool allowImplicitIn = true, |
|
bool checkForOverflow = false, CSharpConversions conversions = null) |
|
{ |
|
Log.WriteLine("Performing overload resolution for " + this); |
|
Log.WriteCollection(" Arguments: ", arguments); |
|
|
|
var typeArgumentArray = this.TypeArguments.ToArray(); |
|
OverloadResolution or = new OverloadResolution(compilation, arguments, argumentNames, typeArgumentArray, conversions); |
|
or.AllowExpandingParams = allowExpandingParams; |
|
or.AllowOptionalParameters = allowOptionalParameters; |
|
or.CheckForOverflow = checkForOverflow; |
|
or.AllowImplicitIn = allowImplicitIn; |
|
|
|
or.AddMethodLists(methodLists); |
|
|
|
if (allowExtensionMethods && !or.FoundApplicableCandidate) |
|
{ |
|
// No applicable match found, so let's try extension methods. |
|
|
|
var extensionMethods = this.GetExtensionMethods(); |
|
|
|
if (extensionMethods.Any()) |
|
{ |
|
Log.WriteLine("No candidate is applicable, trying {0} extension methods groups...", extensionMethods.Count()); |
|
ResolveResult[] extArguments = new ResolveResult[arguments.Length + 1]; |
|
extArguments[0] = new ResolveResult(this.TargetType); |
|
arguments.CopyTo(extArguments, 1); |
|
string[] extArgumentNames = null; |
|
if (argumentNames != null) |
|
{ |
|
extArgumentNames = new string[argumentNames.Length + 1]; |
|
argumentNames.CopyTo(extArgumentNames, 1); |
|
} |
|
var extOr = new OverloadResolution(compilation, extArguments, extArgumentNames, typeArgumentArray, conversions); |
|
extOr.AllowExpandingParams = allowExpandingParams; |
|
extOr.AllowOptionalParameters = allowOptionalParameters; |
|
extOr.IsExtensionMethodInvocation = true; |
|
extOr.CheckForOverflow = checkForOverflow; |
|
|
|
foreach (var g in extensionMethods) |
|
{ |
|
foreach (var method in g) |
|
{ |
|
Log.Indent(); |
|
OverloadResolutionErrors errors = extOr.AddCandidate(method); |
|
Log.Unindent(); |
|
or.LogCandidateAddingResult(" Extension", method, errors); |
|
} |
|
if (extOr.FoundApplicableCandidate) |
|
break; |
|
} |
|
// For the lack of a better comparison function (the one within OverloadResolution |
|
// cannot be used as it depends on the argument set): |
|
if (extOr.FoundApplicableCandidate || or.BestCandidate == null) |
|
{ |
|
// Consider an extension method result better than the normal result only |
|
// if it's applicable; or if there is no normal result. |
|
or = extOr; |
|
} |
|
} |
|
} |
|
Log.WriteLine("Overload resolution finished, best candidate is {0}.", or.GetBestCandidateWithSubstitutedTypeArguments()); |
|
return or; |
|
} |
|
|
|
public override IEnumerable<ResolveResult> GetChildResults() |
|
{ |
|
if (targetResult != null) |
|
return new[] { targetResult }; |
|
else |
|
return Enumerable.Empty<ResolveResult>(); |
|
} |
|
} |
|
}
|
|
|