Browse Source

Use overload resolution to ensure we call the correct indexer + added tests.

pull/1213/head
Siegfried Pammer 7 years ago
parent
commit
a823d74004
  1. 35
      ICSharpCode.Decompiler.Tests/TestCases/Correctness/OverloadResolution.cs
  2. 82
      ICSharpCode.Decompiler/CSharp/CallBuilder.cs

35
ICSharpCode.Decompiler.Tests/TestCases/Correctness/OverloadResolution.cs

@ -31,6 +31,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
TestParamsMethod(); TestParamsMethod();
Generics(); Generics();
ConstructorTest(); ConstructorTest();
TestIndexer();
} }
#region ConstructorTest #region ConstructorTest
@ -182,5 +183,39 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
Console.WriteLine("GenericsTest<" + typeof(T).Name + ">(object: " + x + ");"); Console.WriteLine("GenericsTest<" + typeof(T).Name + ">(object: " + x + ");");
} }
#endregion #endregion
#region IndexerTests
static void TestIndexer()
{
var obj = new IndexerTests();
Console.WriteLine(obj[(object)5]);
obj[(object)5] = null;
Console.WriteLine(obj[5]);
obj[5] = null;
}
#endregion
}
class IndexerTests
{
public object this[object key] {
get {
Console.WriteLine("IndexerTests.get_Item(object key)");
return new object();
}
set {
Console.WriteLine("IndexerTests.set_Item(object key, object value)");
}
}
public object this[int key] {
get {
Console.WriteLine("IndexerTests.get_Item(int key)");
return new object();
}
set {
Console.WriteLine("IndexerTests.set_Item(int key, object value)");
}
}
} }
} }

82
ICSharpCode.Decompiler/CSharp/CallBuilder.cs

