9972 Commits
Author SHA1 Message Date
Siegfried Pammer b8a267d1ca Merge pull request #4238 from icsharpcode/fix/go-to-token-scroll
Center metadata token navigation rows
2026-10-11 22:03:14 +02:00
Siegfried Pammer 5f0b6e3af8 Merge pull request #4237 from icsharpcode/fix/show-line-numbers-default
Keep line numbers hidden by default
2026-10-11 21:31:36 +02:00
Siegfried Pammer 06bed93d76 Center metadata token navigation rows
Token navigation can select rows before the destination table has realized the target container, and DataGrid's own scroll pass can leave the row at an edge. Defer the final scroll until the row exists and center it using the row's actual scroll host.

Assisted-by: OpenCode:openai/gpt-5.5:OpenCode
2026-10-11 21:28:13 +02:00
Siegfried Pammer 4a52b14e69 Keep line numbers hidden by default
The decompiler text view should not opt into visible line numbers independently of DisplaySettings. Keep the XAML default aligned with the persisted setting so first render and loaded settings agree.

Assisted-by: OpenCode:openai/gpt-5.5:OpenCode
2026-10-11 20:22:22 +02:00
Siegfried Pammer 5c77abc24d Merge pull request #4216 from pieroviano/nav-caret-usage-highlight
Add option to highlight usages of the symbol at the caret
2026-10-11 19:51:45 +02:00
Siegfried Pammer 59735a6799 Refine caret symbol highlighting
Fold the caret highlight implementation into the text view, remove click-to-highlight behavior, and make the symbol highlight option/theme colors clearer.

Assisted-by: OpenCode:openai/gpt-5.5:OpenCode
2026-10-11 19:01:24 +02:00
pieroviano 933d4070a9 Add option to highlight usages of the symbol at the caret
While the caret rests on a symbol in the code view, every occurrence of it in the document is marked, so reading a method no longer needs a click per symbol. Off by default and independent of the click highlight's marks.
2026-10-11 18:20:56 +02:00
Siegfried Pammer 65182b892a Merge pull request #4228 from LiquidSnake0/fix-4153-foreach-named-argument
Fix #4153: keep foreach when a named argument inlines the loop variable
2026-10-11 17:11:14 +02:00
Siegfried Pammer d006606987 Merge pull request #4236 from icsharpcode/empty-with-expressions
Transform `value.<Clone>$()` into `value with {}` even if there are no initializers
2026-10-11 16:46:14 +02:00
Siegfried Pammer 2e27fe8ac2 Merge pull request #4235 from icsharpcode/fix/nugetfuzz-refpack-fallback
Fallback from unsupported netcoreapp assets in nugetfuzz
2026-10-11 16:43:52 +02:00
Daniel Grunwald 08b25e7fdc Merge pull request #4232 from dualfroz/fix/4137-conditional-native-int
Fix #4137: Keep nint in conditionals with mixed native int branches
2026-10-11 15:54:59 +02:00
Siegfried Pammer 3982c608dd Apply review suggestions/fixes. 2026-10-11 15:39:37 +02:00
Siegfried Pammer d74e246e90 Fallback from unsupported netcoreapp assets in nugetfuzz
When a package only offers a newer netcoreapp asset than the available stable Microsoft.NETCore.App.Ref package, nugetfuzz cannot build a reference set for that asset. Prefer the net48 asset when one is available so the package can still be included in a corpus sweep.

