Browse Source

Route heap nodes onto the datagrid tab

The grid view's row-virtualisation handles the full heap (CoreLib's
truncation-footer wording are gone. Existing heap tests are rewritten
to assert against MetadataTablePageModel; the obsolete
MetadataHeapTreeNode.PreviewLimit constant follows the cap into the
deleted set.

Assisted-by: Claude:claude-opus-4-7:Claude Code
pull/3755/head
Siegfried Pammer 2 months ago
parent
commit
43b958e2a4
  1. 48
      ILSpy.Tests/Metadata/HeapTreeTests.cs
  2. 20
      ILSpy/Metadata/BlobHeapTreeNode.cs
  3. 20
      ILSpy/Metadata/GuidHeapTreeNode.cs
  4. 2
      ILSpy/Metadata/MetadataHeapTreeNode.cs
  5. 20
      ILSpy/Metadata/StringHeapTreeNode.cs
  6. 20
      ILSpy/Metadata/UserStringHeapTreeNode.cs

48
ILSpy.Tests/Metadata/HeapTreeTests.cs

@ -63,12 +63,11 @@ public class HeapTreeTests @@ -63,12 +63,11 @@ public class HeapTreeTests
}
[AvaloniaTest]
public async Task StringHeapTreeNode_Decompiles_Header_Plus_Preview_Of_Entries()
public async Task StringHeapTreeNode_Opens_Grid_Tab_With_Offset_Length_Value_Columns()
{
// CoreLib's #Strings holds tens of thousands of entries; rendering all of them as
// text would bloat the tab and bog down the editor. Phase 1 dumps a capped preview
// (header + first N rows + truncation footer) so the user can scan the start of the
// heap; Phase 2 swaps to a virtualising DataGrid for the full set.
// CoreLib's #Strings holds tens of thousands of entries; the DataGrid surfaces every
// row through Avalonia's built-in row virtualisation, so unlike the Phase 1 text
// dump there's no preview cap and no truncation footer.
var window = AppComposition.Current.GetExport<MainWindow>();
window.Show();
@ -83,19 +82,16 @@ public class HeapTreeTests @@ -83,19 +82,16 @@ public class HeapTreeTests
var heapNode = metadataNode.Children.OfType<StringHeapTreeNode>().Single();
vm.AssemblyTreeModel.SelectNode(heapNode);
var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync();
tab.Text.Should().Contain("String Heap");
tab.Text.Should().Contain("Offset");
tab.Text.Should().Contain("Length");
tab.Text.Should().Contain("Value");
tab.Text.Should().Contain("more entries", "CoreLib's #Strings is well over the preview cap");
// The preview includes a (count entries) header — should match e.g. "(24573 entries)".
tab.Text.Should().MatchRegex(@"\(\d{3,} entries\)");
var tab = await vm.DockWorkspace.WaitForMetadataTabAsync();
tab.Title.Should().Be("String Heap");
tab.Columns.Select(c => c.Header.ToString()).Should().Equal("Offset", "Length", "Value");
// CoreLib's #Strings is well over a thousand entries; the grid handles them all.
tab.Items.Should().HaveCountGreaterThan(1000);
}
[AvaloniaTest]
public async Task GuidHeapTreeNode_Decompiles_Header_Plus_Index_Length_Value_Columns()
public async Task GuidHeapTreeNode_Opens_Grid_Tab_With_Index_Length_Value_Columns()
{
var window = AppComposition.Current.GetExport<MainWindow>();
window.Show();
@ -110,17 +106,17 @@ public class HeapTreeTests @@ -110,17 +106,17 @@ public class HeapTreeTests
var heapNode = metadataNode.Children.OfType<GuidHeapTreeNode>().Single();
vm.AssemblyTreeModel.SelectNode(heapNode);
var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync();
var tab = await vm.DockWorkspace.WaitForMetadataTabAsync();
tab.Text.Should().Contain("Guid Heap");
tab.Text.Should().Contain("Index");
tab.Text.Should().Contain("Length");
tab.Text.Should().MatchRegex(@"\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b",
"the GUID heap should contain at least one parsed GUID");
tab.Title.Should().Be("Guid Heap");
tab.Columns.Select(c => c.Header.ToString()).Should().Equal("Index", "Length", "Value");
// Each entry's Value is the parsed GUID's lowercase canonical form.
var first = (GuidHeapTreeNode.GuidHeapEntry)tab.Items[0];
first.Value.Should().MatchRegex(@"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$");
}
[AvaloniaTest]
public async Task BlobHeapTreeNode_Decompiles_Header_Plus_Preview_Of_Hex_Encoded_Entries()
public async Task BlobHeapTreeNode_Opens_Grid_Tab_With_Offset_Length_Value_Columns()
{
var window = AppComposition.Current.GetExport<MainWindow>();
window.Show();
@ -135,11 +131,9 @@ public class HeapTreeTests @@ -135,11 +131,9 @@ public class HeapTreeTests
var heapNode = metadataNode.Children.OfType<BlobHeapTreeNode>().Single();
vm.AssemblyTreeModel.SelectNode(heapNode);
var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync();
var tab = await vm.DockWorkspace.WaitForMetadataTabAsync();
tab.Text.Should().Contain("Blob Heap");
tab.Text.Should().Contain("Offset");
tab.Text.Should().Contain("Length");
tab.Text.Should().Contain("Value");
tab.Title.Should().Be("Blob Heap");
tab.Columns.Select(c => c.Header.ToString()).Should().Equal("Offset", "Length", "Value");
}
}

20
ILSpy/Metadata/BlobHeapTreeNode.cs