@ -234,7 +234,7 @@ namespace ICSharpCode.Decompiler.CSharp
int allowedParamCount = (method.ReturnType.IsKnownType(KnownTypeCode.Void) ? 1 : 0); int allowedParamCount = (method.ReturnType.IsKnownType(KnownTypeCode.Void) ? 1 : 0);
if (method.IsAccessor && (method.AccessorOwner.SymbolKind == SymbolKind.Indexer || expectedParameters.Count == allowedParamCount)) { if (method.IsAccessor && (method.AccessorOwner.SymbolKind == SymbolKind.Indexer || expectedParameters.Count == allowedParamCount)) {
Debug.Assert(argumentToParameterMap == null && argumentNames == null); Debug.Assert(argumentToParameterMap == null && argumentNames == null);
return HandleAccessorCall(expectedTargetDetails, method, target, arguments.ToList()); return HandleAccessorCall(expectedTargetDetails, method, target, arguments.ToList(), argumentNames);
} else if (method.Name == "Invoke" && method.DeclaringType.Kind == TypeKind.Delegate && !IsNullConditional(target)) { } else if (method.Name == "Invoke" && method.DeclaringType.Kind == TypeKind.Delegate && !IsNullConditional(target)) {
return new InvocationExpression(target, GetArgumentExpressions(arguments, argumentNames)) return new InvocationExpression(target, GetArgumentExpressions(arguments, argumentNames))
.WithRR(new CSharpInvocationResolveResult(target.ResolveResult, method, argumentResolveResults, isExpandedForm: isExpandedForm)); .WithRR(new CSharpInvocationResolveResult(target.ResolveResult, method, argumentResolveResults, isExpandedForm: isExpandedForm));
@ -289,7 +289,9 @@ namespace ICSharpCode.Decompiler.CSharp
All = 3 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) 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; CallTransformation transform = CallTransformation.None;
bool requireTarget; bool requireTarget;
@ -360,7 +362,7 @@ namespace ICSharpCode.Decompiler.CSharp
return transform; return transform;
} }
private void CastArguments(List<TranslatedExpression> arguments, List<IParameter> expectedParameters) private void CastArguments(IList<TranslatedExpression> arguments, IReadOnlyList<IParameter> expectedParameters)
{ {
for (int i = 0; i < arguments.Count; i++) { for (int i = 0; i < arguments.Count; i++) {
if (settings.AnonymousTypes && expectedParameters[i].Type.ContainsAnonymousType()) { if (settings.AnonymousTypes && expectedParameters[i].Type.ContainsAnonymousType()) {
@ -516,43 +518,71 @@ namespace ICSharpCode.Decompiler.CSharp
return true; return true;
} }
bool IsUnambiguousAccess(ExpectedTargetDetails expectedTargetDetails, ResolveResult target, IMethod method, out IMember foundMember) bool IsUnambiguousAccess(ExpectedTargetDetails expectedTargetDetails, ResolveResult target, IMethod method,
IList<TranslatedExpression> arguments, string[] argumentNames, out IMember foundMember)
{ {
foundMember = null; foundMember = null;
MemberResolveResult result;
if (target == null) { if (target == null) {
result = resolver.ResolveSimpleName(method.AccessorOwner.Name, EmptyList<IType>.Instance, isInvocationTarget: false) as MemberResolveResult; var result = resolver.ResolveSimpleName(method.AccessorOwner.Name,
EmptyList<IType>.Instance,
isInvocationTarget: false) as MemberResolveResult;
if (result == null || result.IsError)
return false;
foundMember = result.Member;
} else { } else {
var lookup = new MemberLookup(resolver.CurrentTypeDefinition, resolver.CurrentTypeDefinition.ParentAssembly); var lookup = new MemberLookup(resolver.CurrentTypeDefinition, resolver.CurrentTypeDefinition.ParentAssembly);
if (method.AccessorOwner.SymbolKind == SymbolKind.Indexer) { if (method.AccessorOwner.SymbolKind == SymbolKind.Indexer) {
// TODO: use OR here, etc. var or = new OverloadResolution(resolver.Compilation,
result = null; arguments.SelectArray(a => a.ResolveResult),
foreach (var methodList in lookup.LookupIndexers(target)) { argumentNames: argumentNames,
foreach (var indexer in methodList) { typeArguments: Empty<IType>.Array,
if (IsAppropriateCallTarget(expectedTargetDetails, method.AccessorOwner, indexer)) { conversions: expressionBuilder.resolver.conversions);
foundMember = indexer; or.AddMethodLists(lookup.LookupIndexers(target));
return true; if (or.BestCandidateErrors != OverloadResolutionErrors.None)
} return false;
} if (or.IsAmbiguous)
} return false;
foundMember = or.GetBestCandidateWithSubstitutedTypeArguments();
} else { } else {
result = lookup.Lookup(target, method.AccessorOwner.Name, EmptyList<IType>.Instance, isInvocation: false) as MemberResolveResult; var result = lookup.Lookup(target,
method.AccessorOwner.Name,
EmptyList<IType>.Instance,
isInvocation: false) as MemberResolveResult;
if (result == null || result.IsError)
return false;
foundMember = result.Member;
} }
} }
foundMember = result?.Member; return foundMember != null && IsAppropriateCallTarget(expectedTargetDetails, method.AccessorOwner, foundMember);
return !(result == null || result.IsError || !IsAppropriateCallTarget(expectedTargetDetails, method.AccessorOwner, result.Member));
} }
ExpressionWithResolveResult HandleAccessorCall(ExpectedTargetDetails expectedTargetDetails, IMethod method, TranslatedExpression target, IList<TranslatedExpression> arguments) ExpressionWithResolveResult HandleAccessorCall(ExpectedTargetDetails expectedTargetDetails, IMethod method,
TranslatedExpression target, IList<TranslatedExpression> arguments, string[] argumentNames)
{ {
bool requireTarget = expressionBuilder.HidesVariableWithName(method.AccessorOwner.Name) bool requireTarget;
|| (method.IsStatic ? !expressionBuilder.IsCurrentOrContainingType(method.DeclaringTypeDefinition) : !(target.Expression is ThisReferenceExpression)); if (method.AccessorOwner.SymbolKind == SymbolKind.Indexer || expressionBuilder.HidesVariableWithName(method.AccessorOwner.Name))
requireTarget = true;
else if (method.IsStatic)
requireTarget = !expressionBuilder.IsCurrentOrContainingType(method.DeclaringTypeDefinition);
else
requireTarget = !(target.Expression is ThisReferenceExpression);
bool targetCasted = false; bool targetCasted = false;
bool isSetter = method.ReturnType.IsKnownType(KnownTypeCode.Void);
bool argumentsCasted = (isSetter && method.Parameters.Count == 1) || (!isSetter && method.Parameters.Count == 0);
var targetResolveResult = requireTarget ? target.ResolveResult : null; var targetResolveResult = requireTarget ? target.ResolveResult : null;
TranslatedExpression value = default(TranslatedExpression);
if (isSetter) {
value = arguments.Last();
arguments.Remove(value);
}
IMember foundMember; IMember foundMember;
while (!IsUnambiguousAccess(expectedTargetDetails, targetResolveResult, method, out foundMember)) { while (!IsUnambiguousAccess(expectedTargetDetails, targetResolveResult, method, arguments, argumentNames, out foundMember)) {
if (!requireTarget) { if (!argumentsCasted) {
argumentsCasted = true;
CastArguments(arguments, method.Parameters);
} else if(!requireTarget) {
requireTarget = true; requireTarget = true;
targetResolveResult = target.ResolveResult; targetResolveResult = target.ResolveResult;
} else if (!targetCasted) { } else if (!targetCasted) {
@ -567,9 +597,7 @@ namespace ICSharpCode.Decompiler.CSharp
var rr = new MemberResolveResult(target.ResolveResult, foundMember); var rr = new MemberResolveResult(target.ResolveResult, foundMember);
if (method.ReturnType.IsKnownType(KnownTypeCode.Void)) { if (isSetter) {
var value = arguments.Last();
arguments.Remove(value);
TranslatedExpression expr; TranslatedExpression expr;
if (arguments.Count != 0) { if (arguments.Count != 0) {

Loading…
Cancel
Save