Assisted-by: OpenCode:openai/gpt-5.5:OpenCode
2026-10-11 14:20:24 +02:00
Siegfried Pammer b4ebe5c79e Merge pull request #4230 from icsharpcode/tools/alloc-profile
Add allocation trace viewer
2026-10-11 14:17:18 +02:00
Daniel Grunwald fa863a8229 #4019: transform value.<Clone>$() into value with {} even if there are no initializers.
This commit also changes the ILAst representation of with-initializers to preserve the `<Clone>$()` call, so that BlockKind.WithInitializer only forces the block into a certain shape, without assigning its special semantics.
2026-10-11 14:05:15 +02:00
Christoph Wille 6a5ecc5641 Merge pull request #3963 from icsharpcode/substring-optimizations
Substring optimizations: eliminate hot-path allocations (audit top 8)
2026-10-11 12:48:01 +02:00
Siegfried Pammer ce59ef5703 Document allocview as a standalone recipe
The tool is only useful if someone can run it from the docs alone, and the
README entry left out the parts that decide whether the attempt works: the
POSIX form of the environment variables, that the trace appears only when the
process exits, which speedscope tab to open, and that the numbers are samples
rather than a measurement.

The README keeps a pointer instead of a second copy, so the two cannot drift.

Assisted-by: Claude:claude-opus-5:Claude Code
2026-10-10 22:42:57 +02:00
Siegfried Pammer 9b993de1ce Add allocview: an allocation flamegraph for a decompiler run
Nothing showed where a decompiler run's bytes go. The runtime already writes
a trace on request - DOTNET_EnableEventPipe needs no tool and no code in
ilspycmd or the UI - and PerfView reads it, but only on Windows, and neither
knows that ICSharpCode.Decompiler.IL is a different concern from
ICSharpCode.Decompiler.TypeSystem.

So the tool is a converter, not a profiler: it turns GCAllocationTick events
into a byte-weighted speedscope profile, which is the part the stock
conversion does not do - dotnet-trace's speedscope output is weighted by
time - and groups the same bytes by area of the decompiler. An allocation is
placed by the namespace its type name mentions, and when the runtime reports
a nested or compiler-generated type without one, by the innermost decompiler
frame on its stack, which names the code that wanted the object rather than
the object.

Assisted-by: Claude:claude-opus-5:Claude Code
2026-10-10 22:42:57 +02:00
Siegfried Pammer 8e50cadcc7 Make debug step recording opt in
Non-UI decompilation runs do not consume debug steps, but debug builds still allocated steppers and step nodes unless each context opted out manually. Keep step contexts empty unless the caller enables RecordSteps, while preserving the shared stepper path for the UI debug-step view.

Assisted-by: OpenCode:openai/gpt-5.5:OpenCode
2026-10-10 21:18:52 +02:00
Siegfried Pammer d4d430ebe4 Avoid debug assert message allocations
Debug assertions should not allocate formatted messages on successful checks. Route interpolated assertion messages through a DEBUG-only handler so the message is built only when the assertion fails.

Assisted-by: OpenCode:openai/gpt-5.5:OpenCode
2026-10-10 18:34:40 +02:00
Christoph Wille 4509babe22 Gate the explicit-impl operator check before allocating the short name
The MetadataMethod constructor cut the post-dot short name for every
static non-generic method whose name contains a dot, only to test it
for an op_ prefix that almost never matches. The prefix is now checked
on a span slice first, so the substring (still required by
OperatorDeclaration.GetOperatorType) is allocated only for actual
explicit-interface operator implementations.

Assisted-by: Claude:claude-fable-5:Claude Code
2026-10-10 18:30:53 +02:00
Christoph Wille 0c5425bdc7 Write IL-with-C# comment slices as spans
The "IL with C#" view cut up to four substrings (prefix, trimmed
prefix, highlighted range, suffix) out of every source line emitted
alongside an IL instruction. ISmartTextOutput now accepts a
ReadOnlySpan<char> - as a default interface method falling back to
Write(text.ToString()) so existing implementers keep working - and
AvaloniaEditTextOutput appends the span straight into its
StringBuilder. The overload lives on ISmartTextOutput rather than
ITextOutput because the latter is netstandard2.0 (no default interface
methods there), where a new member would break every external
implementer of the decompiler library; the highlighted-comment path is
typed against ISmartTextOutput already.

