Browse Source

Fix #1867: Captures of copies of this are not properly handled by the decompiler

pull/1880/head
Siegfried Pammer 6 years ago
parent
commit
f831e4713f
  1. 20
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs
  2. 4
      ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs
  3. 65
      ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs

20
ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs

@ -429,4 +429,24 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -429,4 +429,24 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
#endif
}
public class Issue1867
{
private int value;
public Func<bool> TestLambda(Issue1867 x)
{
Issue1867 m1;
Issue1867 m2;
if (x.value > value) {
m1 = this;
m2 = x;
} else {
m1 = x;
m2 = this;
}
return () => m1.value + 1 == 4 && m2.value > 5;
}
}
}

4
ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs

@ -97,7 +97,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -97,7 +97,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (target == null) {
target = info.UseSites[0].Arguments[0];
if (target.MatchLdFld(out var target1, out var field) && thisVar.Type.Equals(field.Type) && field.Type.Kind == TypeKind.Class && TransformDisplayClassUsage.IsPotentialClosure(context, field.Type.GetDefinition())) {
var variable = function.Descendants.OfType<ILFunction>().SelectMany(f => f.Variables).Where(v => !v.IsThis() && TransformDisplayClassUsage.IsClosure(context, v, null, out var varType, out _) && varType.Equals(field.Type)).OnlyOrDefault();
var variable = function.Descendants.OfType<ILFunction>().SelectMany(f => f.Variables).Where(v => !v.IsThis() && TransformDisplayClassUsage.IsClosure(context, v, out var varType, out _) && varType.Equals(field.Type)).OnlyOrDefault();
if (variable != null) {
target = new LdLoc(variable);
HandleArgument(localFunction, 1, 0, target);
@ -399,7 +399,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -399,7 +399,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false;
if (closureVar.Kind == VariableKind.NamedArgument)
return false;
if (!TransformDisplayClassUsage.IsClosure(context, closureVar, null, out _, out var initializer))
if (!TransformDisplayClassUsage.IsClosure(context, closureVar, out _, out var initializer))
return false;
if (i - firstArgumentIndex >= 0) {
Debug.Assert(i - firstArgumentIndex < function.Method.Parameters.Count && IsClosureParameter(function.Method.Parameters[i - firstArgumentIndex], resolveContext));

65
ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs

@ -21,6 +21,7 @@ using System.Collections.Generic; @@ -21,6 +21,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util;
namespace ICSharpCode.Decompiler.IL.Transforms
{
@ -45,6 +46,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -45,6 +46,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
ILTransformContext context;
readonly Dictionary<ILVariable, DisplayClass> displayClasses = new Dictionary<ILVariable, DisplayClass>();
readonly List<ILInstruction> instructionsToRemove = new List<ILInstruction>();
readonly MultiDictionary<IField, StObj> fieldAssignmentsWithVariableValue = new MultiDictionary<IField, StObj>();
public void Run(ILFunction function, ILTransformContext context)
{
@ -61,10 +63,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -61,10 +63,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms
continue;
if ((context.Settings.AnonymousMethods || context.Settings.ExpressionTrees) && IsClosure(context, v, instructionsToRemove, out ITypeDefinition closureType, out var inst)) {
AddOrUpdateDisplayClass(f, v, closureType, inst, localFunctionClosureParameter: false);
continue;
}
if (context.Settings.LocalFunctions && f.Kind == ILFunctionKind.LocalFunction && v.Kind == VariableKind.Parameter && v.Index > -1 && f.Method.Parameters[v.Index.Value] is IParameter p && LocalFunctionDecompiler.IsClosureParameter(p, decompilationContext)) {
AddOrUpdateDisplayClass(f, v, ((ByReferenceType)p.Type).ElementType.GetDefinition(), f.Body, localFunctionClosureParameter: true);
continue;
}
AnalyzeUseSites(v);
}
}
VisitILFunction(function);
@ -79,10 +84,35 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -79,10 +84,35 @@ namespace ICSharpCode.Decompiler.IL.Transforms
} finally {
instructionsToRemove.Clear();
displayClasses.Clear();
fieldAssignmentsWithVariableValue.Clear();
this.context = null;
}
}
private void AnalyzeUseSites(ILVariable v)
{
foreach (var use in v.LoadInstructions) {
if (!(use.Parent?.Parent is Block))
continue;
if (use.Parent.MatchStFld(out _, out var f, out var value) && value == use) {
fieldAssignmentsWithVariableValue.Add(f, (StObj)use.Parent);
}
if (use.Parent.MatchStsFld(out f, out value) && value == use) {
fieldAssignmentsWithVariableValue.Add(f, (StObj)use.Parent);
}
}
foreach (var use in v.AddressInstructions) {
if (!(use.Parent?.Parent is Block))
continue;
if (use.Parent.MatchStFld(out _, out var f, out var value) && value == use) {
fieldAssignmentsWithVariableValue.Add(f, (StObj)use.Parent);
}
if (use.Parent.MatchStsFld(out f, out value) && value == use) {
fieldAssignmentsWithVariableValue.Add(f, (StObj)use.Parent);
}
}
}
private void AddOrUpdateDisplayClass(ILFunction f, ILVariable v, ITypeDefinition closureType, ILInstruction inst, bool localFunctionClosureParameter)
{
var displayClass = displayClasses.Values.FirstOrDefault(c => c.Definition == closureType);
@ -110,7 +140,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -110,7 +140,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
}
internal static bool IsClosure(ILTransformContext context, ILVariable variable, List<ILInstruction> instructionsToRemove, out ITypeDefinition closureType, out ILInstruction initializer)
internal static bool IsClosure(ILTransformContext context, ILVariable variable, out ITypeDefinition closureType, out ILInstruction initializer)
{
return IsClosure(context, variable, instructionsToRemove: null, out closureType, out initializer);
}
static bool IsClosure(ILTransformContext context, ILVariable variable, List<ILInstruction> instructionsToRemove, out ITypeDefinition closureType, out ILInstruction initializer)
{
closureType = null;
initializer = null;
@ -276,18 +311,22 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -276,18 +311,22 @@ namespace ICSharpCode.Decompiler.IL.Transforms
protected internal override void VisitStObj(StObj inst)
{
inst.Value.AcceptVisitor(this);
if (IsParameterAssignment(inst, out var displayClass, out var field, out var parameter)) {
context.Step($"Detected parameter assignment {parameter.Name}", inst);
displayClass.Variables.Add((IField)field.MemberDefinition, parameter);
instructionsToRemove.Add(inst);
} else if (IsDisplayClassAssignment(inst, out displayClass, out field, out var variable)) {
context.Step($"Detected display-class assignment {variable.Name}", inst);
displayClass.Variables.Add((IField)field.MemberDefinition, variable);
instructionsToRemove.Add(inst);
} else {
inst.Target.AcceptVisitor(this);
EarlyExpressionTransforms.StObjToStLoc(inst, context);
if (inst.Parent is Block) {
if (IsParameterAssignment(inst, out var displayClass, out var field, out var parameter)) {
context.Step($"Detected parameter assignment {parameter.Name}", inst);
displayClass.Variables.Add((IField)field.MemberDefinition, parameter);
instructionsToRemove.Add(inst);
return;
}
if (IsDisplayClassAssignment(inst, out displayClass, out field, out var variable)) {
context.Step($"Detected display-class assignment {variable.Name}", inst);
displayClass.Variables.Add((IField)field.MemberDefinition, variable);
instructionsToRemove.Add(inst);
return;
}
}
inst.Target.AcceptVisitor(this);
EarlyExpressionTransforms.StObjToStLoc(inst, context);
}
protected internal override void VisitLdObj(LdObj inst)
@ -322,6 +361,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -322,6 +361,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
parameter = null;
if (!IsDisplayClassFieldAccess(inst.Target, out var displayClassVar, out displayClass, out field))
return false;
if (fieldAssignmentsWithVariableValue[field].Count != 1)
return false;
if (!(inst.Value.MatchLdLoc(out var v) && v.Kind == VariableKind.Parameter && v.Function == currentFunction && v.Type.Equals(field.Type)))
return false;
if (displayClass.Variables.ContainsKey((IField)field.MemberDefinition))

Loading…
Cancel
Save