|
|
|
|
@ -19,6 +19,9 @@
@@ -19,6 +19,9 @@
|
|
|
|
|
using System; |
|
|
|
|
using System.Collections.Generic; |
|
|
|
|
using System.Linq; |
|
|
|
|
using ICSharpCode.Decompiler.CSharp.Resolver; |
|
|
|
|
using ICSharpCode.Decompiler.CSharp.TypeSystem; |
|
|
|
|
using ICSharpCode.Decompiler.Semantics; |
|
|
|
|
using ICSharpCode.Decompiler.TypeSystem; |
|
|
|
|
using ICSharpCode.Decompiler.Util; |
|
|
|
|
|
|
|
|
|
@ -175,7 +178,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
@@ -175,7 +178,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
|
|
|
|
|
possibleIndexVariables.Add(stloc.Variable, (stloc.ChildIndex, stloc.Value)); |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
(var kind, var newPath, var values, var targetVariable) = AccessPathElement.GetAccessPath(instructions[pos], rootType, possibleIndexVariables, allowDictionaryInitializer: context.Settings.DictionaryInitializers); |
|
|
|
|
var resolveContext = new CSharpTypeResolveContext(context.TypeSystem.Compilation.MainAssembly, context.UsingScope); |
|
|
|
|
(var kind, var newPath, var values, var targetVariable) = AccessPathElement.GetAccessPath(instructions[pos], rootType, context.Settings, resolveContext, possibleIndexVariables); |
|
|
|
|
if (kind == AccessPathKind.Invalid || target != targetVariable) |
|
|
|
|
return false; |
|
|
|
|
// Treat last element separately:
|
|
|
|
|
@ -228,19 +232,23 @@ namespace ICSharpCode.Decompiler.IL.Transforms
@@ -228,19 +232,23 @@ namespace ICSharpCode.Decompiler.IL.Transforms
|
|
|
|
|
|
|
|
|
|
public struct AccessPathElement : IEquatable<AccessPathElement> |
|
|
|
|
{ |
|
|
|
|
public AccessPathElement(IMember member, ILInstruction[] indices = null) |
|
|
|
|
public AccessPathElement(OpCode opCode, IMember member, ILInstruction[] indices = null) |
|
|
|
|
{ |
|
|
|
|
this.OpCode = opCode; |
|
|
|
|
this.Member = member; |
|
|
|
|
this.Indices = indices; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public readonly OpCode OpCode; |
|
|
|
|
public readonly IMember Member; |
|
|
|
|
public readonly ILInstruction[] Indices; |
|
|
|
|
|
|
|
|
|
public override string ToString() => $"[{Member}, {Indices}]"; |
|
|
|
|
|
|
|
|
|
public static (AccessPathKind Kind, List<AccessPathElement> Path, List<ILInstruction> Values, ILVariable Target) GetAccessPath( |
|
|
|
|
ILInstruction instruction, IType rootType, Dictionary<ILVariable, (int Index, ILInstruction Value)> possibleIndexVariables = null, bool allowDictionaryInitializer = false) |
|
|
|
|
ILInstruction instruction, IType rootType, DecompilerSettings settings, |
|
|
|
|
CSharpTypeResolveContext resolveContext = null, |
|
|
|
|
Dictionary<ILVariable, (int Index, ILInstruction Value)> possibleIndexVariables = null) |
|
|
|
|
{ |
|
|
|
|
List<AccessPathElement> path = new List<AccessPathElement>(); |
|
|
|
|
ILVariable target = null; |
|
|
|
|
@ -253,13 +261,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms
@@ -253,13 +261,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms
|
|
|
|
|
case CallInstruction call: |
|
|
|
|
if (!(call is CallVirt || call is Call)) goto default; |
|
|
|
|
method = call.Method; |
|
|
|
|
if (!IsMethodApplicable(method, call.Arguments, rootType)) goto default; |
|
|
|
|
if (resolveContext != null && !IsMethodApplicable(method, call.Arguments, rootType, resolveContext, settings)) goto default; |
|
|
|
|
instruction = call.Arguments[0]; |
|
|
|
|
if (method.IsAccessor) { |
|
|
|
|
var property = method.AccessorOwner as IProperty; |
|
|
|
|
var isGetter = method.Equals(property?.Getter); |
|
|
|
|
var indices = call.Arguments.Skip(1).Take(call.Arguments.Count - (isGetter ? 1 : 2)).ToArray(); |
|
|
|
|
if (indices.Length > 0 && !allowDictionaryInitializer) goto default; |
|
|
|
|
if (indices.Length > 0 && !settings.DictionaryInitializers) goto default; |
|
|
|
|
if (possibleIndexVariables != null) { |
|
|
|
|
// Mark all index variables as used
|
|
|
|
|
foreach (var index in indices.OfType<IInstructionWithVariableOperand>()) { |
|
|
|
|
@ -267,9 +275,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
@@ -267,9 +275,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
|
|
|
|
|
possibleIndexVariables[index.Variable] = (-1, info.Value); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
path.Insert(0, new AccessPathElement(method.AccessorOwner, indices)); |
|
|
|
|
path.Insert(0, new AccessPathElement(call.OpCode, method.AccessorOwner, indices)); |
|
|
|
|
} else { |
|
|
|
|
path.Insert(0, new AccessPathElement(method)); |
|
|
|
|
path.Insert(0, new AccessPathElement(call.OpCode, method)); |
|
|
|
|
} |
|
|
|
|
if (values == null) { |
|
|
|
|
if (method.IsAccessor) { |
|
|
|
|
@ -285,7 +293,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
@@ -285,7 +293,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
|
|
|
|
|
break; |
|
|
|
|
case LdObj ldobj: { |
|
|
|
|
if (ldobj.Target is LdFlda ldflda) { |
|
|
|
|
path.Insert(0, new AccessPathElement(ldflda.Field)); |
|
|
|
|
path.Insert(0, new AccessPathElement(ldobj.OpCode, ldflda.Field)); |
|
|
|
|
instruction = ldflda.Target; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
@ -293,7 +301,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
@@ -293,7 +301,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
|
|
|
|
|
} |
|
|
|
|
case StObj stobj: { |
|
|
|
|
if (stobj.Target is LdFlda ldflda) { |
|
|
|
|
path.Insert(0, new AccessPathElement(ldflda.Field)); |
|
|
|
|
path.Insert(0, new AccessPathElement(stobj.OpCode, ldflda.Field)); |
|
|
|
|
instruction = ldflda.Target; |
|
|
|
|
if (values == null) { |
|
|
|
|
values = new List<ILInstruction>(new[] { stobj.Value }); |
|
|
|
|
@ -312,7 +320,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
@@ -312,7 +320,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
|
|
|
|
|
instruction = null; |
|
|
|
|
break; |
|
|
|
|
case LdFlda ldflda: |
|
|
|
|
path.Insert(0, new AccessPathElement(ldflda.Field)); |
|
|
|
|
path.Insert(0, new AccessPathElement(ldflda.OpCode, ldflda.Field)); |
|
|
|
|
instruction = ldflda.Target; |
|
|
|
|
break; |
|
|
|
|
default: |
|
|
|
|
@ -326,18 +334,23 @@ namespace ICSharpCode.Decompiler.IL.Transforms
@@ -326,18 +334,23 @@ namespace ICSharpCode.Decompiler.IL.Transforms
|
|
|
|
|
return (kind, path, values, target); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static bool IsMethodApplicable(IMethod method, IList<ILInstruction> arguments, IType rootType) |
|
|
|
|
static bool IsMethodApplicable(IMethod method, IReadOnlyList<ILInstruction> arguments, IType rootType, CSharpTypeResolveContext resolveContext, DecompilerSettings settings) |
|
|
|
|
{ |
|
|
|
|
if (!method.IsExtensionMethod && method.IsStatic) |
|
|
|
|
if (method.IsStatic && !method.IsExtensionMethod) |
|
|
|
|
return false; |
|
|
|
|
if (method.IsAccessor) |
|
|
|
|
return true; |
|
|
|
|
if (!"Add".Equals(method.Name, StringComparison.Ordinal) || arguments.Count == 0) |
|
|
|
|
return false; |
|
|
|
|
if (method.IsExtensionMethod) |
|
|
|
|
return settings.ExtensionMethodsInCollectionInitializers |
|
|
|
|
&& CSharp.Transforms.IntroduceExtensionMethods.CanTransformToExtensionMethodCall(method, resolveContext, ignoreTypeArguments: true); |
|
|
|
|
var targetType = GetReturnTypeFromInstruction(arguments[0]) ?? rootType; |
|
|
|
|
if (targetType == null) |
|
|
|
|
return false; |
|
|
|
|
return targetType.GetAllBaseTypes().Any(i => i.IsKnownType(KnownTypeCode.IEnumerable) || i.IsKnownType(KnownTypeCode.IEnumerableOfT)); |
|
|
|
|
if (!targetType.GetAllBaseTypes().Any(i => i.IsKnownType(KnownTypeCode.IEnumerable) || i.IsKnownType(KnownTypeCode.IEnumerableOfT))) |
|
|
|
|
return false; |
|
|
|
|
return CSharp.Transforms.IntroduceExtensionMethods.CanInferTypeArgumentsFromParameters(method, method.Parameters.SelectArray(p => new ResolveResult(p.Type)), resolveContext); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static IType GetReturnTypeFromInstruction(ILInstruction instruction) |
|
|
|
|
|