Browse Source

Merge pull request #976 from icsharpcode/loops

HighLevelLoopTransform
pull/987/head
Siegfried Pammer 8 years ago committed by GitHub
parent
commit
a9ca403260
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 39
      ICSharpCode.Decompiler.Tests/TestCases/Correctness/Capturing.cs
  2. 54
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.cs
  3. 345
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.il
  4. 172
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.opt.il
  5. 178
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.opt.roslyn.il
  6. 341
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.roslyn.il
  7. 1
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  8. 68
      ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
  9. 120
      ICSharpCode.Decompiler/CSharp/Transforms/NormalizeBlockStatements.cs
  10. 15
      ICSharpCode.Decompiler/DecompilerSettings.cs
  11. 1
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  12. 69
      ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs
  13. 1
      ILSpy/Options/DecompilerSettingsPanel.xaml
  14. 2
      ILSpy/Options/DecompilerSettingsPanel.xaml.cs

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

@ -19,6 +19,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
OutsideLoopOverArray(); OutsideLoopOverArray();
OutsideLoopOverArray2(); OutsideLoopOverArray2();
InsideLoopOverArray2(); InsideLoopOverArray2();
NotWhileDueToVariableInsideLoop();
NotDoWhileDueToVariableInsideLoop();
} }
static void TestCase1() static void TestCase1()
@ -174,5 +176,42 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
Console.WriteLine(func()); Console.WriteLine(func());
} }
} }
static int nextVal;
static int GetVal()
{
return ++nextVal & 7;
}
static void NotWhileDueToVariableInsideLoop()
{
Console.WriteLine("NotWhileDueToVariableInsideLoop:");
var functions = new List<Func<int>>();
while (true) {
int v;
if ((v = GetVal()) == 0)
break;
functions.Add(() => v);
}
foreach (var f in functions) {
Console.WriteLine(f());
}
}
static void NotDoWhileDueToVariableInsideLoop()
{
Console.WriteLine("NotDoWhileDueToVariableInsideLoop:");
var functions = new List<Func<int>>();
while (true) {
int v = GetVal();
functions.Add(() => v);
if (v == 0)
break;
}
foreach (var f in functions) {
Console.WriteLine(f());
}
}
} }
} }

54
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.cs

@ -252,6 +252,18 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
} }
} }
public struct DataItem
{
public int Property {
get;
set;
}
public void TestCall()
{
}
}
private IEnumerable<string> alternatives; private IEnumerable<string> alternatives;
private static void Operation(ref int item) private static void Operation(ref int item)
@ -379,8 +391,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
} }
} finally { } finally {
IDisposable disposable = enumerator as IDisposable; IDisposable disposable = enumerator as IDisposable;
if (disposable != null) if (disposable != null) {
disposable.Dispose(); disposable.Dispose();
}
} }
Console.WriteLine("After finally!"); Console.WriteLine("After finally!");
} }
@ -389,7 +402,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{ {
foreach (int item in items) { foreach (int item in items) {
#if ROSLYN && OPT #if ROSLYN && OPT
// The variable names differs based on whether roslyn optimizes out the 'item' variable // The variable name differs based on whether roslyn optimizes out the 'item' variable
int current = item; int current = item;
Loops.Operation(ref current); Loops.Operation(ref current);
#else #else
@ -440,6 +453,37 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
} }
} }
} }
public void ForEachOverListOfStruct(List<DataItem> items, int value)
{
foreach (DataItem item in items) {
DataItem dataItem = item;
dataItem.Property = value;
}
}
public void ForEachOverListOfStruct2(List<DataItem> items, int value)
{
foreach (DataItem item in items) {
#if ROSLYN && OPT
// The variable name differs based on whether roslyn optimizes out the 'item' variable
DataItem current = item;
current.TestCall();
current.Property = value;
#else
DataItem dataItem = item;
dataItem.TestCall();
dataItem.Property = value;
#endif
}
}
public void ForEachOverListOfStruct3(List<DataItem> items, int value)
{
foreach (DataItem item in items) {
item.TestCall();
}
}
#endregion #endregion
public void ForOverArray(string[] array) public void ForOverArray(string[] array)
@ -527,8 +571,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
if (this.Condition("continue")) { if (this.Condition("continue")) {
continue; continue;
} }
if (!this.Condition("break")) if (!this.Condition("break")) {
break; break;
}
} }
Console.WriteLine("End of loop body"); Console.WriteLine("End of loop body");
} }
@ -582,8 +627,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
if (this.Condition("continue")) { if (this.Condition("continue")) {
continue; continue;
} }
if (!this.Condition("not-break")) if (!this.Condition("not-break")) {
break; break;
}
} }
Console.WriteLine("End of loop body"); Console.WriteLine("End of loop body");
} }

345
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.il

