Browse Source

Add more correctness tests and fix bug in UnwrapNestedContainerIfPossible.

pull/897/head
Siegfried Pammer 8 years ago
parent
commit
7d53070f9b
  1. 74
      ICSharpCode.Decompiler.Tests/TestCases/Correctness/Loops.cs
  2. 9
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.cs
  3. 67
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.il
  4. 49
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.opt.il
  5. 42
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.opt.roslyn.il
  6. 55
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.roslyn.il
  7. 14
      ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

74
ICSharpCode.Decompiler.Tests/TestCases/Correctness/Loops.cs

@ -20,6 +20,7 @@ using System; @@ -20,6 +20,7 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
@ -27,11 +28,47 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -27,11 +28,47 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
{
class Loops
{
public class CustomClassEnumeratorWithIDisposable<T> : IDisposable
{
bool next = true;
public T Current {
get {
return default(T);
}
}
public void Dispose()
{
Console.WriteLine("CustomClassEnumeratorWithIDisposable<T>.Dispose()");
}
public bool MoveNext()
{
if (next) {
next = false;
return true;
}
return next;
}
public CustomClassEnumeratorWithIDisposable<T> GetEnumerator()
{
return this;
}
}
static void Operation(ref int item)
{
item++;
}
static T CallWithSideEffect<T>()
{
Console.WriteLine("CallWithSideEffect");
return default(T);
}
static void Main()
{
ForWithMultipleVariables();
@ -42,6 +79,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -42,6 +79,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
NonGenericForeachWithReturn(new object[] { "a", 42, "b", 43 });
ForeachWithReturn(new[] { 42, 43, 44, 45 });
ForeachWithRefUsage(new List<int> { 1, 2, 3, 4, 5 });
Console.WriteLine(FirstOrDefault(new List<int> { 1, 2, 3, 4, 5 }));
Console.WriteLine(NoForeachDueToMultipleCurrentAccess(new List<int> { 1, 2, 3, 4, 5 }));
Console.WriteLine(NoForeachCallWithSideEffect(new CustomClassEnumeratorWithIDisposable<int>()));
}
public static void ForWithMultipleVariables()
@ -148,5 +188,39 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -148,5 +188,39 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
Console.WriteLine("item: " + itemToChange);
}
}
public static T FirstOrDefault<T>(IEnumerable<T> items)
{
T result = default(T);
foreach (T item in items) {
result = item;
break;
}
return result;
}
public static T NoForeachDueToMultipleCurrentAccess<T>(IEnumerable<T> items)
{
Console.WriteLine("NoForeachDueToMultipleCurrentAccess:");
T result = default(T);
using (IEnumerator<T> enumerator = items.GetEnumerator()) {
while (enumerator.MoveNext()) {
result = enumerator.Current;
Console.WriteLine("result: " + result);
}
return enumerator.Current;
}
}
public static T NoForeachCallWithSideEffect<T>(CustomClassEnumeratorWithIDisposable<T> items)
{
Console.WriteLine("NoForeachCallWithSideEffect:");
using (CustomClassEnumeratorWithIDisposable<T> enumerator = items.GetEnumerator()) {
while (enumerator.MoveNext()) {
T result = enumerator.Current;
}
return CallWithSideEffect<T>();
}
}
}
}

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

@ -391,6 +391,15 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -391,6 +391,15 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
Loops.Operation((Func<bool>)(() => c == 5));
}
}
public static T LastOrDefault<T>(IEnumerable<T> items)
{
T result = default(T);
foreach (T item in items) {
result = item;
}
return result;
}
#endregion
//public void ForEachOverArray(string[] array)
//{

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

