Browse Source

Move debug-step stepping to a neutral namespace

The step recorder is shared by C# AST and ILAst replay, so keep the public infrastructure out of IL.Transforms and pass language-specific node navigation through neutral node metadata.

Assisted-by: OpenCode:openai/gpt-5.5:OpenCode
pull/3847/head
Siegfried Pammer 6 days ago committed by Siegfried Pammer
parent
commit
d35c082616
  1. 1
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  2. 52
      ICSharpCode.Decompiler/CSharp/Transforms/TransformContext.cs
  3. 108
      ICSharpCode.Decompiler/DebugSteps/Stepper.cs
  4. 28
      ICSharpCode.Decompiler/IL/Transforms/IILTransform.cs
  5. 32
      ILSpy.Tests/Views/DebugStepsTests.cs
  6. 2
      ILSpy/Languages/CSharpLanguage.DebugSteps.cs
  7. 2
      ILSpy/Languages/CSharpLanguage.cs
  8. 2
      ILSpy/Languages/IDebugStepProvider.cs
  9. 1
      ILSpy/Languages/ILAstLanguage.cs
  10. 4
      ILSpy/TextView/DebugStepHighlighter.cs
  11. 2
      ILSpy/TextView/NodeLookup.cs
  12. 2
      ILSpy/ViewModels/DebugStepsPaneModel.cs
  13. 2
      ILSpy/Views/DebugStepFilterConverter.cs

1
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -33,6 +33,7 @@ using ICSharpCode.Decompiler.CSharp.Resolver;
using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.CSharp.Syntax;
using ICSharpCode.Decompiler.CSharp.Transforms; using ICSharpCode.Decompiler.CSharp.Transforms;
using ICSharpCode.Decompiler.CSharp.TypeSystem; using ICSharpCode.Decompiler.CSharp.TypeSystem;
using ICSharpCode.Decompiler.DebugSteps;
using ICSharpCode.Decompiler.DebugInfo; using ICSharpCode.Decompiler.DebugInfo;
using ICSharpCode.Decompiler.Disassembler; using ICSharpCode.Decompiler.Disassembler;
using ICSharpCode.Decompiler.Documentation; using ICSharpCode.Decompiler.Documentation;

52
ICSharpCode.Decompiler/CSharp/Transforms/TransformContext.cs

@ -24,7 +24,7 @@ using System.Diagnostics;
using System.Threading; using System.Threading;
using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.CSharp.Syntax;
using ICSharpCode.Decompiler.IL.Transforms; using ICSharpCode.Decompiler.DebugSteps;
using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.CSharp.Transforms namespace ICSharpCode.Decompiler.CSharp.Transforms
@ -82,29 +82,14 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
[DebuggerStepThrough] [DebuggerStepThrough]
internal void Step(string description, AstNode? near = null) internal void Step(string description, AstNode? near = null)
{ {
Stepper.Node stepNode; Stepper.Step(description, CreateNodeInfo(near));
try
{
stepNode = Stepper.Step(description, modifiedNode: near);
}
catch (StepLimitReachedException)
{
// The limit fell on this step: Stepper recorded it as LimitReachedStep but threw
// before we could attach the node candidates. Attach them to the limit-reached node
// (the tree is still in its pre-mutation state) so the "show state before" view can
// locate the change, mirroring how the IL path records candidates before its throw.
if (Stepper.LimitReachedStep is { } limitStep)
TrackModifiedNode(limitStep, near);
throw;
}
TrackModifiedNode(stepNode, near);
} }
[Conditional("STEP")] [Conditional("STEP")]
[DebuggerStepThrough] [DebuggerStepThrough]
internal void StepStartGroup(string description, AstNode? near = null) internal void StepStartGroup(string description, AstNode? near = null)
{ {
TrackModifiedNode(Stepper.StartGroup(description, modifiedNode: near), near); Stepper.StartGroup(description, CreateNodeInfo(near));
} }
[Conditional("STEP")] [Conditional("STEP")]
@ -123,28 +108,28 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
{ {
if (Stepper.LastStep != null && modifiedNode != null) if (Stepper.LastStep != null && modifiedNode != null)
{ {
var marker = new DebugStepMarker();
modifiedNode.AddAnnotation(marker);
Stepper.LastStep.ModifiedNode = modifiedNode; Stepper.LastStep.ModifiedNode = modifiedNode;
TrackModifiedNode(Stepper.LastStep, modifiedNode, insertFirst: true); Stepper.LastStep.RecordModifiedNode(modifiedNode, extraIdentity: marker, insertFirst: true);
} }
} }
static void TrackModifiedNode(Stepper.Node step, AstNode? modifiedNode, bool insertFirst = false) static DebugStepNodeInfo? CreateNodeInfo(AstNode? modifiedNode)
{ {
if (modifiedNode == null) if (modifiedNode == null)
return; return null;
// The marker rides CopyAnnotationsFrom so a replaced node's step still resolves to the // The marker rides CopyAnnotationsFrom so a replaced node's step still resolves to the
// emitted text; it is recorded as a second identity for the changed node. The seam // emitted text; it is recorded as a second identity for the changed node. The seam
// neighbours and ancestor chain are captured from the original node on the initial pass // neighbors and ancestor chain are captured from the original node before mutation.
// (insertFirst == false); a produced-node update only prepends the new node.
var marker = new DebugStepMarker(); var marker = new DebugStepMarker();
modifiedNode.AddAnnotation(marker); modifiedNode.AddAnnotation(marker);
step.RecordModifiedNode( return new DebugStepNodeInfo(
modifiedNode, modifiedNode,
insertFirst ? null : modifiedNode.NextSibling, modifiedNode.NextSibling,
insertFirst ? null : modifiedNode.PrevSibling, modifiedNode.PrevSibling,
insertFirst ? null : Ancestors(modifiedNode), Ancestors(modifiedNode),
extraIdentity: marker, extraIdentity: marker);
insertFirst: insertFirst);
static IEnumerable<object> Ancestors(AstNode node) static IEnumerable<object> Ancestors(AstNode node)
{ {
@ -154,13 +139,4 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
} }
} }
/// <summary>
/// Annotation attached to a step's modified node so the debug-step highlighter can still locate
/// that node's rendered range after a later transform copies its annotations onto a replacement
/// (via CopyAnnotationsFrom). This is the only annotation <c>NodeLookup</c> bridges to a text
/// range; indexing every annotation would add dead keys and let shared ones collide by identity.
/// </summary>
public sealed class DebugStepMarker
{
}
} }

108
ICSharpCode.Decompiler/IL/Transforms/Stepper.cs → ICSharpCode.Decompiler/DebugSteps/Stepper.cs

@ -25,15 +25,41 @@ using System.Linq;
using ICSharpCode.Decompiler.Util; using ICSharpCode.Decompiler.Util;
namespace ICSharpCode.Decompiler.IL.Transforms namespace ICSharpCode.Decompiler.DebugSteps
{ {
/// <summary> /// <summary>
/// Exception thrown when an IL transform runs into the <see cref="Stepper.StepLimit"/>. /// Exception thrown when a debug-step-enabled transform pipeline runs into the <see cref="Stepper.StepLimit"/>.
/// </summary> /// </summary>
public class StepLimitReachedException : Exception public class StepLimitReachedException : Exception
{ {
} }
/// <summary>
/// Language-specific node identities and navigation data captured before a transform mutation.
/// </summary>
public readonly struct DebugStepNodeInfo
{
public object Node { get; }
public object? NextSibling { get; }
public object? PreviousSibling { get; }
public IEnumerable<object>? Ancestors { get; }
public object? ExtraIdentity { get; }
public DebugStepNodeInfo(
object node,
object? nextSibling = null,
object? previousSibling = null,
IEnumerable<object>? ancestors = null,
object? extraIdentity = null)
{
Node = node ?? throw new ArgumentNullException(nameof(node));
NextSibling = nextSibling;
PreviousSibling = previousSibling;
Ancestors = ancestors;
ExtraIdentity = extraIdentity;
}
}
/// <summary> /// <summary>
/// Helper class that manages recording transform steps. /// Helper class that manages recording transform steps.
/// </summary> /// </summary>
@ -63,7 +89,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
public class Node public class Node
{ {
public string Description { get; } public string Description { get; }
public ILInstruction? Position { get; set; } public object? Position { get; set; }
public object? ModifiedNode { get; set; } public object? ModifiedNode { get; set; }
/// <summary> /// <summary>
/// Precise identities of the changed node (the node itself, its debug-step marker, and /// Precise identities of the changed node (the node itself, its debug-step marker, and
@ -71,14 +97,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// </summary> /// </summary>
public IList<object> ModifiedNodeCandidates { get; } = new List<object>(); public IList<object> ModifiedNodeCandidates { get; } = new List<object>();
/// <summary> /// <summary>
/// Neighbours of the changed node, recorded before the mutation. When the node itself is /// Neighbors of the changed node, recorded before the mutation. When the node itself is
/// gone from the rendered text (a removal), a caret is placed at the gap they leave: /// gone from the rendered text (a removal), a caret is placed at the gap they leave:
/// at the start of a following neighbour, or the end of a preceding one (AtEnd). /// at the start of a following neighbor, or the end of a preceding one (AtEnd).
/// </summary> /// </summary>
public IList<(object Anchor, bool AtEnd)> SeamAnchors { get; } = new List<(object, bool)>(); public IList<(object Anchor, bool AtEnd)> SeamAnchors { get; } = new List<(object, bool)>();
/// <summary> /// <summary>
/// The changed node's ancestor chain, resolved as a last resort when neither the node /// The changed node's ancestor chain, resolved as a last resort when neither the node
/// nor a seam neighbour has a rendered range. /// nor a seam neighbor has a rendered range.
/// </summary> /// </summary>
public IList<object> AncestorCandidates { get; } = new List<object>(); public IList<object> AncestorCandidates { get; } = new List<object>();
/// <summary> /// <summary>
@ -101,12 +127,23 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// Records the highlight candidates for this step from an already-navigated node: the /// Records the highlight candidates for this step from an already-navigated node: the
/// node's identity (optionally a second identity such as a debug-step marker), its /// node's identity (optionally a second identity such as a debug-step marker), its
/// immediate siblings as seam anchors, and its ancestor chain. Callers pass the /// immediate siblings as seam anchors, and its ancestor chain. Callers pass the
/// neighbours because <see cref="ILInstruction"/> and the C# AST expose navigation /// neighbors because each transform representation exposes navigation differently;
/// differently; the ordering/dedup/seam strategy lives here so the two languages' /// the ordering/dedup/seam strategy lives here so languages'
/// recording can't drift. <paramref name="insertFirst"/> marks a produced-node update /// recording can't drift. <paramref name="insertFirst"/> marks a produced-node update
/// (from EndStep): the node is preferred over existing candidates and the seam and /// (from EndStep): the node is preferred over existing candidates and the seam and
/// ancestor anchors are left untouched, since they were captured from the original node. /// ancestor anchors are left untouched, since they were captured from the original node.
/// </summary> /// </summary>
public void RecordModifiedNode(DebugStepNodeInfo modifiedNode, bool insertFirst = false)
{
RecordModifiedNode(
modifiedNode.Node,
modifiedNode.NextSibling,
modifiedNode.PreviousSibling,
modifiedNode.Ancestors,
modifiedNode.ExtraIdentity,
insertFirst);
}
public void RecordModifiedNode( public void RecordModifiedNode(
object node, object node,
object? nextSibling = null, object? nextSibling = null,
@ -159,44 +196,21 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// May throw <see cref="StepLimitReachedException"/> in debug mode. /// May throw <see cref="StepLimitReachedException"/> in debug mode.
/// </summary> /// </summary>
[DebuggerStepThrough] [DebuggerStepThrough]
public Node Step(string description, ILInstruction? near = null, object? modifiedNode = null) public Node Step(string description, DebugStepNodeInfo? modifiedNode = null)
{
return StepInternal(description, near, modifiedNode ?? near);
}
[DebuggerStepThrough]
private Node StepInternal(string description, ILInstruction? near, object? modifiedNode)
{ {
object? node = modifiedNode?.Node;
var stepNode = new Node($"{step}: {description}") { var stepNode = new Node($"{step}: {description}") {
Position = near, Position = node,
ModifiedNode = modifiedNode, ModifiedNode = node,
BeginStep = step, BeginStep = step,
EndStep = step + 1 EndStep = step + 1
}; };
// Record the IL position, its neighbours, and its ancestor chain as highlight candidates // Record highlight candidates before the limit-reached check below can throw: the debug-step
// here, before the limit-reached check below can throw: the debug-step view halts the // view halts the pipeline at the selected step, so that step would otherwise carry no
// pipeline at the selected step, so that step would otherwise carry no candidates and the // candidates and the "show state before" view could not locate the change.
// "show state before" view could not locate the change. A later transform may detach the if (modifiedNode is { } info)
// exact instruction; a surviving neighbour then anchors a seam caret, and failing that a
// surviving ancestor (ultimately the ILFunction) still resolves.
if (near != null)
{
object? next = null, prev = null;
if (near.Parent is { } parent)
{
int index = near.ChildIndex;
if (index + 1 < parent.Children.Count)
next = parent.Children[index + 1];
if (index - 1 >= 0)
prev = parent.Children[index - 1];
}
stepNode.RecordModifiedNode(near, next, prev, Ancestors(near));
static IEnumerable<object> Ancestors(ILInstruction inst)
{ {
for (var node = inst.Parent; node != null; node = node.Parent) stepNode.RecordModifiedNode(info);
yield return node;
}
} }
if (step == StepLimit) if (step == StepLimit)
{ {
@ -217,9 +231,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
} }
[DebuggerStepThrough] [DebuggerStepThrough]
public Node StartGroup(string description, ILInstruction? near = null, object? modifiedNode = null) public Node StartGroup(string description, DebugStepNodeInfo? modifiedNode = null)
{ {
var stepNode = StepInternal(description, near, modifiedNode ?? near); var stepNode = Step(description, modifiedNode);
groups.Push(stepNode); groups.Push(stepNode);
return stepNode; return stepNode;
} }
@ -254,4 +268,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms
node.EndStep = step; node.EndStep = step;
} }
} }
/// <summary>
/// Annotation attached to a step's modified node so the debug-step highlighter can still locate
/// that node's rendered range after a later transform copies its annotations onto a replacement
/// (via CopyAnnotationsFrom). This is the only annotation the UI bridges to a text range; indexing
/// every annotation would add dead keys and let shared ones collide by identity.
/// </summary>
public sealed class DebugStepMarker
{
}
} }

28
ICSharpCode.Decompiler/IL/Transforms/IILTransform.cs

@ -19,11 +19,13 @@
#nullable enable #nullable enable
using System; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Threading; using System.Threading;
using ICSharpCode.Decompiler.CSharp.Resolver; using ICSharpCode.Decompiler.CSharp.Resolver;
using ICSharpCode.Decompiler.CSharp.TypeSystem; using ICSharpCode.Decompiler.CSharp.TypeSystem;
using ICSharpCode.Decompiler.DebugSteps;
using ICSharpCode.Decompiler.DebugInfo; using ICSharpCode.Decompiler.DebugInfo;
using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util; using ICSharpCode.Decompiler.Util;
@ -105,14 +107,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms
[DebuggerStepThrough] [DebuggerStepThrough]
internal void Step(string description, ILInstruction? near) internal void Step(string description, ILInstruction? near)
{ {
Stepper.Step(description, near); Stepper.Step(description, CreateNodeInfo(near));
} }
[Conditional("STEP")] [Conditional("STEP")]
[DebuggerStepThrough] [DebuggerStepThrough]
internal void StepStartGroup(string description, ILInstruction? near = null) internal void StepStartGroup(string description, ILInstruction? near = null)
{ {
Stepper.StartGroup(description, near); Stepper.StartGroup(description, CreateNodeInfo(near));
} }
[Conditional("STEP")] [Conditional("STEP")]
@ -138,5 +140,27 @@ namespace ICSharpCode.Decompiler.IL.Transforms
step.RecordModifiedNode(modifiedNode, insertFirst: true); step.RecordModifiedNode(modifiedNode, insertFirst: true);
} }
} }
static DebugStepNodeInfo? CreateNodeInfo(ILInstruction? instruction)
{
if (instruction == null)
return null;
object? next = null, previous = null;
if (instruction.Parent is { } parent)
{
int index = instruction.ChildIndex;
if (index + 1 < parent.Children.Count)
next = parent.Children[index + 1];
if (index - 1 >= 0)
previous = parent.Children[index - 1];
}
return new DebugStepNodeInfo(instruction, next, previous, Ancestors(instruction));
static IEnumerable<object> Ancestors(ILInstruction instruction)
{
for (var node = instruction.Parent; node != null; node = node.Parent)
yield return node;
}
}
} }
} }

