A field, auto-property, or event initializer is written once at its
declaration, but in IL it runs in every instance constructor that does not
chain to this(...) (and static initializers run in the static constructor).
The decompiler lifts the initializer from a single constructor, so its
breakpoint was emitted only there and the other constructors had none.
Two causes are addressed:
- The lift discarded the initializer's copies in the other constructors.
They are now kept on MemberInitializerInOtherConstructorsAnnotation and
replayed by SequencePointBuilder, mapping the same source location onto
each constructor's IL.
- PortablePdbWriter only emitted methods that DebugInfoGenerator discovered
through declaration syntax, so a constructor whose declaration is omitted
from the output (implicit default ctor, implicit static ctor, primary
ctor) dropped its generated points. Those functions are now emitted by
walking the sequence-point map directly.
PdbGen fixtures cover single, multiple, this()-chained, implicit, static,
primary-constructor, and field-like event initializers, pinning the
reconstructed breakpoint map against the C# compiler's.
Assisted-by: Claude:claude-opus-4-8:Claude Code
ILFunction.IsAsync is derived from the method signature, so .NET 11
runtime-async methods (MethodImplAsync bit, no MoveNext state machine)
report IsAsync without AsyncAwaitDecompiler ever populating
AsyncDebugInfo. Its Awaits then stays an uninitialized ImmutableArray,
and PortablePdbWriter threw an NRE building the MethodSteppingInformation
blob from that default struct. Runtime-async methods have no yield/resume
offsets to record, so guard on Awaits.IsDefault and omit the blob,
matching the C# compiler, which emits no stepping information for them
either. A genuinely zero-await classic state machine keeps an
initialized empty Awaits and is unaffected.
Assisted-by: Claude:claude-opus-4-8:Claude Code
Comments and preprocessor directives were positional children interleaved
into the child list, and punctuation, keywords and operators were token-node
children. Add a leading/trailing trivia side-channel for comments and
directives, emit it from the output visitor, and re-home every comment
receiver onto it (including inside-block comments as comment-only empty
statements and undecodable attribute arguments as an ErrorExpression). With
locations and sequence points no longer sourced from token nodes, stop
reconstructing them on the locations path and delete CSharpTokenNode,
CSharpModifierToken and InsertSpecialsDecorator. The AST no longer carries
token children or positional comments; output is byte-identical.
Assisted-by: Claude:claude-opus-4-8:Claude Code
The portable-PDB writer carried its knobs (no-logo, pdb id, progress,
progress title) as a growing list of optional WritePdb parameters.
Turn the type into a configured instance whose options are properties,
and add EmbedSourceFiles (default true): when a PDB is generated next
to a project export whose .cs are already on disk, embedding the source
again is redundant, so the caller can turn it off. The per-document
checksum/hash is computed either way, so documents still resolve.
Assisted-by: Claude:claude-opus-4-8:Claude Code
* Support batch PDB generation.
* Use `FileMode.Create` for output PDB files to ensure existing files are fully overwritten/truncated.
* Localize the string `Generating portable PDB...`.
* Refine `GeneratePdbForAssemblies` implementation.
* Replace direct calls to `explorer.exe` with the Shell API to prevent spawning an `explorer.exe` process that doesn't exit automatically on every call.
* Batch calls to `ShellHelper.OpenFolderAndSelectItems` instead of looping `OpenFolderAndSelectItem`.
* Localize the string `Open Explorer`.
* Fix `OpenCmdHere` malfunction when ILSpy is running from a different drive than the OS.
* Refine `GeneratePdbForAssemblies` implementation.
* Replace WinForms `FolderBrowserDialog` with WPF `OpenFolderDialog`.
* Add license header
* Exclude duplicate entries entered by the user within `OpenFolderAndSelectItems`.
* Explicitly declare that `ShellHelper.cs` is a module that allows Pinvoke.
* Use `FileMode.Create` for output PDB files to ensure existing files are fully overwritten/truncated.
* Show original filenames when generating PDBs to improve UX during batch processing.
This commit adds a new parameter to PortablePdbWriter.WritePdb that
allows the caller to provide an implementation of IProgress to receive
status updates on the pdb generation process. Currently, the progress
reports include the number of files generated so far and the total
number of files.
This commit adds a new parameter to PortablePdbWriter.WritePdb that
allows the caller to specify the exact Guid and timestamp that should be
used in the generated PDB. This will be useful for several scenarios
that are interesting for the Visual Studio debugger's integration:
1. Generating a PDB for an assembly that was built without debug info.
The PDB writer currently fails in this case, since the input assembly
has no debug directory from which to extract the relevant info. The
debugger can provide values that will allow us to load the generated
PDB.
2. Generating a PDB for an assembly that has multiple debug directories.
The PDB writer currently uses the first debug directory it finds, but
this isn't necessarily the correct one. The debugger can provide the
correct values.
While support for multi-module assemblies isn't fully working yet; it is clear at this point that we want
to treat each module in a multi-module assembly separately for the purposes of the type system.