The async stepping blob's first field is the compiler-generated catch
handler's IL offset plus one, and 0 when there is nothing to record. ILSpy
wrote the raw offset, so a consumer decoding it as (value - 1) resolved an
address in the middle of an instruction: Mono.Cecil throws
ArgumentNullException while reading the body, which is why the reported
assembly opened in ILSpy but killed ILLink on every MoveNext it had.
CatchHandlerOffset now holds the offset it is named after, or -1 for "none",
and BuildBlob applies the bias - the same model the compiler uses.
It is recorded only where an escaping exception is unlikely to be observed:
an async void method, and an async entry point. Recording it more widely
would be worse than recording it nowhere, because an async Task method
returns its exception through the Task and the debugger would then break on
exceptions user code catches. Measured over csc 1.3.2 to 5.10: async void is
handler+1 and a normal async Task is 0 in every version; only async Main
changed, from 0 to handler+1 between 2.10 and 3.11.
The entry point token names the synchronous '<Main>' shim that exists because
the runtime will not take .entrypoint on an async method, so the method to
record is the one the shim calls. Reading that call is exact; matching the
shim's siblings by name is not, and gets a 'Main' overload beside the real
entry point wrong.
Both tests compare against the compiler's own PDB for the same assembly, so
the blobs describe the same IL and the field compares directly.
Assisted-by: Claude:claude-opus-5:Claude Code
Backfills the standard MIT X11 header on hand-written files that never
got one, attributing each to its first-commit author and year from git
history. Code vendored from dotnet/runtime and Humanizr/Humanizer gets
its origin's license lines and a provenance note instead. Generated
files (Resources.Designer.cs, the version-info template), tool-managed
suppression files, and BAML test-case fixtures intentionally stay
header-less.
Assisted-by: Claude:claude-fable-5:Claude Code
or implicit sequence point without creating overlapping sequence points.
If such a location cannot be found do, nothing. Fill in the
gaps with hidden sequence points.
Also emit a sequence point for
the prolog to account for seqeunce point there emitted by the C#
compiler. Without this, the debugger can stop there on a step in
using the original pdb, then decompile resulting in a no-code at this
location failure.