32
ILSpy.Tests/Views/DebugStepsTests.cs

@ -33,6 +33,7 @@ using AwesomeAssertions;
using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.CSharp;
using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.CSharp.Syntax;
using ICSharpCode.Decompiler.CSharp.Transforms; using ICSharpCode.Decompiler.CSharp.Transforms;
using ICSharpCode.Decompiler.DebugSteps;
using ICSharpCode.ILSpy; using ICSharpCode.ILSpy;
using ICSharpCode.ILSpy.AppEnv; using ICSharpCode.ILSpy.AppEnv;
@ -239,8 +240,7 @@ public class DebugStepsTests
// The first leaf step that acts on a concrete instruction; a step whose Position is null // The first leaf step that acts on a concrete instruction; a step whose Position is null
// (e.g. an empty transform group) has nothing to highlight and is not what a user replays. // (e.g. an empty transform group) has nothing to highlight and is not what a user replays.
static ICSharpCode.Decompiler.IL.Transforms.Stepper.Node? FirstLeafStep( static Stepper.Node? FirstLeafStep(System.Collections.Generic.IEnumerable<Stepper.Node> steps)
System.Collections.Generic.IEnumerable<ICSharpCode.Decompiler.IL.Transforms.Stepper.Node> steps)
{ {
foreach (var step in steps) foreach (var step in steps)
{ {
@ -304,37 +304,37 @@ public class DebugStepsTests
public Task DebugStepHighlighter_Removal_Resolves_To_Seam_Caret() public Task DebugStepHighlighter_Removal_Resolves_To_Seam_Caret()
{ {
// A step whose node was removed has no precise range and (in this fixture) no rendered // A step whose node was removed has no precise range and (in this fixture) no rendered
// ancestor, only surviving neighbours. It must resolve to a zero-length caret at the gap: // ancestor, only surviving neighbors. It must resolve to a zero-length caret at the gap:
// the successor's start when one survives, otherwise the predecessor's end. // the successor's start when one survives, otherwise the predecessor's end.
var successor = new object(); var successor = new object();
var predecessor = new object(); var predecessor = new object();
var successorLookup = new NodeLookup(); var successorLookup = new NodeLookup();
successorLookup.AddNode(successor, 40, 6); successorLookup.AddNode(successor, 40, 6);
var removalWithSuccessor = new ICSharpCode.Decompiler.IL.Transforms.Stepper.Node("0: Remove statement") { var removalWithSuccessor = new Stepper.Node("0: Remove statement") {
BeginStep = 0, BeginStep = 0,
EndStep = 1 EndStep = 1
}; };
removalWithSuccessor.SeamAnchors.Add((successor, false)); removalWithSuccessor.SeamAnchors.Add((successor, false));
removalWithSuccessor.SeamAnchors.Add((predecessor, true)); removalWithSuccessor.SeamAnchors.Add((predecessor, true));
var successorStepper = new ICSharpCode.Decompiler.IL.Transforms.Stepper(); var successorStepper = new Stepper();
successorStepper.Steps.Add(removalWithSuccessor); successorStepper.Steps.Add(removalWithSuccessor);
DebugStepHighlighter.TryResolve(successorStepper, stepLimit: 1, highlightStep: 0, successorLookup, out var caret) DebugStepHighlighter.TryResolve(successorStepper, stepLimit: 1, highlightStep: 0, successorLookup, out var caret)
.Should().BeTrue("a removed node must still resolve to a surviving seam neighbour"); .Should().BeTrue("a removed node must still resolve to a surviving seam neighbor");
caret.Length.Should().Be(0, "a removal has no text to highlight, only a caret at the gap"); caret.Length.Should().Be(0, "a removal has no text to highlight, only a caret at the gap");
caret.Start.Should().Be(40, "the caret sits at the successor's start, where the node was"); caret.Start.Should().Be(40, "the caret sits at the successor's start, where the node was");
// Only the predecessor survives: the caret sits at its end (10 + 5). // Only the predecessor survives: the caret sits at its end (10 + 5).
var predecessorLookup = new NodeLookup(); var predecessorLookup = new NodeLookup();
predecessorLookup.AddNode(predecessor, 10, 5); predecessorLookup.AddNode(predecessor, 10, 5);
var removalWithPredecessor = new ICSharpCode.Decompiler.IL.Transforms.Stepper.Node("0: Remove statement") { var removalWithPredecessor = new Stepper.Node("0: Remove statement") {
BeginStep = 0, BeginStep = 0,
EndStep = 1 EndStep = 1
}; };
removalWithPredecessor.SeamAnchors.Add((successor, false)); removalWithPredecessor.SeamAnchors.Add((successor, false));
removalWithPredecessor.SeamAnchors.Add((predecessor, true)); removalWithPredecessor.SeamAnchors.Add((predecessor, true));
var predecessorStepper = new ICSharpCode.Decompiler.IL.Transforms.Stepper(); var predecessorStepper = new Stepper();
predecessorStepper.Steps.Add(removalWithPredecessor); predecessorStepper.Steps.Add(removalWithPredecessor);
DebugStepHighlighter.TryResolve(predecessorStepper, stepLimit: 1, highlightStep: 0, predecessorLookup, out var endCaret) DebugStepHighlighter.TryResolve(predecessorStepper, stepLimit: 1, highlightStep: 0, predecessorLookup, out var endCaret)
@ -348,9 +348,9 @@ public class DebugStepsTests
public Task DebugStepFilter_Keeps_Matches_And_The_Path_To_Them() public Task DebugStepFilter_Keeps_Matches_And_The_Path_To_Them()
{ {
var converter = new DebugStepFilterConverter(); var converter = new DebugStepFilterConverter();
var matchingLeaf = new ICSharpCode.Decompiler.IL.Transforms.Stepper.Node("3: Introduce query continuation"); var matchingLeaf = new Stepper.Node("3: Introduce query continuation");
var otherLeaf = new ICSharpCode.Decompiler.IL.Transforms.Stepper.Node("4: Flatten switch section block"); var otherLeaf = new Stepper.Node("4: Flatten switch section block");
var group = new ICSharpCode.Decompiler.IL.Transforms.Stepper.Node("CombineQueryExpressions"); var group = new Stepper.Node("CombineQueryExpressions");
group.Children.Add(matchingLeaf); group.Children.Add(matchingLeaf);
group.Children.Add(otherLeaf); group.Children.Add(otherLeaf);
@ -365,7 +365,7 @@ public class DebugStepsTests
Filter(otherLeaf, "continuation").Should().BeFalse(); Filter(otherLeaf, "continuation").Should().BeFalse();
return Task.CompletedTask; return Task.CompletedTask;
bool Filter(ICSharpCode.Decompiler.IL.Transforms.Stepper.Node node, string filter) bool Filter(Stepper.Node node, string filter)
=> (bool)converter.Convert(new object?[] { node, filter }, typeof(bool), null, CultureInfo.InvariantCulture); => (bool)converter.Convert(new object?[] { node, filter }, typeof(bool), null, CultureInfo.InvariantCulture);
} }
@ -377,9 +377,9 @@ public class DebugStepsTests
// would not catch at build time. Realising the view with a populated tree and a live filter // would not catch at build time. Realising the view with a populated tree and a live filter
// must not throw. // must not throw.
var vm = new DebugStepsPaneModel(); var vm = new DebugStepsPaneModel();
var group = new ICSharpCode.Decompiler.IL.Transforms.Stepper.Node("CombineQueryExpressions"); var group = new Stepper.Node("CombineQueryExpressions");
group.Children.Add(new ICSharpCode.Decompiler.IL.Transforms.Stepper.Node("3: Introduce query continuation")); group.Children.Add(new Stepper.Node("3: Introduce query continuation"));
group.Children.Add(new ICSharpCode.Decompiler.IL.Transforms.Stepper.Node("4: Flatten switch section block")); group.Children.Add(new Stepper.Node("4: Flatten switch section block"));
vm.Steps = new[] { group }; vm.Steps = new[] { group };
vm.IsAvailable = true; vm.IsAvailable = true;
@ -483,7 +483,7 @@ public class DebugStepsTests
debugStepsVm.IsAvailable.Should().BeTrue("C# provides debug steps"); debugStepsVm.IsAvailable.Should().BeTrue("C# provides debug steps");
// Simulate a populated tree from the C# run, then flip to the disassembler language. // Simulate a populated tree from the C# run, then flip to the disassembler language.
debugStepsVm.Steps = new[] { new ICSharpCode.Decompiler.IL.Transforms.Stepper.Node("stale") }; debugStepsVm.Steps = new[] { new Stepper.Node("stale") };
languageService.CurrentLanguage = languageService.Languages.OfType<ILLanguage>().First(l => l.Name == "IL"); languageService.CurrentLanguage = languageService.Languages.OfType<ILLanguage>().First(l => l.Name == "IL");
global::Avalonia.Threading.Dispatcher.UIThread.RunJobs(); global::Avalonia.Threading.Dispatcher.UIThread.RunJobs();

2
ILSpy/Languages/CSharpLanguage.DebugSteps.cs

@ -22,7 +22,7 @@ using System;
using ICSharpCode.Decompiler; using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.CSharp;
using ICSharpCode.Decompiler.IL.Transforms; using ICSharpCode.Decompiler.DebugSteps;
using ICSharpCode.ILSpy.AppEnv; using ICSharpCode.ILSpy.AppEnv;
using ICSharpCode.ILSpy.Docking; using ICSharpCode.ILSpy.Docking;

2
ILSpy/Languages/CSharpLanguage.cs

@ -32,8 +32,8 @@ using ICSharpCode.Decompiler.CSharp.OutputVisitor;
using ICSharpCode.Decompiler.CSharp.ProjectDecompiler; using ICSharpCode.Decompiler.CSharp.ProjectDecompiler;
using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.CSharp.Syntax;
using ICSharpCode.Decompiler.CSharp.Transforms; using ICSharpCode.Decompiler.CSharp.Transforms;
using ICSharpCode.Decompiler.DebugSteps;
using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.IL;
using ICSharpCode.Decompiler.IL.Transforms;
using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.Output; using ICSharpCode.Decompiler.Output;
using ICSharpCode.Decompiler.Solution; using ICSharpCode.Decompiler.Solution;

2
ILSpy/Languages/IDebugStepProvider.cs

@ -20,7 +20,7 @@
using System; using System;
using ICSharpCode.Decompiler.IL.Transforms; using ICSharpCode.Decompiler.DebugSteps;
namespace ICSharpCode.ILSpy.Languages namespace ICSharpCode.ILSpy.Languages
{ {

1
ILSpy/Languages/ILAstLanguage.cs

@ -24,6 +24,7 @@ using System.Composition;
using ICSharpCode.Decompiler; using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.CSharp;
using ICSharpCode.Decompiler.DebugSteps;
using ICSharpCode.Decompiler.Disassembler; using ICSharpCode.Decompiler.Disassembler;
using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.IL;
using ICSharpCode.Decompiler.IL.Transforms; using ICSharpCode.Decompiler.IL.Transforms;

4
ILSpy/TextView/DebugStepHighlighter.cs

@ -16,7 +16,7 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
using ICSharpCode.Decompiler.IL.Transforms; using ICSharpCode.Decompiler.DebugSteps;
namespace ICSharpCode.ILSpy.TextView namespace ICSharpCode.ILSpy.TextView
{ {
@ -71,7 +71,7 @@ namespace ICSharpCode.ILSpy.TextView
if (step.ModifiedNode != null && nodeLookup.TryGetRange(step.ModifiedNode, out range)) if (step.ModifiedNode != null && nodeLookup.TryGetRange(step.ModifiedNode, out range))
return true; return true;
// The node itself is gone (a removal): point a zero-length caret at the gap it left, // The node itself is gone (a removal): point a zero-length caret at the gap it left,
// anchored to a surviving neighbour, rather than flooding the enclosing block. // anchored to a surviving neighbor, rather than flooding the enclosing block.
foreach (var (anchor, atEnd) in step.SeamAnchors) foreach (var (anchor, atEnd) in step.SeamAnchors)
{ {
if (nodeLookup.TryGetRange(anchor, out var anchorRange)) if (nodeLookup.TryGetRange(anchor, out var anchorRange))

2
ILSpy/TextView/NodeLookup.cs

@ -19,7 +19,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.CSharp.Syntax;
using ICSharpCode.Decompiler.CSharp.Transforms; using ICSharpCode.Decompiler.DebugSteps;
namespace ICSharpCode.ILSpy.TextView namespace ICSharpCode.ILSpy.TextView
{ {

2
ILSpy/ViewModels/DebugStepsPaneModel.cs

@ -27,8 +27,8 @@ using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Input;
using ICSharpCode.Decompiler.DebugSteps;
using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.IL;
using ICSharpCode.Decompiler.IL.Transforms;
using ICSharpCode.ILSpy.AppEnv; using ICSharpCode.ILSpy.AppEnv;
using ICSharpCode.ILSpy.Commands; using ICSharpCode.ILSpy.Commands;

2
ILSpy/Views/DebugStepFilterConverter.cs

@ -24,7 +24,7 @@ using System.Globalization;
using Avalonia.Data.Converters; using Avalonia.Data.Converters;
using ICSharpCode.Decompiler.IL.Transforms; using ICSharpCode.Decompiler.DebugSteps;
namespace ICSharpCode.ILSpy.Views namespace ICSharpCode.ILSpy.Views
{ {

Loading…
Cancel
Save