Browse Source

Implement support for unwrapping params arrays.

pull/863/head
Siegfried Pammer 8 years ago
parent
commit
706419544f
  1. 112
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  2. 14
      ICSharpCode.Decompiler/Util/CollectionExtensions.cs

112
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -313,16 +313,21 @@ namespace ICSharpCode.Decompiler.CSharp @@ -313,16 +313,21 @@ namespace ICSharpCode.Decompiler.CSharp
protected internal override TranslatedExpression VisitLdNull(LdNull inst, TranslationContext context)
{
return new NullReferenceExpression()
.WithILInstruction(inst)
.WithRR(new ConstantResolveResult(SpecialType.NullType, null));
return GetDefaultValueExpression(SpecialType.NullType, inst);
}
protected internal override TranslatedExpression VisitDefaultValue(DefaultValue inst, TranslationContext context)
{
return new DefaultValueExpression(ConvertType(inst.Type))
.WithILInstruction(inst)
.WithRR(new ConstantResolveResult(inst.Type, null));
return GetDefaultValueExpression(inst.Type, inst);
}
TranslatedExpression GetDefaultValueExpression(IType type, ILInstruction inst = null)
{
var expr = type.IsReferenceType == true ? (Expression)new NullReferenceExpression() : new DefaultValueExpression(ConvertType(type));
var constantType = type.IsReferenceType == true ? SpecialType.NullType : type;
if (inst == null)
return expr.WithoutILInstruction().WithRR(new ConstantResolveResult(constantType, null));
return expr.WithILInstruction(inst).WithRR(new ConstantResolveResult(constantType, null));
}
protected internal override TranslatedExpression VisitSizeOf(SizeOf inst, TranslationContext context)
@ -1125,12 +1130,42 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1125,12 +1130,42 @@ namespace ICSharpCode.Decompiler.CSharp
int firstParamIndex = (method.IsStatic || inst.OpCode == OpCode.NewObj) ? 0 : 1;
// Translate arguments to the expected parameter types
TranslatedExpression[] arguments = new TranslatedExpression[inst.Method.Parameters.Count];
Debug.Assert(inst.Arguments.Count == firstParamIndex + inst.Method.Parameters.Count);
for (int i = 0; i < arguments.Length; i++) {
var parameter = method.Parameters[i];
arguments[i] = Translate(inst.Arguments[firstParamIndex + i])
.ConvertTo(parameter.Type, this, allowImplicitConversion: true);
var arguments = new List<TranslatedExpression>(method.Parameters.Count);
Debug.Assert(inst.Arguments.Count == firstParamIndex + method.Parameters.Count);
var expectedParameters = method.Parameters.ToList();
for (int i = 0; i < method.Parameters.Count; i++) {
var parameter = expectedParameters[i];
var arg = Translate(inst.Arguments[firstParamIndex + i]);
if (parameter.IsParams) {
// 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.
if (arg.ResolveResult is ArrayCreateResolveResult acrr &&
acrr.SizeArguments.Count == 1 &&
acrr.SizeArguments[0].IsCompileTimeConstant &&
acrr.SizeArguments[0].ConstantValue is int length) {
var expectedParametersCopy = expectedParameters.Take(expectedParameters.Count - 1).ToList();
var argumentsCopy = new List<TranslatedExpression>(arguments);
if (length > 0) {
var arrayElements = ((ArrayCreateExpression)arg.Expression).Initializer.Elements.ToArray();
var elementType = ((ArrayType)acrr.Type).ElementType;
for (int j = 0; j < length; j++) {
expectedParametersCopy.Add(new DefaultParameter(elementType, parameter.Name + j));
if (j < arrayElements.Length)
argumentsCopy.Add(new TranslatedExpression(arrayElements[j]));
else
argumentsCopy.Add(GetDefaultValueExpression(elementType));
}
}
if (IsUnambiguousCall(inst, target, method, Array.Empty<IType>(), argumentsCopy) == OverloadResolutionErrors.None) {
expectedParameters = expectedParametersCopy;
arguments = argumentsCopy.SelectList(a => new TranslatedExpression(a.Expression.Detach()));
continue;
}
}
}
arguments.Add(arg.ConvertTo(parameter.Type, this, allowImplicitConversion: true));
if (parameter.IsOut && arguments[i].Expression is DirectionExpression dirExpr) {
dirExpr.FieldDirection = FieldDirection.Out;
@ -1142,11 +1177,12 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1142,11 +1177,12 @@ namespace ICSharpCode.Decompiler.CSharp
var argListArg = new UndocumentedExpression();
argListArg.UndocumentedExpressionType = UndocumentedExpressionType.ArgList;
int paramIndex = regularParameterCount;
argListArg.Arguments.AddRange(arguments.Skip(regularParameterCount).Select(arg => arg.ConvertTo(method.Parameters[paramIndex++].Type, this).Expression));
argListArg.Arguments.AddRange(arguments.Skip(regularParameterCount).Select(arg => arg.ConvertTo(expectedParameters[paramIndex++].Type, this).Expression));
var argListRR = new ResolveResult(SpecialType.ArgList);
arguments = arguments.Take(regularParameterCount)
.Concat(new[] { argListArg.WithoutILInstruction().WithRR(argListRR) }).ToArray();
.Concat(new[] { argListArg.WithoutILInstruction().WithRR(argListRR) }).ToList();
method = (IMethod)method.MemberDefinition;
expectedParameters = method.Parameters.ToList();
}
var argumentResolveResults = arguments.Select(arg => arg.ResolveResult).ToList();
@ -1157,14 +1193,13 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1157,14 +1193,13 @@ namespace ICSharpCode.Decompiler.CSharp
var argumentExpressions = arguments.SelectArray(arg => arg.Expression);
if (settings.AnonymousTypes && method.DeclaringType.IsAnonymousType()) {
AnonymousTypeCreateExpression atce = new AnonymousTypeCreateExpression();
var parameters = inst.Method.Parameters;
if (CanInferAnonymousTypePropertyNamesFromArguments(argumentExpressions, parameters)) {
if (CanInferAnonymousTypePropertyNamesFromArguments(argumentExpressions, expectedParameters)) {
atce.Initializers.AddRange(argumentExpressions);
} else {
for (int i = 0; i < argumentExpressions.Length; i++) {
atce.Initializers.Add(
new NamedExpression {
Name = parameters[i].Name,
Name = expectedParameters[i].Name,
Expression = argumentExpressions[i]
});
}
@ -1178,7 +1213,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1178,7 +1213,7 @@ namespace ICSharpCode.Decompiler.CSharp
}
} else {
int allowedParamCount = (method.ReturnType.IsKnownType(KnownTypeCode.Void) ? 1 : 0);
if (method.IsAccessor && (method.AccessorOwner.SymbolKind == SymbolKind.Indexer || method.Parameters.Count == allowedParamCount)) {
if (method.IsAccessor && (method.AccessorOwner.SymbolKind == SymbolKind.Indexer || expectedParameters.Count == allowedParamCount)) {
return HandleAccessorCall(inst, target, method, arguments.ToList());
} else {
bool requireTypeArguments = false;
@ -1186,23 +1221,8 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1186,23 +1221,8 @@ namespace ICSharpCode.Decompiler.CSharp
bool argumentsCasted = false;
IType[] typeArguments = Array.Empty<IType>();
OverloadResolutionErrors IsUnambiguousCall()
{
var lookup = new MemberLookup(resolver.CurrentTypeDefinition, resolver.CurrentTypeDefinition.ParentAssembly);
var result = lookup.Lookup(target.ResolveResult, method.Name, EmptyList<IType>.Instance, true) as MethodGroupResolveResult;
if (result == null)
return OverloadResolutionErrors.AmbiguousMatch;
var or = new OverloadResolution(resolver.Compilation, arguments.SelectArray(a => a.ResolveResult), typeArguments: typeArguments);
or.AddMethodLists(result.MethodsGroupedByDeclaringType.ToArray());
if (or.BestCandidateErrors != OverloadResolutionErrors.None)
return or.BestCandidateErrors;
if (!IsAppropriateCallTarget(method, or.GetBestCandidateWithSubstitutedTypeArguments(), inst.OpCode == OpCode.CallVirt))
return OverloadResolutionErrors.AmbiguousMatch;
return OverloadResolutionErrors.None;
}
OverloadResolutionErrors errors;
while ((errors = IsUnambiguousCall()) != OverloadResolutionErrors.None) {
while ((errors = IsUnambiguousCall(inst, target, method, typeArguments, arguments)) != OverloadResolutionErrors.None) {
switch (errors) {
case OverloadResolutionErrors.TypeInferenceFailed:
case OverloadResolutionErrors.WrongNumberOfTypeArguments:
@ -1213,9 +1233,9 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1213,9 +1233,9 @@ namespace ICSharpCode.Decompiler.CSharp
default:
if (!argumentsCasted) {
argumentsCasted = true;
for (int i = 0; i < arguments.Length; i++) {
if (!settings.AnonymousTypes || !method.Parameters[i].Type.ContainsAnonymousType())
arguments[i] = arguments[i].ConvertTo(method.Parameters[i].Type, this);
for (int i = 0; i < arguments.Count; i++) {
if (!settings.AnonymousTypes || !expectedParameters[i].Type.ContainsAnonymousType())
arguments[i] = arguments[i].ConvertTo(expectedParameters[i].Type, this);
}
} else if (!targetCasted) {
targetCasted = true;
@ -1247,6 +1267,24 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1247,6 +1267,24 @@ namespace ICSharpCode.Decompiler.CSharp
}
}
OverloadResolutionErrors IsUnambiguousCall(ILInstruction inst, TranslatedExpression target, IMethod method, IType[] typeArguments, IList<TranslatedExpression> arguments)
{
// TODO : MemberLookup does not support ctors. (target is null in that case! -> NullArgumentException is thrown)
if (inst.OpCode == OpCode.NewObj)
return OverloadResolutionErrors.None;
var lookup = new MemberLookup(resolver.CurrentTypeDefinition, resolver.CurrentTypeDefinition.ParentAssembly);
var result = lookup.Lookup(target.ResolveResult, method.Name, EmptyList<IType>.Instance, true) as MethodGroupResolveResult;
if (result == null)
return OverloadResolutionErrors.AmbiguousMatch;
var or = new OverloadResolution(resolver.Compilation, arguments.SelectArray(a => a.ResolveResult), typeArguments: typeArguments);
or.AddMethodLists(result.MethodsGroupedByDeclaringType.ToArray());
if (or.BestCandidateErrors != OverloadResolutionErrors.None)
return or.BestCandidateErrors;
if (!IsAppropriateCallTarget(method, or.GetBestCandidateWithSubstitutedTypeArguments(), inst.OpCode == OpCode.CallVirt))
return OverloadResolutionErrors.AmbiguousMatch;
return OverloadResolutionErrors.None;
}
static bool CanInferAnonymousTypePropertyNamesFromArguments(IList<Expression> args, IList<IParameter> parameters)
{
for (int i = 0; i < args.Count; i++) {

14
ICSharpCode.Decompiler/Util/CollectionExtensions.cs

@ -67,6 +67,20 @@ namespace ICSharpCode.Decompiler.Util @@ -67,6 +67,20 @@ namespace ICSharpCode.Decompiler.Util
return result;
}
/// <summary>
/// Equivalent to <code>collection.Select(func).ToList()</code>, but more efficient as it makes
/// use of the input collection's known size.
/// </summary>
public static List<U> SelectList<T, U>(this ICollection<T> collection, Func<T, U> func)
{
List<U> result = new List<U>(collection.Count);
int index = 0;
foreach (var element in collection) {
result.Add(func(element));
}
return result;
}
public static IEnumerable<U> SelectWithIndex<T, U>(this IEnumerable<T> source, Func<int, T, U> func)
{
int index = 0;

Loading…
Cancel
Save