Assisted-by: Claude:claude-fable-5:Claude Code
2026-10-10 18:30:53 +02:00
Christoph Wille ba78e3cf27 Thread spans through the CleanUpVariableName chain
CleanUpVariableName sits on the per-variable naming path of every
decompiled method and allocated up to three intermediates (backtick
cut, m_/_ prefix strip, lowercase-first concat) before producing its
result, and its callers added further throwaway substrings when
stripping get_/set_/Get/Set and interface-I prefixes. The cuts are now
slices over the original name: ContainsNonPrintableIdentifierChar and
IsValidName gained span overloads, and only the final lowered name is
materialized, in a single allocation via char[] (netstandard2.0 has no
string(span) constructor). IsKeyword keeps its string parameter - it
is called on that final string anyway, and the keyword HashSet has no
span lookup on netstandard2.0.

Assisted-by: Claude:claude-fable-5:Claude Code
2026-10-10 18:30:53 +02:00
Christoph Wille 519ed6e98c Skip the StringBuilder in EscapeIdentifier when nothing needs escaping
EscapeIdentifier runs for every identifier token emitted, yet it built
a StringBuilder plus a fresh result string even for the overwhelmingly
common case of an identifier with no escapable characters. A pre-scan
now returns the original instance untouched in that case, and the
surrogate-pair copy appends the two chars directly instead of cutting
a two-char substring. New unit tests pin the escaping behavior and the
identity fast path.

Assisted-by: Claude:claude-fable-5:Claude Code
2026-10-10 18:30:53 +02:00
Christoph Wille 298bf6160c Render shortcut-form IL opcodes from fixed lookup tables
WriteOpCode allocated two strings per rendered ldarg.N/ldloc.N/stloc.N
instruction (the digit cut off the mnemonic plus the concatenated
local-reference key), even though the shortcut forms only ever produce
the indices 0-3. The digit text and the param_/loc_ reference keys now
come from static tables indexed by opcode arithmetic.

Assisted-by: Claude:claude-fable-5:Claude Code
2026-10-10 18:30:53 +02:00
Christoph Wille 9125b196d4 Fuse the reflection-name and arity parse to cut each string once
SplitTypeParameterCountFromReflectionName allocated the digits after
the backtick only to feed int.TryParse and discard them, and the
TopLevelTypeName constructor cut the name part twice for generic
types (once at the dot, again at the backtick), dropping the first
cut. Both run for every generic reflection name parsed, e.g. for
typeof-valued attribute arguments and string-switch metadata. The
arity is now parsed in place with a digit loop (netstandard2.0 has
no span int.TryParse) and each final string is cut exactly once.

