ILSpy resolves an assembly's references against the target framework it
detects from the TargetFrameworkAttribute. When that attribute is missing,
wrong, or the user wants to force a different framework, there was no way to
hint the correct one, so references could resolve against the wrong runtime
pack or framework directory.
A LoadedAssembly can now carry a TargetFrameworkIdOverride that short-circuits
detection (it is the single value every LoadedAssembly-based resolution path
reads), is persisted in the assembly-list XML, and is carried across a reload
so a runtime change re-resolves against the new framework. The "Set Target
Framework" context-menu entry edits it through a dialog with a free-form text
box and an always-visible list of common monikers to pick from (the app forces
overlay popups, so a dropdown would be clamped inside the small dialog); input
is validated and converted from the short TFM users know (net48) to the long
FrameworkName form the resolver consumes (.NETFramework,Version=v4.8) via
NuGet. The direct DetectTargetFrameworkId callers in the decompiler core
(project export, language version) intentionally keep reading the real
attribute; only reference resolution is overridden.
Resurrects a 2020 prototype (branch tfmoverride) re-implemented against the
current ILSpyX/Avalonia code, whose surrounding structures no longer matched.
Assisted-by: Claude:claude-opus-4-8:Claude Code
Introduce a Roslyn source generator that emits the visitor boilerplate for
the C# AST from [DecompilerAstNode]-tagged node declarations: the
IAstVisitor interface, the AcceptVisitor overloads, the pattern-placeholder
nodes, and the initial DoMatch support. AccessorKind lets an accessor's
keyword be chosen independently of its role, an early step toward shedding
the NRefactory role model.
The Microsoft.DiaSymReader / .PortablePdb packages pull NETStandard.Library
1.6.1, which drags in 4.3.0 builds of System.Net.Http, System.Private.Uri
and System.Text.RegularExpressions, all carrying known advisories (NU1902/
NU1903). They are framework-provided at runtime on net10.0; pin the patched
4.3.4 / 4.3.2 / 4.3.1 via central transitive pinning so NuGet audit passes.
Enabling transitive pinning also aligns a handful of other transitive
packages to their declared central versions.
Assisted-by: Claude:claude-opus-4-8:Claude Code
The Pdb2Xml command (ILSpy) and the PDB round-trip tests (Decompiler.Tests)
reference Microsoft.DiaSymReader*, previously gated on the build host being
Windows. That made dotnet restore resolve a different graph on Windows than
on Linux -- the packages (and their transitive tail: DiaSymReader.PortablePdb,
the legacy NETCore.Platforms/Win32 packages) appear only on Windows, and
DiaSymReader.Native flips between Direct and CentralTransitive. So a checkout
could not be developed across OSes without the committed packages.lock.json
churning on every Windows restore.
Drop the OS gate (keep Debug-only) so the restored graph, and the committed
lock, are identical on every OS. The consuming code is still gated by DEBUG
and WINDOWS, so on non-Windows the packages are restored but never compiled
in; the native asset only resolves for win-* RIDs.
The "Verify package contents" step (which checks the committed *.filelist
snapshots still match the built VSIX/MSI contents) had been excluding
packages.lock.json from its git diff to tolerate that per-OS churn. With the
locks now identical across OSes the carve-out is unnecessary, so it goes back
to a plain git diff --exit-code.
Assisted-by: Claude:claude-opus-4-8:Claude Code
ICSharpCode.Decompiler.Tests builds for the host RID (so the native
Microsoft.DiaSymReader.Native assets the Windows PDB tests need are copied),
which made its packages.lock.json host-specific (linux-x64 vs win-x64) and
drift between Linux and Windows restores. Add an explicit <RuntimeIdentifiers>
list next to the single <RuntimeIdentifier>: the lock then records every RID
host-independently while the build still targets the host RID. Apply the same
list to the TestRunner it pulls in.
TestRunner's lock is now fully portable. The test project's lock still carries
the OS-conditional DiaSymReader.Native difference in its base graph, which the
*.filelist-scoped "Verify package contents" step already tolerates.
Assisted-by: Claude:claude-opus-4-8:Claude Code
Replace the Windows-only .bat helpers (clean / debugbuild / releasebuild /
restore / updatedeps and BuildTools/format) with cross-platform pwsh
scripts at the repo root: restore.ps1, build.ps1 (-Configuration), clean.ps1,
updatedeps.ps1 and BuildTools/format.ps1, alongside the existing publish.ps1.
Enable a packages.lock.json for every project by hoisting
RestorePackagesWithLockFile into the root Directory.Build.props (the four
core libraries set it individually before) and commit the generated locks,
so restores are repeatable and CI can cache packages off them.
Cache the NuGet packages folder in the three setup-dotnet workflows
(build-ilspy, build-frontends, codeql-analysis), keyed on the lock files
per the setup-dotnet caching guidance.
Scope the Debug "Verify package contents" check to the *.filelist outputs
it actually generates. A project's packages.lock.json is keyed only by
(framework, RID), with no host-OS axis, so a lock produced on Linux
legitimately differs from one produced on Windows whenever an OS-conditional
PackageReference applies (Debug+Windows pulls Microsoft.DiaSymReader*). The
Windows restore then rewrites those locks; that churn must not fail a step
whose job is to police the VSIX/MSI file lists.
Also drop the dead ILSpy.BamlDecompiler publish line from
publishlocaldev.ps1, mirroring the earlier publish.ps1 fix.
Assisted-by: Claude:claude-opus-4-8:Claude Code