@ -10,7 +10,7 @@ @@ -10,7 +10,7 @@
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.ver 4:0:0:0
}
.assembly z3mgs0mc
.assembly y5enlto2
{
.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
@ -20,15 +20,15 @@ @@ -20,15 +20,15 @@
.hash algorithm 0x00008004
.ver 0:0:0:0
}
.module z3mgs0mc.dll
// MVID: {DFF5B624-B244-4A64-9EE1-6A76A74C08D2}
.module y5enlto2.dll
// MVID: {7F6CCF81-BE23-4326-8F55-7E3BF0A8B7A2}
.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 )
.imagebase 0x10000000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY
// Image base: 0x01040000
// Image base: 0x009B0000
// =============== CLASS MEMBERS DECLARATION ===================
@ -1385,6 +1385,65 @@ @@ -1385,6 +1385,65 @@
IL_0051: ret
} // end of method Loops::ForeachWithCapturedVariable
.method public hidebysig static !!T LastOrDefault<T>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!T> items) cil managed
{
// Code size 69 (0x45)
.maxstack 2
.locals init (!!T V_0,
!!T V_1,
!!T V_2,
class [mscorlib]System.Collections.Generic.IEnumerator`1<!!T> V_3,
bool V_4)
IL_0000: nop
IL_0001: ldloca.s V_0
IL_0003: initobj !!T
IL_0009: nop
IL_000a: ldarg.0
IL_000b: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!0> class [mscorlib]System.Collections.Generic.IEnumerable`1<!!T>::GetEnumerator()
IL_0010: stloc.3
.try
{
IL_0011: br.s IL_001e
IL_0013: ldloc.3
IL_0014: callvirt instance !0 class [mscorlib]System.Collections.Generic.IEnumerator`1<!!T>::get_Current()
IL_0019: stloc.1
IL_001a: nop
IL_001b: ldloc.1
IL_001c: stloc.0
IL_001d: nop
IL_001e: ldloc.3
IL_001f: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
IL_0024: stloc.s V_4
IL_0026: ldloc.s V_4
IL_0028: brtrue.s IL_0013
IL_002a: leave.s IL_003e
} // end .try
finally
{
IL_002c: ldloc.3
IL_002d: ldnull
IL_002e: ceq
IL_0030: stloc.s V_4
IL_0032: ldloc.s V_4
IL_0034: brtrue.s IL_003d
IL_0036: ldloc.3
IL_0037: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_003c: nop
IL_003d: endfinally
} // end handler
IL_003e: nop
IL_003f: ldloc.0
IL_0040: stloc.2
IL_0041: br.s IL_0043
IL_0043: ldloc.2
IL_0044: ret
} // end of method Loops::LastOrDefault
.method public hidebysig instance void
ForOverArray(string[] 'array') cil managed
{

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

@ -10,7 +10,7 @@ @@ -10,7 +10,7 @@
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.ver 4:0:0:0
}
.assembly xmbkcaue
.assembly jiaqp12i
{
.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
@ -20,15 +20,15 @@ @@ -20,15 +20,15 @@
.hash algorithm 0x00008004
.ver 0:0:0:0
}
.module xmbkcaue.dll
// MVID: {B93067D3-FF54-496F-AEF3-E89E52491AE6}
.module jiaqp12i.dll
// MVID: {34208704-52B8-49EB-BC77-C3A95E1CBA19}
.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 )
.imagebase 0x10000000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY
// Image base: 0x02F70000
// Image base: 0x02520000
// =============== CLASS MEMBERS DECLARATION ===================
@ -1120,6 +1120,47 @@ @@ -1120,6 +1120,47 @@
IL_0048: ret
} // end of method Loops::ForeachWithCapturedVariable
.method public hidebysig static !!T LastOrDefault<T>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!T> items) cil managed
{
// Code size 48 (0x30)
.maxstack 1
.locals init (!!T V_0,
!!T V_1,
class [mscorlib]System.Collections.Generic.IEnumerator`1<!!T> V_2)
IL_0000: ldloca.s V_0
IL_0002: initobj !!T
IL_0008: ldarg.0
IL_0009: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!0> class [mscorlib]System.Collections.Generic.IEnumerable`1<!!T>::GetEnumerator()
IL_000e: stloc.2
.try
{
IL_000f: br.s IL_001a
IL_0011: ldloc.2
IL_0012: callvirt instance !0 class [mscorlib]System.Collections.Generic.IEnumerator`1<!!T>::get_Current()
IL_0017: stloc.1
IL_0018: ldloc.1
IL_0019: stloc.0
IL_001a: ldloc.2
IL_001b: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
IL_0020: brtrue.s IL_0011
IL_0022: leave.s IL_002e
} // end .try
finally
{
IL_0024: ldloc.2
IL_0025: brfalse.s IL_002d
IL_0027: ldloc.2
IL_0028: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_002d: endfinally
} // end handler
IL_002e: ldloc.0
IL_002f: ret
} // end of method Loops::LastOrDefault
.method public hidebysig instance void
ForOverArray(string[] 'array') cil managed
{

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

@ -25,14 +25,14 @@ @@ -25,14 +25,14 @@
.ver 0:0:0:0
}
.module Loops.dll
// MVID: {24FAF99A-6A32-4D9C-94BB-1EEBD46F590F}
// MVID: {8D392B4A-5D21-407A-B579-FCDFA93069CE}
.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 )
.imagebase 0x10000000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY
// Image base: 0x030C0000
// Image base: 0x00DA0000
// =============== CLASS MEMBERS DECLARATION ===================
@ -1088,6 +1088,44 @@ @@ -1088,6 +1088,44 @@
IL_0046: ret
} // end of method Loops::ForeachWithCapturedVariable
.method public hidebysig static !!T LastOrDefault<T>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!T> items) cil managed
{
// Code size 46 (0x2e)
.maxstack 1
.locals init (!!T V_0,
class [mscorlib]System.Collections.Generic.IEnumerator`1<!!T> V_1)
IL_0000: ldloca.s V_0
IL_0002: initobj !!T
IL_0008: ldarg.0
IL_0009: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!0> class [mscorlib]System.Collections.Generic.IEnumerable`1<!!T>::GetEnumerator()
IL_000e: stloc.1
.try
{
IL_000f: br.s IL_0018
IL_0011: ldloc.1
IL_0012: callvirt instance !0 class [mscorlib]System.Collections.Generic.IEnumerator`1<!!T>::get_Current()
IL_0017: stloc.0
IL_0018: ldloc.1
IL_0019: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
IL_001e: brtrue.s IL_0011
IL_0020: leave.s IL_002c
} // end .try
finally
{
IL_0022: ldloc.1
IL_0023: brfalse.s IL_002b
IL_0025: ldloc.1
IL_0026: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_002b: endfinally
} // end handler
IL_002c: ldloc.0
IL_002d: ret
} // end of method Loops::LastOrDefault
.method public hidebysig instance void
ForOverArray(string[] 'array') cil managed
{

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

@ -25,14 +25,14 @@ @@ -25,14 +25,14 @@
.ver 0:0:0:0
}
.module Loops.dll
// MVID: {2A93944A-F762-497A-85EF-B93263897E0E}
// MVID: {C6DBFA5B-CB31-4084-8E6F-7BA877923008}
.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 )
.imagebase 0x10000000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY
// Image base: 0x00B70000
// Image base: 0x00A80000
// =============== CLASS MEMBERS DECLARATION ===================
@ -1303,6 +1303,57 @@ @@ -1303,6 +1303,57 @@
IL_004e: ret
} // end of method Loops::ForeachWithCapturedVariable
.method public hidebysig static !!T LastOrDefault<T>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!T> items) cil managed
{
// Code size 57 (0x39)
.maxstack 1
.locals init (!!T V_0,
class [mscorlib]System.Collections.Generic.IEnumerator`1<!!T> V_1,
!!T V_2,
!!T V_3)
IL_0000: nop
IL_0001: ldloca.s V_0
IL_0003: initobj !!T
IL_0009: nop
IL_000a: ldarg.0
IL_000b: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!0> class [mscorlib]System.Collections.Generic.IEnumerable`1<!!T>::GetEnumerator()
IL_0010: stloc.1
.try
{
IL_0011: br.s IL_001e
IL_0013: ldloc.1
IL_0014: callvirt instance !0 class [mscorlib]System.Collections.Generic.IEnumerator`1<!!T>::get_Current()
IL_0019: stloc.2
IL_001a: nop
IL_001b: ldloc.2
IL_001c: stloc.0
IL_001d: nop
IL_001e: ldloc.1
IL_001f: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
IL_0024: brtrue.s IL_0013
IL_0026: leave.s IL_0033
} // end .try
finally
{
IL_0028: ldloc.1
IL_0029: brfalse.s IL_0032
IL_002b: ldloc.1
IL_002c: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0031: nop
IL_0032: endfinally
} // end handler
IL_0033: ldloc.0
IL_0034: stloc.3
IL_0035: br.s IL_0037
IL_0037: ldloc.3
IL_0038: ret
} // end of method Loops::LastOrDefault
.method public hidebysig instance void
ForOverArray(string[] 'array') cil managed
{

14
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

@ -419,9 +419,18 @@ namespace ICSharpCode.Decompiler.CSharp @@ -419,9 +419,18 @@ namespace ICSharpCode.Decompiler.CSharp
return foreachStmt;
}
/// <summary>
/// Unwraps a nested BlockContainer, if container contains only a single block,
/// and that single block contains only a BlockContainer followed by a Leave instruction.
/// If the leave instruction is a return that carries a value, the container is unwrapped only
/// if the value has no side-effects.
/// Otherwise returns the unmodified container.
/// </summary>
/// <param name="optionalReturnInst">If the leave is a return and has no side-effects, we can move the return out of the using-block and put it after the loop, otherwise returns null.</param>
BlockContainer UnwrapNestedContainerIfPossible(BlockContainer container, out Leave optionalReturnInst)
{
optionalReturnInst = null;
// Check block structure:
if (container.Blocks.Count != 1)
return container;
var nestedBlock = container.Blocks[0];
@ -429,9 +438,12 @@ namespace ICSharpCode.Decompiler.CSharp @@ -429,9 +438,12 @@ namespace ICSharpCode.Decompiler.CSharp
!(nestedBlock.Instructions[0] is BlockContainer nestedContainer) ||
!(nestedBlock.Instructions[1] is Leave leave))
return container;
// If the leave has no value, just unwrap the BlockContainer.
if (leave.MatchLeave(container))
return nestedContainer;
if (leave.IsLeavingFunction) {
// If the leave is a return, we can move the return out of the using-block and put it after the loop
// (but only if the value doesn't have side-effects)
if (leave.IsLeavingFunction && SemanticHelper.IsPure(leave.Value.Flags)) {
optionalReturnInst = leave;
return nestedContainer;
}

Loading…
Cancel
Save