@ -17,13 +17,13 @@ @@ -17,13 +17,13 @@
// DEALINGS IN THE SOFTWARE.
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Metadata;
using ILSpy.Languages;
using ILSpy.ViewModels;
namespace ILSpy.Metadata
{
@ -45,17 +45,11 @@ namespace ILSpy.Metadata @@ -45,17 +45,11 @@ namespace ILSpy.Metadata
public override object Text => $"Blob Heap ({EnsureEntries().Count})";
public override string ToString() => "Blob Heap";
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{
var all = EnsureEntries();
var preview = all.Count > PreviewLimit
? (IReadOnlyList<BlobHeapEntry>)all.GetRange(0, PreviewLimit)
: all;
language.WriteCommentLine(output, $"Blob Heap ({all.Count} entries)");
MetadataTextWriter.WriteTable(language, output, preview);
if (all.Count > preview.Count)
language.WriteCommentLine(output, $"... ({all.Count - preview.Count} more entries; full view ships with the metadata DataGrid in a future release)");
}
public override TabPageModel CreateTab() => new MetadataTablePageModel {
Title = "Blob Heap",
Items = EnsureEntries().Cast<object>().ToList(),
Columns = MetadataColumnBuilder.For<BlobHeapEntry>(),
};
List<BlobHeapEntry> EnsureEntries()
{

20
ILSpy/Metadata/GuidHeapTreeNode.cs

@ -17,13 +17,13 @@ @@ -17,13 +17,13 @@
// DEALINGS IN THE SOFTWARE.
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Metadata;
using ILSpy.Languages;
using ILSpy.ViewModels;
namespace ILSpy.Metadata
{
@ -44,17 +44,11 @@ namespace ILSpy.Metadata @@ -44,17 +44,11 @@ namespace ILSpy.Metadata
public override object Text => $"Guid Heap ({EnsureEntries().Count})";
public override string ToString() => "Guid Heap";
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{
var all = EnsureEntries();
var preview = all.Count > PreviewLimit
? (IReadOnlyList<GuidHeapEntry>)all.GetRange(0, PreviewLimit)
: all;
language.WriteCommentLine(output, $"Guid Heap ({all.Count} entries)");
MetadataTextWriter.WriteTable(language, output, preview);
if (all.Count > preview.Count)
language.WriteCommentLine(output, $"... ({all.Count - preview.Count} more entries; full view ships with the metadata DataGrid in a future release)");
}
public override TabPageModel CreateTab() => new MetadataTablePageModel {
Title = "Guid Heap",
Items = EnsureEntries().Cast<object>().ToList(),
Columns = MetadataColumnBuilder.For<GuidHeapEntry>(),
};
List<GuidHeapEntry> EnsureEntries()
{

2
ILSpy/Metadata/MetadataHeapTreeNode.cs

@ -34,8 +34,6 @@ namespace ILSpy.Metadata @@ -34,8 +34,6 @@ namespace ILSpy.Metadata
/// </summary>
public abstract class MetadataHeapTreeNode : ILSpyTreeNode
{
public const int PreviewLimit = 200;
protected readonly MetadataFile metadataFile;
public HandleKind Kind { get; }

20
ILSpy/Metadata/StringHeapTreeNode.cs

@ -17,13 +17,13 @@ @@ -17,13 +17,13 @@
// DEALINGS IN THE SOFTWARE.
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Metadata;
using ILSpy.Languages;
using ILSpy.ViewModels;
namespace ILSpy.Metadata
{
@ -45,17 +45,11 @@ namespace ILSpy.Metadata @@ -45,17 +45,11 @@ namespace ILSpy.Metadata
public override object Text => $"String Heap ({EnsureEntries().Count})";
public override string ToString() => "String Heap";
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{
var all = EnsureEntries();
var preview = all.Count > PreviewLimit
? (IReadOnlyList<StringHeapEntry>)all.GetRange(0, PreviewLimit)
: all;
language.WriteCommentLine(output, $"String Heap ({all.Count} entries)");
MetadataTextWriter.WriteTable(language, output, preview);
if (all.Count > preview.Count)
language.WriteCommentLine(output, $"... ({all.Count - preview.Count} more entries; full view ships with the metadata DataGrid in a future release)");
}
public override TabPageModel CreateTab() => new MetadataTablePageModel {
Title = "String Heap",
Items = EnsureEntries().Cast<object>().ToList(),
Columns = MetadataColumnBuilder.For<StringHeapEntry>(),
};
List<StringHeapEntry> EnsureEntries()
{

20
ILSpy/Metadata/UserStringHeapTreeNode.cs

@ -17,13 +17,13 @@ @@ -17,13 +17,13 @@
// DEALINGS IN THE SOFTWARE.
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Metadata;
using ILSpy.Languages;
using ILSpy.ViewModels;
namespace ILSpy.Metadata
{
@ -44,17 +44,11 @@ namespace ILSpy.Metadata @@ -44,17 +44,11 @@ namespace ILSpy.Metadata
public override object Text => $"UserString Heap ({EnsureEntries().Count})";
public override string ToString() => "UserString Heap";
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{
var all = EnsureEntries();
var preview = all.Count > PreviewLimit
? (IReadOnlyList<UserStringHeapEntry>)all.GetRange(0, PreviewLimit)
: all;
language.WriteCommentLine(output, $"UserString Heap ({all.Count} entries)");
MetadataTextWriter.WriteTable(language, output, preview);
if (all.Count > preview.Count)
language.WriteCommentLine(output, $"... ({all.Count - preview.Count} more entries; full view ships with the metadata DataGrid in a future release)");
}
public override TabPageModel CreateTab() => new MetadataTablePageModel {
Title = "UserString Heap",
Items = EnsureEntries().Cast<object>().ToList(),
Columns = MetadataColumnBuilder.For<UserStringHeapEntry>(),
};
List<UserStringHeapEntry> EnsureEntries()
{

Loading…
Cancel
Save