Metadata explorer cleanup, flags-filter fixes, and WPF row-details parity - #3759
Merged
siegfriedpammer merged 12 commits intoJun 10, 2026
Merged
Conversation
siegfriedpammer
force-pushed
the
ILSpy-cleaning
branch
from
June 10, 2026 16:00
7e97e4e to
b382a3e
Compare
All six member-level Decompile* entry points wired the same assembly resolver and debug info onto a fresh disassembler before dispatching; the module-aware CreateDisassembler overload now owns that setup. Assisted-by: Claude:claude-fable-5[1m]:Claude Code
All six entity nodes repeated the same registry walk in LoadChildren; AddAnalyzerChildren gives analyzer registration a single home, with subclasses adding their entity-specific rows (accessors, backing fields) before calling it. Assisted-by: Claude:claude-fable-5[1m]:Claude Code
The pane carried four hand-rolled try/catch export lookups that predate AppComposition.TryGetExport and were missed when the other call sites were converted. Assisted-by: Claude:claude-fable-5[1m]:Claude Code
The #Strings/#US/#Blob nodes each open-coded the same offset-chained walk, whose subtlety (handle 0 is a real entry despite being the nil handle) is now documented once in WalkHeap. The TypeDef and ExportedType entries built byte-identical TypeAttributes flag-group tooltips; FlagsTooltip.ForTypeAttributes is the shared factory. Assisted-by: Claude:claude-fable-5[1m]:Claude Code
0x00200000 is the ECMA-335 Forwarder bit (II.23.1.15), set on ExportedType rows that forward a type to another assembly. System.Reflection.TypeAttributes has no member for it, so the enum-driven flag enumeration left it invisible in the Flags group. Assisted-by: Claude:claude-fable-5[1m]:Claude Code
The blob was tracked 100644, so every fresh checkout (and any branch switch in a worktree hosting the shared .git/hooks symlink target) produces a non-executable hook script, which git then silently skips with only an 'ignoredHook' hint. With core.fileMode=false on this clone a plain chmod is git-invisible; the mode has to be fixed in the index. Assisted-by: Claude:claude-fable-5[1m]:Claude Code
siegfriedpammer
force-pushed
the
ILSpy-cleaning
branch
from
June 10, 2026 17:24
b382a3e to
c6c732d
Compare
The schema inferer only walks the enum's declared fields, so the ECMA-335 Forwarder bit (0x00200000, no member in System.Reflection.TypeAttributes) was invisible to the per-column flags filter even though the cell tooltip already shows it. An explicit per-enum extras table appends it as an independent flag, making forwarders filterable on the ExportedType table. Assisted-by: Claude:claude-fable-5[1m]:Claude Code
ECMA-335 names the zero state of most mutually exclusive sub-ranges (NotPublic, AutoLayout, Class, AnsiClass, PrivateScope, ReuseSlot, ...) and the reflection enums declare those members, but the schema inferer skipped zero-valued fields and synthesised a generic '(none)' entry instead. Zero fits every mask, so the member is attributed to a group by declaration order: ECMA-335 and the reflection enums declare each mask directly followed by its members. Filter persistence stores numeric values, so saved filters are unaffected by the label change. Assisted-by: Claude:claude-fable-5[1m]:Claude Code
Clicking inside the open flags filter popup could act on the DataGridColumnHeader underneath: unhandled press/release pairs bubbled out of the popup into the header (which treats them as a sort click and flashes its pressed visual), and on X11 overlay popups the light-dismiss machinery could even re-target a click's press directly at the header while the popup stayed open. The filter UI is now hosted in a Flyout attached to the funnel icon instead of a hand-rolled Popup parked in the header's panel. Avalonia positions Popup as the low-level primitive and recommends Flyout for attached pickers: light dismiss, Escape-to-close, focus handling, and theme-correct presenter chrome come built in (the old hard-coded white background was also wrong in dark mode). The flyout content swallows unhandled wheel and press/release events, since its internal popup is still logically parented to the funnel inside the header. A headless end-to-end regression test drives a real DataGrid with overlay popup hosts (the X11 OverlayPopups=true configuration the app runs with) and raw input through the funnel and every visible flyout control, asserting the header never sorts and never receives an unhandled press or release. Assisted-by: Claude:claude-fable-5[1m]:Claude Code
siegfriedpammer
force-pushed
the
ILSpy-cleaning
branch
from
June 10, 2026 20:28
0826e57 to
8056ce5
Compare
WPF ILSpy used DataGrid row details in three places: the COFF and Optional header views (flag-bit breakdown of the Characteristics / DLL Characteristics words, permanently expanded) and the CustomDebugInformation table (decoded Value blob, visible when selected). The Avalonia port already carried the data — both header nodes populate Entry.RowDetails and the column builder skips it — but nothing displayed it. ProDataGrid builds the row-details template once per details element and recycles the built control across rows, swapping only the DataContext, so a WPF-style template selector would go stale; MetadataRowDetailsControl re-runs its content factory on every DataContext change instead. Per-row initial visibility has no SetDetailsVisibilityForItem equivalent and recycling resets AreDetailsVisible, so the view re-derives it in LoadingRow. Row activation now ignores gestures originating inside the details presenter — selecting text in a details blob must not navigate away. This lands the infrastructure plus the two header consumers; the CustomDebugInformation details build on the EmbeddedSource decoding work and follow with it. ConfigurePage on MetadataTableTreeNode is the hook that consumer overrides. Assisted-by: Claude:claude-fable-5[1m]:Claude Code
Selecting a row in the CustomDebugInformation table now previews its Value blob beneath it, completing WPF row-details parity: state-machine hoisted local scopes, compilation options, metadata references, and tuple element names parse into typed sub-grid rows; source-link JSON shows as text; everything else — unrecognized kinds, embedded source, and malformed structured blobs — degrades to a hex dump, as in WPF. The parser switches on the Kind GUID directly via KnownGuids; the entry deliberately carries no decoded-kind state, leaving that to the kind-column work that builds on this. Assisted-by: Claude:claude-fable-5[1m]:Claude Code
WPF showed a row Offset column and decoded the Kind GUID into one cell combining heap offset, friendly name, and raw GUID; the Avalonia table only carried the bare heap offsets. Offset follows the GetRowOffset convention every other table uses. Kind is plain text rather than a hex column plus tooltip so the per-column filter matches the friendly names. Assisted-by: Claude:claude-fable-5[1m]:Claude Code
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What started as a cleanup sweep over the Avalonia UI grew into a set of metadata-viewer improvements. Grouped by theme:
Cleanup / deduplication
Decompile*entry points repeated the same resolver/debug-info wiring; a module-awareCreateDisassembleroverload now owns that setup.Analyzed*TreeNodeclasses carried verbatim copies of the analyzer-registry walk inLoadChildren; hoisted intoAnalyzerEntityTreeNode.AddAnalyzerChildren.AppComposition.TryGetExporthelper they predated.WalkHeap, documenting the handle-0 subtlety once); TypeDef and ExportedType built byte-identical TypeAttributes flag-group tooltips (nowFlagsTooltip.ForTypeAttributes).Flags tooltips & filters
System.Reflection.TypeAttributes) is now visible in TypeAttributes tooltips and filterable in the per-column flags filter (IsTypeForwarder).NotPublic,AutoLayout,Class,AnsiClass,PrivateScope, ...) instead of a synthesized "(none)", attributed by declaration order (the layout ECMA-335 and the reflection enums share). Saved filters are unaffected — persistence stores numeric values.Filter popup fixes
Flyout(per Avalonia guidance: built-in light dismiss, Escape, focus handling, theme-correct chrome — the old hard-coded white background was also wrong in dark mode), with the content swallowing unhandled wheel/press/release events. A headless end-to-end regression test drives a real DataGrid with overlay popup hosts and raw input through every flyout control.Row details (WPF parity)
MetadataRowDetailsControl(a DataContext-reactive shell, since ProDataGrid recycles built details content across rows), page-model/view wiring, and aConfigurePagehook onMetadataTableTreeNode.Offsetcolumn and the decodedKindcell (heap offset + friendly name + GUID).Infrastructure
BuildTools/pre-commitis now tracked executable (mode 100755) — previously every fresh checkout produced a non-executable hook that git silently skipped.🤖 Generated with Claude Code