|
|
|
@ -74,10 +74,13 @@ namespace ICSharpCode.Decompiler.CSharp
@@ -74,10 +74,13 @@ namespace ICSharpCode.Decompiler.CSharp
|
|
|
|
|
elementRRs.ToImmutableArray() |
|
|
|
|
)).WithILInstruction(inst); |
|
|
|
|
} |
|
|
|
|
return Build(inst.OpCode, inst.Method, inst.Arguments, inst.ConstrainedTo).WithILInstruction(inst); |
|
|
|
|
return Build(inst.OpCode, inst.Method, inst.Arguments, constrainedTo: inst.ConstrainedTo) |
|
|
|
|
.WithILInstruction(inst); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public ExpressionWithResolveResult Build(OpCode callOpCode, IMethod method, IReadOnlyList<ILInstruction> callArguments, |
|
|
|
|
public ExpressionWithResolveResult Build(OpCode callOpCode, IMethod method, |
|
|
|
|
IReadOnlyList<ILInstruction> callArguments, |
|
|
|
|
IReadOnlyList<int> argumentToParameterMap = null, |
|
|
|
|
IType constrainedTo = null) |
|
|
|
|
{ |
|
|
|
|
// Used for Call, CallVirt and NewObj
|
|
|
|
@ -108,16 +111,32 @@ namespace ICSharpCode.Decompiler.CSharp
@@ -108,16 +111,32 @@ namespace ICSharpCode.Decompiler.CSharp
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int firstParamIndex = (method.IsStatic || callOpCode == OpCode.NewObj) ? 0 : 1; |
|
|
|
|
|
|
|
|
|
Debug.Assert(firstParamIndex == 0 || argumentToParameterMap == null |
|
|
|
|
|| argumentToParameterMap[0] == -1); |
|
|
|
|
|
|
|
|
|
// Translate arguments to the expected parameter types
|
|
|
|
|
var arguments = new List<TranslatedExpression>(method.Parameters.Count); |
|
|
|
|
string[] argumentNames = null; |
|
|
|
|
Debug.Assert(callArguments.Count == firstParamIndex + method.Parameters.Count); |
|
|
|
|
var expectedParameters = method.Parameters.ToList(); |
|
|
|
|
var expectedParameters = new List<IParameter>(arguments.Count); // parameters, but in argument order
|
|
|
|
|
bool isExpandedForm = false; |
|
|
|
|
for (int i = 0; i < method.Parameters.Count; i++) { |
|
|
|
|
var parameter = expectedParameters[i]; |
|
|
|
|
var arg = expressionBuilder.Translate(callArguments[firstParamIndex + i], parameter.Type); |
|
|
|
|
if (parameter.IsParams && i + 1 == method.Parameters.Count) { |
|
|
|
|
for (int i = firstParamIndex; i < callArguments.Count; i++) { |
|
|
|
|
IParameter parameter; |
|
|
|
|
if (argumentToParameterMap != null) { |
|
|
|
|
if (argumentNames == null && argumentToParameterMap[i] != i - firstParamIndex) { |
|
|
|
|
// Starting at the first argument that is out-of-place,
|
|
|
|
|
// assign names to that argument and all following arguments:
|
|
|
|
|
argumentNames = new string[method.Parameters.Count]; |
|
|
|
|
} |
|
|
|
|
parameter = method.Parameters[argumentToParameterMap[i]]; |
|
|
|
|
if (argumentNames != null) { |
|
|
|
|
argumentNames[arguments.Count] = parameter.Name; |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
parameter = method.Parameters[i - firstParamIndex]; |
|
|
|
|
} |
|
|
|
|
var arg = expressionBuilder.Translate(callArguments[i], parameter.Type); |
|
|
|
|
if (parameter.IsParams && i + 1 == callArguments.Count && argumentToParameterMap == null) { |
|
|
|
|
// Parameter is marked params
|
|
|
|
|
// If the argument is an array creation, inline all elements into the call and add missing default values.
|
|
|
|
|
// Otherwise handle it normally.
|
|
|
|
@ -138,7 +157,8 @@ namespace ICSharpCode.Decompiler.CSharp
@@ -138,7 +157,8 @@ namespace ICSharpCode.Decompiler.CSharp
|
|
|
|
|
expandedArguments.Add(expressionBuilder.GetDefaultValueExpression(elementType).WithoutILInstruction()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (IsUnambiguousCall(expectedTargetDetails, method, target.ResolveResult, Empty<IType>.Array, expandedArguments, out _) == OverloadResolutionErrors.None) { |
|
|
|
|
Debug.Assert(argumentNames == null); |
|
|
|
|
if (IsUnambiguousCall(expectedTargetDetails, method, target.ResolveResult, Empty<IType>.Array, expandedArguments, argumentNames, out _) == OverloadResolutionErrors.None) { |
|
|
|
|
isExpandedForm = true; |
|
|
|
|
expectedParameters = expandedParameters; |
|
|
|
|
arguments = expandedArguments.SelectList(a => new TranslatedExpression(a.Expression.Detach())); |
|
|
|
@ -147,9 +167,8 @@ namespace ICSharpCode.Decompiler.CSharp
@@ -147,9 +167,8 @@ namespace ICSharpCode.Decompiler.CSharp
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
arguments.Add(arg.ConvertTo(parameter.Type, expressionBuilder, allowImplicitConversion: true)); |
|
|
|
|
|
|
|
|
|
if (parameter.IsOut && arguments[i].Expression is DirectionExpression dirExpr && arguments[i].ResolveResult is ByReferenceResolveResult brrr) { |
|
|
|
|
arg = arg.ConvertTo(parameter.Type, expressionBuilder, allowImplicitConversion: true); |
|
|
|
|
if (parameter.IsOut && arg.Expression is DirectionExpression dirExpr && arg.ResolveResult is ByReferenceResolveResult brrr) { |
|
|
|
|
dirExpr.FieldDirection = FieldDirection.Out; |
|
|
|
|
dirExpr.RemoveAnnotations<ByReferenceResolveResult>(); |
|
|
|
|
if (brrr.ElementResult == null) |
|
|
|
@ -157,8 +176,11 @@ namespace ICSharpCode.Decompiler.CSharp
@@ -157,8 +176,11 @@ namespace ICSharpCode.Decompiler.CSharp
|
|
|
|
|
else |
|
|
|
|
brrr = new ByReferenceResolveResult(brrr.ElementResult, isOut: true); |
|
|
|
|
dirExpr.AddAnnotation(brrr); |
|
|
|
|
arguments[i] = new TranslatedExpression(dirExpr); |
|
|
|
|
arg = new TranslatedExpression(dirExpr); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
arguments.Add(arg); |
|
|
|
|
expectedParameters.Add(parameter); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (method is VarArgInstanceMethod) { |
|
|
|
@ -167,19 +189,22 @@ namespace ICSharpCode.Decompiler.CSharp
@@ -167,19 +189,22 @@ namespace ICSharpCode.Decompiler.CSharp
|
|
|
|
|
argListArg.UndocumentedExpressionType = UndocumentedExpressionType.ArgList; |
|
|
|
|
int paramIndex = regularParameterCount; |
|
|
|
|
var builder = expressionBuilder; |
|
|
|
|
Debug.Assert(argumentToParameterMap == null && argumentNames == null); |
|
|
|
|
argListArg.Arguments.AddRange(arguments.Skip(regularParameterCount).Select(arg => arg.ConvertTo(expectedParameters[paramIndex++].Type, builder).Expression)); |
|
|
|
|
var argListRR = new ResolveResult(SpecialType.ArgList); |
|
|
|
|
arguments = arguments.Take(regularParameterCount) |
|
|
|
|
.Concat(new[] { argListArg.WithoutILInstruction().WithRR(argListRR) }).ToList(); |
|
|
|
|
method = (IMethod)method.MemberDefinition; |
|
|
|
|
method = ((VarArgInstanceMethod)method).BaseMethod; |
|
|
|
|
expectedParameters = method.Parameters.ToList(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var argumentResolveResults = arguments.Select(arg => arg.ResolveResult).ToList(); |
|
|
|
|
|
|
|
|
|
if (callOpCode == OpCode.NewObj) { |
|
|
|
|
ResolveResult rr = new CSharpInvocationResolveResult(target.ResolveResult, method, argumentResolveResults, isExpandedForm: isExpandedForm); |
|
|
|
|
ResolveResult rr = new CSharpInvocationResolveResult(target.ResolveResult, method, argumentResolveResults, |
|
|
|
|
isExpandedForm: isExpandedForm, argumentToParameterMap: argumentToParameterMap); |
|
|
|
|
if (settings.AnonymousTypes && method.DeclaringType.IsAnonymousType()) { |
|
|
|
|
Debug.Assert(argumentToParameterMap == null && argumentNames == null); |
|
|
|
|
var argumentExpressions = arguments.SelectArray(arg => arg.Expression); |
|
|
|
|
AnonymousTypeCreateExpression atce = new AnonymousTypeCreateExpression(); |
|
|
|
|
if (CanInferAnonymousTypePropertyNamesFromArguments(argumentExpressions, expectedParameters)) { |
|
|
|
@ -196,7 +221,7 @@ namespace ICSharpCode.Decompiler.CSharp
@@ -196,7 +221,7 @@ namespace ICSharpCode.Decompiler.CSharp
|
|
|
|
|
return atce |
|
|
|
|
.WithRR(rr); |
|
|
|
|
} else { |
|
|
|
|
if (IsUnambiguousCall(expectedTargetDetails, method, null, Empty<IType>.Array, arguments, out _) != OverloadResolutionErrors.None) { |
|
|
|
|
if (IsUnambiguousCall(expectedTargetDetails, method, null, Empty<IType>.Array, arguments, argumentNames, out _) != OverloadResolutionErrors.None) { |
|
|
|
|
for (int i = 0; i < arguments.Count; i++) { |
|
|
|
|
if (settings.AnonymousTypes && expectedParameters[i].Type.ContainsAnonymousType()) { |
|
|
|
|
if (arguments[i].Expression is LambdaExpression lambda) { |
|
|
|
@ -207,17 +232,21 @@ namespace ICSharpCode.Decompiler.CSharp
@@ -207,17 +232,21 @@ namespace ICSharpCode.Decompiler.CSharp
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return new ObjectCreateExpression(expressionBuilder.ConvertType(method.DeclaringType), arguments.SelectArray(arg => arg.Expression)) |
|
|
|
|
.WithRR(rr); |
|
|
|
|
return new ObjectCreateExpression( |
|
|
|
|
expressionBuilder.ConvertType(method.DeclaringType), |
|
|
|
|
GetArgumentExpressions(arguments, argumentNames) |
|
|
|
|
).WithRR(rr); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
int allowedParamCount = (method.ReturnType.IsKnownType(KnownTypeCode.Void) ? 1 : 0); |
|
|
|
|
if (method.IsAccessor && (method.AccessorOwner.SymbolKind == SymbolKind.Indexer || expectedParameters.Count == allowedParamCount)) { |
|
|
|
|
Debug.Assert(argumentToParameterMap == null && argumentNames == null); |
|
|
|
|
return HandleAccessorCall(expectedTargetDetails, method, target, arguments.ToList()); |
|
|
|
|
} else if (method.Name == "Invoke" && method.DeclaringType.Kind == TypeKind.Delegate && !IsNullConditional(target)) { |
|
|
|
|
return new InvocationExpression(target, arguments.Select(arg => arg.Expression)) |
|
|
|
|
return new InvocationExpression(target, GetArgumentExpressions(arguments, argumentNames)) |
|
|
|
|
.WithRR(new CSharpInvocationResolveResult(target.ResolveResult, method, argumentResolveResults, isExpandedForm: isExpandedForm)); |
|
|
|
|
} else if (IsDelegateEqualityComparison(method, arguments)) { |
|
|
|
|
Debug.Assert(argumentToParameterMap == null && argumentNames == null); |
|
|
|
|
return HandleDelegateEqualityComparison(method, arguments) |
|
|
|
|
.WithRR(new CSharpInvocationResolveResult(target.ResolveResult, method, argumentResolveResults, isExpandedForm: isExpandedForm)); |
|
|
|
|
} else if (method.IsOperator && method.Name == "op_Implicit" && arguments.Count == 1) { |
|
|
|
@ -243,7 +272,7 @@ namespace ICSharpCode.Decompiler.CSharp
@@ -243,7 +272,7 @@ namespace ICSharpCode.Decompiler.CSharp
|
|
|
|
|
var targetResolveResult = requireTarget ? target.ResolveResult : null; |
|
|
|
|
IParameterizedMember foundMethod; |
|
|
|
|
OverloadResolutionErrors errors; |
|
|
|
|
while ((errors = IsUnambiguousCall(expectedTargetDetails, method, targetResolveResult, typeArguments, arguments, out foundMethod)) != OverloadResolutionErrors.None) { |
|
|
|
|
while ((errors = IsUnambiguousCall(expectedTargetDetails, method, targetResolveResult, typeArguments, arguments, argumentNames, out foundMethod)) != OverloadResolutionErrors.None) { |
|
|
|
|
switch (errors) { |
|
|
|
|
case OverloadResolutionErrors.TypeInferenceFailed: |
|
|
|
|
case OverloadResolutionErrors.WrongNumberOfTypeArguments: |
|
|
|
@ -308,13 +337,28 @@ namespace ICSharpCode.Decompiler.CSharp
@@ -308,13 +337,28 @@ namespace ICSharpCode.Decompiler.CSharp
|
|
|
|
|
|
|
|
|
|
if (requireTypeArguments && (!settings.AnonymousTypes || !method.TypeArguments.Any(a => a.ContainsAnonymousType()))) |
|
|
|
|
typeArgumentList.AddRange(method.TypeArguments.Select(expressionBuilder.ConvertType)); |
|
|
|
|
var argumentExpressions = arguments.Select(arg => arg.Expression); |
|
|
|
|
var argumentExpressions = GetArgumentExpressions(arguments, argumentNames); |
|
|
|
|
return new InvocationExpression(targetExpr, argumentExpressions) |
|
|
|
|
.WithRR(new CSharpInvocationResolveResult(target.ResolveResult, foundMethod, argumentResolveResults, isExpandedForm: isExpandedForm)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private IEnumerable<Expression> GetArgumentExpressions(List<TranslatedExpression> arguments, string[] argumentNames) |
|
|
|
|
{ |
|
|
|
|
if (argumentNames == null) { |
|
|
|
|
return arguments.Select(arg => arg.Expression); |
|
|
|
|
} else { |
|
|
|
|
return arguments.Zip(argumentNames, |
|
|
|
|
(arg, name) => { |
|
|
|
|
if (name == null) |
|
|
|
|
return arg.Expression; |
|
|
|
|
else |
|
|
|
|
return new NamedArgumentExpression(name, arg); |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static bool IsNullConditional(Expression expr) |
|
|
|
|
{ |
|
|
|
|
return expr is UnaryOperatorExpression uoe && uoe.Operator == UnaryOperatorType.NullConditional; |
|
|
|
@ -381,11 +425,16 @@ namespace ICSharpCode.Decompiler.CSharp
@@ -381,11 +425,16 @@ namespace ICSharpCode.Decompiler.CSharp
|
|
|
|
|
|
|
|
|
|
OverloadResolutionErrors IsUnambiguousCall(ExpectedTargetDetails expectedTargetDetails, IMethod method, |
|
|
|
|
ResolveResult target, IType[] typeArguments, IList<TranslatedExpression> arguments, |
|
|
|
|
string[] argumentNames, |
|
|
|
|
out IParameterizedMember foundMember) |
|
|
|
|
{ |
|
|
|
|
foundMember = null; |
|
|
|
|
var lookup = new MemberLookup(resolver.CurrentTypeDefinition, resolver.CurrentTypeDefinition.ParentAssembly); |
|
|
|
|
var or = new OverloadResolution(resolver.Compilation, arguments.SelectArray(a => a.ResolveResult), typeArguments: typeArguments); |
|
|
|
|
var or = new OverloadResolution(resolver.Compilation, |
|
|
|
|
arguments.SelectArray(a => a.ResolveResult), |
|
|
|
|
argumentNames: argumentNames, |
|
|
|
|
typeArguments: typeArguments, |
|
|
|
|
conversions: expressionBuilder.resolver.conversions); |
|
|
|
|
if (expectedTargetDetails.CallOpCode == OpCode.NewObj) { |
|
|
|
|
foreach (IMethod ctor in method.DeclaringType.GetConstructors()) { |
|
|
|
|
if (lookup.IsAccessible(ctor, allowProtectedAccess: resolver.CurrentTypeDefinition == method.DeclaringTypeDefinition)) { |
|
|
|
@ -413,7 +462,7 @@ namespace ICSharpCode.Decompiler.CSharp
@@ -413,7 +462,7 @@ namespace ICSharpCode.Decompiler.CSharp
|
|
|
|
|
return OverloadResolutionErrors.None; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static bool CanInferAnonymousTypePropertyNamesFromArguments(IList<Expression> args, IList<IParameter> parameters) |
|
|
|
|
static bool CanInferAnonymousTypePropertyNamesFromArguments(IList<Expression> args, IReadOnlyList<IParameter> parameters) |
|
|
|
|
{ |
|
|
|
|
for (int i = 0; i < args.Count; i++) { |
|
|
|
|
string inferredName; |
|
|
|
@ -624,5 +673,34 @@ namespace ICSharpCode.Decompiler.CSharp
@@ -624,5 +673,34 @@ namespace ICSharpCode.Decompiler.CSharp
|
|
|
|
|
Conversion.MethodGroupConversion(method, func.OpCode == OpCode.LdVirtFtn, false))); |
|
|
|
|
return oce; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
internal TranslatedExpression CallWithNamedArgs(Block block) |
|
|
|
|
{ |
|
|
|
|
Debug.Assert(block.Kind == BlockKind.CallWithNamedArgs); |
|
|
|
|
var call = (CallInstruction)block.FinalInstruction; |
|
|
|
|
var arguments = new ILInstruction[call.Arguments.Count]; |
|
|
|
|
var argumentToParameterMap = new int[arguments.Length]; |
|
|
|
|
int firstParamIndex = call.IsInstanceCall ? 1 : 0; |
|
|
|
|
// Arguments from temporary variables (VariableKind.NamedArgument):
|
|
|
|
|
int pos = 0; |
|
|
|
|
foreach (StLoc stloc in block.Instructions) { |
|
|
|
|
Debug.Assert(stloc.Variable.LoadInstructions.Single().Parent == call); |
|
|
|
|
arguments[pos] = stloc.Value; |
|
|
|
|
argumentToParameterMap[pos] = stloc.Variable.LoadInstructions.Single().ChildIndex - firstParamIndex; |
|
|
|
|
pos++; |
|
|
|
|
} |
|
|
|
|
// Remaining argument:
|
|
|
|
|
foreach (var arg in call.Arguments) { |
|
|
|
|
if (arg.MatchLdLoc(out var v) && v.Kind == VariableKind.NamedArgument) { |
|
|
|
|
continue; // already handled in loop above
|
|
|
|
|
} |
|
|
|
|
arguments[pos] = arg; |
|
|
|
|
argumentToParameterMap[pos] = arg.ChildIndex - firstParamIndex; |
|
|
|
|
pos++; |
|
|
|
|
} |
|
|
|
|
Debug.Assert(pos == arguments.Length); |
|
|
|
|
return Build(call.OpCode, call.Method, arguments, argumentToParameterMap, call.ConstrainedTo) |
|
|
|
|
.WithILInstruction(call).WithILInstruction(block); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |