Skip to content

Fix #3754: omit async stepping info for runtime-async methods - #3832

Merged
siegfriedpammer merged 1 commit into
masterfrom
fix-3754-runtime-async-pdb
Jun 27, 2026
Merged

Fix #3754: omit async stepping info for runtime-async methods#3832
siegfriedpammer merged 1 commit into
masterfrom
fix-3754-runtime-async-pdb

Conversation

@siegfriedpammer

Copy link
Copy Markdown
Member

Fixes #3754.

Problem

Generating a portable PDB for an assembly containing .NET 11 runtime-async methods threw a NullReferenceException instead of producing a PDB:

System.NullReferenceException
   at ICSharpCode.Decompiler.DebugInfo.AsyncDebugInfo.BuildBlob(MethodDefinitionHandle moveNext)
   at ICSharpCode.Decompiler.DebugInfo.PortablePdbWriter.WritePdb(...)

Root cause

ILFunction.IsAsync is derived from the method signature, so runtime-async methods (MethodImplAsync bit, no MoveNext state machine) report IsAsync without AsyncAwaitDecompiler ever populating AsyncDebugInfo. Its Awaits then stays an uninitialized ImmutableArray<Await>, and PortablePdbWriter threw building the MethodSteppingInformation blob from that default struct.

Fix

Guard emission with !function.AsyncDebugInfo.Awaits.IsDefault. Runtime-async methods have no yield/resume offsets to record, so the stepping blob is omitted, 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 (IsDefault, not IsDefaultOrEmpty).

Test

Adds a PdbGen/RuntimeAsync.cs fixture and a PdbGenerationTestRunner.RuntimeAsync test that compiles a runtime-async method in-proc (new runtimeAsync flag on Tester.CompileCSharpWithPdb, mirroring the out-of-process /features:runtime-async=on), runs PortablePdbWriter, and asserts it does not throw and emits no MethodSteppingInformation. The test fails with the NRE before the fix and passes after; the full PdbGen fixture stays green.

Verified end-to-end with ilspycmd -genpdb on a minimal runtime-async assembly (valid PDB, no stepping info) and on the issue's System.Net.Requests.dll (past the NRE).

🤖 Generated with Claude Code

Comment thread ICSharpCode.Decompiler.Tests/Helpers/Tester.cs Outdated
Comment thread ICSharpCode.Decompiler.Tests/TestCases/PdbGen/RuntimeAsync.cs
@siegfriedpammer
siegfriedpammer force-pushed the fix-3754-runtime-async-pdb branch from 964edba to 517446c Compare June 27, 2026 11:53
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
@siegfriedpammer
siegfriedpammer force-pushed the fix-3754-runtime-async-pdb branch from 517446c to 1bdb3c2 Compare June 27, 2026 16:06
@siegfriedpammer
siegfriedpammer merged commit 8c9a104 into master Jun 27, 2026
13 checks passed
@siegfriedpammer
siegfriedpammer deleted the fix-3754-runtime-async-pdb branch June 27, 2026 16:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

NullReferenceException generating portable PDB for runtime-async methods (.NET 11)

1 participant