Browse Source

CallBuilder: Extract overload resolution to its own method.

pull/1213/head
Siegfried Pammer 7 years ago
parent
commit
d8e8735ed0
  1. 102
      ICSharpCode.Decompiler/CSharp/CallBuilder.cs

102
ICSharpCode.Decompiler/CSharp/CallBuilder.cs

@ -245,8 +245,56 @@ namespace ICSharpCode.Decompiler.CSharp @@ -245,8 +245,56 @@ namespace ICSharpCode.Decompiler.CSharp
} else if (method.IsOperator && method.Name == "op_Implicit" && arguments.Count == 1) {
return HandleImplicitConversion(method, arguments[0]);
} else {
bool requireTypeArguments = false;
var transform = GetRequiredTransformationsForCall(expectedTargetDetails, method, ref target,
arguments, argumentNames, expectedParameters, CallTransformation.All, out IParameterizedMember foundMethod);
// Note: after this, 'method' and 'foundMethod' may differ,
// but as far as allowed by IsAppropriateCallTarget().
Expression targetExpr;
string methodName = method.Name;
AstNodeCollection<AstType> typeArgumentList;
if ((transform & CallTransformation.RequireTarget) != 0) {
targetExpr = new MemberReferenceExpression(target.Expression, methodName);
typeArgumentList = ((MemberReferenceExpression)targetExpr).TypeArguments;
// HACK : convert this.Dispose() to ((IDisposable)this).Dispose(), if Dispose is an explicitly implemented interface method.
// settings.AlwaysCastTargetsOfExplicitInterfaceImplementationCalls == true is used in Windows Forms' InitializeComponent methods.
if (method.IsExplicitInterfaceImplementation && (target.Expression is ThisReferenceExpression || settings.AlwaysCastTargetsOfExplicitInterfaceImplementationCalls)) {
var castExpression = new CastExpression(expressionBuilder.ConvertType(method.ImplementedInterfaceMembers[0].DeclaringType), target.Expression);
methodName = method.ImplementedInterfaceMembers[0].Name;
targetExpr = new MemberReferenceExpression(castExpression, methodName);
typeArgumentList = ((MemberReferenceExpression)targetExpr).TypeArguments;
}
} else {
targetExpr = new IdentifierExpression(methodName);
typeArgumentList = ((IdentifierExpression)targetExpr).TypeArguments;
}
if ((transform & CallTransformation.RequireTypeArguments) != 0 && (!settings.AnonymousTypes || !method.TypeArguments.Any(a => a.ContainsAnonymousType())))
typeArgumentList.AddRange(method.TypeArguments.Select(expressionBuilder.ConvertType));
var argumentExpressions = GetArgumentExpressions(arguments, argumentNames);
return new InvocationExpression(targetExpr, argumentExpressions)
.WithRR(new CSharpInvocationResolveResult(target.ResolveResult, foundMethod, argumentResolveResults, isExpandedForm: isExpandedForm));
}
}
}
[Flags]
enum CallTransformation
{
None = 0,
RequireTarget = 1,
RequireTypeArguments = 2,
All = 3
}
private CallTransformation GetRequiredTransformationsForCall(ExpectedTargetDetails expectedTargetDetails, IMethod method, ref TranslatedExpression target, List<TranslatedExpression> arguments, string[] argumentNames, List<IParameter> expectedParameters, CallTransformation allowedTransforms, out IParameterizedMember foundMethod)
{
CallTransformation transform = CallTransformation.None;
bool requireTarget;
bool requireTypeArguments = false;
if ((allowedTransforms & CallTransformation.RequireTarget) != 0) {
if (expressionBuilder.HidesVariableWithName(method.Name)) {
requireTarget = true;
} else {
@ -255,20 +303,27 @@ namespace ICSharpCode.Decompiler.CSharp @@ -255,20 +303,27 @@ namespace ICSharpCode.Decompiler.CSharp
else if (method.Name == ".ctor")
requireTarget = true; // always use target for base/this-ctor-call, the constructor initializer pattern depends on this
else if (target.Expression is BaseReferenceExpression)
requireTarget = (callOpCode != OpCode.CallVirt && method.IsVirtual);
requireTarget = (expectedTargetDetails.CallOpCode != OpCode.CallVirt && method.IsVirtual);
else
requireTarget = !(target.Expression is ThisReferenceExpression);
}
} else {
requireTarget = false;
}
bool targetCasted = false;
bool argumentsCasted = false;
IType[] typeArguments = Empty<IType>.Array;
var targetResolveResult = requireTarget ? target.ResolveResult : null;
IParameterizedMember foundMethod;
OverloadResolutionErrors errors;
while ((errors = IsUnambiguousCall(expectedTargetDetails, method, targetResolveResult, typeArguments, arguments, argumentNames, out foundMethod)) != OverloadResolutionErrors.None) {
switch (errors) {
case OverloadResolutionErrors.TypeInferenceFailed:
if ((allowedTransforms & CallTransformation.RequireTypeArguments) != 0) {
goto case OverloadResolutionErrors.WrongNumberOfTypeArguments;
}
goto default;
case OverloadResolutionErrors.WrongNumberOfTypeArguments:
Debug.Assert((allowedTransforms & CallTransformation.RequireTypeArguments) != 0);
if (requireTypeArguments) goto default;
requireTypeArguments = true;
typeArguments = method.TypeArguments.ToArray();
@ -279,14 +334,14 @@ namespace ICSharpCode.Decompiler.CSharp @@ -279,14 +334,14 @@ namespace ICSharpCode.Decompiler.CSharp
if (!argumentsCasted) {
argumentsCasted = true;
CastArguments(arguments, expectedParameters);
} else if (!requireTarget) {
} else if ((allowedTransforms & CallTransformation.RequireTarget) != 0 && !requireTarget) {
requireTarget = true;
targetResolveResult = target.ResolveResult;
} else if (!targetCasted) {
} else if ((allowedTransforms & CallTransformation.RequireTarget) != 0 && !targetCasted) {
targetCasted = true;
target = target.ConvertTo(method.DeclaringType, expressionBuilder);
targetResolveResult = target.ResolveResult;
} else if (!requireTypeArguments) {
} else if ((allowedTransforms & CallTransformation.RequireTypeArguments) != 0 && !requireTypeArguments) {
requireTypeArguments = true;
typeArguments = method.TypeArguments.ToArray();
} else {
@ -298,36 +353,11 @@ namespace ICSharpCode.Decompiler.CSharp @@ -298,36 +353,11 @@ namespace ICSharpCode.Decompiler.CSharp
foundMethod = method;
break;
}
// Note: after this loop, 'method' and 'foundMethod' may differ,
// but as far as allowed by IsAppropriateCallTarget().
Expression targetExpr;
string methodName = method.Name;
AstNodeCollection<AstType> typeArgumentList;
if (requireTarget) {
targetExpr = new MemberReferenceExpression(target.Expression, methodName);
typeArgumentList = ((MemberReferenceExpression)targetExpr).TypeArguments;
// HACK : convert this.Dispose() to ((IDisposable)this).Dispose(), if Dispose is an explicitly implemented interface method.
// settings.AlwaysCastTargetsOfExplicitInterfaceImplementationCalls == true is used in Windows Forms' InitializeComponent methods.
if (method.IsExplicitInterfaceImplementation && (target.Expression is ThisReferenceExpression || settings.AlwaysCastTargetsOfExplicitInterfaceImplementationCalls)) {
var castExpression = new CastExpression(expressionBuilder.ConvertType(method.ImplementedInterfaceMembers[0].DeclaringType), target.Expression);
methodName = method.ImplementedInterfaceMembers[0].Name;
targetExpr = new MemberReferenceExpression(castExpression, methodName);
typeArgumentList = ((MemberReferenceExpression)targetExpr).TypeArguments;
}
} else {
targetExpr = new IdentifierExpression(methodName);
typeArgumentList = ((IdentifierExpression)targetExpr).TypeArguments;
}
if (requireTypeArguments && (!settings.AnonymousTypes || !method.TypeArguments.Any(a => a.ContainsAnonymousType())))
typeArgumentList.AddRange(method.TypeArguments.Select(expressionBuilder.ConvertType));
var argumentExpressions = GetArgumentExpressions(arguments, argumentNames);
return new InvocationExpression(targetExpr, argumentExpressions)
.WithRR(new CSharpInvocationResolveResult(target.ResolveResult, foundMethod, argumentResolveResults, isExpandedForm: isExpandedForm));
}
}
if (requireTarget)
transform |= CallTransformation.RequireTarget;
if (requireTypeArguments)
transform |= CallTransformation.RequireTypeArguments;
return transform;
}
private void CastArguments(List<TranslatedExpression> arguments, List<IParameter> expectedParameters)

Loading…
Cancel
Save