Browse Source

Carry step recording in the decompilation request

Whether a run records was read from the pane's own state at the moment the
decompiler was built, on a background task. 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 a view that can be attached or
detached between the two is the wrong place to ask.

The flag now travels in DecompilationOptions beside StepLimit and IsDebug, read
from the workspace on the UI thread when a run starts. Putting it on the tab
instead would miss the tabs opened after the pane, and the pane can be opened
before any tab exists at all.

Assisted-by: Claude:claude-opus-5[1m]:Claude Code
pull/4029/head
Siegfried Pammer 3 weeks ago
parent
commit
f24e7faa96
  1. 8
      ILSpy/DecompilationOptions.cs
  2. 9
      ILSpy/Docking/DockWorkspace.cs
  3. 9
      ILSpy/Languages/CSharpLanguage.DebugSteps.cs
  4. 12
      ILSpy/Languages/CSharpLanguage.cs
  5. 6
      ILSpy/TextView/DecompilerTabPageModel.cs
  6. 9
      ILSpy/ViewModels/DebugStepsPaneModel.cs

8
ILSpy/DecompilationOptions.cs

@ -78,6 +78,14 @@ namespace ICSharpCode.ILSpy @@ -78,6 +78,14 @@ namespace ICSharpCode.ILSpy
/// </summary>
public bool IsDebug { get; set; }
/// <summary>
/// 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.
/// </summary>
public bool RecordSteps { get; set; }
/// <summary>
/// Optional sink for whole-project decompilation progress. Wired onto
/// <see cref="ICSharpCode.Decompiler.CSharp.ProjectDecompiler.WholeProjectDecompiler.ProgressIndicator"/>

9
ILSpy/Docking/DockWorkspace.cs

@ -84,6 +84,15 @@ namespace ICSharpCode.ILSpy.Docking @@ -84,6 +84,15 @@ namespace ICSharpCode.ILSpy.Docking
/// behaviour (e.g. ShowOptionsCommand).</summary>
public IDocumentDock? Documents => factory.Documents;
/// <summary>
/// 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 <see cref="DecompilationOptions"/>,
/// 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.
/// </summary>
public bool RecordSteps { get; set; }
public IRelayCommand NavigateBackCommand { get; }
public IRelayCommand NavigateForwardCommand { get; }
public IRelayCommand<NavigationEntry> NavigateToHistoryCommand { get; }

9
ILSpy/Languages/CSharpLanguage.DebugSteps.cs

@ -77,15 +77,6 @@ namespace ICSharpCode.ILSpy.Languages @@ -77,15 +77,6 @@ namespace ICSharpCode.ILSpy.Languages
/// </summary>
static DebugStepsPaneModel? DebugStepsPane => AppComposition.TryGetExport<DebugStepsPaneModel>();
/// <summary>
/// Points the whole pipeline at the shared stepper, but only while the Debug Steps pane is
/// there to display what it records - see <see cref="DebugStepsPaneModel.IsRecording"/>.
/// </summary>
static partial void ConfigureStepRecording(CSharpDecompiler decompiler)
{
decompiler.RecordSteps = DebugStepsPane?.IsRecording ?? false;
}
/// <summary>
/// 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.

12
ILSpy/Languages/CSharpLanguage.cs

@ -327,10 +327,10 @@ namespace ICSharpCode.ILSpy.Languages @@ -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 @@ -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.

6
ILSpy/TextView/DecompilerTabPageModel.cs

@ -395,6 +395,7 @@ namespace ICSharpCode.ILSpy.TextView @@ -395,6 +395,7 @@ namespace ICSharpCode.ILSpy.TextView
int? pendingHighlightStep;
bool pendingIsDebug;
/// <summary>
/// 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 @@ -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<Docking.DockWorkspace>()?.RecordSteps ?? false;
pendingStepLimit = int.MaxValue;
pendingHighlightStep = null;
pendingIsDebug = false;
@ -580,6 +585,7 @@ namespace ICSharpCode.ILSpy.TextView @@ -580,6 +585,7 @@ namespace ICSharpCode.ILSpy.TextView
StepLimit = stepLimit,
HighlightStep = highlightStep,
IsDebug = isDebug,
RecordSteps = recordSteps,
};
try
{

9
ILSpy/ViewModels/DebugStepsPaneModel.cs

@ -415,6 +415,10 @@ namespace ICSharpCode.ILSpy.ViewModels @@ -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<DockWorkspace>() 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 @@ -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<DockWorkspace>();
dock?.ActiveDecompilerTab?.RestartDecompileWithStepLimit(stepLimit, isDebug, highlightStep);
if (dock == null)
return;
dock.RecordSteps = IsRecording;
dock.ActiveDecompilerTab?.RestartDecompileWithStepLimit(stepLimit, isDebug, highlightStep);
}
}
}

Loading…
Cancel
Save