Skip to content

Commit

Permalink
Fix icsharpcode#1867: Captures of copies of this are not properly han…
Browse files Browse the repository at this point in the history
…dled by the decompiler
  • Loading branch information
siegfriedpammer committed Dec 20, 2019
1 parent f08a721 commit f831e47
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -429,4 +429,24 @@ public static Func<string> Issue1773d((int Integer, string String) data)
}
#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;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ public void Run(ILFunction function, ILTransformContext context)
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);
Expand Down Expand Up @@ -399,7 +399,7 @@ bool HandleArgument(ILFunction function, int firstArgumentIndex, int i, ILInstru
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));
Expand Down
65 changes: 53 additions & 12 deletions ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
using System.Diagnostics;
using System.Linq;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util;

namespace ICSharpCode.Decompiler.IL.Transforms
{
Expand All @@ -45,6 +46,7 @@ class DisplayClass
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)
{
Expand All @@ -61,10 +63,13 @@ public void Run(ILFunction function, ILTransformContext context)
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);
Expand All @@ -79,10 +84,35 @@ public void Run(ILFunction function, ILTransformContext context)
} 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);
Expand Down Expand Up @@ -110,7 +140,12 @@ private void AddOrUpdateDisplayClass(ILFunction f, ILVariable v, ITypeDefinition
}
}

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;
Expand Down Expand Up @@ -276,18 +311,22 @@ protected internal override void VisitStLoc(StLoc inst)
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)
Expand Down Expand Up @@ -322,6 +361,8 @@ private bool IsParameterAssignment(StObj inst, out DisplayClass displayClass, ou
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))
Expand Down

0 comments on commit f831e47

Please sign in to comment.