@ -10,7 +10,7 @@
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.ver 4:0:0:0 .ver 4:0:0:0
} }
.assembly n3qdq2uj .assembly dky1g03y
{ {
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 )
.custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx
@ -20,15 +20,15 @@
.hash algorithm 0x00008004 .hash algorithm 0x00008004
.ver 0:0:0:0 .ver 0:0:0:0
} }
.module n3qdq2uj.dll .module dky1g03y.dll
// MVID: {98B4F05B-5E30-48EB-AAF5-A90EA42A94A2} // MVID: {4ADF70C5-8EC0-41AF-A9B1-6DF95B943C1A}
.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 )
.imagebase 0x10000000 .imagebase 0x10000000
.file alignment 0x00000200 .file alignment 0x00000200
.stackreserve 0x00100000 .stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI .subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY .corflags 0x00000001 // ILONLY
// Image base: 0x02AD0000 // Image base: 0x03580000
// =============== CLASS MEMBERS DECLARATION =================== // =============== CLASS MEMBERS DECLARATION ===================
@ -584,6 +584,55 @@
} // end of property CustomStructEnumeratorWithIDisposable`1::Current } // end of property CustomStructEnumeratorWithIDisposable`1::Current
} // end of class CustomStructEnumeratorWithIDisposable`1 } // end of class CustomStructEnumeratorWithIDisposable`1
.class sequential ansi sealed nested public beforefieldinit DataItem
extends [mscorlib]System.ValueType
{
.field private int32 '<Property>k__BackingField'
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
.method public hidebysig specialname
instance int32 get_Property() cil managed
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
// Code size 11 (0xb)
.maxstack 1
.locals init (int32 V_0)
IL_0000: ldarg.0
IL_0001: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::'<Property>k__BackingField'
IL_0006: stloc.0
IL_0007: br.s IL_0009
IL_0009: ldloc.0
IL_000a: ret
} // end of method DataItem::get_Property
.method public hidebysig specialname
instance void set_Property(int32 'value') cil managed
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
// Code size 8 (0x8)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldarg.1
IL_0002: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::'<Property>k__BackingField'
IL_0007: ret
} // end of method DataItem::set_Property
.method public hidebysig instance void
TestCall() cil managed
{
// Code size 2 (0x2)
.maxstack 8
IL_0000: nop
IL_0001: ret
} // end of method DataItem::TestCall
.property instance int32 Property()
{
.get instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::get_Property()
.set instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::set_Property(int32)
} // end of property DataItem::Property
} // end of class DataItem
.class auto ansi sealed nested private beforefieldinit '<>c__DisplayClass1' .class auto ansi sealed nested private beforefieldinit '<>c__DisplayClass1'
extends [mscorlib]System.Object extends [mscorlib]System.Object
{ {
@ -1269,7 +1318,7 @@
.method public hidebysig static void NonGenericForeachWithReturnFallbackTest(class [mscorlib]System.Collections.IEnumerable e) cil managed .method public hidebysig static void NonGenericForeachWithReturnFallbackTest(class [mscorlib]System.Collections.IEnumerable e) cil managed
{ {
// Code size 111 (0x6f) // Code size 113 (0x71)
.maxstack 2 .maxstack 2
.locals init (class [mscorlib]System.Collections.IEnumerator V_0, .locals init (class [mscorlib]System.Collections.IEnumerator V_0,
object V_1, object V_1,
@ -1308,7 +1357,7 @@
IL_0044: nop IL_0044: nop
IL_0045: nop IL_0045: nop
IL_0046: nop IL_0046: nop
IL_0047: leave.s IL_0062 IL_0047: leave.s IL_0064
} // end .try } // end .try
finally finally
@ -1322,19 +1371,21 @@
IL_0053: ceq IL_0053: ceq
IL_0055: stloc.3 IL_0055: stloc.3
IL_0056: ldloc.3 IL_0056: ldloc.3
IL_0057: brtrue.s IL_0060 IL_0057: brtrue.s IL_0062
IL_0059: ldloc.2 IL_0059: nop
IL_005a: callvirt instance void [mscorlib]System.IDisposable::Dispose() IL_005a: ldloc.2
IL_005f: nop IL_005b: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0060: nop IL_0060: nop
IL_0061: endfinally IL_0061: nop
IL_0062: nop
IL_0063: endfinally
} // end handler } // end handler
IL_0062: nop IL_0064: nop
IL_0063: ldstr "After finally!" IL_0065: ldstr "After finally!"
IL_0068: call void [mscorlib]System.Console::WriteLine(string) IL_006a: call void [mscorlib]System.Console::WriteLine(string)
IL_006d: nop IL_006f: nop
IL_006e: ret IL_0070: ret
} // end of method Loops::NonGenericForeachWithReturnFallbackTest } // end of method Loops::NonGenericForeachWithReturnFallbackTest
.method public hidebysig static void ForeachWithRefUsage(class [mscorlib]System.Collections.Generic.List`1<int32> items) cil managed .method public hidebysig static void ForeachWithRefUsage(class [mscorlib]System.Collections.Generic.List`1<int32> items) cil managed
@ -1669,6 +1720,158 @@
IL_0069: ret IL_0069: ret
} // end of method Loops::ForEachBreakWhenFound } // end of method Loops::ForEachBreakWhenFound
.method public hidebysig instance void
ForEachOverListOfStruct(class [mscorlib]System.Collections.Generic.List`1<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem> items,
int32 'value') cil managed
{
// Code size 62 (0x3e)
.maxstack 2
.locals init (valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem V_0,
valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem V_1,
valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem> V_2,
bool V_3)
IL_0000: nop
IL_0001: nop
IL_0002: ldarg.1
IL_0003: callvirt instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<!0> class [mscorlib]System.Collections.Generic.List`1<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem>::GetEnumerator()
IL_0008: stloc.2
.try
{
IL_0009: br.s IL_0020
IL_000b: ldloca.s V_2
IL_000d: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem>::get_Current()
IL_0012: stloc.0
IL_0013: nop
IL_0014: ldloc.0
IL_0015: stloc.1
IL_0016: ldloca.s V_1
IL_0018: ldarg.2
IL_0019: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::set_Property(int32)
IL_001e: nop
IL_001f: nop
IL_0020: ldloca.s V_2
IL_0022: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem>::MoveNext()
IL_0027: stloc.3
IL_0028: ldloc.3
IL_0029: brtrue.s IL_000b
IL_002b: leave.s IL_003c
} // end .try
finally
{
IL_002d: ldloca.s V_2
IL_002f: constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem>
IL_0035: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_003a: nop
IL_003b: endfinally
} // end handler
IL_003c: nop
IL_003d: ret
} // end of method Loops::ForEachOverListOfStruct
.method public hidebysig instance void
ForEachOverListOfStruct2(class [mscorlib]System.Collections.Generic.List`1<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem> items,
int32 'value') cil managed
{
// Code size 70 (0x46)
.maxstack 2
.locals init (valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem V_0,
valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem V_1,
valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem> V_2,
bool V_3)
IL_0000: nop
IL_0001: nop
IL_0002: ldarg.1
IL_0003: callvirt instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<!0> class [mscorlib]System.Collections.Generic.List`1<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem>::GetEnumerator()
IL_0008: stloc.2
.try
{
IL_0009: br.s IL_0028
IL_000b: ldloca.s V_2
IL_000d: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem>::get_Current()
IL_0012: stloc.0
IL_0013: nop
IL_0014: ldloc.0
IL_0015: stloc.1
IL_0016: ldloca.s V_1
IL_0018: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::TestCall()
IL_001d: nop
IL_001e: ldloca.s V_1
IL_0020: ldarg.2
IL_0021: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::set_Property(int32)
IL_0026: nop
IL_0027: nop
IL_0028: ldloca.s V_2
IL_002a: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem>::MoveNext()
IL_002f: stloc.3
IL_0030: ldloc.3
IL_0031: brtrue.s IL_000b
IL_0033: leave.s IL_0044
} // end .try
finally
{
IL_0035: ldloca.s V_2
IL_0037: constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem>
IL_003d: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0042: nop
IL_0043: endfinally
} // end handler
IL_0044: nop
IL_0045: ret
} // end of method Loops::ForEachOverListOfStruct2
.method public hidebysig instance void
ForEachOverListOfStruct3(class [mscorlib]System.Collections.Generic.List`1<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem> items,
int32 'value') cil managed
{
// Code size 59 (0x3b)
.maxstack 1
.locals init (valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem V_0,
valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem> V_1,
bool V_2)
IL_0000: nop
IL_0001: nop
IL_0002: ldarg.1
IL_0003: callvirt instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<!0> class [mscorlib]System.Collections.Generic.List`1<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem>::GetEnumerator()
IL_0008: stloc.1
.try
{
IL_0009: br.s IL_001d
IL_000b: ldloca.s V_1
IL_000d: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem>::get_Current()
IL_0012: stloc.0
IL_0013: nop
IL_0014: ldloca.s V_0
IL_0016: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::TestCall()
IL_001b: nop
IL_001c: nop
IL_001d: ldloca.s V_1
IL_001f: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem>::MoveNext()
IL_0024: stloc.2
IL_0025: ldloc.2
IL_0026: brtrue.s IL_000b
IL_0028: leave.s IL_0039
} // end .try
finally
{
IL_002a: ldloca.s V_1
IL_002c: constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem>
IL_0032: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0037: nop
IL_0038: endfinally
} // end handler
IL_0039: nop
IL_003a: ret
} // end of method Loops::ForEachOverListOfStruct3
.method public hidebysig instance void .method public hidebysig instance void
ForOverArray(string[] 'array') cil managed ForOverArray(string[] 'array') cil managed
{ {
@ -1848,7 +2051,7 @@
.method public hidebysig instance void .method public hidebysig instance void
WhileLoop() cil managed WhileLoop() cil managed
{ {
// Code size 154 (0x9a) // Code size 155 (0x9b)
.maxstack 2 .maxstack 2
.locals init (bool V_0) .locals init (bool V_0)
IL_0000: nop IL_0000: nop
@ -1862,10 +2065,10 @@
IL_0018: ceq IL_0018: ceq
IL_001a: stloc.0 IL_001a: stloc.0
IL_001b: ldloc.0 IL_001b: ldloc.0
IL_001c: brtrue.s IL_008e IL_001c: brtrue.s IL_008f
IL_001e: nop IL_001e: nop
IL_001f: br.s IL_0073 IL_001f: br.s IL_0074
IL_0021: nop IL_0021: nop
IL_0022: ldstr "Loop Body" IL_0022: ldstr "Loop Body"
@ -1878,7 +2081,7 @@
IL_0039: ceq IL_0039: ceq
IL_003b: stloc.0 IL_003b: stloc.0
IL_003c: ldloc.0 IL_003c: ldloc.0
IL_003d: brtrue.s IL_0067 IL_003d: brtrue.s IL_0068
IL_003f: nop IL_003f: nop
IL_0040: ldarg.0 IL_0040: ldarg.0
@ -1891,43 +2094,44 @@
IL_0050: brtrue.s IL_0055 IL_0050: brtrue.s IL_0055
IL_0052: nop IL_0052: nop
IL_0053: br.s IL_0073 IL_0053: br.s IL_0074
IL_0055: ldarg.0 IL_0055: ldarg.0
IL_0056: ldstr "break" IL_0056: ldstr "break"
IL_005b: call instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::Condition(string) IL_005b: call instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::Condition(string)
IL_0060: stloc.0 IL_0060: stloc.0
IL_0061: ldloc.0 IL_0061: ldloc.0
IL_0062: brtrue.s IL_0066 IL_0062: brtrue.s IL_0067
IL_0064: br.s IL_0082 IL_0064: nop
IL_0065: br.s IL_0083
IL_0066: nop IL_0067: nop
IL_0067: ldstr "End of loop body" IL_0068: ldstr "End of loop body"
IL_006c: call void [mscorlib]System.Console::WriteLine(string) IL_006d: call void [mscorlib]System.Console::WriteLine(string)
IL_0071: nop
IL_0072: nop IL_0072: nop
IL_0073: ldarg.0 IL_0073: nop
IL_0074: ldstr "while" IL_0074: ldarg.0
IL_0079: call instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::Condition(string) IL_0075: ldstr "while"
IL_007e: stloc.0 IL_007a: call instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::Condition(string)
IL_007f: ldloc.0 IL_007f: stloc.0
IL_0080: brtrue.s IL_0021 IL_0080: ldloc.0
IL_0081: brtrue.s IL_0021
IL_0082: ldstr "After loop"
IL_0087: call void [mscorlib]System.Console::WriteLine(string) IL_0083: ldstr "After loop"
IL_008c: nop IL_0088: call void [mscorlib]System.Console::WriteLine(string)
IL_008d: nop IL_008d: nop
IL_008e: ldstr "End of method" IL_008e: nop
IL_0093: call void [mscorlib]System.Console::WriteLine(string) IL_008f: ldstr "End of method"
IL_0098: nop IL_0094: call void [mscorlib]System.Console::WriteLine(string)
IL_0099: ret IL_0099: nop
IL_009a: ret
} // end of method Loops::WhileLoop } // end of method Loops::WhileLoop
.method public hidebysig instance void .method public hidebysig instance void
ForLoop() cil managed ForLoop() cil managed
{ {
// Code size 160 (0xa0) // Code size 161 (0xa1)
.maxstack 2 .maxstack 2
.locals init (int32 V_0, .locals init (int32 V_0,
bool V_1) bool V_1)
@ -1942,12 +2146,12 @@
IL_0018: ceq IL_0018: ceq
IL_001a: stloc.1 IL_001a: stloc.1
IL_001b: ldloc.1 IL_001b: ldloc.1
IL_001c: brtrue.s IL_0094 IL_001c: brtrue.s IL_0095
IL_001e: nop IL_001e: nop
IL_001f: ldc.i4.0 IL_001f: ldc.i4.0
IL_0020: stloc.0 IL_0020: stloc.0
IL_0021: br.s IL_0079 IL_0021: br.s IL_007a
IL_0023: nop IL_0023: nop
IL_0024: ldstr "Loop Body" IL_0024: ldstr "Loop Body"
@ -1960,7 +2164,7 @@
IL_003b: ceq IL_003b: ceq
IL_003d: stloc.1 IL_003d: stloc.1
IL_003e: ldloc.1 IL_003e: ldloc.1
IL_003f: brtrue.s IL_0069 IL_003f: brtrue.s IL_006a
IL_0041: nop IL_0041: nop
IL_0042: ldarg.0 IL_0042: ldarg.0
@ -1973,41 +2177,42 @@
IL_0052: brtrue.s IL_0057 IL_0052: brtrue.s IL_0057
IL_0054: nop IL_0054: nop
IL_0055: br.s IL_0075 IL_0055: br.s IL_0076
IL_0057: ldarg.0 IL_0057: ldarg.0
IL_0058: ldstr "not-break" IL_0058: ldstr "not-break"
IL_005d: call instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::Condition(string) IL_005d: call instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::Condition(string)
IL_0062: stloc.1 IL_0062: stloc.1
IL_0063: ldloc.1 IL_0063: ldloc.1
IL_0064: brtrue.s IL_0068 IL_0064: brtrue.s IL_0069
IL_0066: br.s IL_0088 IL_0066: nop
IL_0067: br.s IL_0089
IL_0068: nop IL_0069: nop
IL_0069: ldstr "End of loop body" IL_006a: ldstr "End of loop body"
IL_006e: call void [mscorlib]System.Console::WriteLine(string) IL_006f: call void [mscorlib]System.Console::WriteLine(string)
IL_0073: nop
IL_0074: nop IL_0074: nop
IL_0075: ldloc.0 IL_0075: nop
IL_0076: ldc.i4.1 IL_0076: ldloc.0
IL_0077: add IL_0077: ldc.i4.1
IL_0078: stloc.0 IL_0078: add
IL_0079: ldarg.0 IL_0079: stloc.0
IL_007a: ldstr "for" IL_007a: ldarg.0
IL_007f: call instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::Condition(string) IL_007b: ldstr "for"
IL_0084: stloc.1 IL_0080: call instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::Condition(string)
IL_0085: ldloc.1 IL_0085: stloc.1
IL_0086: brtrue.s IL_0023 IL_0086: ldloc.1
IL_0087: brtrue.s IL_0023
IL_0088: ldstr "After loop"
IL_008d: call void [mscorlib]System.Console::WriteLine(string) IL_0089: ldstr "After loop"
IL_0092: nop IL_008e: call void [mscorlib]System.Console::WriteLine(string)
IL_0093: nop IL_0093: nop
IL_0094: ldstr "End of method" IL_0094: nop
IL_0099: call void [mscorlib]System.Console::WriteLine(string) IL_0095: ldstr "End of method"
IL_009e: nop IL_009a: call void [mscorlib]System.Console::WriteLine(string)
IL_009f: ret IL_009f: nop
IL_00a0: ret
} // end of method Loops::ForLoop } // end of method Loops::ForLoop
.method public hidebysig specialname rtspecialname .method public hidebysig specialname rtspecialname

172
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.opt.il

@ -10,7 +10,7 @@
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.ver 4:0:0:0 .ver 4:0:0:0
} }
.assembly spdzj2hg .assembly trg3dx5v
{ {
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 )
.custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx
@ -20,15 +20,15 @@
.hash algorithm 0x00008004 .hash algorithm 0x00008004
.ver 0:0:0:0 .ver 0:0:0:0
} }
.module spdzj2hg.dll .module trg3dx5v.dll
// MVID: {D8CD3A33-FD06-4791-A4E6-AC836DA34D06} // MVID: {39502A20-4431-466D-88D2-F47B21FB8C58}
.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 )
.imagebase 0x10000000 .imagebase 0x10000000
.file alignment 0x00000200 .file alignment 0x00000200
.stackreserve 0x00100000 .stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI .subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY .corflags 0x00000001 // ILONLY
// Image base: 0x002F0000 // Image base: 0x00B30000
// =============== CLASS MEMBERS DECLARATION =================== // =============== CLASS MEMBERS DECLARATION ===================
@ -506,6 +506,49 @@
} // end of property CustomStructEnumeratorWithIDisposable`1::Current } // end of property CustomStructEnumeratorWithIDisposable`1::Current
} // end of class CustomStructEnumeratorWithIDisposable`1 } // end of class CustomStructEnumeratorWithIDisposable`1
.class sequential ansi sealed nested public beforefieldinit DataItem
extends [mscorlib]System.ValueType
{
.field private int32 '<Property>k__BackingField'
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
.method public hidebysig specialname
instance int32 get_Property() cil managed
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::'<Property>k__BackingField'
IL_0006: ret
} // end of method DataItem::get_Property
.method public hidebysig specialname
instance void set_Property(int32 'value') cil managed
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
// Code size 8 (0x8)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldarg.1
IL_0002: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::'<Property>k__BackingField'
IL_0007: ret
} // end of method DataItem::set_Property
.method public hidebysig instance void
TestCall() cil managed
{
// Code size 1 (0x1)
.maxstack 8
IL_0000: ret
} // end of method DataItem::TestCall
.property instance int32 Property()
{
.get instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::get_Property()
.set instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::set_Property(int32)
} // end of property DataItem::Property
} // end of class DataItem
.class auto ansi sealed nested private beforefieldinit '<>c__DisplayClass1' .class auto ansi sealed nested private beforefieldinit '<>c__DisplayClass1'
extends [mscorlib]System.Object extends [mscorlib]System.Object
{ {
@ -1336,6 +1379,127 @@
IL_0056: ret IL_0056: ret
} // end of method Loops::ForEachBreakWhenFound } // end of method Loops::ForEachBreakWhenFound
.method public hidebysig instance void
ForEachOverListOfStruct(class [mscorlib]System.Collections.Generic.List`1<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem> items,
int32 'value') cil managed
{
// Code size 53 (0x35)
.maxstack 2
.locals init (valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem V_0,
valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem V_1,
valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem> V_2)
IL_0000: ldarg.1
IL_0001: callvirt instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<!0> class [mscorlib]System.Collections.Generic.List`1<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem>::GetEnumerator()
IL_0006: stloc.2
.try
{
IL_0007: br.s IL_001b
IL_0009: ldloca.s V_2
IL_000b: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem>::get_Current()
IL_0010: stloc.0
IL_0011: ldloc.0
IL_0012: stloc.1
IL_0013: ldloca.s V_1
IL_0015: ldarg.2
IL_0016: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::set_Property(int32)
IL_001b: ldloca.s V_2
IL_001d: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem>::MoveNext()
IL_0022: brtrue.s IL_0009
IL_0024: leave.s IL_0034
} // end .try
finally
{
IL_0026: ldloca.s V_2
IL_0028: constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem>
IL_002e: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0033: endfinally
} // end handler
IL_0034: ret
} // end of method Loops::ForEachOverListOfStruct
.method public hidebysig instance void
ForEachOverListOfStruct2(class [mscorlib]System.Collections.Generic.List`1<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem> items,
int32 'value') cil managed
{
// Code size 60 (0x3c)
.maxstack 2
.locals init (valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem V_0,
valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem V_1,
valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem> V_2)
IL_0000: ldarg.1
IL_0001: callvirt instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<!0> class [mscorlib]System.Collections.Generic.List`1<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem>::GetEnumerator()
IL_0006: stloc.2
.try
{
IL_0007: br.s IL_0022
IL_0009: ldloca.s V_2
IL_000b: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem>::get_Current()
IL_0010: stloc.0
IL_0011: ldloc.0
IL_0012: stloc.1
IL_0013: ldloca.s V_1
IL_0015: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::TestCall()
IL_001a: ldloca.s V_1
IL_001c: ldarg.2
IL_001d: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::set_Property(int32)
IL_0022: ldloca.s V_2
IL_0024: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem>::MoveNext()
IL_0029: brtrue.s IL_0009
IL_002b: leave.s IL_003b
} // end .try
finally
{
IL_002d: ldloca.s V_2
IL_002f: constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem>
IL_0035: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_003a: endfinally
} // end handler
IL_003b: ret
} // end of method Loops::ForEachOverListOfStruct2
.method public hidebysig instance void
ForEachOverListOfStruct3(class [mscorlib]System.Collections.Generic.List`1<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem> items,
int32 'value') cil managed
{
// Code size 50 (0x32)
.maxstack 1
.locals init (valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem V_0,
valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem> V_1)
IL_0000: ldarg.1
IL_0001: callvirt instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<!0> class [mscorlib]System.Collections.Generic.List`1<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem>::GetEnumerator()
IL_0006: stloc.1
.try
{
IL_0007: br.s IL_0018
IL_0009: ldloca.s V_1
IL_000b: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem>::get_Current()
IL_0010: stloc.0
IL_0011: ldloca.s V_0
IL_0013: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::TestCall()
IL_0018: ldloca.s V_1
IL_001a: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem>::MoveNext()
IL_001f: brtrue.s IL_0009
IL_0021: leave.s IL_0031
} // end .try
finally
{
IL_0023: ldloca.s V_1
IL_0025: constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem>
IL_002b: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0030: endfinally
} // end handler
IL_0031: ret
} // end of method Loops::ForEachOverListOfStruct3
.method public hidebysig instance void .method public hidebysig instance void
ForOverArray(string[] 'array') cil managed ForOverArray(string[] 'array') cil managed
{ {

178
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.opt.roslyn.il

@ -25,14 +25,14 @@
.ver 0:0:0:0 .ver 0:0:0:0
} }
.module Loops.dll .module Loops.dll
// MVID: {B3F85A6A-79B4-41C0-9146-2088985BFC34} // MVID: {F193AB7E-127F-4EFE-9BF9-ADF41756C267}
.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 )
.imagebase 0x10000000 .imagebase 0x10000000
.file alignment 0x00000200 .file alignment 0x00000200
.stackreserve 0x00100000 .stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI .subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY .corflags 0x00000001 // ILONLY
// Image base: 0x02550000 // Image base: 0x03460000
// =============== CLASS MEMBERS DECLARATION =================== // =============== CLASS MEMBERS DECLARATION ===================
@ -510,7 +510,50 @@
} // end of property CustomStructEnumeratorWithIDisposable`1::Current } // end of property CustomStructEnumeratorWithIDisposable`1::Current
} // end of class CustomStructEnumeratorWithIDisposable`1 } // end of class CustomStructEnumeratorWithIDisposable`1
.class auto ansi sealed nested private beforefieldinit '<>c__DisplayClass25_0' .class sequential ansi sealed nested public beforefieldinit DataItem
extends [mscorlib]System.ValueType
{
.field private int32 '<Property>k__BackingField'
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
.method public hidebysig specialname
instance int32 get_Property() cil managed
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::'<Property>k__BackingField'
IL_0006: ret
} // end of method DataItem::get_Property
.method public hidebysig specialname
instance void set_Property(int32 'value') cil managed
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
// Code size 8 (0x8)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldarg.1
IL_0002: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::'<Property>k__BackingField'
IL_0007: ret
} // end of method DataItem::set_Property
.method public hidebysig instance void
TestCall() cil managed
{
// Code size 1 (0x1)
.maxstack 8
IL_0000: ret
} // end of method DataItem::TestCall
.property instance int32 Property()
{
.get instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::get_Property()
.set instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::set_Property(int32)
} // end of property DataItem::Property
} // end of class DataItem
.class auto ansi sealed nested private beforefieldinit '<>c__DisplayClass26_0'
extends [mscorlib]System.Object extends [mscorlib]System.Object
{ {
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
@ -523,7 +566,7 @@
IL_0000: ldarg.0 IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret IL_0006: ret
} // end of method '<>c__DisplayClass25_0'::.ctor } // end of method '<>c__DisplayClass26_0'::.ctor
.method assembly hidebysig instance bool .method assembly hidebysig instance bool
'<ForeachWithCapturedVariable>b__0'() cil managed '<ForeachWithCapturedVariable>b__0'() cil managed
@ -531,13 +574,13 @@
// Code size 10 (0xa) // Code size 10 (0xa)
.maxstack 8 .maxstack 8
IL_0000: ldarg.0 IL_0000: ldarg.0
IL_0001: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass25_0'::c IL_0001: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass26_0'::c
IL_0006: ldc.i4.5 IL_0006: ldc.i4.5
IL_0007: ceq IL_0007: ceq
IL_0009: ret IL_0009: ret
} // end of method '<>c__DisplayClass25_0'::'<ForeachWithCapturedVariable>b__0' } // end of method '<>c__DisplayClass26_0'::'<ForeachWithCapturedVariable>b__0'
} // end of class '<>c__DisplayClass25_0' } // end of class '<>c__DisplayClass26_0'
.field private class [mscorlib]System.Collections.Generic.IEnumerable`1<string> alternatives .field private class [mscorlib]System.Collections.Generic.IEnumerable`1<string> alternatives
.method private hidebysig static void Operation(int32& item) cil managed .method private hidebysig static void Operation(int32& item) cil managed
@ -1101,11 +1144,11 @@
IL_0009: ldloca.s V_0 IL_0009: ldloca.s V_0
IL_000b: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<int32>::get_Current() IL_000b: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<int32>::get_Current()
IL_0010: stloc.1 IL_0010: stloc.1
IL_0011: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass25_0'::.ctor() IL_0011: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass26_0'::.ctor()
IL_0016: dup IL_0016: dup
IL_0017: ldloc.1 IL_0017: ldloc.1
IL_0018: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass25_0'::c IL_0018: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass26_0'::c
IL_001d: ldftn instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass25_0'::'<ForeachWithCapturedVariable>b__0'() IL_001d: ldftn instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass26_0'::'<ForeachWithCapturedVariable>b__0'()
IL_0023: newobj instance void class [mscorlib]System.Func`1<bool>::.ctor(object, IL_0023: newobj instance void class [mscorlib]System.Func`1<bool>::.ctor(object,
native int) native int)
IL_0028: call void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::Operation(class [mscorlib]System.Func`1<bool>) IL_0028: call void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::Operation(class [mscorlib]System.Func`1<bool>)
@ -1295,6 +1338,121 @@
IL_0058: ret IL_0058: ret
} // end of method Loops::ForEachBreakWhenFound } // end of method Loops::ForEachBreakWhenFound
.method public hidebysig instance void
ForEachOverListOfStruct(class [mscorlib]System.Collections.Generic.List`1<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem> items,
int32 'value') cil managed
{
// Code size 51 (0x33)
.maxstack 2
.locals init (valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem> V_0,
valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem V_1)
IL_0000: ldarg.1
IL_0001: callvirt instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<!0> class [mscorlib]System.Collections.Generic.List`1<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem>::GetEnumerator()
IL_0006: stloc.0
.try
{
IL_0007: br.s IL_0019
IL_0009: ldloca.s V_0
IL_000b: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem>::get_Current()
IL_0010: stloc.1
IL_0011: ldloca.s V_1
IL_0013: ldarg.2
IL_0014: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::set_Property(int32)
IL_0019: ldloca.s V_0
IL_001b: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem>::MoveNext()
IL_0020: brtrue.s IL_0009
IL_0022: leave.s IL_0032
} // end .try
finally
{
IL_0024: ldloca.s V_0
IL_0026: constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem>
IL_002c: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0031: endfinally
} // end handler
IL_0032: ret
} // end of method Loops::ForEachOverListOfStruct
.method public hidebysig instance void
ForEachOverListOfStruct2(class [mscorlib]System.Collections.Generic.List`1<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem> items,
int32 'value') cil managed
{
// Code size 58 (0x3a)
.maxstack 2
.locals init (valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem> V_0,
valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem V_1)
IL_0000: ldarg.1
IL_0001: callvirt instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<!0> class [mscorlib]System.Collections.Generic.List`1<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem>::GetEnumerator()
IL_0006: stloc.0
.try
{
IL_0007: br.s IL_0020
IL_0009: ldloca.s V_0
IL_000b: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem>::get_Current()
IL_0010: stloc.1
IL_0011: ldloca.s V_1
IL_0013: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::TestCall()
IL_0018: ldloca.s V_1
IL_001a: ldarg.2
IL_001b: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::set_Property(int32)
IL_0020: ldloca.s V_0
IL_0022: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem>::MoveNext()
IL_0027: brtrue.s IL_0009
IL_0029: leave.s IL_0039
} // end .try
finally
{
IL_002b: ldloca.s V_0
IL_002d: constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem>
IL_0033: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0038: endfinally
} // end handler
IL_0039: ret
} // end of method Loops::ForEachOverListOfStruct2
.method public hidebysig instance void
ForEachOverListOfStruct3(class [mscorlib]System.Collections.Generic.List`1<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem> items,
int32 'value') cil managed
{
// Code size 50 (0x32)
.maxstack 1
.locals init (valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem> V_0,
valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem V_1)
IL_0000: ldarg.1
IL_0001: callvirt instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<!0> class [mscorlib]System.Collections.Generic.List`1<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem>::GetEnumerator()
IL_0006: stloc.0
.try
{
IL_0007: br.s IL_0018
IL_0009: ldloca.s V_0
IL_000b: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem>::get_Current()
IL_0010: stloc.1
IL_0011: ldloca.s V_1
IL_0013: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::TestCall()
IL_0018: ldloca.s V_0
IL_001a: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem>::MoveNext()
IL_001f: brtrue.s IL_0009
IL_0021: leave.s IL_0031
} // end .try
finally
{
IL_0023: ldloca.s V_0
IL_0025: constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem>
IL_002b: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0030: endfinally
} // end handler
IL_0031: ret
} // end of method Loops::ForEachOverListOfStruct3
.method public hidebysig instance void .method public hidebysig instance void
ForOverArray(string[] 'array') cil managed ForOverArray(string[] 'array') cil managed
{ {

341
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.roslyn.il

@ -25,14 +25,14 @@
.ver 0:0:0:0 .ver 0:0:0:0
} }
.module Loops.dll .module Loops.dll
// MVID: {8261F08F-23F6-43C1-9650-C5B6A10A0F30} // MVID: {8F6E41CD-7AE5-437C-BACB-4A672A0014D0}
.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 )
.imagebase 0x10000000 .imagebase 0x10000000
.file alignment 0x00000200 .file alignment 0x00000200
.stackreserve 0x00100000 .stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI .subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY .corflags 0x00000001 // ILONLY
// Image base: 0x00720000 // Image base: 0x00C80000
// =============== CLASS MEMBERS DECLARATION =================== // =============== CLASS MEMBERS DECLARATION ===================
@ -592,7 +592,52 @@
} // end of property CustomStructEnumeratorWithIDisposable`1::Current } // end of property CustomStructEnumeratorWithIDisposable`1::Current
} // end of class CustomStructEnumeratorWithIDisposable`1 } // end of class CustomStructEnumeratorWithIDisposable`1
.class auto ansi sealed nested private beforefieldinit '<>c__DisplayClass25_0' .class sequential ansi sealed nested public beforefieldinit DataItem
extends [mscorlib]System.ValueType
{
.field private int32 '<Property>k__BackingField'
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
.custom instance void [mscorlib]System.Diagnostics.DebuggerBrowsableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggerBrowsableState) = ( 01 00 00 00 00 00 00 00 )
.method public hidebysig specialname
instance int32 get_Property() cil managed
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::'<Property>k__BackingField'
IL_0006: ret
} // end of method DataItem::get_Property
.method public hidebysig specialname
instance void set_Property(int32 'value') cil managed
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
// Code size 8 (0x8)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldarg.1
IL_0002: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::'<Property>k__BackingField'
IL_0007: ret
} // end of method DataItem::set_Property
.method public hidebysig instance void
TestCall() cil managed
{
// Code size 2 (0x2)
.maxstack 8
IL_0000: nop
IL_0001: ret
} // end of method DataItem::TestCall
.property instance int32 Property()
{
.get instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::get_Property()
.set instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::set_Property(int32)
} // end of property DataItem::Property
} // end of class DataItem
.class auto ansi sealed nested private beforefieldinit '<>c__DisplayClass26_0'
extends [mscorlib]System.Object extends [mscorlib]System.Object
{ {
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
@ -606,7 +651,7 @@
IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: nop IL_0006: nop
IL_0007: ret IL_0007: ret
} // end of method '<>c__DisplayClass25_0'::.ctor } // end of method '<>c__DisplayClass26_0'::.ctor
.method assembly hidebysig instance bool .method assembly hidebysig instance bool
'<ForeachWithCapturedVariable>b__0'() cil managed '<ForeachWithCapturedVariable>b__0'() cil managed
@ -614,13 +659,13 @@
// Code size 10 (0xa) // Code size 10 (0xa)
.maxstack 8 .maxstack 8
IL_0000: ldarg.0 IL_0000: ldarg.0
IL_0001: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass25_0'::c IL_0001: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass26_0'::c
IL_0006: ldc.i4.5 IL_0006: ldc.i4.5
IL_0007: ceq IL_0007: ceq
IL_0009: ret IL_0009: ret
} // end of method '<>c__DisplayClass25_0'::'<ForeachWithCapturedVariable>b__0' } // end of method '<>c__DisplayClass26_0'::'<ForeachWithCapturedVariable>b__0'
} // end of class '<>c__DisplayClass25_0' } // end of class '<>c__DisplayClass26_0'
.field private class [mscorlib]System.Collections.Generic.IEnumerable`1<string> alternatives .field private class [mscorlib]System.Collections.Generic.IEnumerable`1<string> alternatives
.method private hidebysig static void Operation(int32& item) cil managed .method private hidebysig static void Operation(int32& item) cil managed
@ -1189,7 +1234,7 @@
.method public hidebysig static void NonGenericForeachWithReturnFallbackTest(class [mscorlib]System.Collections.IEnumerable e) cil managed .method public hidebysig static void NonGenericForeachWithReturnFallbackTest(class [mscorlib]System.Collections.IEnumerable e) cil managed
{ {
// Code size 109 (0x6d) // Code size 111 (0x6f)
.maxstack 2 .maxstack 2
.locals init (class [mscorlib]System.Collections.IEnumerator V_0, .locals init (class [mscorlib]System.Collections.IEnumerator V_0,
bool V_1, bool V_1,
@ -1227,7 +1272,7 @@
IL_0041: nop IL_0041: nop
IL_0042: nop IL_0042: nop
IL_0043: nop IL_0043: nop
IL_0044: leave.s IL_0061 IL_0044: leave.s IL_0063
} // end .try } // end .try
finally finally
@ -1241,18 +1286,20 @@
IL_0050: cgt.un IL_0050: cgt.un
IL_0052: stloc.s V_4 IL_0052: stloc.s V_4
IL_0054: ldloc.s V_4 IL_0054: ldloc.s V_4
IL_0056: brfalse.s IL_005f IL_0056: brfalse.s IL_0061
IL_0058: ldloc.3 IL_0058: nop
IL_0059: callvirt instance void [mscorlib]System.IDisposable::Dispose() IL_0059: ldloc.3
IL_005e: nop IL_005a: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_005f: nop IL_005f: nop
IL_0060: endfinally IL_0060: nop
IL_0061: nop
IL_0062: endfinally
} // end handler } // end handler
IL_0061: ldstr "After finally!" IL_0063: ldstr "After finally!"
IL_0066: call void [mscorlib]System.Console::WriteLine(string) IL_0068: call void [mscorlib]System.Console::WriteLine(string)
IL_006b: nop IL_006d: nop
IL_006c: ret IL_006e: ret
} // end of method Loops::NonGenericForeachWithReturnFallbackTest } // end of method Loops::NonGenericForeachWithReturnFallbackTest
.method public hidebysig static void ForeachWithRefUsage(class [mscorlib]System.Collections.Generic.List`1<int32> items) cil managed .method public hidebysig static void ForeachWithRefUsage(class [mscorlib]System.Collections.Generic.List`1<int32> items) cil managed
@ -1305,7 +1352,7 @@
.maxstack 2 .maxstack 2
.locals init (valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<int32> V_0, .locals init (valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<int32> V_0,
int32 V_1, int32 V_1,
class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass25_0' V_2) class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass26_0' V_2)
IL_0000: nop IL_0000: nop
IL_0001: nop IL_0001: nop
IL_0002: ldarg.0 IL_0002: ldarg.0
@ -1318,14 +1365,14 @@
IL_000b: ldloca.s V_0 IL_000b: ldloca.s V_0
IL_000d: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<int32>::get_Current() IL_000d: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<int32>::get_Current()
IL_0012: stloc.1 IL_0012: stloc.1
IL_0013: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass25_0'::.ctor() IL_0013: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass26_0'::.ctor()
IL_0018: stloc.2 IL_0018: stloc.2
IL_0019: nop IL_0019: nop
IL_001a: ldloc.2 IL_001a: ldloc.2
IL_001b: ldloc.1 IL_001b: ldloc.1
IL_001c: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass25_0'::c IL_001c: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass26_0'::c
IL_0021: ldloc.2 IL_0021: ldloc.2
IL_0022: ldftn instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass25_0'::'<ForeachWithCapturedVariable>b__0'() IL_0022: ldftn instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass26_0'::'<ForeachWithCapturedVariable>b__0'()
IL_0028: newobj instance void class [mscorlib]System.Func`1<bool>::.ctor(object, IL_0028: newobj instance void class [mscorlib]System.Func`1<bool>::.ctor(object,
native int) native int)
IL_002d: call void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::Operation(class [mscorlib]System.Func`1<bool>) IL_002d: call void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::Operation(class [mscorlib]System.Func`1<bool>)
@ -1554,6 +1601,146 @@
IL_0060: ret IL_0060: ret
} // end of method Loops::ForEachBreakWhenFound } // end of method Loops::ForEachBreakWhenFound
.method public hidebysig instance void
ForEachOverListOfStruct(class [mscorlib]System.Collections.Generic.List`1<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem> items,
int32 'value') cil managed
{
// Code size 59 (0x3b)
.maxstack 2
.locals init (valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem> V_0,
valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem V_1,
valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem V_2)
IL_0000: nop
IL_0001: nop
IL_0002: ldarg.1
IL_0003: callvirt instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<!0> class [mscorlib]System.Collections.Generic.List`1<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem>::GetEnumerator()
IL_0008: stloc.0
.try
{
IL_0009: br.s IL_0020
IL_000b: ldloca.s V_0
IL_000d: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem>::get_Current()
IL_0012: stloc.1
IL_0013: nop
IL_0014: ldloc.1
IL_0015: stloc.2
IL_0016: ldloca.s V_2
IL_0018: ldarg.2
IL_0019: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::set_Property(int32)
IL_001e: nop
IL_001f: nop
IL_0020: ldloca.s V_0
IL_0022: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem>::MoveNext()
IL_0027: brtrue.s IL_000b
IL_0029: leave.s IL_003a
} // end .try
finally
{
IL_002b: ldloca.s V_0
IL_002d: constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem>
IL_0033: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0038: nop
IL_0039: endfinally
} // end handler
IL_003a: ret
} // end of method Loops::ForEachOverListOfStruct
.method public hidebysig instance void
ForEachOverListOfStruct2(class [mscorlib]System.Collections.Generic.List`1<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem> items,
int32 'value') cil managed
{
// Code size 67 (0x43)
.maxstack 2
.locals init (valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem> V_0,
valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem V_1,
valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem V_2)
IL_0000: nop
IL_0001: nop
IL_0002: ldarg.1
IL_0003: callvirt instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<!0> class [mscorlib]System.Collections.Generic.List`1<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem>::GetEnumerator()
IL_0008: stloc.0
.try
{
IL_0009: br.s IL_0028
IL_000b: ldloca.s V_0
IL_000d: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem>::get_Current()
IL_0012: stloc.1
IL_0013: nop
IL_0014: ldloc.1
IL_0015: stloc.2
IL_0016: ldloca.s V_2
IL_0018: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::TestCall()
IL_001d: nop
IL_001e: ldloca.s V_2
IL_0020: ldarg.2
IL_0021: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::set_Property(int32)
IL_0026: nop
IL_0027: nop
IL_0028: ldloca.s V_0
IL_002a: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem>::MoveNext()
IL_002f: brtrue.s IL_000b
IL_0031: leave.s IL_0042
} // end .try
finally
{
IL_0033: ldloca.s V_0
IL_0035: constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem>
IL_003b: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0040: nop
IL_0041: endfinally
} // end handler
IL_0042: ret
} // end of method Loops::ForEachOverListOfStruct2
.method public hidebysig instance void
ForEachOverListOfStruct3(class [mscorlib]System.Collections.Generic.List`1<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem> items,
int32 'value') cil managed
{
// Code size 56 (0x38)
.maxstack 1
.locals init (valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem> V_0,
valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem V_1)
IL_0000: nop
IL_0001: nop
IL_0002: ldarg.1
IL_0003: callvirt instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<!0> class [mscorlib]System.Collections.Generic.List`1<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem>::GetEnumerator()
IL_0008: stloc.0
.try
{
IL_0009: br.s IL_001d
IL_000b: ldloca.s V_0
IL_000d: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem>::get_Current()
IL_0012: stloc.1
IL_0013: nop
IL_0014: ldloca.s V_1
IL_0016: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::TestCall()
IL_001b: nop
IL_001c: nop
IL_001d: ldloca.s V_0
IL_001f: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem>::MoveNext()
IL_0024: brtrue.s IL_000b
IL_0026: leave.s IL_0037
} // end .try
finally
{
IL_0028: ldloca.s V_0
IL_002a: constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem>
IL_0030: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0035: nop
IL_0036: endfinally
} // end handler
IL_0037: ret
} // end of method Loops::ForEachOverListOfStruct3
.method public hidebysig instance void .method public hidebysig instance void
ForOverArray(string[] 'array') cil managed ForOverArray(string[] 'array') cil managed
{ {
@ -1732,7 +1919,7 @@
.method public hidebysig instance void .method public hidebysig instance void
WhileLoop() cil managed WhileLoop() cil managed
{ {
// Code size 150 (0x96) // Code size 151 (0x97)
.maxstack 2 .maxstack 2
.locals init (bool V_0, .locals init (bool V_0,
bool V_1, bool V_1,
@ -1748,10 +1935,10 @@
IL_0012: call instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::Condition(string) IL_0012: call instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::Condition(string)
IL_0017: stloc.0 IL_0017: stloc.0
IL_0018: ldloc.0 IL_0018: ldloc.0
IL_0019: brfalse.s IL_008a IL_0019: brfalse.s IL_008b
IL_001b: nop IL_001b: nop
IL_001c: br.s IL_006d IL_001c: br.s IL_006e
IL_001e: nop IL_001e: nop
IL_001f: ldstr "Loop Body" IL_001f: ldstr "Loop Body"
@ -1762,7 +1949,7 @@
IL_0030: call instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::Condition(string) IL_0030: call instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::Condition(string)
IL_0035: stloc.1 IL_0035: stloc.1
IL_0036: ldloc.1 IL_0036: ldloc.1
IL_0037: brfalse.s IL_0061 IL_0037: brfalse.s IL_0062
IL_0039: nop IL_0039: nop
IL_003a: ldarg.0 IL_003a: ldarg.0
@ -1773,7 +1960,7 @@
IL_0047: brfalse.s IL_004c IL_0047: brfalse.s IL_004c
IL_0049: nop IL_0049: nop
IL_004a: br.s IL_006d IL_004a: br.s IL_006e
IL_004c: ldarg.0 IL_004c: ldarg.0
IL_004d: ldstr "break" IL_004d: ldstr "break"
@ -1782,36 +1969,37 @@
IL_0058: ceq IL_0058: ceq
IL_005a: stloc.3 IL_005a: stloc.3
IL_005b: ldloc.3 IL_005b: ldloc.3
IL_005c: brfalse.s IL_0060 IL_005c: brfalse.s IL_0061
IL_005e: br.s IL_007e IL_005e: nop
IL_005f: br.s IL_007f
IL_0060: nop IL_0061: nop
IL_0061: ldstr "End of loop body" IL_0062: ldstr "End of loop body"
IL_0066: call void [mscorlib]System.Console::WriteLine(string) IL_0067: call void [mscorlib]System.Console::WriteLine(string)
IL_006b: nop
IL_006c: nop IL_006c: nop
IL_006d: ldarg.0 IL_006d: nop
IL_006e: ldstr "while" IL_006e: ldarg.0
IL_0073: call instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::Condition(string) IL_006f: ldstr "while"
IL_0078: stloc.s V_4 IL_0074: call instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::Condition(string)
IL_007a: ldloc.s V_4 IL_0079: stloc.s V_4
IL_007c: brtrue.s IL_001e IL_007b: ldloc.s V_4
IL_007d: brtrue.s IL_001e
IL_007e: ldstr "After loop"
IL_0083: call void [mscorlib]System.Console::WriteLine(string) IL_007f: ldstr "After loop"
IL_0088: nop IL_0084: call void [mscorlib]System.Console::WriteLine(string)
IL_0089: nop IL_0089: nop
IL_008a: ldstr "End of method" IL_008a: nop
IL_008f: call void [mscorlib]System.Console::WriteLine(string) IL_008b: ldstr "End of method"
IL_0094: nop IL_0090: call void [mscorlib]System.Console::WriteLine(string)
IL_0095: ret IL_0095: nop
IL_0096: ret
} // end of method Loops::WhileLoop } // end of method Loops::WhileLoop
.method public hidebysig instance void .method public hidebysig instance void
ForLoop() cil managed ForLoop() cil managed
{ {
// Code size 158 (0x9e) // Code size 159 (0x9f)
.maxstack 2 .maxstack 2
.locals init (bool V_0, .locals init (bool V_0,
int32 V_1, int32 V_1,
@ -1828,12 +2016,12 @@
IL_0012: call instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::Condition(string) IL_0012: call instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::Condition(string)
IL_0017: stloc.0 IL_0017: stloc.0
IL_0018: ldloc.0 IL_0018: ldloc.0
IL_0019: brfalse.s IL_0092 IL_0019: brfalse.s IL_0093
IL_001b: nop IL_001b: nop
IL_001c: ldc.i4.0 IL_001c: ldc.i4.0
IL_001d: stloc.1 IL_001d: stloc.1
IL_001e: br.s IL_0075 IL_001e: br.s IL_0076
IL_0020: nop IL_0020: nop
IL_0021: ldstr "Loop Body" IL_0021: ldstr "Loop Body"
@ -1844,7 +2032,7 @@
IL_0032: call instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::Condition(string) IL_0032: call instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::Condition(string)
IL_0037: stloc.2 IL_0037: stloc.2
IL_0038: ldloc.2 IL_0038: ldloc.2
IL_0039: brfalse.s IL_0065 IL_0039: brfalse.s IL_0066
IL_003b: nop IL_003b: nop
IL_003c: ldarg.0 IL_003c: ldarg.0
@ -1855,7 +2043,7 @@
IL_0049: brfalse.s IL_004e IL_0049: brfalse.s IL_004e
IL_004b: nop IL_004b: nop
IL_004c: br.s IL_0071 IL_004c: br.s IL_0072
IL_004e: ldarg.0 IL_004e: ldarg.0
IL_004f: ldstr "not-break" IL_004f: ldstr "not-break"
@ -1864,34 +2052,35 @@
IL_005a: ceq IL_005a: ceq
IL_005c: stloc.s V_4 IL_005c: stloc.s V_4
IL_005e: ldloc.s V_4 IL_005e: ldloc.s V_4
IL_0060: brfalse.s IL_0064 IL_0060: brfalse.s IL_0065
IL_0062: br.s IL_0086 IL_0062: nop
IL_0063: br.s IL_0087
IL_0064: nop IL_0065: nop
IL_0065: ldstr "End of loop body" IL_0066: ldstr "End of loop body"
IL_006a: call void [mscorlib]System.Console::WriteLine(string) IL_006b: call void [mscorlib]System.Console::WriteLine(string)
IL_006f: nop
IL_0070: nop IL_0070: nop
IL_0071: ldloc.1 IL_0071: nop
IL_0072: ldc.i4.1 IL_0072: ldloc.1
IL_0073: add IL_0073: ldc.i4.1
IL_0074: stloc.1 IL_0074: add
IL_0075: ldarg.0 IL_0075: stloc.1
IL_0076: ldstr "for" IL_0076: ldarg.0
IL_007b: call instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::Condition(string) IL_0077: ldstr "for"
IL_0080: stloc.s V_5 IL_007c: call instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::Condition(string)
IL_0082: ldloc.s V_5 IL_0081: stloc.s V_5
IL_0084: brtrue.s IL_0020 IL_0083: ldloc.s V_5
IL_0085: brtrue.s IL_0020
IL_0086: ldstr "After loop"
IL_008b: call void [mscorlib]System.Console::WriteLine(string) IL_0087: ldstr "After loop"
IL_0090: nop IL_008c: call void [mscorlib]System.Console::WriteLine(string)
IL_0091: nop IL_0091: nop
IL_0092: ldstr "End of method" IL_0092: nop
IL_0097: call void [mscorlib]System.Console::WriteLine(string) IL_0093: ldstr "End of method"
IL_009c: nop IL_0098: call void [mscorlib]System.Console::WriteLine(string)
IL_009d: ret IL_009d: nop
IL_009e: ret
} // end of method Loops::ForLoop } // end of method Loops::ForLoop
.method public hidebysig specialname rtspecialname .method public hidebysig specialname rtspecialname

1
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -152,6 +152,7 @@ namespace ICSharpCode.Decompiler.CSharp
new IntroduceExtensionMethods(), // must run after IntroduceUsingDeclarations new IntroduceExtensionMethods(), // must run after IntroduceUsingDeclarations
new IntroduceQueryExpressions(), // must run after IntroduceExtensionMethods new IntroduceQueryExpressions(), // must run after IntroduceExtensionMethods
new CombineQueryExpressions(), new CombineQueryExpressions(),
new NormalizeBlockStatements(),
new FlattenSwitchBlocks(), new FlattenSwitchBlocks(),
new FixNameCollisions(), new FixNameCollisions(),
new AddXmlDocumentationTransform(), new AddXmlDocumentationTransform(),

68
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

@ -471,6 +471,19 @@ namespace ICSharpCode.Decompiler.CSharp
instToReplace.ReplaceWith(new LdLoc(foreachVariable)); instToReplace.ReplaceWith(new LdLoc(foreachVariable));
body.Instructions.Insert(0, new StLoc(foreachVariable, instToReplace)); body.Instructions.Insert(0, new StLoc(foreachVariable, instToReplace));
break; break;
case RequiredGetCurrentTransformation.IntroduceNewVariableAndLocalCopy:
foreachVariable = currentFunction.RegisterVariable(
VariableKind.ForeachLocal, type,
AssignVariableNames.GenerateForeachVariableName(currentFunction, collectionExpr.Annotation<ILInstruction>())
);
var localCopyVariable = currentFunction.RegisterVariable(
VariableKind.Local, type,
AssignVariableNames.GenerateVariableName(currentFunction, type)
);
instToReplace.Parent.ReplaceWith(new LdLoca(localCopyVariable));
body.Instructions.Insert(0, new StLoc(localCopyVariable, new LdLoc(foreachVariable)));
body.Instructions.Insert(0, new StLoc(foreachVariable, instToReplace));
break;
} }
// Convert the modified body to C# AST: // Convert the modified body to C# AST:
var whileLoop = (WhileStatement)ConvertAsBlock(container).First(); var whileLoop = (WhileStatement)ConvertAsBlock(container).First();
@ -563,7 +576,20 @@ namespace ICSharpCode.Decompiler.CSharp
/// ... (ldloc foreachVar) ... /// ... (ldloc foreachVar) ...
/// </code> /// </code>
/// </summary> /// </summary>
IntroduceNewVariable IntroduceNewVariable,
/// <summary>
/// No store was found, thus create a new variable and use it as foreach variable.
/// Additionally it is necessary to copy the value of the foreach variable to another local
/// to allow safe modification of its value.
/// <code>
/// ... addressof(call get_Current()) ...
/// =>
/// stloc foreachVar(call get_Current())
/// stloc copy(ldloc foreachVar)
/// ... (ldloca copy) ...
/// </code>
/// </summary>
IntroduceNewVariableAndLocalCopy
} }
/// <summary> /// <summary>
@ -603,6 +629,11 @@ namespace ICSharpCode.Decompiler.CSharp
return RequiredGetCurrentTransformation.UseExistingVariable; return RequiredGetCurrentTransformation.UseExistingVariable;
} }
} }
// In optimized Roslyn code it can happen that the foreach variable is referenced via addressof
// We only do this unwrapping if where dealing with a custom struct type.
if (CurrentIsStructSetterTarget(inst, singleGetter)) {
return RequiredGetCurrentTransformation.IntroduceNewVariableAndLocalCopy;
}
// No suitable variable was found: we need a new one. // No suitable variable was found: we need a new one.
return RequiredGetCurrentTransformation.IntroduceNewVariable; return RequiredGetCurrentTransformation.IntroduceNewVariable;
} }
@ -616,13 +647,46 @@ namespace ICSharpCode.Decompiler.CSharp
{ {
if (storeInst.Variable.LoadInstructions.Any(ld => !ld.IsDescendantOf(usingContainer))) if (storeInst.Variable.LoadInstructions.Any(ld => !ld.IsDescendantOf(usingContainer)))
return false; return false;
if (storeInst.Variable.AddressInstructions.Any(la => !la.IsDescendantOf(usingContainer) || !ILInlining.IsUsedAsThisPointerInCall(la))) if (storeInst.Variable.AddressInstructions.Any(la => !la.IsDescendantOf(usingContainer) || !ILInlining.IsUsedAsThisPointerInCall(la) || IsTargetOfSetterCall(la, la.Variable.Type)))
return false; return false;
if (storeInst.Variable.StoreInstructions.OfType<ILInstruction>().Any(st => st != storeInst)) if (storeInst.Variable.StoreInstructions.OfType<ILInstruction>().Any(st => st != storeInst))
return false; return false;
return true; return true;
} }
/// <summary>
/// Returns true if singleGetter is a value type and its address is used as setter target.
/// </summary>
bool CurrentIsStructSetterTarget(ILInstruction inst, CallInstruction singleGetter)
{
if (!(inst.Parent is AddressOf addr))
return false;
return IsTargetOfSetterCall(addr, singleGetter.Method.ReturnType);
}
bool IsTargetOfSetterCall(ILInstruction inst, IType targetType)
{
if (inst.ChildIndex != 0)
return false;
if (targetType.IsReferenceType ?? false)
return false;
switch (inst.Parent.OpCode) {
case OpCode.Call:
case OpCode.CallVirt:
var targetMethod = ((CallInstruction)inst.Parent).Method;
if (!targetMethod.IsAccessor || targetMethod.IsStatic)
return false;
switch (targetMethod.AccessorOwner) {
case IProperty p:
return p.Setter == targetMethod;
default:
return true;
}
default:
return false;
}
}
bool ParentIsCurrentGetter(ILInstruction inst) bool ParentIsCurrentGetter(ILInstruction inst)
{ {
return inst.Parent is CallInstruction cv && cv.Method.IsAccessor && return inst.Parent is CallInstruction cv && cv.Method.IsAccessor &&

120
ICSharpCode.Decompiler/CSharp/Transforms/NormalizeBlockStatements.cs

@ -0,0 +1,120 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ICSharpCode.Decompiler.CSharp.Syntax;
namespace ICSharpCode.Decompiler.CSharp.Transforms
{
class NormalizeBlockStatements : DepthFirstAstVisitor, IAstTransform
{
TransformContext context;
public override void VisitIfElseStatement(IfElseStatement ifElseStatement)
{
base.VisitIfElseStatement(ifElseStatement);
DoTransform(ifElseStatement.TrueStatement, ifElseStatement);
DoTransform(ifElseStatement.FalseStatement, ifElseStatement);
}
public override void VisitWhileStatement(WhileStatement whileStatement)
{
base.VisitWhileStatement(whileStatement);
InsertBlock(whileStatement.EmbeddedStatement);
}
public override void VisitDoWhileStatement(DoWhileStatement doWhileStatement)
{
base.VisitDoWhileStatement(doWhileStatement);
InsertBlock(doWhileStatement.EmbeddedStatement);
}
public override void VisitForeachStatement(ForeachStatement foreachStatement)
{
base.VisitForeachStatement(foreachStatement);
InsertBlock(foreachStatement.EmbeddedStatement);
}
public override void VisitForStatement(ForStatement forStatement)
{
base.VisitForStatement(forStatement);
InsertBlock(forStatement.EmbeddedStatement);
}
public override void VisitFixedStatement(FixedStatement fixedStatement)
{
base.VisitFixedStatement(fixedStatement);
InsertBlock(fixedStatement.EmbeddedStatement);
}
public override void VisitLockStatement(LockStatement lockStatement)
{
base.VisitLockStatement(lockStatement);
InsertBlock(lockStatement.EmbeddedStatement);
}
public override void VisitUsingStatement(UsingStatement usingStatement)
{
base.VisitUsingStatement(usingStatement);
DoTransform(usingStatement.EmbeddedStatement, usingStatement);
}
void DoTransform(Statement statement, Statement parent)
{
if (statement.IsNull) return;
if (context.Settings.AlwaysUseBraces) {
if (!IsElseIf(statement, parent)) {
InsertBlock(statement);
}
} else {
if (statement is BlockStatement b && b.Statements.Count == 1 && IsAllowedAsEmbeddedStatement(b.Statements.First(), parent)) {
statement.ReplaceWith(b.Statements.First().Detach());
} else if (!IsAllowedAsEmbeddedStatement(statement, parent)) {
InsertBlock(statement);
}
}
}
bool IsElseIf(Statement statement, Statement parent)
{
return parent is IfElseStatement && statement.Role == IfElseStatement.FalseRole;
}
static void InsertBlock(Statement statement)
{
if (statement.IsNull) return;
if (!(statement is BlockStatement)) {
var b = new BlockStatement();
statement.ReplaceWith(b);
b.Add(statement);
}
}
bool IsAllowedAsEmbeddedStatement(Statement statement, Statement parent)
{
switch (statement) {
case IfElseStatement ies:
return parent is IfElseStatement && ies.Role == IfElseStatement.FalseRole;
case VariableDeclarationStatement vds:
case WhileStatement ws:
case DoWhileStatement dws:
case SwitchStatement ss:
case ForeachStatement fes:
case ForStatement fs:
case LockStatement ls:
case FixedStatement fxs:
return false;
case UsingStatement us:
return parent is UsingStatement;
default:
return !(parent?.Parent is IfElseStatement);
}
}
void IAstTransform.Run(AstNode rootNode, TransformContext context)
{
this.context = context;
rootNode.AcceptVisitor(this);
}
}
}

15
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -178,6 +178,21 @@ namespace ICSharpCode.Decompiler
} }
} }
bool alwaysUseBraces = true;
/// <summary>
/// Gets/Sets whether to use braces for single-statement-blocks.
/// </summary>
public bool AlwaysUseBraces {
get { return alwaysUseBraces; }
set {
if (alwaysUseBraces != value) {
alwaysUseBraces = value;
OnPropertyChanged();
}
}
}
bool forEachStatement = true; bool forEachStatement = true;
/// <summary> /// <summary>