The digit loop only accepts plain ASCII digits, so suffixes like
`+1 that int.TryParse tolerated are now rejected; such names are not
legal reflection names. New unit tests pin the parse edge cases.

Assisted-by: Claude:claude-fable-5:Claude Code
2026-10-10 18:30:53 +02:00
Christoph Wille 1699b119c1 Prepare search terms once per run instead of per candidate name
AbstractSearchStrategy.IsMatch runs for every metadata row of every
loaded module on each keystroke, yet it re-stripped the operator prefix
per name and, for fuzzy terms, additionally lowercased both the name
and the term - the largest allocation source in interactive search.
The terms are invariant for the lifetime of a strategy (each keystroke
builds a new request and strategy), so the stripping and lowercasing
now happen once in the constructor, and the noncontiguous matcher
compares spans with per-char ToLowerInvariant. This switches fuzzy
matching from culture-sensitive to invariant lowercasing, which is the
appropriate semantic for matching metadata names.

The new IsMatchTests pin the +, -, =, ~ operator semantics (including
the pre-existing quirk that =Name compares against the backtick-
suffixed name of generic types) as observed before the change.

Assisted-by: Claude:claude-fable-5:Claude Code
2026-10-10 18:30:53 +02:00
Christoph Wille 2c80eed848 Parse the digit suffix inline in AssignVariableNames.SplitName
The loop preceding the parse has already proven that the tail consists
solely of ASCII digits, so the Substring+int.TryParse pair only
re-validated them at the cost of a throwaway string allocation.
SplitName runs per variable and per reserved-name registration for
every decompiled method, making this one of the hottest Substring call
sites in the decompiler. Accumulating the digits inline keeps the
TryParse overflow semantics (fall back to number=1 and the unchanged
name) without allocating.

Assisted-by: Claude:claude-fable-5:Claude Code
2026-10-10 18:30:53 +02:00
Siegfried Pammer 3491b15e6b Merge pull request #4229 from icsharpcode/avoid-unnecessary-settings-clone
Avoid cloning settings for every method
2026-10-10 15:49:12 +02:00
Siegfried Pammer 098de864b9 Avoid cloning settings for every method
Most methods use the decompiler settings read-only. Clone only for the WinForms InitializeComponent special case that temporarily overrides settings before running the method pipeline.

Assisted-by: OpenCode:openai/gpt-5.5:OpenCode
2026-10-10 15:15:52 +02:00
Siegfried Pammer c3da4a8358 Merge pull request #4225 from icsharpcode/fix/continue-leave-in-catch
Fix #2829: Emit continue for leave through try-finally
2026-10-10 14:59:21 +02:00
Siegfried Pammer 803d44e07f Make continue-in-catch test match issue shape
The regression only needs the nested catch inside an enclosing handler from #2829. Removing the explicit try/finally keeps the fixture focused on the failing control-flow shape instead of compiler-generated cleanup details.

Assisted-by: OpenCode:openai/gpt-5.5:OpenCode
2026-10-10 14:31:14 +02:00
DualFroz 77a02d16fe Fix #4137: Keep nint in conditionals with mixed native int branches
When the branches of a conditional have different types, its type falls
back to the IL stack slot type, which is (U)IntPtr. IntroduceNativeIntTypeOnLocals
only retypes variables, so with native integers enabled the branches got
cast to IntPtr. Map (U)IntPtr to n(u)int the same way boxing does; pointer
types are unaffected.
2026-10-10 13:50:52 +02:00
Siegfried Pammer d77238a4b6 Merge pull request #4227 from icsharpcode/christophwille/magick-net
Replace ImageSharp with Magick.NET in the fixture generator
2026-10-10 11:12:06 +02:00
Selim Selimi 40b1691a91 Fix #4153: keep foreach when a named argument inlines the loop variable
Inlining may extend a named-argument block: CanExtendNamedArgument moves an
expression into the first named-argument store (or the second one for an
instance call) and checks reordering against the stored values. The reverse
move was refused: Block.CanInlineIntoSlot rejected every slot of a
CallWithNamedArgs block, and CanMoveInto checked reordering against the whole
store instruction, which counts as a write to a local. The foreach pattern
relies on CanUninline to pull the Current getter back out of the loop body's
first statement, so once the getter had been inlined into a named-argument
block the loop was emitted as using + while (MoveNext()). Both checks now
mirror what inlining allows, so an expression inlined into a named-argument
block can always be un-inlined again.

Assisted-by: Claude:claude-fable-5-1:Claude Code
2026-10-09 16:38:08 +02:00
Christoph Wille 3252a4087b Replace ImageSharp with Magick.NET in the fixture generator
ImageSharp 4.x / ImageSharp.Drawing 3.x add a build-time license check
that fails Release builds without a Six Labors license key, and the
older 3.x / 2.x line is the last one without it. Magick.NET is Apache
2.0, bundles the native ImageMagick library for win/linux/osx, and
covers every encoder the fixtures need (PNG/BMP/JPG/GIF), so the
generator moves to it instead of carrying a license key.

The checked-in binary fixtures are regenerated with the new encoders;
the rendered shape (square, circle, diagonal stripe, border) is the
same, only the encoded bytes differ.

Assisted-by: Claude:claude-fable-5-1:Claude Code
2026-10-09 09:54:27 +02:00
Christoph Wille ad2cc62293 Merge pull request #4221 from icsharpcode/christophwille/mt
-mt runs a build's projects concurrently inside a single MSBuild process
2026-10-09 09:32:57 +02:00
Christoph Wille 14577b99c9 Merge pull request #4149 from icsharpcode/fix/4143-taskbar-icon
Re-send the window icon after a theme flip or resume
2026-10-09 09:31:34 +02:00
Siegfried Pammer 04a0b6f1d0 Fix #2829: Emit continue for leave through try-finally
A catch inside an enclosing EH region can leave to a try body that immediately branches to the loop continuation. Treat that leave as a continue so decompilation does not expose the synthetic end label for the catch path.

Assisted-by: OpenCode:openai/gpt-5.5:OpenCode
2026-10-08 23:07:27 +02:00
Siegfried Pammer df79031e93 Merge pull request #4223 from icsharpcode/fix/2744-resx-resource-writer
Fix ResXResourceWriter empty output and type names
2026-10-08 20:57:31 +02:00
Siegfried Pammer aa18f6c075 Merge pull request #4222 from icsharpcode/fix/2199-vb-closure
Fix/2199 vb closure
2026-10-08 20:55:56 +02:00
Siegfried Pammer 73ec67e2af Allow configuring ResX assembly names
Resource export sometimes needs framework-specific ResX type names. Exposing the WinForms and mscorlib assembly suffixes keeps the writer default-compatible while letting callers choose older resource type names when needed.

Assisted-by: OpenCode:openai/gpt-5.5:OpenCode
2026-10-08 19:28:07 +02:00
Siegfried Pammer 6b43671564 Write empty ResX files on dispose
ResX export should still produce a valid file when no resources were added and the writer is finalized through Close or Dispose. Calling Generate from Close preserves creation timing while matching the empty-file behavior.

Assisted-by: OpenCode:openai/gpt-5.5:OpenCode
2026-10-08 19:26:44 +02:00
Siegfried Pammer 565e1a3442 Handle legacy VB delegate wrappers
VB can lower event-handler closures through generated wrapper methods, closure copy constructors, and immediately invoked VB anonymous delegate helpers. Recognizing and unwrapping those shapes leaves the expected lambda form and lets captured locals be scalar-replaced instead of exposing generated closure artifacts.

Assisted-by: OpenCode:openai/gpt-5.5:OpenCode
2026-10-08 17:52:13 +02:00
Siegfried Pammer b379738b7f Merge pull request #4218 from icsharpcode/christophwille/avalonia-followups-4042
Fix the Avalonia follow-ups from #4042
2026-10-08 17:25:43 +02:00
Siegfried Pammer accbd3eda0 Merge pull request #4220 from icsharpcode/fix/3180-nullprop-extension-method
Fix #3180: avoid invalid null-conditional extension calls
2026-10-07 20:15:07 +02:00
Christoph Wille 6d6b2a0b77 -mt runs a build's projects concurrently inside a single MSBuild process https://github.com/dotnet/core/blob/main/release-notes/11.0/preview/preview7/msbuild.md#multithreaded-mode-progress 2026-10-07 14:27:01 +02:00
Siegfried Pammer e5d6104c9f Tighten null-propagation extension receiver check
Avoid reusing a temporary receiver's stored value for extension-method lookup when its inferred source type no longer matches the local type.

Assisted-by: OpenCode:openai/gpt-5.5:OpenCode
2026-10-07 07:22:12 +02:00
Siegfried Pammer 7a56e5d9e2 Merge pull request #4217 from icsharpcode/fix/dynamic-constructor-token-references
Improve dynamic reference tooltips
2026-10-06 22:39:56 +02:00
Christoph Wille fa6b5f3b26 Let the editor gutter margins react to the left button only
AvaloniaEdit's line-number margin moves the caret and selects the line,
and a fold marker toggles its fold, on any pointer button; the WPF
margins only reacted to the left button. A middle click opens a
reference in a new tab over the text, so one that lands on the gutter
should do nothing instead of folding code or moving the caret. The text
area itself still sees every button.

#4042

Assisted-by: Claude:claude-fable-5-1:Claude Code
2026-10-06 08:24:20 +02:00