Browse Source

Fix #963: foreach-over-array mishandles captured variables

pull/925/merge
Siegfried Pammer 8 years ago
parent
commit
dff671b1d1
  1. 53
      ICSharpCode.Decompiler.Tests/TestCases/Correctness/Capturing.cs
  2. 3
      ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs

53
ICSharpCode.Decompiler.Tests/TestCases/Correctness/Capturing.cs

@ -16,6 +16,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -16,6 +16,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
TestCase4("TestCase4");
OutsideLoop();
InsideLoop();
OutsideLoopOverArray();
OutsideLoopOverArray2();
InsideLoopOverArray2();
}
static void TestCase1()
@ -121,5 +124,55 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -121,5 +124,55 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
Console.WriteLine(func());
}
}
static void OutsideLoopOverArray()
{
Console.WriteLine("OutsideLoopOverArray:");
var functions = new List<Func<int>>();
var array = new int[] { 1, 2, 3 };
int val; // declared outside loop
// The decompiler cannot convert this to a foreach-loop without
// changing the lambda capture semantics.
for (int i = 0; i < array.Length; ++i) {
val = array[i];
functions.Add(() => val);
}
foreach (var func in functions) {
Console.WriteLine(func());
}
}
static void OutsideLoopOverArray2()
{
Console.WriteLine("OutsideLoopOverArray2:");
var functions = new List<Func<int>>();
var array = new int[] { 1, 2, 3 };
int val; // declared outside loop
// The decompiler can convert this to a foreach-loop, but the 'val'
// variable must be declared outside.
for (int i = 0; i < array.Length; ++i) {
int element = array[i];
val = element * 2;
functions.Add(() => val);
}
foreach (var func in functions) {
Console.WriteLine(func());
}
}
static void InsideLoopOverArray2()
{
Console.WriteLine("InsideLoopOverArray2:");
var functions = new List<Func<int>>();
var array = new int[] { 1, 2, 3 };
for (int i = 0; i < array.Length; ++i) {
int element = array[i];
int val = element * 2;
functions.Add(() => val);
}
foreach (var func in functions) {
Console.WriteLine(func());
}
}
}
}

3
ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs

@ -251,7 +251,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -251,7 +251,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
var itemVariable = m.Get<IdentifierExpression>("itemVariable").Single().GetILVariable();
var indexVariable = m.Get<IdentifierExpression>("indexVariable").Single().GetILVariable();
var arrayVariable = m.Get<IdentifierExpression>("arrayVariable").Single().GetILVariable();
if (!itemVariable.IsSingleDefinition)
if (!itemVariable.IsSingleDefinition || itemVariable.CaptureScope != null)
return null;
if (indexVariable.StoreCount != 2 || indexVariable.LoadCount != 3 || indexVariable.AddressCount != 0)
return null;
@ -264,6 +264,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -264,6 +264,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
InExpression = m.Get<IdentifierExpression>("arrayVariable").Single().Detach(),
EmbeddedStatement = body
};
foreachStmt.CopyAnnotationsFrom(forStatement);
itemVariable.Kind = IL.VariableKind.ForeachLocal;
// Add the variable annotation for highlighting (TokenTextWriter expects it directly on the ForeachStatement).
foreachStmt.AddAnnotation(new ILVariableResolveResult(itemVariable, itemVariable.Type));

Loading…
Cancel
Save