.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.
 
 
 
 

291 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.UnknownType)
{
if (methods == null)
throw new ArgumentNullException("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 <see cref="SpecializedMethod"/>
/// 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) {
IType[] inferredTypes;
if (CSharpResolver.IsEligibleExtensionMethod(this.TargetType, method, true, out 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 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.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>();
}
}
}