Browse Source

Fix #3439: Regressed decompilation of variables in lambda scope

pull/3443/head
Siegfried Pammer 2 months ago
parent
commit
7d0262d779
  1. 1
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  2. 6
      ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
  3. 48
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Issue3439.cs
  4. 24
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs
  5. 120
      ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs

1
ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj

@ -133,6 +133,7 @@ @@ -133,6 +133,7 @@
<Compile Include="TestCases\ILPretty\Issue3421.cs" />
<Compile Include="TestCases\ILPretty\MonoFixed.cs" />
<Compile Include="TestCases\Pretty\Comparisons.cs" />
<Compile Include="TestCases\Pretty\Issue3439.cs" />
<None Include="TestCases\VBPretty\VBAutomaticEvents.vb" />
<Compile Include="TestCases\VBPretty\VBAutomaticEvents.cs" />
<Compile Include="TestCases\VBPretty\VBNonGenericForEach.cs" />

6
ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs

@ -604,6 +604,12 @@ namespace ICSharpCode.Decompiler.Tests @@ -604,6 +604,12 @@ namespace ICSharpCode.Decompiler.Tests
await RunForLibrary(cscOptions: cscOptions, configureDecompiler: settings => settings.SetLanguageVersion(CSharp.LanguageVersion.CSharp6));
}
[Test]
public async Task Issue3439([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions)
{
await RunForLibrary(cscOptions: cscOptions);
}
[Test]
public async Task AssemblyCustomAttributes([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions)
{

48
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Issue3439.cs

@ -0,0 +1,48 @@ @@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
internal class VariableScopeTest
{
private class Item
{
public long Key;
public string Value;
}
private void Test(List<string> list1)
{
AddAction(delegate (List<Item> list2) {
long num2 = 1L;
foreach (string item in list1)
{
list2.Add(new Item {
Key = num2,
Value = item
});
num2++;
}
});
int num = 1;
foreach (string item2 in list1)
{
int preservedName = num;
num++;
AddAction(item2, delegate (object x) {
SetValue(x, preservedName);
});
}
}
private static void AddAction(Action<List<Item>> action)
{
}
private static void AddAction(string name, Action<object> action)
{
}
private static void SetValue(object obj, int value)
{
}
}

24
ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs

@ -53,15 +53,15 @@ namespace LocalFunctions @@ -53,15 +53,15 @@ namespace LocalFunctions
{
t2 = default(T2);
int l = 0;
return NonStaticMethod2<T1>() + NonStaticMethod2<T2>() + z.GetHashCode();
int NonStaticMethod2<T4>()
return NonStaticMethod3<T1>() + NonStaticMethod3<T2>() + z.GetHashCode();
int NonStaticMethod3<T4>()
{
return i2 + l + NonStaticMethod<T4>(0) + StaticMethod<decimal>();
}
}
}
return MixedLocalFunction<T1>() + MixedLocalFunction<T2>() + StaticMethod<decimal>() + StaticMethod<int>() + NonStaticMethod3() + StaticMethod4<object>(null) + StaticMethod5<T1>();
int NonStaticMethod3()
return MixedLocalFunction<T1>() + MixedLocalFunction<T2>() + StaticMethod<decimal>() + StaticMethod<int>() + NonStaticMethod2() + StaticMethod4<object>(null) + StaticMethod5<T1>();
int NonStaticMethod2()
{
return GetHashCode();
}
@ -127,16 +127,16 @@ namespace LocalFunctions @@ -127,16 +127,16 @@ namespace LocalFunctions
{
t2 = default(T2);
int l = 0;
return StaticInvokeAsFunc(NonStaticMethod2<T1>) + StaticInvokeAsFunc(NonStaticMethod2<T2>) + z.GetHashCode();
int NonStaticMethod2<T4>()
return StaticInvokeAsFunc(NonStaticMethod3<T1>) + StaticInvokeAsFunc(NonStaticMethod3<T2>) + z.GetHashCode();
int NonStaticMethod3<T4>()
{
return i2 + l + StaticInvokeAsFunc(NonStaticMethod<T4>) + StaticInvokeAsFunc(StaticMethod<decimal>);
}
}
}
Console.WriteLine(t2);
return StaticInvokeAsFunc(MixedLocalFunction2Delegate<T1>) + StaticInvokeAsFunc(MixedLocalFunction2Delegate<T2>) + StaticInvokeAsFunc(StaticMethod<decimal>) + StaticInvokeAsFunc(StaticMethod<int>) + StaticInvokeAsFunc(NonStaticMethod3) + StaticInvokeAsFunc(StaticMethod4<T1>) + new Func<object, int>(StaticMethod5<object>)(null) + StaticInvokeAsFunc2<object>(StaticMethod5<object>) + new Func<Func<object, int>, int>(StaticInvokeAsFunc2<object>)(StaticMethod5<object>);
int NonStaticMethod3()
return StaticInvokeAsFunc(MixedLocalFunction2Delegate<T1>) + StaticInvokeAsFunc(MixedLocalFunction2Delegate<T2>) + StaticInvokeAsFunc(StaticMethod<decimal>) + StaticInvokeAsFunc(StaticMethod<int>) + StaticInvokeAsFunc(NonStaticMethod2) + StaticInvokeAsFunc(StaticMethod4<T1>) + new Func<object, int>(StaticMethod5<object>)(null) + StaticInvokeAsFunc2<object>(StaticMethod5<object>) + new Func<Func<object, int>, int>(StaticInvokeAsFunc2<object>)(StaticMethod5<object>);
int NonStaticMethod2()
{
return GetHashCode();
}
@ -732,15 +732,15 @@ namespace LocalFunctions @@ -732,15 +732,15 @@ namespace LocalFunctions
{
t0 = 0;
int t2 = t0;
return new Func<int>(ZZZ2)();
int ZZZ2()
return new Func<int>(ZZZ3)();
int ZZZ3()
{
t0 = 0;
t2 = 0;
return ZZZ3();
return ZZZ2();
}
}
int ZZZ3()
int ZZZ2()
{
t0 = 0;
int t3 = t0;

120
ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs

@ -55,6 +55,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -55,6 +55,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
};
ILTransformContext context;
Queue<(ILFunction function, VariableScope parentScope)> workList;
const char maxLoopVariableName = 'n';
public class VariableScope
@ -441,7 +442,70 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -441,7 +442,70 @@ namespace ICSharpCode.Decompiler.IL.Transforms
public void Run(ILFunction function, ILTransformContext context)
{
this.context = context;
function.AcceptVisitor(this, null);
this.workList ??= new Queue<(ILFunction function, VariableScope parentScope)>();
this.workList.Clear();
this.workList.Enqueue((function, null));
while (this.workList.Count > 0)
{
var (currentFunction, parentContext) = this.workList.Dequeue();
if (currentFunction.Kind == ILFunctionKind.LocalFunction)
{
// assign names to local functions
if (!LocalFunctionDecompiler.ParseLocalFunctionName(currentFunction.Name, out _, out var newName) || !IsValidName(newName))
newName = null;
string nameWithoutNumber;
int number;
if (!string.IsNullOrEmpty(newName))
{
nameWithoutNumber = SplitName(newName, out number);
}
else
{
nameWithoutNumber = "f";
number = 1;
}
int count;
if (!parentContext.IsReservedVariableName(nameWithoutNumber, out int currentIndex))
{
count = 1;
}
else
{
if (currentIndex < number)
count = number;
else
count = Math.Max(number, currentIndex) + 1;
}
parentContext.ReserveVariableName(nameWithoutNumber, count);
if (count > 1)
{
newName = nameWithoutNumber + count.ToString();
}
else
{
newName = nameWithoutNumber;
}
currentFunction.Name = newName;
currentFunction.ReducedMethod.Name = newName;
parentContext.Add((MethodDefinitionHandle)currentFunction.ReducedMethod.MetadataToken, newName);
}
var nestedContext = new VariableScope(currentFunction, this.context, parentContext);
currentFunction.Body.AcceptVisitor(this, nestedContext);
foreach (var localFunction in currentFunction.LocalFunctions)
workList.Enqueue((localFunction, nestedContext));
if (currentFunction.Kind != ILFunctionKind.TopLevelFunction)
{
foreach (var p in currentFunction.Variables.Where(v => v.Kind == VariableKind.Parameter && v.Index >= 0))
{
p.Name = nestedContext.AssignNameIfUnassigned(p);
}
}
}
}
Unit VisitChildren(ILInstruction inst, VariableScope context)
@ -507,59 +571,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -507,59 +571,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
protected internal override Unit VisitILFunction(ILFunction function, VariableScope context)
{
if (function.Kind == ILFunctionKind.LocalFunction)
{
// assign names to local functions
if (!LocalFunctionDecompiler.ParseLocalFunctionName(function.Name, out _, out var newName) || !IsValidName(newName))
newName = null;
string nameWithoutNumber;
int number;
if (!string.IsNullOrEmpty(newName))
{
nameWithoutNumber = SplitName(newName, out number);
}
else
{
nameWithoutNumber = "f";
number = 1;
}
int count;
if (!context.IsReservedVariableName(nameWithoutNumber, out int currentIndex))
{
count = 1;
}
else
{
if (currentIndex < number)
count = number;
else
count = Math.Max(number, currentIndex) + 1;
}
context.ReserveVariableName(nameWithoutNumber, count);
if (count > 1)
{
newName = nameWithoutNumber + count.ToString();
}
else
{
newName = nameWithoutNumber;
}
function.Name = newName;
function.ReducedMethod.Name = newName;
context.Add((MethodDefinitionHandle)function.ReducedMethod.MetadataToken, newName);
}
var nestedContext = new VariableScope(function, this.context, context);
base.VisitILFunction(function, nestedContext);
if (function.Kind != ILFunctionKind.TopLevelFunction)
{
foreach (var p in function.Variables.Where(v => v.Kind == VariableKind.Parameter && v.Index >= 0))
{
p.Name = nestedContext.AssignNameIfUnassigned(p);
}
}
workList.Enqueue((function, context));
return default;
}

Loading…
Cancel
Save