diff --git a/ICSharpCode.Decompiler/DecompilerSettings.cs b/ICSharpCode.Decompiler/DecompilerSettings.cs index d716877ad..091026c7a 100644 --- a/ICSharpCode.Decompiler/DecompilerSettings.cs +++ b/ICSharpCode.Decompiler/DecompilerSettings.cs @@ -72,7 +72,7 @@ namespace ICSharpCode.Decompiler } } - bool asyncAwait = false; + bool asyncAwait = true; /// /// Decompile async methods. diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs b/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs index ac54e6d8b..0988298d1 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs @@ -85,6 +85,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow AnalyzeStateMachine(function); DetectAwaitPattern(function); + CleanDoFinallyBodies(function); context.Step("Translate fields to local accesses", function); MarkGeneratedVariables(function); @@ -809,5 +810,56 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow } } } + + /// + /// Eliminates usage of doFinallyBodies + /// + private void CleanDoFinallyBodies(ILFunction function) + { + if (doFinallyBodies == null) { + return; // roslyn-compiled code doesn't use doFinallyBodies + } + context.StepStartGroup("CleanDoFinallyBodies", function); + Block entryPoint = GetBodyEntryPoint(function.Body as BlockContainer); + if (entryPoint != null && entryPoint.Instructions[0].MatchStLoc(doFinallyBodies, out var value) && value.MatchLdcI4(1)) { + // Remove initial doFinallyBodies assignment, if it wasn't already removed when + // we rearranged the control flow. + entryPoint.Instructions.RemoveAt(0); + } + if (doFinallyBodies.StoreInstructions.Count != 0 || doFinallyBodies.AddressCount != 0) { + // misdetected another variable as doFinallyBodies? + // reintroduce the initial store of ldc.i4(1) + context.Step("Re-introduce misdetected doFinallyBodies", function); + ((BlockContainer)function.Body).EntryPoint.Instructions.Insert(0, + new StLoc(doFinallyBodies, new LdcI4(1))); + return; + } + foreach (var tryFinally in function.Descendants.OfType()) { + entryPoint = GetBodyEntryPoint(tryFinally.FinallyBlock as BlockContainer); + if (entryPoint?.Instructions[0] is IfInstruction ifInst) { + if (ifInst.Condition is LogicNot logicNot && logicNot.Argument.MatchLdLoc(doFinallyBodies)) { + context.Step("Remove if(doFinallyBodies) from try-finally", tryFinally); + // condition will always be false now that we're using 'await' instructions + entryPoint.Instructions.RemoveAt(0); + } + } + } + // if there's any remaining loads (there shouldn't be), replace them with the constant 1 + foreach (LdLoc load in doFinallyBodies.LoadInstructions) { + load.ReplaceWith(new LdcI4(1) { ILRange = load.ILRange }); + } + context.StepEndGroup(keepIfEmpty: true); + } + + Block GetBodyEntryPoint(BlockContainer body) + { + if (body == null) + return null; + Block entryPoint = body.EntryPoint; + while (entryPoint.Instructions[0].MatchBranch(out var targetBlock) && targetBlock.IncomingEdgeCount == 1) { + entryPoint = targetBlock; + } + return entryPoint; + } } }