From 0d3a3bee82449a479b16d4ce134d930ab6ad1b44 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 27 Sep 2019 16:33:03 +0200 Subject: [PATCH] Fix #1703: Support async methods that never return normally. Closes #1678. --- .../TestCases/Pretty/Async.cs | 18 +++++++++++++++ .../TestCases/Pretty/CustomTaskType.cs | 4 +++- .../IL/ControlFlow/AsyncAwaitDecompiler.cs | 22 ++++++++++++++++--- 3 files changed, 40 insertions(+), 4 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs index 9f6b5c58a..7d51bc732 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs @@ -167,6 +167,24 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty }; } + public static async Task AlwaysThrow() + { + throw null; + } + + public static async Task InfiniteLoop() + { + while (true) { + } + } + + public static async Task InfiniteLoopWithAwait() + { + while (true) { + await Task.Delay(10); + } + } + #if CS70 public static async Task AsyncLocalFunctions() { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomTaskType.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomTaskType.cs index 65309ac46..80164a385 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomTaskType.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomTaskType.cs @@ -1,4 +1,6 @@ -using System; +#pragma warning disable 1998 + +using System; using System.Collections.Generic; using System.Threading.Tasks; diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs b/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs index ec20ef25c..63610c57e 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs @@ -85,6 +85,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow TryCatch mainTryCatch; Block setResultAndExitBlock; // block that is jumped to for return statements int finalState; // final state after the setResultAndExitBlock + bool finalStateKnown; ILVariable resultVar; // the variable that gets returned by the setResultAndExitBlock ILVariable doFinallyBodies; @@ -352,7 +353,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow moveNextFunction = YieldReturnDecompiler.CreateILAst(moveNextMethod, context); if (!(moveNextFunction.Body is BlockContainer blockContainer)) throw new SymbolicAnalysisFailedException(); - if (blockContainer.Blocks.Count != 2) + if (blockContainer.Blocks.Count != 2 && blockContainer.Blocks.Count != 1) throw new SymbolicAnalysisFailedException(); if (blockContainer.EntryPoint.IncomingEdgeCount != 1) throw new SymbolicAnalysisFailedException(); @@ -389,7 +390,14 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow doFinallyBodies = initDoFinallyBodies.Variable; } - setResultAndExitBlock = blockContainer.Blocks[1]; + setResultAndExitBlock = blockContainer.Blocks.ElementAtOrDefault(1); + if (setResultAndExitBlock == null) { + // This block can be absent if the function never exits normally, + // but always throws an exception/loops infinitely. + resultVar = null; + finalStateKnown = false; // final state will be detected in ValidateCatchBlock() instead + return; + } // stobj System.Int32(ldflda [Field ICSharpCode.Decompiler.Tests.TestCases.Pretty.Async+d__7.<>1__state](ldloc this), ldc.i4 -2) // call SetResult(ldflda [Field ICSharpCode.Decompiler.Tests.TestCases.Pretty.Async+d__7.<>t__builder](ldloc this), ldloc result) // leave IL_0000 @@ -397,6 +405,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow throw new SymbolicAnalysisFailedException(); if (!MatchStateAssignment(setResultAndExitBlock.Instructions[0], out finalState)) throw new SymbolicAnalysisFailedException(); + finalStateKnown = true; if (!MatchCall(setResultAndExitBlock.Instructions[1], "SetResult", out var args)) throw new SymbolicAnalysisFailedException(); if (!IsBuilderFieldOnThis(args[0])) @@ -442,8 +451,15 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow if (!stloc.Value.MatchLdLoc(handler.Variable)) throw new SymbolicAnalysisFailedException(); // stfld <>1__state(ldloc this, ldc.i4 -2) - if (!MatchStateAssignment(catchBlock.Instructions[1], out int newState) || newState != finalState) + if (!MatchStateAssignment(catchBlock.Instructions[1], out int newState)) throw new SymbolicAnalysisFailedException(); + if (finalStateKnown) { + if (newState != finalState) + throw new SymbolicAnalysisFailedException(); + } else { + finalState = newState; + finalStateKnown = true; + } // call SetException(ldfld <>t__builder(ldloc this), ldloc exception) if (!MatchCall(catchBlock.Instructions[2], "SetException", out var args)) throw new SymbolicAnalysisFailedException();