diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs b/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs index ff98e0eea..3260678fa 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs @@ -56,6 +56,8 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow int finalState; // final state after the setResultAndExitBlock ILVariable resultVar; // the variable that gets returned by the setResultAndExitBlock + ILVariable doFinallyBodies; + public void Run(ILFunction function, ILTransformContext context) { if (!context.Settings.AsyncAwait) @@ -267,7 +269,15 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow ++pos; } mainTryCatch = blockContainer.EntryPoint.Instructions[pos] as TryCatch; - // TryCatch will be validated in ValidateCatchBlock() + // CatchHandler will be validated in ValidateCatchBlock() + + if (((BlockContainer)mainTryCatch.TryBlock).EntryPoint.Instructions[0] is StLoc initDoFinallyBodies + && initDoFinallyBodies.Variable.Kind == VariableKind.Local + && initDoFinallyBodies.Variable.Type.IsKnownType(KnownTypeCode.Boolean) + && initDoFinallyBodies.Value.MatchLdcI4(1)) + { + doFinallyBodies = initDoFinallyBodies.Variable; + } setResultAndExitBlock = blockContainer.Blocks[1]; // stobj System.Int32(ldflda [Field ICSharpCode.Decompiler.Tests.TestCases.Pretty.Async+d__7.<>1__state](ldloc this), ldc.i4 -2) @@ -413,6 +423,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow // Use a separate state range analysis per container. var sra = new StateRangeAnalysis(StateRangeAnalysisMode.AsyncMoveNext, stateField, cachedStateVar); sra.CancellationToken = context.CancellationToken; + sra.doFinallyBodies = doFinallyBodies; sra.AssignStateRanges(container, LongSet.Universe); foreach (var block in container.Blocks) { @@ -429,6 +440,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow } } } + // Skip the state dispatcher and directly jump to the initial state var entryPoint = sra.FindBlock(container, initialState); if (entryPoint != null) { container.Blocks.Insert(0, new Block { @@ -448,6 +460,17 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow state = 0; context.CancellationToken.ThrowIfCancellationRequested(); int pos = block.Instructions.Count - 2; + if (doFinallyBodies != null && block.Instructions[pos] is StLoc storeDoFinallyBodies) { + if (!(storeDoFinallyBodies.Variable.Kind == VariableKind.Local + && storeDoFinallyBodies.Variable.Type.IsKnownType(KnownTypeCode.Boolean) + && storeDoFinallyBodies.Variable.Index == doFinallyBodies.Index)) { + return false; + } + if (!storeDoFinallyBodies.Value.MatchLdcI4(0)) + return false; + pos--; + } + // call AwaitUnsafeOnCompleted(ldflda <>t__builder(ldloc this), ldloca awaiter, ldloc this) if (!MatchCall(block.Instructions[pos], "AwaitUnsafeOnCompleted", out var callArgs)) return false; diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/StateRangeAnalysis.cs b/ICSharpCode.Decompiler/IL/ControlFlow/StateRangeAnalysis.cs index a5d65c845..0a50c5bf8 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/StateRangeAnalysis.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/StateRangeAnalysis.cs @@ -58,6 +58,8 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow readonly Dictionary ranges = new Dictionary(); readonly internal Dictionary finallyMethodToStateRange; // used only for IteratorDispose + internal ILVariable doFinallyBodies; + public StateRangeAnalysis(StateRangeAnalysisMode mode, IField stateField, ILVariable cachedStateVar = null) { this.mode = mode; @@ -149,6 +151,10 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow return LongSet.Empty; case Nop nop: return stateRange; + case StLoc stloc when stloc.Variable == doFinallyBodies: + // pre-roslyn async/await uses a generated 'bool doFinallyBodies'; + // do not treat this as user code. + return stateRange; case StLoc stloc when stloc.Variable.IsSingleDefinition: val = evalContext.Eval(stloc.Value); if (val.Type == SymbolicValueType.State && val.Constant == 0) {