diff --git a/ILSpy/DecompilationOptions.cs b/ILSpy/DecompilationOptions.cs
index 576286aac..3db61bb38 100644
--- a/ILSpy/DecompilationOptions.cs
+++ b/ILSpy/DecompilationOptions.cs
@@ -78,6 +78,14 @@ namespace ICSharpCode.ILSpy
///
public bool IsDebug { get; set; }
+ ///
+ /// When true, the pipeline records its steps so a step view can display them. It travels with
+ /// the request rather than being read from the view: a step index only means anything against
+ /// a run recorded the same way, so the full run and the step-limited replay of one of its
+ /// indices have to agree on it, and the decompile that reads it happens on a background task.
+ ///
+ public bool RecordSteps { get; set; }
+
///
/// Optional sink for whole-project decompilation progress. Wired onto
///
diff --git a/ILSpy/Docking/DockWorkspace.cs b/ILSpy/Docking/DockWorkspace.cs
index ca88b3367..3e32ee93f 100644
--- a/ILSpy/Docking/DockWorkspace.cs
+++ b/ILSpy/Docking/DockWorkspace.cs
@@ -84,6 +84,15 @@ namespace ICSharpCode.ILSpy.Docking
/// behaviour (e.g. ShowOptionsCommand).
public IDocumentDock? Documents => factory.Documents;
+ ///
+ /// Whether decompiles started in this workspace record their transform steps. Set on the UI
+ /// thread by whoever displays them and copied into each run's ,
+ /// so a background decompile never samples live view state - and every tab, including ones
+ /// opened later, records the same way. A step index only means anything against a run
+ /// recorded like the one the index was taken from.
+ ///
+ public bool RecordSteps { get; set; }
+
public IRelayCommand NavigateBackCommand { get; }
public IRelayCommand NavigateForwardCommand { get; }
public IRelayCommand NavigateToHistoryCommand { get; }
diff --git a/ILSpy/Languages/CSharpLanguage.DebugSteps.cs b/ILSpy/Languages/CSharpLanguage.DebugSteps.cs
index 5e729e4cb..b09f9bd61 100644
--- a/ILSpy/Languages/CSharpLanguage.DebugSteps.cs
+++ b/ILSpy/Languages/CSharpLanguage.DebugSteps.cs
@@ -77,15 +77,6 @@ namespace ICSharpCode.ILSpy.Languages
///
static DebugStepsPaneModel? DebugStepsPane => AppComposition.TryGetExport();
- ///
- /// Points the whole pipeline at the shared stepper, but only while the Debug Steps pane is
- /// there to display what it records - see .
- ///
- static partial void ConfigureStepRecording(CSharpDecompiler decompiler)
- {
- decompiler.RecordSteps = DebugStepsPane?.IsRecording ?? false;
- }
-
///
/// Drops the recorded steps of the last run. The stepper pins every ILAst its steps captured, so
/// this is what actually releases that memory once nothing is displaying it.
diff --git a/ILSpy/Languages/CSharpLanguage.cs b/ILSpy/Languages/CSharpLanguage.cs
index fa4654ec8..a7f800a42 100644
--- a/ILSpy/Languages/CSharpLanguage.cs
+++ b/ILSpy/Languages/CSharpLanguage.cs
@@ -327,10 +327,10 @@ namespace ICSharpCode.ILSpy.Languages
};
decompiler.Stepper.StepLimit = options.StepLimit;
decompiler.Stepper.IsDebug = options.IsDebug;
- // The Debug Steps pane walks the whole pipeline, so the IL transforms have to record into
- // the same stepper as the C# AST transforms. Implemented only under DEBUG, where the pane
- // exists; a Release decompiler records no steps at all.
- ConfigureStepRecording(decompiler);
+ // The Debug Steps pane walks the whole pipeline, so the IL transforms record into the same
+ // stepper as the C# AST transforms. Which runs record is decided by whoever started the
+ // decompile, not by the pane's state at the moment this runs on a background task.
+ decompiler.RecordSteps = options.RecordSteps;
if (options.EscapeInvalidIdentifiers)
decompiler.AstTransforms.Add(new EscapeInvalidIdentifiers());
return decompiler;
@@ -776,10 +776,6 @@ namespace ICSharpCode.ILSpy.Languages
}
}
- // Implemented only under DEBUG (CSharpLanguage.DebugSteps.cs): enables IL-transform step
- // recording while the Debug Steps pane is open. A no-op partial in Release.
- static partial void ConfigureStepRecording(CSharpDecompiler decompiler);
-
// Implemented only under DEBUG (CSharpLanguage.DebugSteps.cs): writes the ILAst the pipeline was
// halted in, when a Debug Steps replay stopped it before there was any C# to show. A no-op
// partial in Release, where nothing sets a step limit.
diff --git a/ILSpy/TextView/DecompilerTabPageModel.cs b/ILSpy/TextView/DecompilerTabPageModel.cs
index 565b9b0b4..269f5e5c2 100644
--- a/ILSpy/TextView/DecompilerTabPageModel.cs
+++ b/ILSpy/TextView/DecompilerTabPageModel.cs
@@ -395,6 +395,7 @@ namespace ICSharpCode.ILSpy.TextView
int? pendingHighlightStep;
bool pendingIsDebug;
+
///
/// Output-length safety limits (characters): a decompile that produces more than the active
/// limit is stopped and replaced with a "too much code" message rather than hanging/OOMing the
@@ -559,6 +560,10 @@ namespace ICSharpCode.ILSpy.TextView
var stepLimit = pendingStepLimit;
var highlightStep = pendingHighlightStep;
var isDebug = pendingIsDebug;
+ // Unlike the per-run overrides above, this is not reset: it describes the tab, and
+ // every run has to record the same way or a step index picked from one tree would
+ // select a different step on replay.
+ var recordSteps = AppEnv.AppComposition.TryGetExport()?.RecordSteps ?? false;
pendingStepLimit = int.MaxValue;
pendingHighlightStep = null;
pendingIsDebug = false;
@@ -580,6 +585,7 @@ namespace ICSharpCode.ILSpy.TextView
StepLimit = stepLimit,
HighlightStep = highlightStep,
IsDebug = isDebug,
+ RecordSteps = recordSteps,
};
try
{
diff --git a/ILSpy/ViewModels/DebugStepsPaneModel.cs b/ILSpy/ViewModels/DebugStepsPaneModel.cs
index 661abc6e8..ee0fdef70 100644
--- a/ILSpy/ViewModels/DebugStepsPaneModel.cs
+++ b/ILSpy/ViewModels/DebugStepsPaneModel.cs
@@ -415,6 +415,10 @@ namespace ICSharpCode.ILSpy.ViewModels
if (IsRecording == enabled)
return;
IsRecording = enabled;
+ // The workspace carries the flag into every run it starts, including the ones the user
+ // triggers by selecting another node, so closing the pane has to clear it there too.
+ if (AppComposition.TryGetExport() is { } workspace)
+ workspace.RecordSteps = enabled;
if (enabled)
{
// C# is the only language that records anything, so re-running any other one would
@@ -434,7 +438,10 @@ namespace ICSharpCode.ILSpy.ViewModels
lastSelectedStep = stepLimit;
// Composition unavailable in design-time previews; the gesture is a no-op there.
var dock = AppComposition.TryGetExport();
- dock?.ActiveDecompilerTab?.RestartDecompileWithStepLimit(stepLimit, isDebug, highlightStep);
+ if (dock == null)
+ return;
+ dock.RecordSteps = IsRecording;
+ dock.ActiveDecompilerTab?.RestartDecompileWithStepLimit(stepLimit, isDebug, highlightStep);
}
}
}