From 37474ae5e90e0502c973032edf331c81cdd1ca74 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 12 Nov 2017 17:12:54 +0100 Subject: [PATCH] Fix #447: second part ReturnFromDoWhileInTryFinally --- .../TestCases/Pretty/Loops.cs | 17 ++++++ .../TestCases/Pretty/Loops.il | 60 +++++++++++++++++-- .../TestCases/Pretty/Loops.opt.il | 43 +++++++++++-- .../TestCases/Pretty/Loops.opt.roslyn.il | 39 +++++++++++- .../TestCases/Pretty/Loops.roslyn.il | 53 +++++++++++++++- .../IL/Transforms/HighLevelLoopTransform.cs | 50 +++++++++++++--- 6 files changed, 241 insertions(+), 21 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.cs index ca003d34c..d8228adaf 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.cs @@ -637,5 +637,22 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } Console.WriteLine("End of method"); } + + public void ReturnFromDoWhileInTryFinally() + { + try { + do { + if (this.Condition("return")) { + return; + } + } while (this.Condition("repeat")); + + Environment.GetCommandLineArgs(); + } finally { + Environment.GetCommandLineArgs(); + } + + Environment.GetCommandLineArgs(); + } } } diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.il index 35154948d..5e15bab89 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.il @@ -10,7 +10,7 @@ .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } -.assembly dky1g03y +.assembly fawxdzyv { .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 @@ .hash algorithm 0x00008004 .ver 0:0:0:0 } -.module dky1g03y.dll -// MVID: {4ADF70C5-8EC0-41AF-A9B1-6DF95B943C1A} +.module fawxdzyv.dll +// MVID: {8B0E086E-9CF4-420F-8491-C1950C43B6C0} .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: 0x03580000 +// Image base: 0x03590000 // =============== CLASS MEMBERS DECLARATION =================== @@ -2215,6 +2215,58 @@ IL_00a0: ret } // end of method Loops::ForLoop + .method public hidebysig instance void + ReturnFromDoWhileInTryFinally() cil managed + { + // Code size 67 (0x43) + .maxstack 2 + .locals init (bool V_0) + IL_0000: nop + .try + { + IL_0001: nop + IL_0002: nop + IL_0003: ldarg.0 + IL_0004: ldstr "return" + IL_0009: call instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::Condition(string) + IL_000e: ldc.i4.0 + IL_000f: ceq + IL_0011: stloc.0 + IL_0012: ldloc.0 + IL_0013: brtrue.s IL_0018 + + IL_0015: nop + IL_0016: leave.s IL_0041 + + IL_0018: nop + IL_0019: ldarg.0 + IL_001a: ldstr "repeat" + IL_001f: call instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::Condition(string) + IL_0024: stloc.0 + IL_0025: ldloc.0 + IL_0026: brtrue.s IL_0002 + + IL_0028: call string[] [mscorlib]System.Environment::GetCommandLineArgs() + IL_002d: pop + IL_002e: nop + IL_002f: leave.s IL_003a + + } // end .try + finally + { + IL_0031: nop + IL_0032: call string[] [mscorlib]System.Environment::GetCommandLineArgs() + IL_0037: pop + IL_0038: nop + IL_0039: endfinally + } // end handler + IL_003a: nop + IL_003b: call string[] [mscorlib]System.Environment::GetCommandLineArgs() + IL_0040: pop + IL_0041: nop + IL_0042: ret + } // end of method Loops::ReturnFromDoWhileInTryFinally + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.opt.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.opt.il index 49f97f432..b832997d6 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.opt.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.opt.il @@ -10,7 +10,7 @@ .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } -.assembly trg3dx5v +.assembly w2cet5qb { .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 @@ .hash algorithm 0x00008004 .ver 0:0:0:0 } -.module trg3dx5v.dll -// MVID: {39502A20-4431-466D-88D2-F47B21FB8C58} +.module w2cet5qb.dll +// MVID: {F3696C2D-E180-4496-B78F-ACC7FD27833A} .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: 0x00B30000 +// Image base: 0x04C90000 // =============== CLASS MEMBERS DECLARATION =================== @@ -1719,6 +1719,41 @@ IL_007b: ret } // end of method Loops::ForLoop + .method public hidebysig instance void + ReturnFromDoWhileInTryFinally() cil managed + { + // Code size 50 (0x32) + .maxstack 2 + .try + { + IL_0000: ldarg.0 + IL_0001: ldstr "return" + IL_0006: call instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::Condition(string) + IL_000b: brfalse.s IL_000f + + IL_000d: leave.s IL_0031 + + IL_000f: ldarg.0 + IL_0010: ldstr "repeat" + IL_0015: call instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::Condition(string) + IL_001a: brtrue.s IL_0000 + + IL_001c: call string[] [mscorlib]System.Environment::GetCommandLineArgs() + IL_0021: pop + IL_0022: leave.s IL_002b + + } // end .try + finally + { + IL_0024: call string[] [mscorlib]System.Environment::GetCommandLineArgs() + IL_0029: pop + IL_002a: endfinally + } // end handler + IL_002b: call string[] [mscorlib]System.Environment::GetCommandLineArgs() + IL_0030: pop + IL_0031: ret + } // end of method Loops::ReturnFromDoWhileInTryFinally + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.opt.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.opt.roslyn.il index d10c64239..05f9debc8 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.opt.roslyn.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.opt.roslyn.il @@ -25,14 +25,14 @@ .ver 0:0:0:0 } .module Loops.dll -// MVID: {F193AB7E-127F-4EFE-9BF9-ADF41756C267} +// MVID: {54789F7A-02C9-433E-A0D4-8F5763DBAB55} .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: 0x03460000 +// Image base: 0x034F0000 // =============== CLASS MEMBERS DECLARATION =================== @@ -1672,6 +1672,41 @@ IL_007b: ret } // end of method Loops::ForLoop + .method public hidebysig instance void + ReturnFromDoWhileInTryFinally() cil managed + { + // Code size 50 (0x32) + .maxstack 2 + .try + { + IL_0000: ldarg.0 + IL_0001: ldstr "return" + IL_0006: call instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::Condition(string) + IL_000b: brfalse.s IL_000f + + IL_000d: leave.s IL_0031 + + IL_000f: ldarg.0 + IL_0010: ldstr "repeat" + IL_0015: call instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::Condition(string) + IL_001a: brtrue.s IL_0000 + + IL_001c: call string[] [mscorlib]System.Environment::GetCommandLineArgs() + IL_0021: pop + IL_0022: leave.s IL_002b + + } // end .try + finally + { + IL_0024: call string[] [mscorlib]System.Environment::GetCommandLineArgs() + IL_0029: pop + IL_002a: endfinally + } // end handler + IL_002b: call string[] [mscorlib]System.Environment::GetCommandLineArgs() + IL_0030: pop + IL_0031: ret + } // end of method Loops::ReturnFromDoWhileInTryFinally + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.roslyn.il index 98fe40405..f69ffe5da 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.roslyn.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.roslyn.il @@ -25,14 +25,14 @@ .ver 0:0:0:0 } .module Loops.dll -// MVID: {8F6E41CD-7AE5-437C-BACB-4A672A0014D0} +// MVID: {AF6A735F-62A4-480B-8A6B-50E8BB64AD30} .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: 0x00C80000 +// Image base: 0x05400000 // =============== CLASS MEMBERS DECLARATION =================== @@ -2083,6 +2083,55 @@ IL_009e: ret } // end of method Loops::ForLoop + .method public hidebysig instance void + ReturnFromDoWhileInTryFinally() cil managed + { + // Code size 62 (0x3e) + .maxstack 2 + .locals init (bool V_0, + bool V_1) + IL_0000: nop + .try + { + IL_0001: nop + IL_0002: nop + IL_0003: ldarg.0 + IL_0004: ldstr "return" + IL_0009: call instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::Condition(string) + IL_000e: stloc.0 + IL_000f: ldloc.0 + IL_0010: brfalse.s IL_0015 + + IL_0012: nop + IL_0013: leave.s IL_003d + + IL_0015: nop + IL_0016: ldarg.0 + IL_0017: ldstr "repeat" + IL_001c: call instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::Condition(string) + IL_0021: stloc.1 + IL_0022: ldloc.1 + IL_0023: brtrue.s IL_0002 + + IL_0025: call string[] [mscorlib]System.Environment::GetCommandLineArgs() + IL_002a: pop + IL_002b: nop + IL_002c: leave.s IL_0037 + + } // end .try + finally + { + IL_002e: nop + IL_002f: call string[] [mscorlib]System.Environment::GetCommandLineArgs() + IL_0034: pop + IL_0035: nop + IL_0036: endfinally + } // end handler + IL_0037: call string[] [mscorlib]System.Environment::GetCommandLineArgs() + IL_003c: pop + IL_003d: ret + } // end of method Loops::ReturnFromDoWhileInTryFinally + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { diff --git a/ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs index 955a8b05e..fcaa6945a 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Text; using ICSharpCode.Decompiler.Util; @@ -109,7 +110,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// bool MatchDoWhileLoop(BlockContainer loop) { - (List conditions, ILInstruction exit, bool swap, bool split) = AnalyzeDoWhileConditions(loop); + (List conditions, ILInstruction exit, bool swap, bool split, bool unwrap) = AnalyzeDoWhileConditions(loop); // not a do-while loop, exit. if (conditions == null || conditions.Count == 0) return false; @@ -117,6 +118,24 @@ namespace ICSharpCode.Decompiler.IL.Transforms Block conditionBlock; // first we remove all extracted instructions from the original block. var originalBlock = (Block)exit.Parent; + if (unwrap) { + // we found a condition block nested in a condition that is followed by a return statement: + // we flip the condition and swap the blocks + Debug.Assert(originalBlock.Parent is IfInstruction); + var returnCondition = (IfInstruction)originalBlock.Parent; + var topLevelBlock = (Block)returnCondition.Parent; + Debug.Assert(topLevelBlock.Parent == loop); + var leaveFunction = topLevelBlock.Instructions[returnCondition.ChildIndex + 1]; + Debug.Assert(leaveFunction.MatchReturn(out _)); + returnCondition.Condition = Comp.LogicNot(returnCondition.Condition); + returnCondition.TrueInst = leaveFunction; + // simplify the condition: + new ExpressionTransforms().Run(topLevelBlock, returnCondition.ChildIndex, new StatementTransformContext(new BlockTransformContext(context))); + topLevelBlock.Instructions.RemoveAt(returnCondition.ChildIndex + 1); + topLevelBlock.Instructions.AddRange(originalBlock.Instructions); + originalBlock = topLevelBlock; + split = true; + } originalBlock.Instructions.RemoveRange(originalBlock.Instructions.Count - conditions.Count - 1, conditions.Count + 1); // we need to split the block: if (split) { @@ -161,22 +180,21 @@ namespace ICSharpCode.Decompiler.IL.Transforms return true; } - static (List conditions, ILInstruction exit, bool swap, bool split) AnalyzeDoWhileConditions(BlockContainer loop) + static (List conditions, ILInstruction exit, bool swap, bool split, bool unwrap) AnalyzeDoWhileConditions(BlockContainer loop) { - bool swap; // we iterate over all blocks from the bottom, because the entry-point // should only be considered as condition block, if there are no other blocks. foreach (var block in loop.Blocks.Reverse()) { // first we match the end of the block: - if (MatchDoWhileConditionBlock(loop, block, out swap)) { + if (MatchDoWhileConditionBlock(loop, block, out bool swap, out bool unwrapCondtionBlock, out Block conditionBlock)) { // now collect all instructions that are usable as loop conditions - var conditions = CollectConditions(loop, block, swap); + var conditions = CollectConditions(loop, conditionBlock, swap); // split only if the block is either the entry-point or contains other instructions as well. - var split = block == loop.EntryPoint || block.Instructions.Count > conditions.Count + 1; // + 1 is the final leave/branch. - return (conditions, block.Instructions.Last(), swap, split); + var split = conditionBlock == loop.EntryPoint || conditionBlock.Instructions.Count > conditions.Count + 1; // + 1 is the final leave/branch. + return (conditions, conditionBlock.Instructions.Last(), swap, split, unwrapCondtionBlock); } } - return (null, null, false, false); + return (null, null, false, false, false); } /// @@ -216,7 +234,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; } - static bool MatchDoWhileConditionBlock(BlockContainer loop, Block block, out bool swapBranches) + static bool MatchDoWhileConditionBlock(BlockContainer loop, Block block, out bool swapBranches, out bool unwrapCondtionBlock, out Block conditionBlock) { // match the end of the block: // if (condition) branch entry-point else nop @@ -225,6 +243,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms // if (condition) leave loop else nop // branch entry-point swapBranches = false; + unwrapCondtionBlock = false; + conditionBlock = block; // empty block? if (block.Instructions.Count < 2) return false; @@ -233,6 +253,18 @@ namespace ICSharpCode.Decompiler.IL.Transforms // no IfInstruction or already transformed? if (ifInstruction == null || !ifInstruction.FalseInst.MatchNop()) return false; + // the block ends in a return statement preceeded by an IfInstruction + // take a look at the nested block and check if that might be a condition block + if (last.MatchReturn(out _) && ifInstruction.TrueInst is Block nestedConditionBlock) { + if (nestedConditionBlock.Instructions.Count < 2) + return false; + last = nestedConditionBlock.Instructions.Last(); + ifInstruction = nestedConditionBlock.Instructions.SecondToLastOrDefault() as IfInstruction; + if (ifInstruction == null || !ifInstruction.FalseInst.MatchNop()) + return false; + unwrapCondtionBlock = true; + conditionBlock = nestedConditionBlock; + } // if the last instruction is a branch // we assume the branch instructions need to be swapped. if (last.MatchBranch(loop.EntryPoint))