1
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -230,6 +230,7 @@
<Compile Include="CSharp\Transforms\FlattenSwitchBlocks.cs" /> <Compile Include="CSharp\Transforms\FlattenSwitchBlocks.cs" />
<Compile Include="CSharp\Transforms\IntroduceExtensionMethods.cs" /> <Compile Include="CSharp\Transforms\IntroduceExtensionMethods.cs" />
<Compile Include="CSharp\Transforms\IntroduceQueryExpressions.cs" /> <Compile Include="CSharp\Transforms\IntroduceQueryExpressions.cs" />
<Compile Include="CSharp\Transforms\NormalizeBlockStatements.cs" />
<Compile Include="CSharp\Transforms\PrettifyAssignments.cs" /> <Compile Include="CSharp\Transforms\PrettifyAssignments.cs" />
<Compile Include="CSharp\Transforms\RemoveCLSCompliantAttribute.cs" /> <Compile Include="CSharp\Transforms\RemoveCLSCompliantAttribute.cs" />
<Compile Include="CSharp\TranslationContext.cs" /> <Compile Include="CSharp\TranslationContext.cs" />

69
ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs

@ -48,7 +48,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
} }
} }
bool MatchWhileLoop(BlockContainer loop, out ILInstruction condition, out Block loopBody) bool MatchWhileLoop(BlockContainer loop, out IfInstruction condition, out Block loopBody)
{ {
// while-loop: // while-loop:
// if (loop-condition) br loop-content-block // if (loop-condition) br loop-content-block
@ -64,7 +64,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false; return false;
if (!ifInstruction.FalseInst.MatchNop()) if (!ifInstruction.FalseInst.MatchNop())
return false; return false;
condition = ifInstruction.Condition; if (UsesVariableCapturedInLoop(loop, ifInstruction.Condition))
return false;
condition = ifInstruction;
var trueInst = ifInstruction.TrueInst; var trueInst = ifInstruction.TrueInst;
if (!loop.EntryPoint.Instructions[1].MatchLeave(loop)) if (!loop.EntryPoint.Instructions[1].MatchLeave(loop))
return false; return false;
@ -90,13 +92,21 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return true; return true;
} }
void SplitConditions(ILInstruction expression, List<ILInstruction> conditions)
{
if (expression.MatchLogicAnd(out var l, out var r)) {
SplitConditions(l, conditions);
SplitConditions(r, conditions);
} else {
conditions.Add(expression);
}
}
/// <summary> /// <summary>
/// Matches a do-while loop and performs the following transformations: /// Matches a do-while loop and performs the following transformations:
/// - combine all compatible conditions into one IfInstruction. /// - combine all compatible conditions into one IfInstruction.
/// - extract conditions into a condition block, or move the existing condition block to the end. /// - extract conditions into a condition block, or move the existing condition block to the end.
/// </summary> /// </summary>
/// <param name="loop"></param>
/// <returns></returns>
bool MatchDoWhileLoop(BlockContainer loop) bool MatchDoWhileLoop(BlockContainer loop)
{ {
(List<IfInstruction> conditions, ILInstruction exit, bool swap, bool split) = AnalyzeDoWhileConditions(loop); (List<IfInstruction> conditions, ILInstruction exit, bool swap, bool split) = AnalyzeDoWhileConditions(loop);
@ -180,6 +190,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
while (i >= 0 && block.Instructions[i] is IfInstruction ifInst) { while (i >= 0 && block.Instructions[i] is IfInstruction ifInst) {
if (!ifInst.FalseInst.MatchNop()) if (!ifInst.FalseInst.MatchNop())
break; break;
if (UsesVariableCapturedInLoop(loop, ifInst.Condition))
break;
if (swap) { if (swap) {
if (!ifInst.TrueInst.MatchLeave(loop)) if (!ifInst.TrueInst.MatchLeave(loop))
break; break;
@ -195,6 +207,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return list; return list;
} }
static bool UsesVariableCapturedInLoop(BlockContainer loop, ILInstruction condition)
{
foreach (var inst in condition.Descendants.OfType<IInstructionWithVariableOperand>()) {
if (inst.Variable.CaptureScope == loop)
return true;
}
return false;
}
static bool MatchDoWhileConditionBlock(BlockContainer loop, Block block, out bool swapBranches) static bool MatchDoWhileConditionBlock(BlockContainer loop, Block block, out bool swapBranches)
{ {
// match the end of the block: // match the end of the block:
@ -230,7 +251,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return true; return true;
} }
bool MatchForLoop(BlockContainer loop, ILInstruction condition, Block whileLoopBody) bool MatchForLoop(BlockContainer loop, IfInstruction whileCondition, Block whileLoopBody)
{ {
// for loops have exactly two incoming edges at the entry point. // for loops have exactly two incoming edges at the entry point.
if (loop.EntryPoint.IncomingEdgeCount != 2) if (loop.EntryPoint.IncomingEdgeCount != 2)
@ -267,10 +288,35 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// the increment variable must be local/stack variable // the increment variable must be local/stack variable
if (incrementVariable.Kind == VariableKind.Parameter) if (incrementVariable.Kind == VariableKind.Parameter)
return false; return false;
// the increment variable must be checked in the condition // split conditions:
if (!condition.Descendants.Any(inst => inst.MatchLdLoc(incrementVariable))) var conditions = new List<ILInstruction>();
SplitConditions(whileCondition.Condition, conditions);
IfInstruction forCondition = null;
int numberOfConditions = 0;
foreach (var condition in conditions) {
// the increment variable must be used in the condition
if (!condition.Descendants.Any(inst => inst.MatchLdLoc(incrementVariable)))
break;
// condition should not contain an assignment
if (condition.Descendants.Any(IsAssignment))
break;
if (forCondition == null) {
forCondition = new IfInstruction(condition, whileCondition.TrueInst, whileCondition.FalseInst);
} else {
forCondition.Condition = IfInstruction.LogicAnd(forCondition.Condition, condition);
}
numberOfConditions++;
}
if (numberOfConditions == 0)
return false; return false;
context.Step("Transform to for loop", loop); context.Step("Transform to for loop", loop);
// split condition block:
whileCondition.ReplaceWith(forCondition);
new ExpressionTransforms().Run(loop.EntryPoint, forCondition.ChildIndex, new StatementTransformContext(new BlockTransformContext(context)));
for (int i = conditions.Count - 1; i >= numberOfConditions; i--) {
whileLoopBody.Instructions.Insert(0, new IfInstruction(Comp.LogicNot(conditions[i]), new Leave(loop)));
new ExpressionTransforms().Run(whileLoopBody, 0, new StatementTransformContext(new BlockTransformContext(context)));
}
// create a new increment block and add it at the end: // create a new increment block and add it at the end:
int secondToLastIndex = secondToLast.ChildIndex; int secondToLastIndex = secondToLast.ChildIndex;
var newIncremenBlock = new Block(); var newIncremenBlock = new Block();
@ -287,6 +333,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return true; return true;
} }
bool IsAssignment(ILInstruction inst)
{
if (inst is StLoc)
return true;
if (inst is CompoundAssignmentInstruction)
return true;
return false;
}
/// <summary> /// <summary>
/// Returns true if the instruction is stloc v(add(ldloc v, arg)) /// Returns true if the instruction is stloc v(add(ldloc v, arg))
/// or stloc v(compound.assign(ldloc v, arg)) /// or stloc v(compound.assign(ldloc v, arg))

1
ILSpy/Options/DecompilerSettingsPanel.xaml

@ -17,5 +17,6 @@
<CheckBox IsChecked="{Binding RemoveDeadCode}">Remove dead and side effect free code</CheckBox> <CheckBox IsChecked="{Binding RemoveDeadCode}">Remove dead and side effect free code</CheckBox>
<CheckBox IsChecked="{Binding UsingDeclarations}">Insert using declarations</CheckBox> <CheckBox IsChecked="{Binding UsingDeclarations}">Insert using declarations</CheckBox>
<CheckBox IsChecked="{Binding FullyQualifyAmbiguousTypeNames}">Fully qualify ambiguous type names</CheckBox> <CheckBox IsChecked="{Binding FullyQualifyAmbiguousTypeNames}">Fully qualify ambiguous type names</CheckBox>
<CheckBox IsChecked="{Binding AlwaysUseBraces}">Always use braces</CheckBox>
</StackPanel> </StackPanel>
</UserControl> </UserControl>

2
ILSpy/Options/DecompilerSettingsPanel.xaml.cs

@ -63,6 +63,7 @@ namespace ICSharpCode.ILSpy.Options
s.FoldBraces = (bool?)e.Attribute("foldBraces") ?? s.FoldBraces; s.FoldBraces = (bool?)e.Attribute("foldBraces") ?? s.FoldBraces;
s.UsingDeclarations = (bool?)e.Attribute("usingDeclarations") ?? s.UsingDeclarations; s.UsingDeclarations = (bool?)e.Attribute("usingDeclarations") ?? s.UsingDeclarations;
s.FullyQualifyAmbiguousTypeNames = (bool?)e.Attribute("fullyQualifyAmbiguousTypeNames") ?? s.FullyQualifyAmbiguousTypeNames; s.FullyQualifyAmbiguousTypeNames = (bool?)e.Attribute("fullyQualifyAmbiguousTypeNames") ?? s.FullyQualifyAmbiguousTypeNames;
s.AlwaysUseBraces = (bool?)e.Attribute("alwaysUseBraces") ?? s.AlwaysUseBraces;
return s; return s;
} }
@ -84,6 +85,7 @@ namespace ICSharpCode.ILSpy.Options
section.SetAttributeValue("foldBraces", s.RemoveDeadCode); section.SetAttributeValue("foldBraces", s.RemoveDeadCode);
section.SetAttributeValue("usingDeclarations", s.UsingDeclarations); section.SetAttributeValue("usingDeclarations", s.UsingDeclarations);
section.SetAttributeValue("fullyQualifyAmbiguousTypeNames", s.FullyQualifyAmbiguousTypeNames); section.SetAttributeValue("fullyQualifyAmbiguousTypeNames", s.FullyQualifyAmbiguousTypeNames);
section.SetAttributeValue("alwaysUseBraces", s.AlwaysUseBraces);
XElement existingElement = root.Element("DecompilerSettings"); XElement existingElement = root.Element("DecompilerSettings");
if (existingElement != null) if (existingElement != null)

Loading…
Cancel
Save