Browse Source

Fix #1703: Support async methods that never return normally.

Closes #1678.
pull/1725/head
Daniel Grunwald 6 years ago
parent
commit
0d3a3bee82
  1. 18
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs
  2. 4
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomTaskType.cs
  3. 22
      ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs

18
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs

@ -167,6 +167,24 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -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<int> AsyncLocalFunctions()
{

4
ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomTaskType.cs

@ -1,4 +1,6 @@ @@ -1,4 +1,6 @@
using System;
#pragma warning disable 1998
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

22
ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs

@ -85,6 +85,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -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 @@ -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 @@ -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+<SimpleBoolTaskMethod>d__7.<>1__state](ldloc this), ldc.i4 -2)
// call SetResult(ldflda [Field ICSharpCode.Decompiler.Tests.TestCases.Pretty.Async+<SimpleBoolTaskMethod>d__7.<>t__builder](ldloc this), ldloc result)
// leave IL_0000
@ -397,6 +405,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -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 @@ -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();

Loading…
Cancel
Save