diff --git a/ILSpy.Tests/Views/DebugStepsTests.cs b/ILSpy.Tests/Views/DebugStepsTests.cs
index 4c26dadb0..1413e30ac 100644
--- a/ILSpy.Tests/Views/DebugStepsTests.cs
+++ b/ILSpy.Tests/Views/DebugStepsTests.cs
@@ -172,15 +172,13 @@ public class DebugStepsTests
var replayStep = transformGroupWithChanges.Children.First();
var tab = vm.DockWorkspace.ActiveDecompilerTab!;
- tab.RestartDecompileWithStepLimit(replayStep.BeginStep, isDebug: false, replayStep.BeginStep);
- tab = await vm.DockWorkspace.WaitForDecompiledTextAsync();
+ await tab.RestartDecompileWithStepLimit(replayStep.BeginStep, isDebug: false, replayStep.BeginStep);
tab.Text.Should().NotBeNullOrWhiteSpace("C# replay before a selected AST mutation step must still emit code");
tab.DebugStepHighlight.Should().NotBeNull("C# replay before a selected AST mutation step must locate the changed node");
debugStepsVm.Steps.Should().BeSameAs(collectedSteps,
"a step-limited C# replay must not replace the full step tree shown by the pane");
- tab.RestartDecompileWithStepLimit(replayStep.EndStep, isDebug: false, replayStep.BeginStep);
- tab = await vm.DockWorkspace.WaitForDecompiledTextAsync();
+ await tab.RestartDecompileWithStepLimit(replayStep.EndStep, isDebug: false, replayStep.BeginStep);
tab.Text.Should().NotBeNullOrWhiteSpace("C# replay after a selected AST mutation step must still emit code");
tab.DebugStepHighlight.Should().NotBeNull("C# replay after a selected AST mutation step must locate the changed node");
debugStepsVm.Steps.Should().BeSameAs(collectedSteps,
@@ -226,15 +224,13 @@ public class DebugStepsTests
var collectedSteps = debugStepsVm.Steps;
var tab = vm.DockWorkspace.ActiveDecompilerTab!;
- tab.RestartDecompileWithStepLimit(replayStep!.BeginStep, isDebug: false, replayStep.BeginStep);
- tab = await vm.DockWorkspace.WaitForDecompiledTextAsync();
+ await tab.RestartDecompileWithStepLimit(replayStep!.BeginStep, isDebug: false, replayStep.BeginStep);
tab.Text.Should().NotBeNullOrWhiteSpace("ILAst replay before a selected step must still emit IL");
tab.DebugStepHighlight.Should().NotBeNull("ILAst replay before a selected step must locate the changed instruction");
debugStepsVm.Steps.Should().BeSameAs(collectedSteps,
"a step-limited ILAst replay must not replace the full step tree shown by the pane");
- tab.RestartDecompileWithStepLimit(replayStep.EndStep, isDebug: false, replayStep.BeginStep);
- tab = await vm.DockWorkspace.WaitForDecompiledTextAsync();
+ await tab.RestartDecompileWithStepLimit(replayStep.EndStep, isDebug: false, replayStep.BeginStep);
tab.Text.Should().NotBeNullOrWhiteSpace("ILAst replay after a selected step must still emit IL");
tab.DebugStepHighlight.Should().NotBeNull("ILAst replay after a selected step must locate the changed instruction");
diff --git a/ILSpy/TextView/DecompilerTabPageModel.cs b/ILSpy/TextView/DecompilerTabPageModel.cs
index 4091e3012..34376996e 100644
--- a/ILSpy/TextView/DecompilerTabPageModel.cs
+++ b/ILSpy/TextView/DecompilerTabPageModel.cs
@@ -401,12 +401,12 @@ namespace ICSharpCode.ILSpy.TextView
/// is ). toggles the transforms'
/// verbose-debug emission. No-op when there's nothing currently being decompiled.
///
- public void RestartDecompileWithStepLimit(int stepLimit, bool isDebug, int? highlightStep = null)
+ public Task RestartDecompileWithStepLimit(int stepLimit, bool isDebug, int? highlightStep = null)
{
pendingStepLimit = stepLimit;
pendingHighlightStep = highlightStep;
pendingIsDebug = isDebug;
- StartDecompile();
+ return StartDecompile();
}
/// Re-runs the current decompile with a larger output-length limit (the "Display code
@@ -463,16 +463,20 @@ namespace ICSharpCode.ILSpy.TextView
ICSharpCode.ILSpy.Commands.SaveCodeHelper.SaveNodeAsync(node, languageService, dockWorkspace).HandleExceptions();
}
- // Fire-and-forget wrapper around DecompileAsync that observes the resulting Task.
- // Without this, exceptions raised by the dispatched property setters (e.g. the
- // PropertyChanged subscribers in DecompilerTextView) become UnobservedTaskException
- // faults that the finalizer thread rethrows much later, hiding the originating bug.
- void StartDecompile()
+ // Starts a decompile and returns the Task that completes once the output has been applied,
+ // so callers (e.g. the Debug Steps replay, and tests) can await completion deterministically
+ // instead of polling IsDecompiling/Text. A fault-observing continuation is attached so that
+ // exceptions raised by the dispatched property setters (e.g. the PropertyChanged subscribers
+ // in DecompilerTextView) don't become UnobservedTaskException faults the finalizer thread
+ // rethrows much later, hiding the originating bug.
+ Task StartDecompile()
{
- pendingDecompile = DecompileAsync().ContinueWith(t => {
+ var decompile = DecompileAsync();
+ pendingDecompile = decompile.ContinueWith(t => {
if (t.Exception is { } ex)
System.Diagnostics.Debug.WriteLine($"DecompileAsync faulted: {ex.Flatten()}");
}, TaskContinuationOptions.OnlyOnFaulted | TaskContinuationOptions.ExecuteSynchronously);
+ return decompile;
}
///