[cDAC] RuntimeSignatureDecoder and centralized Signature contract (2/5) - #127636

Merged
steveisok merged 13 commits into
dotnet:mainfrom
max-charlamb:cdac-stackrefs-pr2
May 11, 2026
Merged

[cDAC] RuntimeSignatureDecoder and centralized Signature contract (2/5)#127636
steveisok merged 13 commits into
dotnet:mainfrom
max-charlamb:cdac-stackrefs-pr2

Conversation

@max-charlamb

@max-charlambmax-charlamb commented May 1, 2026

Copy link
Copy Markdown
Member

Summary

Part 2 of 5 stacked PRs splitting #126408. Builds on #127395 (merged).

This PR adds a cDAC ECMA-335 signature decoder that closely mirrors System.Reflection.Metadata.SignatureDecoder and supports the two runtime-only extensions used in CoreCLR-internal signatures: ELEMENT_TYPE_INTERNAL (0x21) and ELEMENT_TYPE_CMOD_INTERNAL (0x22). The decoder is then used by both the Signature contract (for field signatures) and the StackWalk contract (for signature-based GC reference scanning of transition frames).

What this PR contains

RuntimeSignatureDecoder (SRM-aligned polyfill):

  • RuntimeSignatureDecoder<TType, TGenericContext> -- readonly struct mirroring SRM's SignatureDecoder<TType, TGenericContext> (DecodeType, DecodeFieldSignature, DecodeMethodSignature, DecodeLocalSignature, all taking ref BlobReader).
  • IRuntimeSignatureTypeProvider<TType, TGenericContext> -- superset of SRM's ISignatureTypeProvider adding GetInternalType(TargetPointer) and GetInternalModifiedType(TargetPointer, TType, bool) for the two runtime-only encodings.
  • SignatureTypeProvider (the existing field-signature provider) implements IRuntimeSignatureTypeProvider so internal types in field signatures resolve via RuntimeTypeSystem.GetTypeHandle.

Signature-based GC reference scanning in StackWalk:

  • GcSignatureTypeProvider (internal, in StackWalkHelpers) classifies each method-signature parameter as Ref, Interior, Other (value type or larger-than-slot), or None.
  • A new GcSignatureContext(TypeHandle classContext, MethodDescHandle methodContext) record struct is plumbed through RuntimeSignatureDecoder<GcTypeKind, GcSignatureContext> so ELEMENT_TYPE_VAR / ELEMENT_TYPE_MVAR placeholders resolve to the method's actual class / method instantiation -- matching native SigTypeContext-driven PeekElemTypeNormalized behavior.
  • GcScanner.PromoteCallerStack constructs the provider per call and walks the TransitionBlock using a reserved-slot count derived from IsInstance / return-buffer / RequiresInstArg / IsAsyncMethod / ARM64 x8, reporting each parameter slot as a GC reference, interior pointer, or skip.
  • Signature acquisition mirrors native MethodDesc::GetSig: prefers IsStoredSigMethodDesc (dynamic, EEImpl, array methods) before falling back to the metadata token, so caller-stack roots of dynamic and array-method transition frames are handled. Stored sigs are pinned with an inline fixed block and read via a BlobReader, matching the existing SigFormat.cs pattern.
  • This is the cDAC equivalent of native TransitionFrame::PromoteCallerStack and is used for PrestubMethodFrame, CallCountingHelperFrame, and the StubDispatchFrame / ExternalMethodFrame fallback when no GCRefMap is available.

Signature contract surface stays minimal:

  • ISignatureDecoder continues to expose only DecodeFieldSignature(BlobHandle, ModuleHandle, TypeHandle).
  • GC-specific decoding lives entirely inside the StackWalk contract; the Signature contract has no GC dependencies.

cDAC documentation:

  • docs/design/datacontracts/SignatureDecoder.md -- describes RuntimeSignatureDecoder, IRuntimeSignatureTypeProvider, and the ELEMENT_TYPE_INTERNAL / CMOD_INTERNAL extensions; refreshed DecodeFieldSignature code sample.
  • docs/design/datacontracts/StackWalk.md -- new "Signature-Based Scanning" section under GC stack reference scanning, covering GcSignatureTypeProvider (with module scoping, GcSignatureContext, and enum normalization), the PromoteCallerStack algorithm, the reserved-slot table, and limitations vs. native.

Testing

  • Build: clean, 0 warnings / 0 errors.
  • 1921 / 1921 cDAC unit tests pass.
  • Behavioral verification against the legacy DAC happens via the GC-stress verification harness introduced later in the PR series.

Note

This PR description was created with AI assistance from Copilot.

- Move RuntimeSignatureDecoder to Contracts/Signature/
- Move GcSignatureTypeProvider to Contracts/Signature/ with module-scoped caching
- Add DecodeMethodSignatureForGC(BlobHandle, ModuleHandle) to ISignatureDecoder
- Add DecodeFieldSignature to RuntimeSignatureDecoder
- Add BlobHandleSignatureReader for lazy blob reading
- SignatureTypeProvider implements IRuntimeSignatureTypeProvider
- Switch DecodeFieldSignature to use RuntimeSignatureDecoder
- GcScanner uses contract API instead of direct decoder construction
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @steveisok, @tommcdon, @dotnet/dotnet-diag
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR is part of the cDAC signature-decoding refactor, introducing a custom RuntimeSignatureDecoder capable of handling runtime-internal signature element types (ELEMENT_TYPE_INTERNAL / ELEMENT_TYPE_CMOD_INTERNAL) and centralizing GC-oriented signature decoding behind the ISignatureDecoder contract.

Changes:

  • Added RuntimeSignatureDecoder plus signature-reader abstractions (ISignatureReader, span/blob-backed readers) to support decoding from different signature sources.
  • Extended the ISignatureDecoder contract with DecodeMethodSignatureForGC(...) and promoted GcTypeKind to a public abstraction.
  • Updated stack GC scanning (GcScanner) and signature type providers to route decoding through the centralized contract API.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Show a summary per file
FileDescription
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/GC/GcScanner.csSwitches caller-signature decoding to the ISignatureDecoder.DecodeMethodSignatureForGC contract API.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/SignatureTypeProvider.csUpdates the type provider to implement IRuntimeSignatureTypeProvider and adds internal-type callbacks.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/SignatureReaders.csIntroduces abstractions for reading signature bytes from spans and metadata blobs.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/SignatureDecoder_1.csAdds module-scoped GC provider caching and implements DecodeMethodSignatureForGC using RuntimeSignatureDecoder.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/RuntimeSignatureDecoder.csNew runtime-aware ECMA-335-ish signature decoder with support for internal runtime element types.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/GcSignatureTypeProvider.csMoves/updates GC classification provider to implement the runtime-aware provider interface and classify internal types via RTS.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ISignatureDecoder.csExtends the public contract surface with GcTypeKind and DecodeMethodSignatureForGC.
Comments suppressed due to low confidence (1)

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/GcSignatureTypeProvider.cs:26

  • GcSignatureTypeProvider stores _target and _moduleHandle but never uses either of them (the implementation uses the target method parameter instead). This is dead state that should be removed, or the implementation should consistently use the stored fields if they're intended for future module/target-scoped behavior.

Max Charlamband others added 2 commits May 1, 2026 00:50
Rewrites RuntimeSignatureDecoder as a readonly struct that mirrors SRM's SignatureDecoder API (ref BlobReader per method, allowTypeSpecifications flag), with only ELEMENT_TYPE_INTERNAL (0x21) and CMOD_INTERNAL (0x22) added on top via the new IRuntimeSignatureTypeProvider interface. Drops the custom ISignatureReader/BlobHandleSignatureReader/SpanSignatureReader abstraction since BlobReader already provides lazy reading.
Fixes two latent bugs vs SRM: TypeDefOrRefOrSpec tag=3 now throws (was incorrectly returning Object), and the leading element type code is read as a compressed integer rather than a raw byte.
Moves GC-specific signature decoding out of the Signature contract into the StackWalk contract (Option B). GcSignatureTypeProvider and GcTypeKind move to Contracts/StackWalk/GC/ under the StackWalkHelpers namespace. ISignatureDecoder no longer exposes DecodeMethodSignatureForGC; the _gcProviders cache is removed from SignatureDecoder_1; GcScanner constructs RuntimeSignatureDecoder<GcTypeKind, object?> directly.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…adataReader
Splits IRuntimeSignatureTypeProvider into its own file (matches cDAC convention for internal interfaces). Makes MetadataReader required on RuntimeSignatureDecoder (matches SRM exactly; both call sites already pass non-null) and removes the null-forgiving operators. Reorders constructor params to (provider, target, metadataReader, genericContext) to mirror SRM's parameter order.
Cleanup: removes unused _mdhProviders dictionary and GetMethodDescHandleProvider from SignatureDecoder_1; removes unused _target/_moduleHandle fields and constructor from GcSignatureTypeProvider; renames _metadataReaderOpt to _metadataReader (Opt suffix is non-standard for a nullable-typed field); converts block comment to line comments.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings May 1, 2026 05:04

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.

Drop the redundant `Target target` parameter from
`IRuntimeSignatureTypeProvider.GetInternalType` and
`GetInternalModifiedType`. Providers now capture the target in their
own constructor when they need it, matching `SignatureTypeProvider<T>`.
`GcSignatureTypeProvider` gains a `Target` constructor parameter.
Update the cDAC documentation:
* `SignatureDecoder.md` -- describe `RuntimeSignatureDecoder`,
`IRuntimeSignatureTypeProvider`, and the runtime-only
`ELEMENT_TYPE_INTERNAL` / `ELEMENT_TYPE_CMOD_INTERNAL` extensions;
refresh the `DecodeFieldSignature` code sample.
* `StackWalk.md` -- new Signature-Based Scanning section covering
`GcSignatureTypeProvider`, the `PromoteCallerStack` algorithm,
reserved-slot table, and limitations vs. native.
Also revert a stray whitespace change in `ISignatureDecoder.cs`.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@max-charlamb
max-charlamb marked this pull request as ready for review May 1, 2026 05:16
CopilotAI review requested due to automatic review settings May 1, 2026 05:16

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.

Comments suppressed due to low confidence (2)

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/SignatureTypeProvider.cs:25

  • The private field _target is assigned in the constructor but never read in this type, which will trigger CS0414 (and in this repo, warnings are treated as errors by default). Please remove _target (and its assignment) or start using it (e.g., for validation) to avoid build breaks.
 private readonly Target _target;
private readonly Contracts.ModuleHandle _moduleHandle;
private readonly Contracts.ILoader _loader;
private readonly Contracts.IRuntimeTypeSystem _runtimeTypeSystem;
public SignatureTypeProvider(Target target, Contracts.ModuleHandle moduleHandle)
{
_target = target;
_moduleHandle = moduleHandle;
_loader = target.Contracts.Loader;
_runtimeTypeSystem = target.Contracts.RuntimeTypeSystem;

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/GC/GcScanner.cs:380

  • isValueTypeThis is always initialized to false and never updated, so this is never reported with GC_CALL_INTERIOR even for value-type instance methods. Since you already resolve the declaring TypeHandle earlier, it seems like this should be computed (e.g., via IRuntimeTypeSystem.IsValueType(typeHandle)) and included in the existing try/catch alongside RequiresInstArg / IsAsyncMethod.
 bool hasThis = methodSig.Header.IsInstance;
bool hasRetBuf = methodSig.ReturnType is GcTypeKind.Other;
bool requiresInstArg = false;
bool isAsync = false;
bool isValueTypeThis = false;
try
{
requiresInstArg = rts.RequiresInstArg(mdh);
isAsync = rts.IsAsyncMethod(mdh);
}
catch
{
}
PromoteCallerStackHelper(transitionBlock, methodSig, hasThis, hasRetBuf,
requiresInstArg, isAsync, isValueTypeThis, scanContext);
}

Comment threaddocs/design/datacontracts/SignatureDecoder.md Outdated
Max Charlamband others added 2 commits May 1, 2026 01:34
…rovider
Match native MethodDesc::GetSig in GcScanner.PromoteCallerStack: try
IsStoredSigMethodDesc first (dynamic, EEImpl, and array method descs) and
read the stored signature via an inline pinned BlobReader before falling
back to the metadata token lookup. Cache the GcSignatureTypeProvider on
the GcScanner so it is allocated once instead of per call.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…mitive
GcSignatureTypeProvider now classifies type parameters and TypeDef/TypeRef
tokens using the loaded TypeHandle, matching native SigTypeContext-driven
PeekElemTypeNormalized behavior:
- VAR/MVAR placeholders are resolved against the method's actual class /
method instantiation via a new GcSignatureContext(TypeHandle, MethodDescHandle)
passed as the SRM generic context.
- TypeDef/TypeRef tokens are resolved via the module's TypeDefToMethodTable /
TypeRefToMethodTable lookup tables; enums collapse to their underlying
primitive (None) via IRuntimeTypeSystem.IsEnum, matching
MethodTable::GetInternalCorElementType.
The provider is now module-scoped, so it is constructed per PromoteCallerStack
call rather than cached on GcScanner.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings May 1, 2026 05:51

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.

Comment threaddocs/design/datacontracts/SignatureDecoder.md Outdated
- IRuntimeSignatureTypeProvider: internal -> public to match SignatureTypeProvider's
visibility (avoids the "public class implements internal interface" warning while
keeping the provider type available to downstream consumers).
- RuntimeSignatureDecoder: drop the unused _target field and the redundant
ArgumentNullException.ThrowIfNull(provider/metadataReader) calls; the file is
in a nullable-enabled context, so the type system already enforces non-null.
- SignatureDecoder.md: drop the bogus `target,` argument from the GetInternalType
/ GetInternalModifiedType bullets, and reword GetTypeFromDefinition /
GetTypeFromReference's "returns null" to "returns a default TypeHandle
(Address == TargetPointer.Null)" since the API returns a struct value.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@max-charlamb
max-charlamb requested a review from rcj1May 1, 2026 15:08
Comment threaddocs/design/datacontracts/SignatureDecoder.md Outdated
Comment threaddocs/design/datacontracts/Signature.md Outdated
Comment threaddocs/design/datacontracts/Signature.md Outdated
Comment threaddocs/design/datacontracts/StackWalk.md Outdated
Comment threaddocs/design/datacontracts/Signature.md Outdated
Comment threaddocs/design/datacontracts/Signature.md Outdated
Comment threaddocs/design/datacontracts/Signature.md Outdated
CopilotAI review requested due to automatic review settings May 4, 2026 17:45

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 4 comments.

Comments suppressed due to low confidence (1)

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ISignature.cs:16

  • Renaming the public abstractions from ISignatureDecoder/SignatureDecoder to ISignature/Signature is a source/binary breaking change for any consumer of Microsoft.Diagnostics.DataContractReader.Abstractions, and this PR doesn't preserve compatibility shims. The PR metadata only links other PRs, not an api-approved issue, so this public surface either needs prior approval or it needs to stay internal until the rename is reviewed.

Comment threadsrc/coreclr/vm/datadescriptor/datadescriptor.inc
Comment threaddocs/design/datacontracts/StackWalk.md
Match native SigTypeContext::InitTypeContext: array MTs use the element
type as their class instantiation. RuntimeTypeSystem.GetInstantiation
returns an empty span for arrays, so signature decoding for array
accessor methods (e.g., int[]::Set with VAR 0) was misclassifying the
element-type slot as Ref. Special-case IsArray and use GetTypeParam,
the managed equivalent of MethodTable::GetArrayInstantiation.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
rcj1
rcj1 approved these changes May 8, 2026
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings May 8, 2026 21:56
@max-charlamb
max-charlamb enabled auto-merge (squash) May 8, 2026 22:01

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ISignature.cs:16

  • Renaming the contract from SignatureDecoder to Signature changes the wire-level lookup key (TContract.Name) used by CachingContractRegistry (it calls _tryGetContractVersion(TContract.Name, ...)). This will break compatibility between newer readers and older runtimes/dumps that only advertise SignatureDecoder (and vice versa), since contract versioning can’t help if the name doesn’t match. Consider keeping the contract name stable (e.g., keep IContract.Name as "SignatureDecoder"), or publish an alias contract on the runtime side and keep the legacy ISignatureDecoder surface as a forwarding shim so existing readers can still resolve the contract.

Comment threaddocs/design/datacontracts/StackWalk.md
@max-charlamb

Copy link
Copy Markdown
MemberAuthor

/ba-g unrelated test failures

@max-charlamb
max-charlamb disabled auto-merge May 10, 2026 02:42
@max-charlamb
max-charlamb enabled auto-merge (squash) May 10, 2026 02:44
@max-charlamb

Copy link
Copy Markdown
MemberAuthor

/ba-g cDAC only change, the only cDAC test failure is a known issue on osx

@steveisok
steveisok disabled auto-merge May 11, 2026 14:53
@steveisok
steveisok merged commit 6f89eaf into dotnet:mainMay 11, 2026
120 of 125 checks passed
jakobbotsch pushed a commit to jakobbotsch/runtime that referenced this pull request May 12, 2026
…5) (dotnet#127636)
## Summary
Part 2 of 5 stacked PRs splitting
[dotnet#126408](dotnet#126408). Builds on
[dotnet#127395](dotnet#127395) (merged).
This PR adds a cDAC ECMA-335 signature decoder that closely mirrors
`System.Reflection.Metadata.SignatureDecoder` and supports the two
runtime-only extensions used in CoreCLR-internal signatures:
`ELEMENT_TYPE_INTERNAL` (`0x21`) and `ELEMENT_TYPE_CMOD_INTERNAL`
(`0x22`). The decoder is then used by both the Signature contract (for
field signatures) and the StackWalk contract (for signature-based GC
reference scanning of transition frames).
### What this PR contains
**`RuntimeSignatureDecoder` (SRM-aligned polyfill):**
- `RuntimeSignatureDecoder<TType, TGenericContext>` -- readonly struct
mirroring SRM's `SignatureDecoder<TType, TGenericContext>`
(`DecodeType`, `DecodeFieldSignature`, `DecodeMethodSignature`,
`DecodeLocalSignature`, all taking `ref BlobReader`).
- `IRuntimeSignatureTypeProvider<TType, TGenericContext>` -- superset of
SRM's `ISignatureTypeProvider` adding `GetInternalType(TargetPointer)`
and `GetInternalModifiedType(TargetPointer, TType, bool)` for the two
runtime-only encodings.
- `SignatureTypeProvider` (the existing field-signature provider)
implements `IRuntimeSignatureTypeProvider` so internal types in field
signatures resolve via `RuntimeTypeSystem.GetTypeHandle`.
**Signature-based GC reference scanning in StackWalk:**
- `GcSignatureTypeProvider` (internal, in `StackWalkHelpers`) classifies
each method-signature parameter as `Ref`, `Interior`, `Other` (value
type or larger-than-slot), or `None`.
- A new `GcSignatureContext(TypeHandle classContext, MethodDescHandle
methodContext)` record struct is plumbed through
`RuntimeSignatureDecoder<GcTypeKind, GcSignatureContext>` so
`ELEMENT_TYPE_VAR` / `ELEMENT_TYPE_MVAR` placeholders resolve to the
method's actual class / method instantiation -- matching native
`SigTypeContext`-driven `PeekElemTypeNormalized` behavior.
- `GcScanner.PromoteCallerStack` constructs the provider per call and
walks the `TransitionBlock` using a reserved-slot count derived from
`IsInstance` / return-buffer / `RequiresInstArg` / `IsAsyncMethod` /
ARM64 `x8`, reporting each parameter slot as a GC reference, interior
pointer, or skip.
- Signature acquisition mirrors native `MethodDesc::GetSig`: prefers
`IsStoredSigMethodDesc` (dynamic, EEImpl, array methods) before falling
back to the metadata token, so caller-stack roots of dynamic and
array-method transition frames are handled. Stored sigs are pinned with
an inline `fixed` block and read via a `BlobReader`, matching the
existing `SigFormat.cs` pattern.
- This is the cDAC equivalent of native
`TransitionFrame::PromoteCallerStack` and is used for
`PrestubMethodFrame`, `CallCountingHelperFrame`, and the
`StubDispatchFrame` / `ExternalMethodFrame` fallback when no GCRefMap is
available.
**Signature contract surface stays minimal:**
- `ISignatureDecoder` continues to expose only
`DecodeFieldSignature(BlobHandle, ModuleHandle, TypeHandle)`.
- GC-specific decoding lives entirely inside the StackWalk contract; the
Signature contract has no GC dependencies.
**cDAC documentation:**
- `docs/design/datacontracts/SignatureDecoder.md` -- describes
`RuntimeSignatureDecoder`, `IRuntimeSignatureTypeProvider`, and the
`ELEMENT_TYPE_INTERNAL` / `CMOD_INTERNAL` extensions; refreshed
`DecodeFieldSignature` code sample.
- `docs/design/datacontracts/StackWalk.md` -- new "Signature-Based
Scanning" section under GC stack reference scanning, covering
`GcSignatureTypeProvider` (with module scoping, `GcSignatureContext`,
and enum normalization), the `PromoteCallerStack` algorithm, the
reserved-slot table, and limitations vs. native.
### Testing
- Build: clean, 0 warnings / 0 errors.
- 1921 / 1921 cDAC unit tests pass.
- Behavioral verification against the legacy DAC happens via the
GC-stress verification harness introduced later in the PR series.
> [!NOTE]
> This PR description was created with AI assistance from Copilot.
---------
Co-authored-by: Max Charlamb <maxcharlamb@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jun 11, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@max-charlamb@jkotas@rcj1@steveisok
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

[cDAC] RuntimeSignatureDecoder and centralized Signature contract (2/5) - #127636

Merged
steveisok merged 13 commits into
dotnet:mainfrom
max-charlamb:cdac-stackrefs-pr2
May 11, 2026
Merged

[cDAC] RuntimeSignatureDecoder and centralized Signature contract (2/5)#127636
steveisok merged 13 commits into
dotnet:mainfrom
max-charlamb:cdac-stackrefs-pr2

Conversation

@max-charlamb

@max-charlambmax-charlamb commented May 1, 2026

Copy link
Copy Markdown
Member

Summary

Part 2 of 5 stacked PRs splitting #126408. Builds on #127395 (merged).

This PR adds a cDAC ECMA-335 signature decoder that closely mirrors System.Reflection.Metadata.SignatureDecoder and supports the two runtime-only extensions used in CoreCLR-internal signatures: ELEMENT_TYPE_INTERNAL (0x21) and ELEMENT_TYPE_CMOD_INTERNAL (0x22). The decoder is then used by both the Signature contract (for field signatures) and the StackWalk contract (for signature-based GC reference scanning of transition frames).

What this PR contains

RuntimeSignatureDecoder (SRM-aligned polyfill):

  • RuntimeSignatureDecoder<TType, TGenericContext> -- readonly struct mirroring SRM's SignatureDecoder<TType, TGenericContext> (DecodeType, DecodeFieldSignature, DecodeMethodSignature, DecodeLocalSignature, all taking ref BlobReader).
  • IRuntimeSignatureTypeProvider<TType, TGenericContext> -- superset of SRM's ISignatureTypeProvider adding GetInternalType(TargetPointer) and GetInternalModifiedType(TargetPointer, TType, bool) for the two runtime-only encodings.
  • SignatureTypeProvider (the existing field-signature provider) implements IRuntimeSignatureTypeProvider so internal types in field signatures resolve via RuntimeTypeSystem.GetTypeHandle.

Signature-based GC reference scanning in StackWalk:

  • GcSignatureTypeProvider (internal, in StackWalkHelpers) classifies each method-signature parameter as Ref, Interior, Other (value type or larger-than-slot), or None.
  • A new GcSignatureContext(TypeHandle classContext, MethodDescHandle methodContext) record struct is plumbed through RuntimeSignatureDecoder<GcTypeKind, GcSignatureContext> so ELEMENT_TYPE_VAR / ELEMENT_TYPE_MVAR placeholders resolve to the method's actual class / method instantiation -- matching native SigTypeContext-driven PeekElemTypeNormalized behavior.
  • GcScanner.PromoteCallerStack constructs the provider per call and walks the TransitionBlock using a reserved-slot count derived from IsInstance / return-buffer / RequiresInstArg / IsAsyncMethod / ARM64 x8, reporting each parameter slot as a GC reference, interior pointer, or skip.
  • Signature acquisition mirrors native MethodDesc::GetSig: prefers IsStoredSigMethodDesc (dynamic, EEImpl, array methods) before falling back to the metadata token, so caller-stack roots of dynamic and array-method transition frames are handled. Stored sigs are pinned with an inline fixed block and read via a BlobReader, matching the existing SigFormat.cs pattern.
  • This is the cDAC equivalent of native TransitionFrame::PromoteCallerStack and is used for PrestubMethodFrame, CallCountingHelperFrame, and the StubDispatchFrame / ExternalMethodFrame fallback when no GCRefMap is available.

Signature contract surface stays minimal:

  • ISignatureDecoder continues to expose only DecodeFieldSignature(BlobHandle, ModuleHandle, TypeHandle).
  • GC-specific decoding lives entirely inside the StackWalk contract; the Signature contract has no GC dependencies.

cDAC documentation:

  • docs/design/datacontracts/SignatureDecoder.md -- describes RuntimeSignatureDecoder, IRuntimeSignatureTypeProvider, and the ELEMENT_TYPE_INTERNAL / CMOD_INTERNAL extensions; refreshed DecodeFieldSignature code sample.
  • docs/design/datacontracts/StackWalk.md -- new "Signature-Based Scanning" section under GC stack reference scanning, covering GcSignatureTypeProvider (with module scoping, GcSignatureContext, and enum normalization), the PromoteCallerStack algorithm, the reserved-slot table, and limitations vs. native.

Testing

  • Build: clean, 0 warnings / 0 errors.
  • 1921 / 1921 cDAC unit tests pass.
  • Behavioral verification against the legacy DAC happens via the GC-stress verification harness introduced later in the PR series.

Note

This PR description was created with AI assistance from Copilot.

- Move RuntimeSignatureDecoder to Contracts/Signature/
- Move GcSignatureTypeProvider to Contracts/Signature/ with module-scoped caching
- Add DecodeMethodSignatureForGC(BlobHandle, ModuleHandle) to ISignatureDecoder
- Add DecodeFieldSignature to RuntimeSignatureDecoder
- Add BlobHandleSignatureReader for lazy blob reading
- SignatureTypeProvider implements IRuntimeSignatureTypeProvider
- Switch DecodeFieldSignature to use RuntimeSignatureDecoder
- GcScanner uses contract API instead of direct decoder construction
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @steveisok, @tommcdon, @dotnet/dotnet-diag
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR is part of the cDAC signature-decoding refactor, introducing a custom RuntimeSignatureDecoder capable of handling runtime-internal signature element types (ELEMENT_TYPE_INTERNAL / ELEMENT_TYPE_CMOD_INTERNAL) and centralizing GC-oriented signature decoding behind the ISignatureDecoder contract.

Changes:

  • Added RuntimeSignatureDecoder plus signature-reader abstractions (ISignatureReader, span/blob-backed readers) to support decoding from different signature sources.
  • Extended the ISignatureDecoder contract with DecodeMethodSignatureForGC(...) and promoted GcTypeKind to a public abstraction.
  • Updated stack GC scanning (GcScanner) and signature type providers to route decoding through the centralized contract API.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Show a summary per file
FileDescription
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/GC/GcScanner.csSwitches caller-signature decoding to the ISignatureDecoder.DecodeMethodSignatureForGC contract API.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/SignatureTypeProvider.csUpdates the type provider to implement IRuntimeSignatureTypeProvider and adds internal-type callbacks.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/SignatureReaders.csIntroduces abstractions for reading signature bytes from spans and metadata blobs.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/SignatureDecoder_1.csAdds module-scoped GC provider caching and implements DecodeMethodSignatureForGC using RuntimeSignatureDecoder.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/RuntimeSignatureDecoder.csNew runtime-aware ECMA-335-ish signature decoder with support for internal runtime element types.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/GcSignatureTypeProvider.csMoves/updates GC classification provider to implement the runtime-aware provider interface and classify internal types via RTS.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ISignatureDecoder.csExtends the public contract surface with GcTypeKind and DecodeMethodSignatureForGC.
Comments suppressed due to low confidence (1)

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/GcSignatureTypeProvider.cs:26

  • GcSignatureTypeProvider stores _target and _moduleHandle but never uses either of them (the implementation uses the target method parameter instead). This is dead state that should be removed, or the implementation should consistently use the stored fields if they're intended for future module/target-scoped behavior.

Max Charlamband others added 2 commits May 1, 2026 00:50
Rewrites RuntimeSignatureDecoder as a readonly struct that mirrors SRM's SignatureDecoder API (ref BlobReader per method, allowTypeSpecifications flag), with only ELEMENT_TYPE_INTERNAL (0x21) and CMOD_INTERNAL (0x22) added on top via the new IRuntimeSignatureTypeProvider interface. Drops the custom ISignatureReader/BlobHandleSignatureReader/SpanSignatureReader abstraction since BlobReader already provides lazy reading.
Fixes two latent bugs vs SRM: TypeDefOrRefOrSpec tag=3 now throws (was incorrectly returning Object), and the leading element type code is read as a compressed integer rather than a raw byte.
Moves GC-specific signature decoding out of the Signature contract into the StackWalk contract (Option B). GcSignatureTypeProvider and GcTypeKind move to Contracts/StackWalk/GC/ under the StackWalkHelpers namespace. ISignatureDecoder no longer exposes DecodeMethodSignatureForGC; the _gcProviders cache is removed from SignatureDecoder_1; GcScanner constructs RuntimeSignatureDecoder<GcTypeKind, object?> directly.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…adataReader
Splits IRuntimeSignatureTypeProvider into its own file (matches cDAC convention for internal interfaces). Makes MetadataReader required on RuntimeSignatureDecoder (matches SRM exactly; both call sites already pass non-null) and removes the null-forgiving operators. Reorders constructor params to (provider, target, metadataReader, genericContext) to mirror SRM's parameter order.
Cleanup: removes unused _mdhProviders dictionary and GetMethodDescHandleProvider from SignatureDecoder_1; removes unused _target/_moduleHandle fields and constructor from GcSignatureTypeProvider; renames _metadataReaderOpt to _metadataReader (Opt suffix is non-standard for a nullable-typed field); converts block comment to line comments.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings May 1, 2026 05:04

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.

Drop the redundant `Target target` parameter from
`IRuntimeSignatureTypeProvider.GetInternalType` and
`GetInternalModifiedType`. Providers now capture the target in their
own constructor when they need it, matching `SignatureTypeProvider<T>`.
`GcSignatureTypeProvider` gains a `Target` constructor parameter.
Update the cDAC documentation:
* `SignatureDecoder.md` -- describe `RuntimeSignatureDecoder`,
`IRuntimeSignatureTypeProvider`, and the runtime-only
`ELEMENT_TYPE_INTERNAL` / `ELEMENT_TYPE_CMOD_INTERNAL` extensions;
refresh the `DecodeFieldSignature` code sample.
* `StackWalk.md` -- new Signature-Based Scanning section covering
`GcSignatureTypeProvider`, the `PromoteCallerStack` algorithm,
reserved-slot table, and limitations vs. native.
Also revert a stray whitespace change in `ISignatureDecoder.cs`.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@max-charlamb
max-charlamb marked this pull request as ready for review May 1, 2026 05:16
CopilotAI review requested due to automatic review settings May 1, 2026 05:16

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.

Comments suppressed due to low confidence (2)

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/SignatureTypeProvider.cs:25

  • The private field _target is assigned in the constructor but never read in this type, which will trigger CS0414 (and in this repo, warnings are treated as errors by default). Please remove _target (and its assignment) or start using it (e.g., for validation) to avoid build breaks.
 private readonly Target _target;
private readonly Contracts.ModuleHandle _moduleHandle;
private readonly Contracts.ILoader _loader;
private readonly Contracts.IRuntimeTypeSystem _runtimeTypeSystem;
public SignatureTypeProvider(Target target, Contracts.ModuleHandle moduleHandle)
{
_target = target;
_moduleHandle = moduleHandle;
_loader = target.Contracts.Loader;
_runtimeTypeSystem = target.Contracts.RuntimeTypeSystem;

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/GC/GcScanner.cs:380

  • isValueTypeThis is always initialized to false and never updated, so this is never reported with GC_CALL_INTERIOR even for value-type instance methods. Since you already resolve the declaring TypeHandle earlier, it seems like this should be computed (e.g., via IRuntimeTypeSystem.IsValueType(typeHandle)) and included in the existing try/catch alongside RequiresInstArg / IsAsyncMethod.
 bool hasThis = methodSig.Header.IsInstance;
bool hasRetBuf = methodSig.ReturnType is GcTypeKind.Other;
bool requiresInstArg = false;
bool isAsync = false;
bool isValueTypeThis = false;
try
{
requiresInstArg = rts.RequiresInstArg(mdh);
isAsync = rts.IsAsyncMethod(mdh);
}
catch
{
}
PromoteCallerStackHelper(transitionBlock, methodSig, hasThis, hasRetBuf,
requiresInstArg, isAsync, isValueTypeThis, scanContext);
}

Comment threaddocs/design/datacontracts/SignatureDecoder.md Outdated
Max Charlamband others added 2 commits May 1, 2026 01:34
…rovider
Match native MethodDesc::GetSig in GcScanner.PromoteCallerStack: try
IsStoredSigMethodDesc first (dynamic, EEImpl, and array method descs) and
read the stored signature via an inline pinned BlobReader before falling
back to the metadata token lookup. Cache the GcSignatureTypeProvider on
the GcScanner so it is allocated once instead of per call.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…mitive
GcSignatureTypeProvider now classifies type parameters and TypeDef/TypeRef
tokens using the loaded TypeHandle, matching native SigTypeContext-driven
PeekElemTypeNormalized behavior:
- VAR/MVAR placeholders are resolved against the method's actual class /
method instantiation via a new GcSignatureContext(TypeHandle, MethodDescHandle)
passed as the SRM generic context.
- TypeDef/TypeRef tokens are resolved via the module's TypeDefToMethodTable /
TypeRefToMethodTable lookup tables; enums collapse to their underlying
primitive (None) via IRuntimeTypeSystem.IsEnum, matching
MethodTable::GetInternalCorElementType.
The provider is now module-scoped, so it is constructed per PromoteCallerStack
call rather than cached on GcScanner.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings May 1, 2026 05:51

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.

Comment threaddocs/design/datacontracts/SignatureDecoder.md Outdated
- IRuntimeSignatureTypeProvider: internal -> public to match SignatureTypeProvider's
visibility (avoids the "public class implements internal interface" warning while
keeping the provider type available to downstream consumers).
- RuntimeSignatureDecoder: drop the unused _target field and the redundant
ArgumentNullException.ThrowIfNull(provider/metadataReader) calls; the file is
in a nullable-enabled context, so the type system already enforces non-null.
- SignatureDecoder.md: drop the bogus `target,` argument from the GetInternalType
/ GetInternalModifiedType bullets, and reword GetTypeFromDefinition /
GetTypeFromReference's "returns null" to "returns a default TypeHandle
(Address == TargetPointer.Null)" since the API returns a struct value.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@max-charlamb
max-charlamb requested a review from rcj1May 1, 2026 15:08
Comment threaddocs/design/datacontracts/SignatureDecoder.md Outdated
Comment threaddocs/design/datacontracts/Signature.md Outdated
Comment threaddocs/design/datacontracts/Signature.md Outdated
Comment threaddocs/design/datacontracts/StackWalk.md Outdated
Comment threaddocs/design/datacontracts/Signature.md Outdated
Comment threaddocs/design/datacontracts/Signature.md Outdated
Comment threaddocs/design/datacontracts/Signature.md Outdated
CopilotAI review requested due to automatic review settings May 4, 2026 17:45

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 4 comments.

Comments suppressed due to low confidence (1)

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ISignature.cs:16

  • Renaming the public abstractions from ISignatureDecoder/SignatureDecoder to ISignature/Signature is a source/binary breaking change for any consumer of Microsoft.Diagnostics.DataContractReader.Abstractions, and this PR doesn't preserve compatibility shims. The PR metadata only links other PRs, not an api-approved issue, so this public surface either needs prior approval or it needs to stay internal until the rename is reviewed.

Comment threadsrc/coreclr/vm/datadescriptor/datadescriptor.inc
Comment threaddocs/design/datacontracts/StackWalk.md
Match native SigTypeContext::InitTypeContext: array MTs use the element
type as their class instantiation. RuntimeTypeSystem.GetInstantiation
returns an empty span for arrays, so signature decoding for array
accessor methods (e.g., int[]::Set with VAR 0) was misclassifying the
element-type slot as Ref. Special-case IsArray and use GetTypeParam,
the managed equivalent of MethodTable::GetArrayInstantiation.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
rcj1
rcj1 approved these changes May 8, 2026
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings May 8, 2026 21:56
@max-charlamb
max-charlamb enabled auto-merge (squash) May 8, 2026 22:01

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ISignature.cs:16

  • Renaming the contract from SignatureDecoder to Signature changes the wire-level lookup key (TContract.Name) used by CachingContractRegistry (it calls _tryGetContractVersion(TContract.Name, ...)). This will break compatibility between newer readers and older runtimes/dumps that only advertise SignatureDecoder (and vice versa), since contract versioning can’t help if the name doesn’t match. Consider keeping the contract name stable (e.g., keep IContract.Name as "SignatureDecoder"), or publish an alias contract on the runtime side and keep the legacy ISignatureDecoder surface as a forwarding shim so existing readers can still resolve the contract.

Comment threaddocs/design/datacontracts/StackWalk.md
@max-charlamb

Copy link
Copy Markdown
MemberAuthor

/ba-g unrelated test failures

@max-charlamb
max-charlamb disabled auto-merge May 10, 2026 02:42
@max-charlamb
max-charlamb enabled auto-merge (squash) May 10, 2026 02:44
@max-charlamb

Copy link
Copy Markdown
MemberAuthor

/ba-g cDAC only change, the only cDAC test failure is a known issue on osx

@steveisok
steveisok disabled auto-merge May 11, 2026 14:53
@steveisok
steveisok merged commit 6f89eaf into dotnet:mainMay 11, 2026
120 of 125 checks passed
jakobbotsch pushed a commit to jakobbotsch/runtime that referenced this pull request May 12, 2026
…5) (dotnet#127636)
## Summary
Part 2 of 5 stacked PRs splitting
[dotnet#126408](dotnet#126408). Builds on
[dotnet#127395](dotnet#127395) (merged).
This PR adds a cDAC ECMA-335 signature decoder that closely mirrors
`System.Reflection.Metadata.SignatureDecoder` and supports the two
runtime-only extensions used in CoreCLR-internal signatures:
`ELEMENT_TYPE_INTERNAL` (`0x21`) and `ELEMENT_TYPE_CMOD_INTERNAL`
(`0x22`). The decoder is then used by both the Signature contract (for
field signatures) and the StackWalk contract (for signature-based GC
reference scanning of transition frames).
### What this PR contains
**`RuntimeSignatureDecoder` (SRM-aligned polyfill):**
- `RuntimeSignatureDecoder<TType, TGenericContext>` -- readonly struct
mirroring SRM's `SignatureDecoder<TType, TGenericContext>`
(`DecodeType`, `DecodeFieldSignature`, `DecodeMethodSignature`,
`DecodeLocalSignature`, all taking `ref BlobReader`).
- `IRuntimeSignatureTypeProvider<TType, TGenericContext>` -- superset of
SRM's `ISignatureTypeProvider` adding `GetInternalType(TargetPointer)`
and `GetInternalModifiedType(TargetPointer, TType, bool)` for the two
runtime-only encodings.
- `SignatureTypeProvider` (the existing field-signature provider)
implements `IRuntimeSignatureTypeProvider` so internal types in field
signatures resolve via `RuntimeTypeSystem.GetTypeHandle`.
**Signature-based GC reference scanning in StackWalk:**
- `GcSignatureTypeProvider` (internal, in `StackWalkHelpers`) classifies
each method-signature parameter as `Ref`, `Interior`, `Other` (value
type or larger-than-slot), or `None`.
- A new `GcSignatureContext(TypeHandle classContext, MethodDescHandle
methodContext)` record struct is plumbed through
`RuntimeSignatureDecoder<GcTypeKind, GcSignatureContext>` so
`ELEMENT_TYPE_VAR` / `ELEMENT_TYPE_MVAR` placeholders resolve to the
method's actual class / method instantiation -- matching native
`SigTypeContext`-driven `PeekElemTypeNormalized` behavior.
- `GcScanner.PromoteCallerStack` constructs the provider per call and
walks the `TransitionBlock` using a reserved-slot count derived from
`IsInstance` / return-buffer / `RequiresInstArg` / `IsAsyncMethod` /
ARM64 `x8`, reporting each parameter slot as a GC reference, interior
pointer, or skip.
- Signature acquisition mirrors native `MethodDesc::GetSig`: prefers
`IsStoredSigMethodDesc` (dynamic, EEImpl, array methods) before falling
back to the metadata token, so caller-stack roots of dynamic and
array-method transition frames are handled. Stored sigs are pinned with
an inline `fixed` block and read via a `BlobReader`, matching the
existing `SigFormat.cs` pattern.
- This is the cDAC equivalent of native
`TransitionFrame::PromoteCallerStack` and is used for
`PrestubMethodFrame`, `CallCountingHelperFrame`, and the
`StubDispatchFrame` / `ExternalMethodFrame` fallback when no GCRefMap is
available.
**Signature contract surface stays minimal:**
- `ISignatureDecoder` continues to expose only
`DecodeFieldSignature(BlobHandle, ModuleHandle, TypeHandle)`.
- GC-specific decoding lives entirely inside the StackWalk contract; the
Signature contract has no GC dependencies.
**cDAC documentation:**
- `docs/design/datacontracts/SignatureDecoder.md` -- describes
`RuntimeSignatureDecoder`, `IRuntimeSignatureTypeProvider`, and the
`ELEMENT_TYPE_INTERNAL` / `CMOD_INTERNAL` extensions; refreshed
`DecodeFieldSignature` code sample.
- `docs/design/datacontracts/StackWalk.md` -- new "Signature-Based
Scanning" section under GC stack reference scanning, covering
`GcSignatureTypeProvider` (with module scoping, `GcSignatureContext`,
and enum normalization), the `PromoteCallerStack` algorithm, the
reserved-slot table, and limitations vs. native.
### Testing
- Build: clean, 0 warnings / 0 errors.
- 1921 / 1921 cDAC unit tests pass.
- Behavioral verification against the legacy DAC happens via the
GC-stress verification harness introduced later in the PR series.
> [!NOTE]
> This PR description was created with AI assistance from Copilot.
---------
Co-authored-by: Max Charlamb <maxcharlamb@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jun 11, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@max-charlamb@jkotas@rcj1@steveisok
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

[cDAC] RuntimeSignatureDecoder and centralized Signature contract (2/5) - #127636

Merged
steveisok merged 13 commits into
dotnet:mainfrom
max-charlamb:cdac-stackrefs-pr2
May 11, 2026
Merged

[cDAC] RuntimeSignatureDecoder and centralized Signature contract (2/5)#127636
steveisok merged 13 commits into
dotnet:mainfrom
max-charlamb:cdac-stackrefs-pr2

Conversation

@max-charlamb

@max-charlambmax-charlamb commented May 1, 2026

Copy link
Copy Markdown
Member

Summary

Part 2 of 5 stacked PRs splitting #126408. Builds on #127395 (merged).

This PR adds a cDAC ECMA-335 signature decoder that closely mirrors System.Reflection.Metadata.SignatureDecoder and supports the two runtime-only extensions used in CoreCLR-internal signatures: ELEMENT_TYPE_INTERNAL (0x21) and ELEMENT_TYPE_CMOD_INTERNAL (0x22). The decoder is then used by both the Signature contract (for field signatures) and the StackWalk contract (for signature-based GC reference scanning of transition frames).

What this PR contains

RuntimeSignatureDecoder (SRM-aligned polyfill):

  • RuntimeSignatureDecoder<TType, TGenericContext> -- readonly struct mirroring SRM's SignatureDecoder<TType, TGenericContext> (DecodeType, DecodeFieldSignature, DecodeMethodSignature, DecodeLocalSignature, all taking ref BlobReader).
  • IRuntimeSignatureTypeProvider<TType, TGenericContext> -- superset of SRM's ISignatureTypeProvider adding GetInternalType(TargetPointer) and GetInternalModifiedType(TargetPointer, TType, bool) for the two runtime-only encodings.
  • SignatureTypeProvider (the existing field-signature provider) implements IRuntimeSignatureTypeProvider so internal types in field signatures resolve via RuntimeTypeSystem.GetTypeHandle.

Signature-based GC reference scanning in StackWalk:

  • GcSignatureTypeProvider (internal, in StackWalkHelpers) classifies each method-signature parameter as Ref, Interior, Other (value type or larger-than-slot), or None.
  • A new GcSignatureContext(TypeHandle classContext, MethodDescHandle methodContext) record struct is plumbed through RuntimeSignatureDecoder<GcTypeKind, GcSignatureContext> so ELEMENT_TYPE_VAR / ELEMENT_TYPE_MVAR placeholders resolve to the method's actual class / method instantiation -- matching native SigTypeContext-driven PeekElemTypeNormalized behavior.
  • GcScanner.PromoteCallerStack constructs the provider per call and walks the TransitionBlock using a reserved-slot count derived from IsInstance / return-buffer / RequiresInstArg / IsAsyncMethod / ARM64 x8, reporting each parameter slot as a GC reference, interior pointer, or skip.
  • Signature acquisition mirrors native MethodDesc::GetSig: prefers IsStoredSigMethodDesc (dynamic, EEImpl, array methods) before falling back to the metadata token, so caller-stack roots of dynamic and array-method transition frames are handled. Stored sigs are pinned with an inline fixed block and read via a BlobReader, matching the existing SigFormat.cs pattern.
  • This is the cDAC equivalent of native TransitionFrame::PromoteCallerStack and is used for PrestubMethodFrame, CallCountingHelperFrame, and the StubDispatchFrame / ExternalMethodFrame fallback when no GCRefMap is available.

Signature contract surface stays minimal:

  • ISignatureDecoder continues to expose only DecodeFieldSignature(BlobHandle, ModuleHandle, TypeHandle).
  • GC-specific decoding lives entirely inside the StackWalk contract; the Signature contract has no GC dependencies.

cDAC documentation:

  • docs/design/datacontracts/SignatureDecoder.md -- describes RuntimeSignatureDecoder, IRuntimeSignatureTypeProvider, and the ELEMENT_TYPE_INTERNAL / CMOD_INTERNAL extensions; refreshed DecodeFieldSignature code sample.
  • docs/design/datacontracts/StackWalk.md -- new "Signature-Based Scanning" section under GC stack reference scanning, covering GcSignatureTypeProvider (with module scoping, GcSignatureContext, and enum normalization), the PromoteCallerStack algorithm, the reserved-slot table, and limitations vs. native.

Testing

  • Build: clean, 0 warnings / 0 errors.
  • 1921 / 1921 cDAC unit tests pass.
  • Behavioral verification against the legacy DAC happens via the GC-stress verification harness introduced later in the PR series.

Note

This PR description was created with AI assistance from Copilot.

- Move RuntimeSignatureDecoder to Contracts/Signature/
- Move GcSignatureTypeProvider to Contracts/Signature/ with module-scoped caching
- Add DecodeMethodSignatureForGC(BlobHandle, ModuleHandle) to ISignatureDecoder
- Add DecodeFieldSignature to RuntimeSignatureDecoder
- Add BlobHandleSignatureReader for lazy blob reading
- SignatureTypeProvider implements IRuntimeSignatureTypeProvider
- Switch DecodeFieldSignature to use RuntimeSignatureDecoder
- GcScanner uses contract API instead of direct decoder construction
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @steveisok, @tommcdon, @dotnet/dotnet-diag
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR is part of the cDAC signature-decoding refactor, introducing a custom RuntimeSignatureDecoder capable of handling runtime-internal signature element types (ELEMENT_TYPE_INTERNAL / ELEMENT_TYPE_CMOD_INTERNAL) and centralizing GC-oriented signature decoding behind the ISignatureDecoder contract.

Changes:

  • Added RuntimeSignatureDecoder plus signature-reader abstractions (ISignatureReader, span/blob-backed readers) to support decoding from different signature sources.
  • Extended the ISignatureDecoder contract with DecodeMethodSignatureForGC(...) and promoted GcTypeKind to a public abstraction.
  • Updated stack GC scanning (GcScanner) and signature type providers to route decoding through the centralized contract API.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Show a summary per file
FileDescription
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/GC/GcScanner.csSwitches caller-signature decoding to the ISignatureDecoder.DecodeMethodSignatureForGC contract API.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/SignatureTypeProvider.csUpdates the type provider to implement IRuntimeSignatureTypeProvider and adds internal-type callbacks.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/SignatureReaders.csIntroduces abstractions for reading signature bytes from spans and metadata blobs.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/SignatureDecoder_1.csAdds module-scoped GC provider caching and implements DecodeMethodSignatureForGC using RuntimeSignatureDecoder.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/RuntimeSignatureDecoder.csNew runtime-aware ECMA-335-ish signature decoder with support for internal runtime element types.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/GcSignatureTypeProvider.csMoves/updates GC classification provider to implement the runtime-aware provider interface and classify internal types via RTS.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ISignatureDecoder.csExtends the public contract surface with GcTypeKind and DecodeMethodSignatureForGC.
Comments suppressed due to low confidence (1)

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/GcSignatureTypeProvider.cs:26

  • GcSignatureTypeProvider stores _target and _moduleHandle but never uses either of them (the implementation uses the target method parameter instead). This is dead state that should be removed, or the implementation should consistently use the stored fields if they're intended for future module/target-scoped behavior.

Max Charlamband others added 2 commits May 1, 2026 00:50
Rewrites RuntimeSignatureDecoder as a readonly struct that mirrors SRM's SignatureDecoder API (ref BlobReader per method, allowTypeSpecifications flag), with only ELEMENT_TYPE_INTERNAL (0x21) and CMOD_INTERNAL (0x22) added on top via the new IRuntimeSignatureTypeProvider interface. Drops the custom ISignatureReader/BlobHandleSignatureReader/SpanSignatureReader abstraction since BlobReader already provides lazy reading.
Fixes two latent bugs vs SRM: TypeDefOrRefOrSpec tag=3 now throws (was incorrectly returning Object), and the leading element type code is read as a compressed integer rather than a raw byte.
Moves GC-specific signature decoding out of the Signature contract into the StackWalk contract (Option B). GcSignatureTypeProvider and GcTypeKind move to Contracts/StackWalk/GC/ under the StackWalkHelpers namespace. ISignatureDecoder no longer exposes DecodeMethodSignatureForGC; the _gcProviders cache is removed from SignatureDecoder_1; GcScanner constructs RuntimeSignatureDecoder<GcTypeKind, object?> directly.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…adataReader
Splits IRuntimeSignatureTypeProvider into its own file (matches cDAC convention for internal interfaces). Makes MetadataReader required on RuntimeSignatureDecoder (matches SRM exactly; both call sites already pass non-null) and removes the null-forgiving operators. Reorders constructor params to (provider, target, metadataReader, genericContext) to mirror SRM's parameter order.
Cleanup: removes unused _mdhProviders dictionary and GetMethodDescHandleProvider from SignatureDecoder_1; removes unused _target/_moduleHandle fields and constructor from GcSignatureTypeProvider; renames _metadataReaderOpt to _metadataReader (Opt suffix is non-standard for a nullable-typed field); converts block comment to line comments.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings May 1, 2026 05:04

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.

Drop the redundant `Target target` parameter from
`IRuntimeSignatureTypeProvider.GetInternalType` and
`GetInternalModifiedType`. Providers now capture the target in their
own constructor when they need it, matching `SignatureTypeProvider<T>`.
`GcSignatureTypeProvider` gains a `Target` constructor parameter.
Update the cDAC documentation:
* `SignatureDecoder.md` -- describe `RuntimeSignatureDecoder`,
`IRuntimeSignatureTypeProvider`, and the runtime-only
`ELEMENT_TYPE_INTERNAL` / `ELEMENT_TYPE_CMOD_INTERNAL` extensions;
refresh the `DecodeFieldSignature` code sample.
* `StackWalk.md` -- new Signature-Based Scanning section covering
`GcSignatureTypeProvider`, the `PromoteCallerStack` algorithm,
reserved-slot table, and limitations vs. native.
Also revert a stray whitespace change in `ISignatureDecoder.cs`.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@max-charlamb
max-charlamb marked this pull request as ready for review May 1, 2026 05:16
CopilotAI review requested due to automatic review settings May 1, 2026 05:16

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.

Comments suppressed due to low confidence (2)

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/SignatureTypeProvider.cs:25

  • The private field _target is assigned in the constructor but never read in this type, which will trigger CS0414 (and in this repo, warnings are treated as errors by default). Please remove _target (and its assignment) or start using it (e.g., for validation) to avoid build breaks.
 private readonly Target _target;
private readonly Contracts.ModuleHandle _moduleHandle;
private readonly Contracts.ILoader _loader;
private readonly Contracts.IRuntimeTypeSystem _runtimeTypeSystem;
public SignatureTypeProvider(Target target, Contracts.ModuleHandle moduleHandle)
{
_target = target;
_moduleHandle = moduleHandle;
_loader = target.Contracts.Loader;
_runtimeTypeSystem = target.Contracts.RuntimeTypeSystem;

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/GC/GcScanner.cs:380

  • isValueTypeThis is always initialized to false and never updated, so this is never reported with GC_CALL_INTERIOR even for value-type instance methods. Since you already resolve the declaring TypeHandle earlier, it seems like this should be computed (e.g., via IRuntimeTypeSystem.IsValueType(typeHandle)) and included in the existing try/catch alongside RequiresInstArg / IsAsyncMethod.
 bool hasThis = methodSig.Header.IsInstance;
bool hasRetBuf = methodSig.ReturnType is GcTypeKind.Other;
bool requiresInstArg = false;
bool isAsync = false;
bool isValueTypeThis = false;
try
{
requiresInstArg = rts.RequiresInstArg(mdh);
isAsync = rts.IsAsyncMethod(mdh);
}
catch
{
}
PromoteCallerStackHelper(transitionBlock, methodSig, hasThis, hasRetBuf,
requiresInstArg, isAsync, isValueTypeThis, scanContext);
}

Comment threaddocs/design/datacontracts/SignatureDecoder.md Outdated
Max Charlamband others added 2 commits May 1, 2026 01:34
…rovider
Match native MethodDesc::GetSig in GcScanner.PromoteCallerStack: try
IsStoredSigMethodDesc first (dynamic, EEImpl, and array method descs) and
read the stored signature via an inline pinned BlobReader before falling
back to the metadata token lookup. Cache the GcSignatureTypeProvider on
the GcScanner so it is allocated once instead of per call.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…mitive
GcSignatureTypeProvider now classifies type parameters and TypeDef/TypeRef
tokens using the loaded TypeHandle, matching native SigTypeContext-driven
PeekElemTypeNormalized behavior:
- VAR/MVAR placeholders are resolved against the method's actual class /
method instantiation via a new GcSignatureContext(TypeHandle, MethodDescHandle)
passed as the SRM generic context.
- TypeDef/TypeRef tokens are resolved via the module's TypeDefToMethodTable /
TypeRefToMethodTable lookup tables; enums collapse to their underlying
primitive (None) via IRuntimeTypeSystem.IsEnum, matching
MethodTable::GetInternalCorElementType.
The provider is now module-scoped, so it is constructed per PromoteCallerStack
call rather than cached on GcScanner.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings May 1, 2026 05:51

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.

Comment threaddocs/design/datacontracts/SignatureDecoder.md Outdated
- IRuntimeSignatureTypeProvider: internal -> public to match SignatureTypeProvider's
visibility (avoids the "public class implements internal interface" warning while
keeping the provider type available to downstream consumers).
- RuntimeSignatureDecoder: drop the unused _target field and the redundant
ArgumentNullException.ThrowIfNull(provider/metadataReader) calls; the file is
in a nullable-enabled context, so the type system already enforces non-null.
- SignatureDecoder.md: drop the bogus `target,` argument from the GetInternalType
/ GetInternalModifiedType bullets, and reword GetTypeFromDefinition /
GetTypeFromReference's "returns null" to "returns a default TypeHandle
(Address == TargetPointer.Null)" since the API returns a struct value.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@max-charlamb
max-charlamb requested a review from rcj1May 1, 2026 15:08
Comment threaddocs/design/datacontracts/SignatureDecoder.md Outdated
Comment threaddocs/design/datacontracts/Signature.md Outdated
Comment threaddocs/design/datacontracts/Signature.md Outdated
Comment threaddocs/design/datacontracts/StackWalk.md Outdated
Comment threaddocs/design/datacontracts/Signature.md Outdated
Comment threaddocs/design/datacontracts/Signature.md Outdated
Comment threaddocs/design/datacontracts/Signature.md Outdated
CopilotAI review requested due to automatic review settings May 4, 2026 17:45

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 4 comments.

Comments suppressed due to low confidence (1)

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ISignature.cs:16

  • Renaming the public abstractions from ISignatureDecoder/SignatureDecoder to ISignature/Signature is a source/binary breaking change for any consumer of Microsoft.Diagnostics.DataContractReader.Abstractions, and this PR doesn't preserve compatibility shims. The PR metadata only links other PRs, not an api-approved issue, so this public surface either needs prior approval or it needs to stay internal until the rename is reviewed.

Comment threadsrc/coreclr/vm/datadescriptor/datadescriptor.inc
Comment threaddocs/design/datacontracts/StackWalk.md
Match native SigTypeContext::InitTypeContext: array MTs use the element
type as their class instantiation. RuntimeTypeSystem.GetInstantiation
returns an empty span for arrays, so signature decoding for array
accessor methods (e.g., int[]::Set with VAR 0) was misclassifying the
element-type slot as Ref. Special-case IsArray and use GetTypeParam,
the managed equivalent of MethodTable::GetArrayInstantiation.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
rcj1
rcj1 approved these changes May 8, 2026
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings May 8, 2026 21:56
@max-charlamb
max-charlamb enabled auto-merge (squash) May 8, 2026 22:01

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ISignature.cs:16

  • Renaming the contract from SignatureDecoder to Signature changes the wire-level lookup key (TContract.Name) used by CachingContractRegistry (it calls _tryGetContractVersion(TContract.Name, ...)). This will break compatibility between newer readers and older runtimes/dumps that only advertise SignatureDecoder (and vice versa), since contract versioning can’t help if the name doesn’t match. Consider keeping the contract name stable (e.g., keep IContract.Name as "SignatureDecoder"), or publish an alias contract on the runtime side and keep the legacy ISignatureDecoder surface as a forwarding shim so existing readers can still resolve the contract.

Comment threaddocs/design/datacontracts/StackWalk.md
@max-charlamb

Copy link
Copy Markdown
MemberAuthor

/ba-g unrelated test failures

@max-charlamb
max-charlamb disabled auto-merge May 10, 2026 02:42
@max-charlamb
max-charlamb enabled auto-merge (squash) May 10, 2026 02:44
@max-charlamb

Copy link
Copy Markdown
MemberAuthor

/ba-g cDAC only change, the only cDAC test failure is a known issue on osx

@steveisok
steveisok disabled auto-merge May 11, 2026 14:53
@steveisok
steveisok merged commit 6f89eaf into dotnet:mainMay 11, 2026
120 of 125 checks passed
jakobbotsch pushed a commit to jakobbotsch/runtime that referenced this pull request May 12, 2026
…5) (dotnet#127636)
## Summary
Part 2 of 5 stacked PRs splitting
[dotnet#126408](dotnet#126408). Builds on
[dotnet#127395](dotnet#127395) (merged).
This PR adds a cDAC ECMA-335 signature decoder that closely mirrors
`System.Reflection.Metadata.SignatureDecoder` and supports the two
runtime-only extensions used in CoreCLR-internal signatures:
`ELEMENT_TYPE_INTERNAL` (`0x21`) and `ELEMENT_TYPE_CMOD_INTERNAL`
(`0x22`). The decoder is then used by both the Signature contract (for
field signatures) and the StackWalk contract (for signature-based GC
reference scanning of transition frames).
### What this PR contains
**`RuntimeSignatureDecoder` (SRM-aligned polyfill):**
- `RuntimeSignatureDecoder<TType, TGenericContext>` -- readonly struct
mirroring SRM's `SignatureDecoder<TType, TGenericContext>`
(`DecodeType`, `DecodeFieldSignature`, `DecodeMethodSignature`,
`DecodeLocalSignature`, all taking `ref BlobReader`).
- `IRuntimeSignatureTypeProvider<TType, TGenericContext>` -- superset of
SRM's `ISignatureTypeProvider` adding `GetInternalType(TargetPointer)`
and `GetInternalModifiedType(TargetPointer, TType, bool)` for the two
runtime-only encodings.
- `SignatureTypeProvider` (the existing field-signature provider)
implements `IRuntimeSignatureTypeProvider` so internal types in field
signatures resolve via `RuntimeTypeSystem.GetTypeHandle`.
**Signature-based GC reference scanning in StackWalk:**
- `GcSignatureTypeProvider` (internal, in `StackWalkHelpers`) classifies
each method-signature parameter as `Ref`, `Interior`, `Other` (value
type or larger-than-slot), or `None`.
- A new `GcSignatureContext(TypeHandle classContext, MethodDescHandle
methodContext)` record struct is plumbed through
`RuntimeSignatureDecoder<GcTypeKind, GcSignatureContext>` so
`ELEMENT_TYPE_VAR` / `ELEMENT_TYPE_MVAR` placeholders resolve to the
method's actual class / method instantiation -- matching native
`SigTypeContext`-driven `PeekElemTypeNormalized` behavior.
- `GcScanner.PromoteCallerStack` constructs the provider per call and
walks the `TransitionBlock` using a reserved-slot count derived from
`IsInstance` / return-buffer / `RequiresInstArg` / `IsAsyncMethod` /
ARM64 `x8`, reporting each parameter slot as a GC reference, interior
pointer, or skip.
- Signature acquisition mirrors native `MethodDesc::GetSig`: prefers
`IsStoredSigMethodDesc` (dynamic, EEImpl, array methods) before falling
back to the metadata token, so caller-stack roots of dynamic and
array-method transition frames are handled. Stored sigs are pinned with
an inline `fixed` block and read via a `BlobReader`, matching the
existing `SigFormat.cs` pattern.
- This is the cDAC equivalent of native
`TransitionFrame::PromoteCallerStack` and is used for
`PrestubMethodFrame`, `CallCountingHelperFrame`, and the
`StubDispatchFrame` / `ExternalMethodFrame` fallback when no GCRefMap is
available.
**Signature contract surface stays minimal:**
- `ISignatureDecoder` continues to expose only
`DecodeFieldSignature(BlobHandle, ModuleHandle, TypeHandle)`.
- GC-specific decoding lives entirely inside the StackWalk contract; the
Signature contract has no GC dependencies.
**cDAC documentation:**
- `docs/design/datacontracts/SignatureDecoder.md` -- describes
`RuntimeSignatureDecoder`, `IRuntimeSignatureTypeProvider`, and the
`ELEMENT_TYPE_INTERNAL` / `CMOD_INTERNAL` extensions; refreshed
`DecodeFieldSignature` code sample.
- `docs/design/datacontracts/StackWalk.md` -- new "Signature-Based
Scanning" section under GC stack reference scanning, covering
`GcSignatureTypeProvider` (with module scoping, `GcSignatureContext`,
and enum normalization), the `PromoteCallerStack` algorithm, the
reserved-slot table, and limitations vs. native.
### Testing
- Build: clean, 0 warnings / 0 errors.
- 1921 / 1921 cDAC unit tests pass.
- Behavioral verification against the legacy DAC happens via the
GC-stress verification harness introduced later in the PR series.
> [!NOTE]
> This PR description was created with AI assistance from Copilot.
---------
Co-authored-by: Max Charlamb <maxcharlamb@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jun 11, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@max-charlamb@jkotas@rcj1@steveisok
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

[cDAC] RuntimeSignatureDecoder and centralized Signature contract (2/5) - #127636

Merged
steveisok merged 13 commits into
dotnet:mainfrom
max-charlamb:cdac-stackrefs-pr2
May 11, 2026
Merged

[cDAC] RuntimeSignatureDecoder and centralized Signature contract (2/5)#127636
steveisok merged 13 commits into
dotnet:mainfrom
max-charlamb:cdac-stackrefs-pr2

Conversation

@max-charlamb

@max-charlambmax-charlamb commented May 1, 2026

Copy link
Copy Markdown
Member

Summary

Part 2 of 5 stacked PRs splitting #126408. Builds on #127395 (merged).

This PR adds a cDAC ECMA-335 signature decoder that closely mirrors System.Reflection.Metadata.SignatureDecoder and supports the two runtime-only extensions used in CoreCLR-internal signatures: ELEMENT_TYPE_INTERNAL (0x21) and ELEMENT_TYPE_CMOD_INTERNAL (0x22). The decoder is then used by both the Signature contract (for field signatures) and the StackWalk contract (for signature-based GC reference scanning of transition frames).

What this PR contains

RuntimeSignatureDecoder (SRM-aligned polyfill):

  • RuntimeSignatureDecoder<TType, TGenericContext> -- readonly struct mirroring SRM's SignatureDecoder<TType, TGenericContext> (DecodeType, DecodeFieldSignature, DecodeMethodSignature, DecodeLocalSignature, all taking ref BlobReader).
  • IRuntimeSignatureTypeProvider<TType, TGenericContext> -- superset of SRM's ISignatureTypeProvider adding GetInternalType(TargetPointer) and GetInternalModifiedType(TargetPointer, TType, bool) for the two runtime-only encodings.
  • SignatureTypeProvider (the existing field-signature provider) implements IRuntimeSignatureTypeProvider so internal types in field signatures resolve via RuntimeTypeSystem.GetTypeHandle.

Signature-based GC reference scanning in StackWalk:

  • GcSignatureTypeProvider (internal, in StackWalkHelpers) classifies each method-signature parameter as Ref, Interior, Other (value type or larger-than-slot), or None.
  • A new GcSignatureContext(TypeHandle classContext, MethodDescHandle methodContext) record struct is plumbed through RuntimeSignatureDecoder<GcTypeKind, GcSignatureContext> so ELEMENT_TYPE_VAR / ELEMENT_TYPE_MVAR placeholders resolve to the method's actual class / method instantiation -- matching native SigTypeContext-driven PeekElemTypeNormalized behavior.
  • GcScanner.PromoteCallerStack constructs the provider per call and walks the TransitionBlock using a reserved-slot count derived from IsInstance / return-buffer / RequiresInstArg / IsAsyncMethod / ARM64 x8, reporting each parameter slot as a GC reference, interior pointer, or skip.
  • Signature acquisition mirrors native MethodDesc::GetSig: prefers IsStoredSigMethodDesc (dynamic, EEImpl, array methods) before falling back to the metadata token, so caller-stack roots of dynamic and array-method transition frames are handled. Stored sigs are pinned with an inline fixed block and read via a BlobReader, matching the existing SigFormat.cs pattern.
  • This is the cDAC equivalent of native TransitionFrame::PromoteCallerStack and is used for PrestubMethodFrame, CallCountingHelperFrame, and the StubDispatchFrame / ExternalMethodFrame fallback when no GCRefMap is available.

Signature contract surface stays minimal:

  • ISignatureDecoder continues to expose only DecodeFieldSignature(BlobHandle, ModuleHandle, TypeHandle).
  • GC-specific decoding lives entirely inside the StackWalk contract; the Signature contract has no GC dependencies.

cDAC documentation:

  • docs/design/datacontracts/SignatureDecoder.md -- describes RuntimeSignatureDecoder, IRuntimeSignatureTypeProvider, and the ELEMENT_TYPE_INTERNAL / CMOD_INTERNAL extensions; refreshed DecodeFieldSignature code sample.
  • docs/design/datacontracts/StackWalk.md -- new "Signature-Based Scanning" section under GC stack reference scanning, covering GcSignatureTypeProvider (with module scoping, GcSignatureContext, and enum normalization), the PromoteCallerStack algorithm, the reserved-slot table, and limitations vs. native.

Testing

  • Build: clean, 0 warnings / 0 errors.
  • 1921 / 1921 cDAC unit tests pass.
  • Behavioral verification against the legacy DAC happens via the GC-stress verification harness introduced later in the PR series.

Note

This PR description was created with AI assistance from Copilot.

- Move RuntimeSignatureDecoder to Contracts/Signature/
- Move GcSignatureTypeProvider to Contracts/Signature/ with module-scoped caching
- Add DecodeMethodSignatureForGC(BlobHandle, ModuleHandle) to ISignatureDecoder
- Add DecodeFieldSignature to RuntimeSignatureDecoder
- Add BlobHandleSignatureReader for lazy blob reading
- SignatureTypeProvider implements IRuntimeSignatureTypeProvider
- Switch DecodeFieldSignature to use RuntimeSignatureDecoder
- GcScanner uses contract API instead of direct decoder construction
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @steveisok, @tommcdon, @dotnet/dotnet-diag
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR is part of the cDAC signature-decoding refactor, introducing a custom RuntimeSignatureDecoder capable of handling runtime-internal signature element types (ELEMENT_TYPE_INTERNAL / ELEMENT_TYPE_CMOD_INTERNAL) and centralizing GC-oriented signature decoding behind the ISignatureDecoder contract.

Changes:

  • Added RuntimeSignatureDecoder plus signature-reader abstractions (ISignatureReader, span/blob-backed readers) to support decoding from different signature sources.
  • Extended the ISignatureDecoder contract with DecodeMethodSignatureForGC(...) and promoted GcTypeKind to a public abstraction.
  • Updated stack GC scanning (GcScanner) and signature type providers to route decoding through the centralized contract API.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Show a summary per file
FileDescription
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/GC/GcScanner.csSwitches caller-signature decoding to the ISignatureDecoder.DecodeMethodSignatureForGC contract API.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/SignatureTypeProvider.csUpdates the type provider to implement IRuntimeSignatureTypeProvider and adds internal-type callbacks.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/SignatureReaders.csIntroduces abstractions for reading signature bytes from spans and metadata blobs.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/SignatureDecoder_1.csAdds module-scoped GC provider caching and implements DecodeMethodSignatureForGC using RuntimeSignatureDecoder.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/RuntimeSignatureDecoder.csNew runtime-aware ECMA-335-ish signature decoder with support for internal runtime element types.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/GcSignatureTypeProvider.csMoves/updates GC classification provider to implement the runtime-aware provider interface and classify internal types via RTS.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ISignatureDecoder.csExtends the public contract surface with GcTypeKind and DecodeMethodSignatureForGC.
Comments suppressed due to low confidence (1)

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/GcSignatureTypeProvider.cs:26

  • GcSignatureTypeProvider stores _target and _moduleHandle but never uses either of them (the implementation uses the target method parameter instead). This is dead state that should be removed, or the implementation should consistently use the stored fields if they're intended for future module/target-scoped behavior.

Max Charlamband others added 2 commits May 1, 2026 00:50
Rewrites RuntimeSignatureDecoder as a readonly struct that mirrors SRM's SignatureDecoder API (ref BlobReader per method, allowTypeSpecifications flag), with only ELEMENT_TYPE_INTERNAL (0x21) and CMOD_INTERNAL (0x22) added on top via the new IRuntimeSignatureTypeProvider interface. Drops the custom ISignatureReader/BlobHandleSignatureReader/SpanSignatureReader abstraction since BlobReader already provides lazy reading.
Fixes two latent bugs vs SRM: TypeDefOrRefOrSpec tag=3 now throws (was incorrectly returning Object), and the leading element type code is read as a compressed integer rather than a raw byte.
Moves GC-specific signature decoding out of the Signature contract into the StackWalk contract (Option B). GcSignatureTypeProvider and GcTypeKind move to Contracts/StackWalk/GC/ under the StackWalkHelpers namespace. ISignatureDecoder no longer exposes DecodeMethodSignatureForGC; the _gcProviders cache is removed from SignatureDecoder_1; GcScanner constructs RuntimeSignatureDecoder<GcTypeKind, object?> directly.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…adataReader
Splits IRuntimeSignatureTypeProvider into its own file (matches cDAC convention for internal interfaces). Makes MetadataReader required on RuntimeSignatureDecoder (matches SRM exactly; both call sites already pass non-null) and removes the null-forgiving operators. Reorders constructor params to (provider, target, metadataReader, genericContext) to mirror SRM's parameter order.
Cleanup: removes unused _mdhProviders dictionary and GetMethodDescHandleProvider from SignatureDecoder_1; removes unused _target/_moduleHandle fields and constructor from GcSignatureTypeProvider; renames _metadataReaderOpt to _metadataReader (Opt suffix is non-standard for a nullable-typed field); converts block comment to line comments.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings May 1, 2026 05:04

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.

Drop the redundant `Target target` parameter from
`IRuntimeSignatureTypeProvider.GetInternalType` and
`GetInternalModifiedType`. Providers now capture the target in their
own constructor when they need it, matching `SignatureTypeProvider<T>`.
`GcSignatureTypeProvider` gains a `Target` constructor parameter.
Update the cDAC documentation:
* `SignatureDecoder.md` -- describe `RuntimeSignatureDecoder`,
`IRuntimeSignatureTypeProvider`, and the runtime-only
`ELEMENT_TYPE_INTERNAL` / `ELEMENT_TYPE_CMOD_INTERNAL` extensions;
refresh the `DecodeFieldSignature` code sample.
* `StackWalk.md` -- new Signature-Based Scanning section covering
`GcSignatureTypeProvider`, the `PromoteCallerStack` algorithm,
reserved-slot table, and limitations vs. native.
Also revert a stray whitespace change in `ISignatureDecoder.cs`.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@max-charlamb
max-charlamb marked this pull request as ready for review May 1, 2026 05:16
CopilotAI review requested due to automatic review settings May 1, 2026 05:16

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.

Comments suppressed due to low confidence (2)

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/SignatureTypeProvider.cs:25

  • The private field _target is assigned in the constructor but never read in this type, which will trigger CS0414 (and in this repo, warnings are treated as errors by default). Please remove _target (and its assignment) or start using it (e.g., for validation) to avoid build breaks.
 private readonly Target _target;
private readonly Contracts.ModuleHandle _moduleHandle;
private readonly Contracts.ILoader _loader;
private readonly Contracts.IRuntimeTypeSystem _runtimeTypeSystem;
public SignatureTypeProvider(Target target, Contracts.ModuleHandle moduleHandle)
{
_target = target;
_moduleHandle = moduleHandle;
_loader = target.Contracts.Loader;
_runtimeTypeSystem = target.Contracts.RuntimeTypeSystem;

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/GC/GcScanner.cs:380

  • isValueTypeThis is always initialized to false and never updated, so this is never reported with GC_CALL_INTERIOR even for value-type instance methods. Since you already resolve the declaring TypeHandle earlier, it seems like this should be computed (e.g., via IRuntimeTypeSystem.IsValueType(typeHandle)) and included in the existing try/catch alongside RequiresInstArg / IsAsyncMethod.
 bool hasThis = methodSig.Header.IsInstance;
bool hasRetBuf = methodSig.ReturnType is GcTypeKind.Other;
bool requiresInstArg = false;
bool isAsync = false;
bool isValueTypeThis = false;
try
{
requiresInstArg = rts.RequiresInstArg(mdh);
isAsync = rts.IsAsyncMethod(mdh);
}
catch
{
}
PromoteCallerStackHelper(transitionBlock, methodSig, hasThis, hasRetBuf,
requiresInstArg, isAsync, isValueTypeThis, scanContext);
}

Comment threaddocs/design/datacontracts/SignatureDecoder.md Outdated
Max Charlamband others added 2 commits May 1, 2026 01:34
…rovider
Match native MethodDesc::GetSig in GcScanner.PromoteCallerStack: try
IsStoredSigMethodDesc first (dynamic, EEImpl, and array method descs) and
read the stored signature via an inline pinned BlobReader before falling
back to the metadata token lookup. Cache the GcSignatureTypeProvider on
the GcScanner so it is allocated once instead of per call.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…mitive
GcSignatureTypeProvider now classifies type parameters and TypeDef/TypeRef
tokens using the loaded TypeHandle, matching native SigTypeContext-driven
PeekElemTypeNormalized behavior:
- VAR/MVAR placeholders are resolved against the method's actual class /
method instantiation via a new GcSignatureContext(TypeHandle, MethodDescHandle)
passed as the SRM generic context.
- TypeDef/TypeRef tokens are resolved via the module's TypeDefToMethodTable /
TypeRefToMethodTable lookup tables; enums collapse to their underlying
primitive (None) via IRuntimeTypeSystem.IsEnum, matching
MethodTable::GetInternalCorElementType.
The provider is now module-scoped, so it is constructed per PromoteCallerStack
call rather than cached on GcScanner.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings May 1, 2026 05:51

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.

Comment threaddocs/design/datacontracts/SignatureDecoder.md Outdated
- IRuntimeSignatureTypeProvider: internal -> public to match SignatureTypeProvider's
visibility (avoids the "public class implements internal interface" warning while
keeping the provider type available to downstream consumers).
- RuntimeSignatureDecoder: drop the unused _target field and the redundant
ArgumentNullException.ThrowIfNull(provider/metadataReader) calls; the file is
in a nullable-enabled context, so the type system already enforces non-null.
- SignatureDecoder.md: drop the bogus `target,` argument from the GetInternalType
/ GetInternalModifiedType bullets, and reword GetTypeFromDefinition /
GetTypeFromReference's "returns null" to "returns a default TypeHandle
(Address == TargetPointer.Null)" since the API returns a struct value.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@max-charlamb
max-charlamb requested a review from rcj1May 1, 2026 15:08
Comment threaddocs/design/datacontracts/SignatureDecoder.md Outdated
Comment threaddocs/design/datacontracts/Signature.md Outdated
Comment threaddocs/design/datacontracts/Signature.md Outdated
Comment threaddocs/design/datacontracts/StackWalk.md Outdated
Comment threaddocs/design/datacontracts/Signature.md Outdated
Comment threaddocs/design/datacontracts/Signature.md Outdated
Comment threaddocs/design/datacontracts/Signature.md Outdated
CopilotAI review requested due to automatic review settings May 4, 2026 17:45

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 4 comments.

Comments suppressed due to low confidence (1)

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ISignature.cs:16

  • Renaming the public abstractions from ISignatureDecoder/SignatureDecoder to ISignature/Signature is a source/binary breaking change for any consumer of Microsoft.Diagnostics.DataContractReader.Abstractions, and this PR doesn't preserve compatibility shims. The PR metadata only links other PRs, not an api-approved issue, so this public surface either needs prior approval or it needs to stay internal until the rename is reviewed.

Comment threadsrc/coreclr/vm/datadescriptor/datadescriptor.inc
Comment threaddocs/design/datacontracts/StackWalk.md
Match native SigTypeContext::InitTypeContext: array MTs use the element
type as their class instantiation. RuntimeTypeSystem.GetInstantiation
returns an empty span for arrays, so signature decoding for array
accessor methods (e.g., int[]::Set with VAR 0) was misclassifying the
element-type slot as Ref. Special-case IsArray and use GetTypeParam,
the managed equivalent of MethodTable::GetArrayInstantiation.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
rcj1
rcj1 approved these changes May 8, 2026
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings May 8, 2026 21:56
@max-charlamb
max-charlamb enabled auto-merge (squash) May 8, 2026 22:01

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ISignature.cs:16

  • Renaming the contract from SignatureDecoder to Signature changes the wire-level lookup key (TContract.Name) used by CachingContractRegistry (it calls _tryGetContractVersion(TContract.Name, ...)). This will break compatibility between newer readers and older runtimes/dumps that only advertise SignatureDecoder (and vice versa), since contract versioning can’t help if the name doesn’t match. Consider keeping the contract name stable (e.g., keep IContract.Name as "SignatureDecoder"), or publish an alias contract on the runtime side and keep the legacy ISignatureDecoder surface as a forwarding shim so existing readers can still resolve the contract.

Comment threaddocs/design/datacontracts/StackWalk.md
@max-charlamb

Copy link
Copy Markdown
MemberAuthor

/ba-g unrelated test failures

@max-charlamb
max-charlamb disabled auto-merge May 10, 2026 02:42
@max-charlamb
max-charlamb enabled auto-merge (squash) May 10, 2026 02:44
@max-charlamb

Copy link
Copy Markdown
MemberAuthor

/ba-g cDAC only change, the only cDAC test failure is a known issue on osx

@steveisok
steveisok disabled auto-merge May 11, 2026 14:53
@steveisok
steveisok merged commit 6f89eaf into dotnet:mainMay 11, 2026
120 of 125 checks passed
jakobbotsch pushed a commit to jakobbotsch/runtime that referenced this pull request May 12, 2026
…5) (dotnet#127636)
## Summary
Part 2 of 5 stacked PRs splitting
[dotnet#126408](dotnet#126408). Builds on
[dotnet#127395](dotnet#127395) (merged).
This PR adds a cDAC ECMA-335 signature decoder that closely mirrors
`System.Reflection.Metadata.SignatureDecoder` and supports the two
runtime-only extensions used in CoreCLR-internal signatures:
`ELEMENT_TYPE_INTERNAL` (`0x21`) and `ELEMENT_TYPE_CMOD_INTERNAL`
(`0x22`). The decoder is then used by both the Signature contract (for
field signatures) and the StackWalk contract (for signature-based GC
reference scanning of transition frames).
### What this PR contains
**`RuntimeSignatureDecoder` (SRM-aligned polyfill):**
- `RuntimeSignatureDecoder<TType, TGenericContext>` -- readonly struct
mirroring SRM's `SignatureDecoder<TType, TGenericContext>`
(`DecodeType`, `DecodeFieldSignature`, `DecodeMethodSignature`,
`DecodeLocalSignature`, all taking `ref BlobReader`).
- `IRuntimeSignatureTypeProvider<TType, TGenericContext>` -- superset of
SRM's `ISignatureTypeProvider` adding `GetInternalType(TargetPointer)`
and `GetInternalModifiedType(TargetPointer, TType, bool)` for the two
runtime-only encodings.
- `SignatureTypeProvider` (the existing field-signature provider)
implements `IRuntimeSignatureTypeProvider` so internal types in field
signatures resolve via `RuntimeTypeSystem.GetTypeHandle`.
**Signature-based GC reference scanning in StackWalk:**
- `GcSignatureTypeProvider` (internal, in `StackWalkHelpers`) classifies
each method-signature parameter as `Ref`, `Interior`, `Other` (value
type or larger-than-slot), or `None`.
- A new `GcSignatureContext(TypeHandle classContext, MethodDescHandle
methodContext)` record struct is plumbed through
`RuntimeSignatureDecoder<GcTypeKind, GcSignatureContext>` so
`ELEMENT_TYPE_VAR` / `ELEMENT_TYPE_MVAR` placeholders resolve to the
method's actual class / method instantiation -- matching native
`SigTypeContext`-driven `PeekElemTypeNormalized` behavior.
- `GcScanner.PromoteCallerStack` constructs the provider per call and
walks the `TransitionBlock` using a reserved-slot count derived from
`IsInstance` / return-buffer / `RequiresInstArg` / `IsAsyncMethod` /
ARM64 `x8`, reporting each parameter slot as a GC reference, interior
pointer, or skip.
- Signature acquisition mirrors native `MethodDesc::GetSig`: prefers
`IsStoredSigMethodDesc` (dynamic, EEImpl, array methods) before falling
back to the metadata token, so caller-stack roots of dynamic and
array-method transition frames are handled. Stored sigs are pinned with
an inline `fixed` block and read via a `BlobReader`, matching the
existing `SigFormat.cs` pattern.
- This is the cDAC equivalent of native
`TransitionFrame::PromoteCallerStack` and is used for
`PrestubMethodFrame`, `CallCountingHelperFrame`, and the
`StubDispatchFrame` / `ExternalMethodFrame` fallback when no GCRefMap is
available.
**Signature contract surface stays minimal:**
- `ISignatureDecoder` continues to expose only
`DecodeFieldSignature(BlobHandle, ModuleHandle, TypeHandle)`.
- GC-specific decoding lives entirely inside the StackWalk contract; the
Signature contract has no GC dependencies.
**cDAC documentation:**
- `docs/design/datacontracts/SignatureDecoder.md` -- describes
`RuntimeSignatureDecoder`, `IRuntimeSignatureTypeProvider`, and the
`ELEMENT_TYPE_INTERNAL` / `CMOD_INTERNAL` extensions; refreshed
`DecodeFieldSignature` code sample.
- `docs/design/datacontracts/StackWalk.md` -- new "Signature-Based
Scanning" section under GC stack reference scanning, covering
`GcSignatureTypeProvider` (with module scoping, `GcSignatureContext`,
and enum normalization), the `PromoteCallerStack` algorithm, the
reserved-slot table, and limitations vs. native.
### Testing
- Build: clean, 0 warnings / 0 errors.
- 1921 / 1921 cDAC unit tests pass.
- Behavioral verification against the legacy DAC happens via the
GC-stress verification harness introduced later in the PR series.
> [!NOTE]
> This PR description was created with AI assistance from Copilot.
---------
Co-authored-by: Max Charlamb <maxcharlamb@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jun 11, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@max-charlamb@jkotas@rcj1@steveisok
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

[cDAC] RuntimeSignatureDecoder and centralized Signature contract (2/5) - #127636

Merged
steveisok merged 13 commits into
dotnet:mainfrom
max-charlamb:cdac-stackrefs-pr2
May 11, 2026
Merged

[cDAC] RuntimeSignatureDecoder and centralized Signature contract (2/5)#127636
steveisok merged 13 commits into
dotnet:mainfrom
max-charlamb:cdac-stackrefs-pr2

Conversation

@max-charlamb

@max-charlambmax-charlamb commented May 1, 2026

Copy link
Copy Markdown
Member

Summary

Part 2 of 5 stacked PRs splitting #126408. Builds on #127395 (merged).

This PR adds a cDAC ECMA-335 signature decoder that closely mirrors System.Reflection.Metadata.SignatureDecoder and supports the two runtime-only extensions used in CoreCLR-internal signatures: ELEMENT_TYPE_INTERNAL (0x21) and ELEMENT_TYPE_CMOD_INTERNAL (0x22). The decoder is then used by both the Signature contract (for field signatures) and the StackWalk contract (for signature-based GC reference scanning of transition frames).

What this PR contains

RuntimeSignatureDecoder (SRM-aligned polyfill):

  • RuntimeSignatureDecoder<TType, TGenericContext> -- readonly struct mirroring SRM's SignatureDecoder<TType, TGenericContext> (DecodeType, DecodeFieldSignature, DecodeMethodSignature, DecodeLocalSignature, all taking ref BlobReader).
  • IRuntimeSignatureTypeProvider<TType, TGenericContext> -- superset of SRM's ISignatureTypeProvider adding GetInternalType(TargetPointer) and GetInternalModifiedType(TargetPointer, TType, bool) for the two runtime-only encodings.
  • SignatureTypeProvider (the existing field-signature provider) implements IRuntimeSignatureTypeProvider so internal types in field signatures resolve via RuntimeTypeSystem.GetTypeHandle.

Signature-based GC reference scanning in StackWalk:

  • GcSignatureTypeProvider (internal, in StackWalkHelpers) classifies each method-signature parameter as Ref, Interior, Other (value type or larger-than-slot), or None.
  • A new GcSignatureContext(TypeHandle classContext, MethodDescHandle methodContext) record struct is plumbed through RuntimeSignatureDecoder<GcTypeKind, GcSignatureContext> so ELEMENT_TYPE_VAR / ELEMENT_TYPE_MVAR placeholders resolve to the method's actual class / method instantiation -- matching native SigTypeContext-driven PeekElemTypeNormalized behavior.
  • GcScanner.PromoteCallerStack constructs the provider per call and walks the TransitionBlock using a reserved-slot count derived from IsInstance / return-buffer / RequiresInstArg / IsAsyncMethod / ARM64 x8, reporting each parameter slot as a GC reference, interior pointer, or skip.
  • Signature acquisition mirrors native MethodDesc::GetSig: prefers IsStoredSigMethodDesc (dynamic, EEImpl, array methods) before falling back to the metadata token, so caller-stack roots of dynamic and array-method transition frames are handled. Stored sigs are pinned with an inline fixed block and read via a BlobReader, matching the existing SigFormat.cs pattern.
  • This is the cDAC equivalent of native TransitionFrame::PromoteCallerStack and is used for PrestubMethodFrame, CallCountingHelperFrame, and the StubDispatchFrame / ExternalMethodFrame fallback when no GCRefMap is available.

Signature contract surface stays minimal:

  • ISignatureDecoder continues to expose only DecodeFieldSignature(BlobHandle, ModuleHandle, TypeHandle).
  • GC-specific decoding lives entirely inside the StackWalk contract; the Signature contract has no GC dependencies.

cDAC documentation:

  • docs/design/datacontracts/SignatureDecoder.md -- describes RuntimeSignatureDecoder, IRuntimeSignatureTypeProvider, and the ELEMENT_TYPE_INTERNAL / CMOD_INTERNAL extensions; refreshed DecodeFieldSignature code sample.
  • docs/design/datacontracts/StackWalk.md -- new "Signature-Based Scanning" section under GC stack reference scanning, covering GcSignatureTypeProvider (with module scoping, GcSignatureContext, and enum normalization), the PromoteCallerStack algorithm, the reserved-slot table, and limitations vs. native.

Testing

  • Build: clean, 0 warnings / 0 errors.
  • 1921 / 1921 cDAC unit tests pass.
  • Behavioral verification against the legacy DAC happens via the GC-stress verification harness introduced later in the PR series.

Note

This PR description was created with AI assistance from Copilot.

- Move RuntimeSignatureDecoder to Contracts/Signature/
- Move GcSignatureTypeProvider to Contracts/Signature/ with module-scoped caching
- Add DecodeMethodSignatureForGC(BlobHandle, ModuleHandle) to ISignatureDecoder
- Add DecodeFieldSignature to RuntimeSignatureDecoder
- Add BlobHandleSignatureReader for lazy blob reading
- SignatureTypeProvider implements IRuntimeSignatureTypeProvider
- Switch DecodeFieldSignature to use RuntimeSignatureDecoder
- GcScanner uses contract API instead of direct decoder construction
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @steveisok, @tommcdon, @dotnet/dotnet-diag
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR is part of the cDAC signature-decoding refactor, introducing a custom RuntimeSignatureDecoder capable of handling runtime-internal signature element types (ELEMENT_TYPE_INTERNAL / ELEMENT_TYPE_CMOD_INTERNAL) and centralizing GC-oriented signature decoding behind the ISignatureDecoder contract.

Changes:

  • Added RuntimeSignatureDecoder plus signature-reader abstractions (ISignatureReader, span/blob-backed readers) to support decoding from different signature sources.
  • Extended the ISignatureDecoder contract with DecodeMethodSignatureForGC(...) and promoted GcTypeKind to a public abstraction.
  • Updated stack GC scanning (GcScanner) and signature type providers to route decoding through the centralized contract API.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Show a summary per file
FileDescription
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/GC/GcScanner.csSwitches caller-signature decoding to the ISignatureDecoder.DecodeMethodSignatureForGC contract API.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/SignatureTypeProvider.csUpdates the type provider to implement IRuntimeSignatureTypeProvider and adds internal-type callbacks.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/SignatureReaders.csIntroduces abstractions for reading signature bytes from spans and metadata blobs.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/SignatureDecoder_1.csAdds module-scoped GC provider caching and implements DecodeMethodSignatureForGC using RuntimeSignatureDecoder.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/RuntimeSignatureDecoder.csNew runtime-aware ECMA-335-ish signature decoder with support for internal runtime element types.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/GcSignatureTypeProvider.csMoves/updates GC classification provider to implement the runtime-aware provider interface and classify internal types via RTS.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ISignatureDecoder.csExtends the public contract surface with GcTypeKind and DecodeMethodSignatureForGC.
Comments suppressed due to low confidence (1)

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/GcSignatureTypeProvider.cs:26

  • GcSignatureTypeProvider stores _target and _moduleHandle but never uses either of them (the implementation uses the target method parameter instead). This is dead state that should be removed, or the implementation should consistently use the stored fields if they're intended for future module/target-scoped behavior.

Max Charlamband others added 2 commits May 1, 2026 00:50
Rewrites RuntimeSignatureDecoder as a readonly struct that mirrors SRM's SignatureDecoder API (ref BlobReader per method, allowTypeSpecifications flag), with only ELEMENT_TYPE_INTERNAL (0x21) and CMOD_INTERNAL (0x22) added on top via the new IRuntimeSignatureTypeProvider interface. Drops the custom ISignatureReader/BlobHandleSignatureReader/SpanSignatureReader abstraction since BlobReader already provides lazy reading.
Fixes two latent bugs vs SRM: TypeDefOrRefOrSpec tag=3 now throws (was incorrectly returning Object), and the leading element type code is read as a compressed integer rather than a raw byte.
Moves GC-specific signature decoding out of the Signature contract into the StackWalk contract (Option B). GcSignatureTypeProvider and GcTypeKind move to Contracts/StackWalk/GC/ under the StackWalkHelpers namespace. ISignatureDecoder no longer exposes DecodeMethodSignatureForGC; the _gcProviders cache is removed from SignatureDecoder_1; GcScanner constructs RuntimeSignatureDecoder<GcTypeKind, object?> directly.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…adataReader
Splits IRuntimeSignatureTypeProvider into its own file (matches cDAC convention for internal interfaces). Makes MetadataReader required on RuntimeSignatureDecoder (matches SRM exactly; both call sites already pass non-null) and removes the null-forgiving operators. Reorders constructor params to (provider, target, metadataReader, genericContext) to mirror SRM's parameter order.
Cleanup: removes unused _mdhProviders dictionary and GetMethodDescHandleProvider from SignatureDecoder_1; removes unused _target/_moduleHandle fields and constructor from GcSignatureTypeProvider; renames _metadataReaderOpt to _metadataReader (Opt suffix is non-standard for a nullable-typed field); converts block comment to line comments.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings May 1, 2026 05:04

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.

Drop the redundant `Target target` parameter from
`IRuntimeSignatureTypeProvider.GetInternalType` and
`GetInternalModifiedType`. Providers now capture the target in their
own constructor when they need it, matching `SignatureTypeProvider<T>`.
`GcSignatureTypeProvider` gains a `Target` constructor parameter.
Update the cDAC documentation:
* `SignatureDecoder.md` -- describe `RuntimeSignatureDecoder`,
`IRuntimeSignatureTypeProvider`, and the runtime-only
`ELEMENT_TYPE_INTERNAL` / `ELEMENT_TYPE_CMOD_INTERNAL` extensions;
refresh the `DecodeFieldSignature` code sample.
* `StackWalk.md` -- new Signature-Based Scanning section covering
`GcSignatureTypeProvider`, the `PromoteCallerStack` algorithm,
reserved-slot table, and limitations vs. native.
Also revert a stray whitespace change in `ISignatureDecoder.cs`.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@max-charlamb
max-charlamb marked this pull request as ready for review May 1, 2026 05:16
CopilotAI review requested due to automatic review settings May 1, 2026 05:16

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.

Comments suppressed due to low confidence (2)

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/SignatureTypeProvider.cs:25

  • The private field _target is assigned in the constructor but never read in this type, which will trigger CS0414 (and in this repo, warnings are treated as errors by default). Please remove _target (and its assignment) or start using it (e.g., for validation) to avoid build breaks.
 private readonly Target _target;
private readonly Contracts.ModuleHandle _moduleHandle;
private readonly Contracts.ILoader _loader;
private readonly Contracts.IRuntimeTypeSystem _runtimeTypeSystem;
public SignatureTypeProvider(Target target, Contracts.ModuleHandle moduleHandle)
{
_target = target;
_moduleHandle = moduleHandle;
_loader = target.Contracts.Loader;
_runtimeTypeSystem = target.Contracts.RuntimeTypeSystem;

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/GC/GcScanner.cs:380

  • isValueTypeThis is always initialized to false and never updated, so this is never reported with GC_CALL_INTERIOR even for value-type instance methods. Since you already resolve the declaring TypeHandle earlier, it seems like this should be computed (e.g., via IRuntimeTypeSystem.IsValueType(typeHandle)) and included in the existing try/catch alongside RequiresInstArg / IsAsyncMethod.
 bool hasThis = methodSig.Header.IsInstance;
bool hasRetBuf = methodSig.ReturnType is GcTypeKind.Other;
bool requiresInstArg = false;
bool isAsync = false;
bool isValueTypeThis = false;
try
{
requiresInstArg = rts.RequiresInstArg(mdh);
isAsync = rts.IsAsyncMethod(mdh);
}
catch
{
}
PromoteCallerStackHelper(transitionBlock, methodSig, hasThis, hasRetBuf,
requiresInstArg, isAsync, isValueTypeThis, scanContext);
}

Comment threaddocs/design/datacontracts/SignatureDecoder.md Outdated
Max Charlamband others added 2 commits May 1, 2026 01:34
…rovider
Match native MethodDesc::GetSig in GcScanner.PromoteCallerStack: try
IsStoredSigMethodDesc first (dynamic, EEImpl, and array method descs) and
read the stored signature via an inline pinned BlobReader before falling
back to the metadata token lookup. Cache the GcSignatureTypeProvider on
the GcScanner so it is allocated once instead of per call.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…mitive
GcSignatureTypeProvider now classifies type parameters and TypeDef/TypeRef
tokens using the loaded TypeHandle, matching native SigTypeContext-driven
PeekElemTypeNormalized behavior:
- VAR/MVAR placeholders are resolved against the method's actual class /
method instantiation via a new GcSignatureContext(TypeHandle, MethodDescHandle)
passed as the SRM generic context.
- TypeDef/TypeRef tokens are resolved via the module's TypeDefToMethodTable /
TypeRefToMethodTable lookup tables; enums collapse to their underlying
primitive (None) via IRuntimeTypeSystem.IsEnum, matching
MethodTable::GetInternalCorElementType.
The provider is now module-scoped, so it is constructed per PromoteCallerStack
call rather than cached on GcScanner.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings May 1, 2026 05:51

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.

Comment threaddocs/design/datacontracts/SignatureDecoder.md Outdated
- IRuntimeSignatureTypeProvider: internal -> public to match SignatureTypeProvider's
visibility (avoids the "public class implements internal interface" warning while
keeping the provider type available to downstream consumers).
- RuntimeSignatureDecoder: drop the unused _target field and the redundant
ArgumentNullException.ThrowIfNull(provider/metadataReader) calls; the file is
in a nullable-enabled context, so the type system already enforces non-null.
- SignatureDecoder.md: drop the bogus `target,` argument from the GetInternalType
/ GetInternalModifiedType bullets, and reword GetTypeFromDefinition /
GetTypeFromReference's "returns null" to "returns a default TypeHandle
(Address == TargetPointer.Null)" since the API returns a struct value.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@max-charlamb
max-charlamb requested a review from rcj1May 1, 2026 15:08
Comment threaddocs/design/datacontracts/SignatureDecoder.md Outdated
Comment threaddocs/design/datacontracts/Signature.md Outdated
Comment threaddocs/design/datacontracts/Signature.md Outdated
Comment threaddocs/design/datacontracts/StackWalk.md Outdated
Comment threaddocs/design/datacontracts/Signature.md Outdated
Comment threaddocs/design/datacontracts/Signature.md Outdated
Comment threaddocs/design/datacontracts/Signature.md Outdated
CopilotAI review requested due to automatic review settings May 4, 2026 17:45

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 4 comments.

Comments suppressed due to low confidence (1)

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ISignature.cs:16

  • Renaming the public abstractions from ISignatureDecoder/SignatureDecoder to ISignature/Signature is a source/binary breaking change for any consumer of Microsoft.Diagnostics.DataContractReader.Abstractions, and this PR doesn't preserve compatibility shims. The PR metadata only links other PRs, not an api-approved issue, so this public surface either needs prior approval or it needs to stay internal until the rename is reviewed.

Comment threadsrc/coreclr/vm/datadescriptor/datadescriptor.inc
Comment threaddocs/design/datacontracts/StackWalk.md
Match native SigTypeContext::InitTypeContext: array MTs use the element
type as their class instantiation. RuntimeTypeSystem.GetInstantiation
returns an empty span for arrays, so signature decoding for array
accessor methods (e.g., int[]::Set with VAR 0) was misclassifying the
element-type slot as Ref. Special-case IsArray and use GetTypeParam,
the managed equivalent of MethodTable::GetArrayInstantiation.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
rcj1
rcj1 approved these changes May 8, 2026
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings May 8, 2026 21:56
@max-charlamb
max-charlamb enabled auto-merge (squash) May 8, 2026 22:01

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ISignature.cs:16

  • Renaming the contract from SignatureDecoder to Signature changes the wire-level lookup key (TContract.Name) used by CachingContractRegistry (it calls _tryGetContractVersion(TContract.Name, ...)). This will break compatibility between newer readers and older runtimes/dumps that only advertise SignatureDecoder (and vice versa), since contract versioning can’t help if the name doesn’t match. Consider keeping the contract name stable (e.g., keep IContract.Name as "SignatureDecoder"), or publish an alias contract on the runtime side and keep the legacy ISignatureDecoder surface as a forwarding shim so existing readers can still resolve the contract.

Comment threaddocs/design/datacontracts/StackWalk.md
@max-charlamb

Copy link
Copy Markdown
MemberAuthor

/ba-g unrelated test failures

@max-charlamb
max-charlamb disabled auto-merge May 10, 2026 02:42
@max-charlamb
max-charlamb enabled auto-merge (squash) May 10, 2026 02:44
@max-charlamb

Copy link
Copy Markdown
MemberAuthor

/ba-g cDAC only change, the only cDAC test failure is a known issue on osx

@steveisok
steveisok disabled auto-merge May 11, 2026 14:53
@steveisok
steveisok merged commit 6f89eaf into dotnet:mainMay 11, 2026
120 of 125 checks passed
jakobbotsch pushed a commit to jakobbotsch/runtime that referenced this pull request May 12, 2026
…5) (dotnet#127636)
## Summary
Part 2 of 5 stacked PRs splitting
[dotnet#126408](dotnet#126408). Builds on
[dotnet#127395](dotnet#127395) (merged).
This PR adds a cDAC ECMA-335 signature decoder that closely mirrors
`System.Reflection.Metadata.SignatureDecoder` and supports the two
runtime-only extensions used in CoreCLR-internal signatures:
`ELEMENT_TYPE_INTERNAL` (`0x21`) and `ELEMENT_TYPE_CMOD_INTERNAL`
(`0x22`). The decoder is then used by both the Signature contract (for
field signatures) and the StackWalk contract (for signature-based GC
reference scanning of transition frames).
### What this PR contains
**`RuntimeSignatureDecoder` (SRM-aligned polyfill):**
- `RuntimeSignatureDecoder<TType, TGenericContext>` -- readonly struct
mirroring SRM's `SignatureDecoder<TType, TGenericContext>`
(`DecodeType`, `DecodeFieldSignature`, `DecodeMethodSignature`,
`DecodeLocalSignature`, all taking `ref BlobReader`).
- `IRuntimeSignatureTypeProvider<TType, TGenericContext>` -- superset of
SRM's `ISignatureTypeProvider` adding `GetInternalType(TargetPointer)`
and `GetInternalModifiedType(TargetPointer, TType, bool)` for the two
runtime-only encodings.
- `SignatureTypeProvider` (the existing field-signature provider)
implements `IRuntimeSignatureTypeProvider` so internal types in field
signatures resolve via `RuntimeTypeSystem.GetTypeHandle`.
**Signature-based GC reference scanning in StackWalk:**
- `GcSignatureTypeProvider` (internal, in `StackWalkHelpers`) classifies
each method-signature parameter as `Ref`, `Interior`, `Other` (value
type or larger-than-slot), or `None`.
- A new `GcSignatureContext(TypeHandle classContext, MethodDescHandle
methodContext)` record struct is plumbed through
`RuntimeSignatureDecoder<GcTypeKind, GcSignatureContext>` so
`ELEMENT_TYPE_VAR` / `ELEMENT_TYPE_MVAR` placeholders resolve to the
method's actual class / method instantiation -- matching native
`SigTypeContext`-driven `PeekElemTypeNormalized` behavior.
- `GcScanner.PromoteCallerStack` constructs the provider per call and
walks the `TransitionBlock` using a reserved-slot count derived from
`IsInstance` / return-buffer / `RequiresInstArg` / `IsAsyncMethod` /
ARM64 `x8`, reporting each parameter slot as a GC reference, interior
pointer, or skip.
- Signature acquisition mirrors native `MethodDesc::GetSig`: prefers
`IsStoredSigMethodDesc` (dynamic, EEImpl, array methods) before falling
back to the metadata token, so caller-stack roots of dynamic and
array-method transition frames are handled. Stored sigs are pinned with
an inline `fixed` block and read via a `BlobReader`, matching the
existing `SigFormat.cs` pattern.
- This is the cDAC equivalent of native
`TransitionFrame::PromoteCallerStack` and is used for
`PrestubMethodFrame`, `CallCountingHelperFrame`, and the
`StubDispatchFrame` / `ExternalMethodFrame` fallback when no GCRefMap is
available.
**Signature contract surface stays minimal:**
- `ISignatureDecoder` continues to expose only
`DecodeFieldSignature(BlobHandle, ModuleHandle, TypeHandle)`.
- GC-specific decoding lives entirely inside the StackWalk contract; the
Signature contract has no GC dependencies.
**cDAC documentation:**
- `docs/design/datacontracts/SignatureDecoder.md` -- describes
`RuntimeSignatureDecoder`, `IRuntimeSignatureTypeProvider`, and the
`ELEMENT_TYPE_INTERNAL` / `CMOD_INTERNAL` extensions; refreshed
`DecodeFieldSignature` code sample.
- `docs/design/datacontracts/StackWalk.md` -- new "Signature-Based
Scanning" section under GC stack reference scanning, covering
`GcSignatureTypeProvider` (with module scoping, `GcSignatureContext`,
and enum normalization), the `PromoteCallerStack` algorithm, the
reserved-slot table, and limitations vs. native.
### Testing
- Build: clean, 0 warnings / 0 errors.
- 1921 / 1921 cDAC unit tests pass.
- Behavioral verification against the legacy DAC happens via the
GC-stress verification harness introduced later in the PR series.
> [!NOTE]
> This PR description was created with AI assistance from Copilot.
---------
Co-authored-by: Max Charlamb <maxcharlamb@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jun 11, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@max-charlamb@jkotas@rcj1@steveisok
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

[cDAC] RuntimeSignatureDecoder and centralized Signature contract (2/5) - #127636

Merged
steveisok merged 13 commits into
dotnet:mainfrom
max-charlamb:cdac-stackrefs-pr2
May 11, 2026
Merged

[cDAC] RuntimeSignatureDecoder and centralized Signature contract (2/5)#127636
steveisok merged 13 commits into
dotnet:mainfrom
max-charlamb:cdac-stackrefs-pr2

Conversation

@max-charlamb

@max-charlambmax-charlamb commented May 1, 2026

Copy link
Copy Markdown
Member

Summary

Part 2 of 5 stacked PRs splitting #126408. Builds on #127395 (merged).

This PR adds a cDAC ECMA-335 signature decoder that closely mirrors System.Reflection.Metadata.SignatureDecoder and supports the two runtime-only extensions used in CoreCLR-internal signatures: ELEMENT_TYPE_INTERNAL (0x21) and ELEMENT_TYPE_CMOD_INTERNAL (0x22). The decoder is then used by both the Signature contract (for field signatures) and the StackWalk contract (for signature-based GC reference scanning of transition frames).

What this PR contains

RuntimeSignatureDecoder (SRM-aligned polyfill):

  • RuntimeSignatureDecoder<TType, TGenericContext> -- readonly struct mirroring SRM's SignatureDecoder<TType, TGenericContext> (DecodeType, DecodeFieldSignature, DecodeMethodSignature, DecodeLocalSignature, all taking ref BlobReader).
  • IRuntimeSignatureTypeProvider<TType, TGenericContext> -- superset of SRM's ISignatureTypeProvider adding GetInternalType(TargetPointer) and GetInternalModifiedType(TargetPointer, TType, bool) for the two runtime-only encodings.
  • SignatureTypeProvider (the existing field-signature provider) implements IRuntimeSignatureTypeProvider so internal types in field signatures resolve via RuntimeTypeSystem.GetTypeHandle.

Signature-based GC reference scanning in StackWalk:

  • GcSignatureTypeProvider (internal, in StackWalkHelpers) classifies each method-signature parameter as Ref, Interior, Other (value type or larger-than-slot), or None.
  • A new GcSignatureContext(TypeHandle classContext, MethodDescHandle methodContext) record struct is plumbed through RuntimeSignatureDecoder<GcTypeKind, GcSignatureContext> so ELEMENT_TYPE_VAR / ELEMENT_TYPE_MVAR placeholders resolve to the method's actual class / method instantiation -- matching native SigTypeContext-driven PeekElemTypeNormalized behavior.
  • GcScanner.PromoteCallerStack constructs the provider per call and walks the TransitionBlock using a reserved-slot count derived from IsInstance / return-buffer / RequiresInstArg / IsAsyncMethod / ARM64 x8, reporting each parameter slot as a GC reference, interior pointer, or skip.
  • Signature acquisition mirrors native MethodDesc::GetSig: prefers IsStoredSigMethodDesc (dynamic, EEImpl, array methods) before falling back to the metadata token, so caller-stack roots of dynamic and array-method transition frames are handled. Stored sigs are pinned with an inline fixed block and read via a BlobReader, matching the existing SigFormat.cs pattern.
  • This is the cDAC equivalent of native TransitionFrame::PromoteCallerStack and is used for PrestubMethodFrame, CallCountingHelperFrame, and the StubDispatchFrame / ExternalMethodFrame fallback when no GCRefMap is available.

Signature contract surface stays minimal:

  • ISignatureDecoder continues to expose only DecodeFieldSignature(BlobHandle, ModuleHandle, TypeHandle).
  • GC-specific decoding lives entirely inside the StackWalk contract; the Signature contract has no GC dependencies.

cDAC documentation:

  • docs/design/datacontracts/SignatureDecoder.md -- describes RuntimeSignatureDecoder, IRuntimeSignatureTypeProvider, and the ELEMENT_TYPE_INTERNAL / CMOD_INTERNAL extensions; refreshed DecodeFieldSignature code sample.
  • docs/design/datacontracts/StackWalk.md -- new "Signature-Based Scanning" section under GC stack reference scanning, covering GcSignatureTypeProvider (with module scoping, GcSignatureContext, and enum normalization), the PromoteCallerStack algorithm, the reserved-slot table, and limitations vs. native.

Testing

  • Build: clean, 0 warnings / 0 errors.
  • 1921 / 1921 cDAC unit tests pass.
  • Behavioral verification against the legacy DAC happens via the GC-stress verification harness introduced later in the PR series.

Note

This PR description was created with AI assistance from Copilot.

- Move RuntimeSignatureDecoder to Contracts/Signature/
- Move GcSignatureTypeProvider to Contracts/Signature/ with module-scoped caching
- Add DecodeMethodSignatureForGC(BlobHandle, ModuleHandle) to ISignatureDecoder
- Add DecodeFieldSignature to RuntimeSignatureDecoder
- Add BlobHandleSignatureReader for lazy blob reading
- SignatureTypeProvider implements IRuntimeSignatureTypeProvider
- Switch DecodeFieldSignature to use RuntimeSignatureDecoder
- GcScanner uses contract API instead of direct decoder construction
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @steveisok, @tommcdon, @dotnet/dotnet-diag
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR is part of the cDAC signature-decoding refactor, introducing a custom RuntimeSignatureDecoder capable of handling runtime-internal signature element types (ELEMENT_TYPE_INTERNAL / ELEMENT_TYPE_CMOD_INTERNAL) and centralizing GC-oriented signature decoding behind the ISignatureDecoder contract.

Changes:

  • Added RuntimeSignatureDecoder plus signature-reader abstractions (ISignatureReader, span/blob-backed readers) to support decoding from different signature sources.
  • Extended the ISignatureDecoder contract with DecodeMethodSignatureForGC(...) and promoted GcTypeKind to a public abstraction.
  • Updated stack GC scanning (GcScanner) and signature type providers to route decoding through the centralized contract API.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Show a summary per file
FileDescription
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/GC/GcScanner.csSwitches caller-signature decoding to the ISignatureDecoder.DecodeMethodSignatureForGC contract API.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/SignatureTypeProvider.csUpdates the type provider to implement IRuntimeSignatureTypeProvider and adds internal-type callbacks.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/SignatureReaders.csIntroduces abstractions for reading signature bytes from spans and metadata blobs.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/SignatureDecoder_1.csAdds module-scoped GC provider caching and implements DecodeMethodSignatureForGC using RuntimeSignatureDecoder.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/RuntimeSignatureDecoder.csNew runtime-aware ECMA-335-ish signature decoder with support for internal runtime element types.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/GcSignatureTypeProvider.csMoves/updates GC classification provider to implement the runtime-aware provider interface and classify internal types via RTS.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ISignatureDecoder.csExtends the public contract surface with GcTypeKind and DecodeMethodSignatureForGC.
Comments suppressed due to low confidence (1)

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/GcSignatureTypeProvider.cs:26

  • GcSignatureTypeProvider stores _target and _moduleHandle but never uses either of them (the implementation uses the target method parameter instead). This is dead state that should be removed, or the implementation should consistently use the stored fields if they're intended for future module/target-scoped behavior.

Max Charlamband others added 2 commits May 1, 2026 00:50
Rewrites RuntimeSignatureDecoder as a readonly struct that mirrors SRM's SignatureDecoder API (ref BlobReader per method, allowTypeSpecifications flag), with only ELEMENT_TYPE_INTERNAL (0x21) and CMOD_INTERNAL (0x22) added on top via the new IRuntimeSignatureTypeProvider interface. Drops the custom ISignatureReader/BlobHandleSignatureReader/SpanSignatureReader abstraction since BlobReader already provides lazy reading.
Fixes two latent bugs vs SRM: TypeDefOrRefOrSpec tag=3 now throws (was incorrectly returning Object), and the leading element type code is read as a compressed integer rather than a raw byte.
Moves GC-specific signature decoding out of the Signature contract into the StackWalk contract (Option B). GcSignatureTypeProvider and GcTypeKind move to Contracts/StackWalk/GC/ under the StackWalkHelpers namespace. ISignatureDecoder no longer exposes DecodeMethodSignatureForGC; the _gcProviders cache is removed from SignatureDecoder_1; GcScanner constructs RuntimeSignatureDecoder<GcTypeKind, object?> directly.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…adataReader
Splits IRuntimeSignatureTypeProvider into its own file (matches cDAC convention for internal interfaces). Makes MetadataReader required on RuntimeSignatureDecoder (matches SRM exactly; both call sites already pass non-null) and removes the null-forgiving operators. Reorders constructor params to (provider, target, metadataReader, genericContext) to mirror SRM's parameter order.
Cleanup: removes unused _mdhProviders dictionary and GetMethodDescHandleProvider from SignatureDecoder_1; removes unused _target/_moduleHandle fields and constructor from GcSignatureTypeProvider; renames _metadataReaderOpt to _metadataReader (Opt suffix is non-standard for a nullable-typed field); converts block comment to line comments.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings May 1, 2026 05:04

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.

Drop the redundant `Target target` parameter from
`IRuntimeSignatureTypeProvider.GetInternalType` and
`GetInternalModifiedType`. Providers now capture the target in their
own constructor when they need it, matching `SignatureTypeProvider<T>`.
`GcSignatureTypeProvider` gains a `Target` constructor parameter.
Update the cDAC documentation:
* `SignatureDecoder.md` -- describe `RuntimeSignatureDecoder`,
`IRuntimeSignatureTypeProvider`, and the runtime-only
`ELEMENT_TYPE_INTERNAL` / `ELEMENT_TYPE_CMOD_INTERNAL` extensions;
refresh the `DecodeFieldSignature` code sample.
* `StackWalk.md` -- new Signature-Based Scanning section covering
`GcSignatureTypeProvider`, the `PromoteCallerStack` algorithm,
reserved-slot table, and limitations vs. native.
Also revert a stray whitespace change in `ISignatureDecoder.cs`.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@max-charlamb
max-charlamb marked this pull request as ready for review May 1, 2026 05:16
CopilotAI review requested due to automatic review settings May 1, 2026 05:16

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.

Comments suppressed due to low confidence (2)

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/SignatureTypeProvider.cs:25

  • The private field _target is assigned in the constructor but never read in this type, which will trigger CS0414 (and in this repo, warnings are treated as errors by default). Please remove _target (and its assignment) or start using it (e.g., for validation) to avoid build breaks.
 private readonly Target _target;
private readonly Contracts.ModuleHandle _moduleHandle;
private readonly Contracts.ILoader _loader;
private readonly Contracts.IRuntimeTypeSystem _runtimeTypeSystem;
public SignatureTypeProvider(Target target, Contracts.ModuleHandle moduleHandle)
{
_target = target;
_moduleHandle = moduleHandle;
_loader = target.Contracts.Loader;
_runtimeTypeSystem = target.Contracts.RuntimeTypeSystem;

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/GC/GcScanner.cs:380

  • isValueTypeThis is always initialized to false and never updated, so this is never reported with GC_CALL_INTERIOR even for value-type instance methods. Since you already resolve the declaring TypeHandle earlier, it seems like this should be computed (e.g., via IRuntimeTypeSystem.IsValueType(typeHandle)) and included in the existing try/catch alongside RequiresInstArg / IsAsyncMethod.
 bool hasThis = methodSig.Header.IsInstance;
bool hasRetBuf = methodSig.ReturnType is GcTypeKind.Other;
bool requiresInstArg = false;
bool isAsync = false;
bool isValueTypeThis = false;
try
{
requiresInstArg = rts.RequiresInstArg(mdh);
isAsync = rts.IsAsyncMethod(mdh);
}
catch
{
}
PromoteCallerStackHelper(transitionBlock, methodSig, hasThis, hasRetBuf,
requiresInstArg, isAsync, isValueTypeThis, scanContext);
}

Comment threaddocs/design/datacontracts/SignatureDecoder.md Outdated
Max Charlamband others added 2 commits May 1, 2026 01:34
…rovider
Match native MethodDesc::GetSig in GcScanner.PromoteCallerStack: try
IsStoredSigMethodDesc first (dynamic, EEImpl, and array method descs) and
read the stored signature via an inline pinned BlobReader before falling
back to the metadata token lookup. Cache the GcSignatureTypeProvider on
the GcScanner so it is allocated once instead of per call.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…mitive
GcSignatureTypeProvider now classifies type parameters and TypeDef/TypeRef
tokens using the loaded TypeHandle, matching native SigTypeContext-driven
PeekElemTypeNormalized behavior:
- VAR/MVAR placeholders are resolved against the method's actual class /
method instantiation via a new GcSignatureContext(TypeHandle, MethodDescHandle)
passed as the SRM generic context.
- TypeDef/TypeRef tokens are resolved via the module's TypeDefToMethodTable /
TypeRefToMethodTable lookup tables; enums collapse to their underlying
primitive (None) via IRuntimeTypeSystem.IsEnum, matching
MethodTable::GetInternalCorElementType.
The provider is now module-scoped, so it is constructed per PromoteCallerStack
call rather than cached on GcScanner.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings May 1, 2026 05:51

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.

Comment threaddocs/design/datacontracts/SignatureDecoder.md Outdated
- IRuntimeSignatureTypeProvider: internal -> public to match SignatureTypeProvider's
visibility (avoids the "public class implements internal interface" warning while
keeping the provider type available to downstream consumers).
- RuntimeSignatureDecoder: drop the unused _target field and the redundant
ArgumentNullException.ThrowIfNull(provider/metadataReader) calls; the file is
in a nullable-enabled context, so the type system already enforces non-null.
- SignatureDecoder.md: drop the bogus `target,` argument from the GetInternalType
/ GetInternalModifiedType bullets, and reword GetTypeFromDefinition /
GetTypeFromReference's "returns null" to "returns a default TypeHandle
(Address == TargetPointer.Null)" since the API returns a struct value.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@max-charlamb
max-charlamb requested a review from rcj1May 1, 2026 15:08
Comment threaddocs/design/datacontracts/SignatureDecoder.md Outdated
Comment threaddocs/design/datacontracts/Signature.md Outdated
Comment threaddocs/design/datacontracts/Signature.md Outdated
Comment threaddocs/design/datacontracts/StackWalk.md Outdated
Comment threaddocs/design/datacontracts/Signature.md Outdated
Comment threaddocs/design/datacontracts/Signature.md Outdated
Comment threaddocs/design/datacontracts/Signature.md Outdated
CopilotAI review requested due to automatic review settings May 4, 2026 17:45

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 4 comments.

Comments suppressed due to low confidence (1)

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ISignature.cs:16

  • Renaming the public abstractions from ISignatureDecoder/SignatureDecoder to ISignature/Signature is a source/binary breaking change for any consumer of Microsoft.Diagnostics.DataContractReader.Abstractions, and this PR doesn't preserve compatibility shims. The PR metadata only links other PRs, not an api-approved issue, so this public surface either needs prior approval or it needs to stay internal until the rename is reviewed.

Comment threadsrc/coreclr/vm/datadescriptor/datadescriptor.inc
Comment threaddocs/design/datacontracts/StackWalk.md
Match native SigTypeContext::InitTypeContext: array MTs use the element
type as their class instantiation. RuntimeTypeSystem.GetInstantiation
returns an empty span for arrays, so signature decoding for array
accessor methods (e.g., int[]::Set with VAR 0) was misclassifying the
element-type slot as Ref. Special-case IsArray and use GetTypeParam,
the managed equivalent of MethodTable::GetArrayInstantiation.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
rcj1
rcj1 approved these changes May 8, 2026
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings May 8, 2026 21:56
@max-charlamb
max-charlamb enabled auto-merge (squash) May 8, 2026 22:01

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ISignature.cs:16

  • Renaming the contract from SignatureDecoder to Signature changes the wire-level lookup key (TContract.Name) used by CachingContractRegistry (it calls _tryGetContractVersion(TContract.Name, ...)). This will break compatibility between newer readers and older runtimes/dumps that only advertise SignatureDecoder (and vice versa), since contract versioning can’t help if the name doesn’t match. Consider keeping the contract name stable (e.g., keep IContract.Name as "SignatureDecoder"), or publish an alias contract on the runtime side and keep the legacy ISignatureDecoder surface as a forwarding shim so existing readers can still resolve the contract.

Comment threaddocs/design/datacontracts/StackWalk.md
@max-charlamb

Copy link
Copy Markdown
MemberAuthor

/ba-g unrelated test failures

@max-charlamb
max-charlamb disabled auto-merge May 10, 2026 02:42
@max-charlamb
max-charlamb enabled auto-merge (squash) May 10, 2026 02:44
@max-charlamb

Copy link
Copy Markdown
MemberAuthor

/ba-g cDAC only change, the only cDAC test failure is a known issue on osx

@steveisok
steveisok disabled auto-merge May 11, 2026 14:53
@steveisok
steveisok merged commit 6f89eaf into dotnet:mainMay 11, 2026
120 of 125 checks passed
jakobbotsch pushed a commit to jakobbotsch/runtime that referenced this pull request May 12, 2026
…5) (dotnet#127636)
## Summary
Part 2 of 5 stacked PRs splitting
[dotnet#126408](dotnet#126408). Builds on
[dotnet#127395](dotnet#127395) (merged).
This PR adds a cDAC ECMA-335 signature decoder that closely mirrors
`System.Reflection.Metadata.SignatureDecoder` and supports the two
runtime-only extensions used in CoreCLR-internal signatures:
`ELEMENT_TYPE_INTERNAL` (`0x21`) and `ELEMENT_TYPE_CMOD_INTERNAL`
(`0x22`). The decoder is then used by both the Signature contract (for
field signatures) and the StackWalk contract (for signature-based GC
reference scanning of transition frames).
### What this PR contains
**`RuntimeSignatureDecoder` (SRM-aligned polyfill):**
- `RuntimeSignatureDecoder<TType, TGenericContext>` -- readonly struct
mirroring SRM's `SignatureDecoder<TType, TGenericContext>`
(`DecodeType`, `DecodeFieldSignature`, `DecodeMethodSignature`,
`DecodeLocalSignature`, all taking `ref BlobReader`).
- `IRuntimeSignatureTypeProvider<TType, TGenericContext>` -- superset of
SRM's `ISignatureTypeProvider` adding `GetInternalType(TargetPointer)`
and `GetInternalModifiedType(TargetPointer, TType, bool)` for the two
runtime-only encodings.
- `SignatureTypeProvider` (the existing field-signature provider)
implements `IRuntimeSignatureTypeProvider` so internal types in field
signatures resolve via `RuntimeTypeSystem.GetTypeHandle`.
**Signature-based GC reference scanning in StackWalk:**
- `GcSignatureTypeProvider` (internal, in `StackWalkHelpers`) classifies
each method-signature parameter as `Ref`, `Interior`, `Other` (value
type or larger-than-slot), or `None`.
- A new `GcSignatureContext(TypeHandle classContext, MethodDescHandle
methodContext)` record struct is plumbed through
`RuntimeSignatureDecoder<GcTypeKind, GcSignatureContext>` so
`ELEMENT_TYPE_VAR` / `ELEMENT_TYPE_MVAR` placeholders resolve to the
method's actual class / method instantiation -- matching native
`SigTypeContext`-driven `PeekElemTypeNormalized` behavior.
- `GcScanner.PromoteCallerStack` constructs the provider per call and
walks the `TransitionBlock` using a reserved-slot count derived from
`IsInstance` / return-buffer / `RequiresInstArg` / `IsAsyncMethod` /
ARM64 `x8`, reporting each parameter slot as a GC reference, interior
pointer, or skip.
- Signature acquisition mirrors native `MethodDesc::GetSig`: prefers
`IsStoredSigMethodDesc` (dynamic, EEImpl, array methods) before falling
back to the metadata token, so caller-stack roots of dynamic and
array-method transition frames are handled. Stored sigs are pinned with
an inline `fixed` block and read via a `BlobReader`, matching the
existing `SigFormat.cs` pattern.
- This is the cDAC equivalent of native
`TransitionFrame::PromoteCallerStack` and is used for
`PrestubMethodFrame`, `CallCountingHelperFrame`, and the
`StubDispatchFrame` / `ExternalMethodFrame` fallback when no GCRefMap is
available.
**Signature contract surface stays minimal:**
- `ISignatureDecoder` continues to expose only
`DecodeFieldSignature(BlobHandle, ModuleHandle, TypeHandle)`.
- GC-specific decoding lives entirely inside the StackWalk contract; the
Signature contract has no GC dependencies.
**cDAC documentation:**
- `docs/design/datacontracts/SignatureDecoder.md` -- describes
`RuntimeSignatureDecoder`, `IRuntimeSignatureTypeProvider`, and the
`ELEMENT_TYPE_INTERNAL` / `CMOD_INTERNAL` extensions; refreshed
`DecodeFieldSignature` code sample.
- `docs/design/datacontracts/StackWalk.md` -- new "Signature-Based
Scanning" section under GC stack reference scanning, covering
`GcSignatureTypeProvider` (with module scoping, `GcSignatureContext`,
and enum normalization), the `PromoteCallerStack` algorithm, the
reserved-slot table, and limitations vs. native.
### Testing
- Build: clean, 0 warnings / 0 errors.
- 1921 / 1921 cDAC unit tests pass.
- Behavioral verification against the legacy DAC happens via the
GC-stress verification harness introduced later in the PR series.
> [!NOTE]
> This PR description was created with AI assistance from Copilot.
---------
Co-authored-by: Max Charlamb <maxcharlamb@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jun 11, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@max-charlamb@jkotas@rcj1@steveisok
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

[cDAC] RuntimeSignatureDecoder and centralized Signature contract (2/5) - #127636

Merged
steveisok merged 13 commits into
dotnet:mainfrom
max-charlamb:cdac-stackrefs-pr2
May 11, 2026
Merged

[cDAC] RuntimeSignatureDecoder and centralized Signature contract (2/5)#127636
steveisok merged 13 commits into
dotnet:mainfrom
max-charlamb:cdac-stackrefs-pr2

Conversation

@max-charlamb

@max-charlambmax-charlamb commented May 1, 2026

Copy link
Copy Markdown
Member

Summary

Part 2 of 5 stacked PRs splitting #126408. Builds on #127395 (merged).

This PR adds a cDAC ECMA-335 signature decoder that closely mirrors System.Reflection.Metadata.SignatureDecoder and supports the two runtime-only extensions used in CoreCLR-internal signatures: ELEMENT_TYPE_INTERNAL (0x21) and ELEMENT_TYPE_CMOD_INTERNAL (0x22). The decoder is then used by both the Signature contract (for field signatures) and the StackWalk contract (for signature-based GC reference scanning of transition frames).

What this PR contains

RuntimeSignatureDecoder (SRM-aligned polyfill):

  • RuntimeSignatureDecoder<TType, TGenericContext> -- readonly struct mirroring SRM's SignatureDecoder<TType, TGenericContext> (DecodeType, DecodeFieldSignature, DecodeMethodSignature, DecodeLocalSignature, all taking ref BlobReader).
  • IRuntimeSignatureTypeProvider<TType, TGenericContext> -- superset of SRM's ISignatureTypeProvider adding GetInternalType(TargetPointer) and GetInternalModifiedType(TargetPointer, TType, bool) for the two runtime-only encodings.
  • SignatureTypeProvider (the existing field-signature provider) implements IRuntimeSignatureTypeProvider so internal types in field signatures resolve via RuntimeTypeSystem.GetTypeHandle.

Signature-based GC reference scanning in StackWalk:

  • GcSignatureTypeProvider (internal, in StackWalkHelpers) classifies each method-signature parameter as Ref, Interior, Other (value type or larger-than-slot), or None.
  • A new GcSignatureContext(TypeHandle classContext, MethodDescHandle methodContext) record struct is plumbed through RuntimeSignatureDecoder<GcTypeKind, GcSignatureContext> so ELEMENT_TYPE_VAR / ELEMENT_TYPE_MVAR placeholders resolve to the method's actual class / method instantiation -- matching native SigTypeContext-driven PeekElemTypeNormalized behavior.
  • GcScanner.PromoteCallerStack constructs the provider per call and walks the TransitionBlock using a reserved-slot count derived from IsInstance / return-buffer / RequiresInstArg / IsAsyncMethod / ARM64 x8, reporting each parameter slot as a GC reference, interior pointer, or skip.
  • Signature acquisition mirrors native MethodDesc::GetSig: prefers IsStoredSigMethodDesc (dynamic, EEImpl, array methods) before falling back to the metadata token, so caller-stack roots of dynamic and array-method transition frames are handled. Stored sigs are pinned with an inline fixed block and read via a BlobReader, matching the existing SigFormat.cs pattern.
  • This is the cDAC equivalent of native TransitionFrame::PromoteCallerStack and is used for PrestubMethodFrame, CallCountingHelperFrame, and the StubDispatchFrame / ExternalMethodFrame fallback when no GCRefMap is available.

Signature contract surface stays minimal:

  • ISignatureDecoder continues to expose only DecodeFieldSignature(BlobHandle, ModuleHandle, TypeHandle).
  • GC-specific decoding lives entirely inside the StackWalk contract; the Signature contract has no GC dependencies.

cDAC documentation:

  • docs/design/datacontracts/SignatureDecoder.md -- describes RuntimeSignatureDecoder, IRuntimeSignatureTypeProvider, and the ELEMENT_TYPE_INTERNAL / CMOD_INTERNAL extensions; refreshed DecodeFieldSignature code sample.
  • docs/design/datacontracts/StackWalk.md -- new "Signature-Based Scanning" section under GC stack reference scanning, covering GcSignatureTypeProvider (with module scoping, GcSignatureContext, and enum normalization), the PromoteCallerStack algorithm, the reserved-slot table, and limitations vs. native.

Testing

  • Build: clean, 0 warnings / 0 errors.
  • 1921 / 1921 cDAC unit tests pass.
  • Behavioral verification against the legacy DAC happens via the GC-stress verification harness introduced later in the PR series.

Note

This PR description was created with AI assistance from Copilot.

- Move RuntimeSignatureDecoder to Contracts/Signature/
- Move GcSignatureTypeProvider to Contracts/Signature/ with module-scoped caching
- Add DecodeMethodSignatureForGC(BlobHandle, ModuleHandle) to ISignatureDecoder
- Add DecodeFieldSignature to RuntimeSignatureDecoder
- Add BlobHandleSignatureReader for lazy blob reading
- SignatureTypeProvider implements IRuntimeSignatureTypeProvider
- Switch DecodeFieldSignature to use RuntimeSignatureDecoder
- GcScanner uses contract API instead of direct decoder construction
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @steveisok, @tommcdon, @dotnet/dotnet-diag
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR is part of the cDAC signature-decoding refactor, introducing a custom RuntimeSignatureDecoder capable of handling runtime-internal signature element types (ELEMENT_TYPE_INTERNAL / ELEMENT_TYPE_CMOD_INTERNAL) and centralizing GC-oriented signature decoding behind the ISignatureDecoder contract.

Changes:

  • Added RuntimeSignatureDecoder plus signature-reader abstractions (ISignatureReader, span/blob-backed readers) to support decoding from different signature sources.
  • Extended the ISignatureDecoder contract with DecodeMethodSignatureForGC(...) and promoted GcTypeKind to a public abstraction.
  • Updated stack GC scanning (GcScanner) and signature type providers to route decoding through the centralized contract API.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Show a summary per file
FileDescription
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/GC/GcScanner.csSwitches caller-signature decoding to the ISignatureDecoder.DecodeMethodSignatureForGC contract API.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/SignatureTypeProvider.csUpdates the type provider to implement IRuntimeSignatureTypeProvider and adds internal-type callbacks.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/SignatureReaders.csIntroduces abstractions for reading signature bytes from spans and metadata blobs.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/SignatureDecoder_1.csAdds module-scoped GC provider caching and implements DecodeMethodSignatureForGC using RuntimeSignatureDecoder.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/RuntimeSignatureDecoder.csNew runtime-aware ECMA-335-ish signature decoder with support for internal runtime element types.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/GcSignatureTypeProvider.csMoves/updates GC classification provider to implement the runtime-aware provider interface and classify internal types via RTS.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ISignatureDecoder.csExtends the public contract surface with GcTypeKind and DecodeMethodSignatureForGC.
Comments suppressed due to low confidence (1)

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/GcSignatureTypeProvider.cs:26

  • GcSignatureTypeProvider stores _target and _moduleHandle but never uses either of them (the implementation uses the target method parameter instead). This is dead state that should be removed, or the implementation should consistently use the stored fields if they're intended for future module/target-scoped behavior.

Max Charlamband others added 2 commits May 1, 2026 00:50
Rewrites RuntimeSignatureDecoder as a readonly struct that mirrors SRM's SignatureDecoder API (ref BlobReader per method, allowTypeSpecifications flag), with only ELEMENT_TYPE_INTERNAL (0x21) and CMOD_INTERNAL (0x22) added on top via the new IRuntimeSignatureTypeProvider interface. Drops the custom ISignatureReader/BlobHandleSignatureReader/SpanSignatureReader abstraction since BlobReader already provides lazy reading.
Fixes two latent bugs vs SRM: TypeDefOrRefOrSpec tag=3 now throws (was incorrectly returning Object), and the leading element type code is read as a compressed integer rather than a raw byte.
Moves GC-specific signature decoding out of the Signature contract into the StackWalk contract (Option B). GcSignatureTypeProvider and GcTypeKind move to Contracts/StackWalk/GC/ under the StackWalkHelpers namespace. ISignatureDecoder no longer exposes DecodeMethodSignatureForGC; the _gcProviders cache is removed from SignatureDecoder_1; GcScanner constructs RuntimeSignatureDecoder<GcTypeKind, object?> directly.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…adataReader
Splits IRuntimeSignatureTypeProvider into its own file (matches cDAC convention for internal interfaces). Makes MetadataReader required on RuntimeSignatureDecoder (matches SRM exactly; both call sites already pass non-null) and removes the null-forgiving operators. Reorders constructor params to (provider, target, metadataReader, genericContext) to mirror SRM's parameter order.
Cleanup: removes unused _mdhProviders dictionary and GetMethodDescHandleProvider from SignatureDecoder_1; removes unused _target/_moduleHandle fields and constructor from GcSignatureTypeProvider; renames _metadataReaderOpt to _metadataReader (Opt suffix is non-standard for a nullable-typed field); converts block comment to line comments.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings May 1, 2026 05:04

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.

Drop the redundant `Target target` parameter from
`IRuntimeSignatureTypeProvider.GetInternalType` and
`GetInternalModifiedType`. Providers now capture the target in their
own constructor when they need it, matching `SignatureTypeProvider<T>`.
`GcSignatureTypeProvider` gains a `Target` constructor parameter.
Update the cDAC documentation:
* `SignatureDecoder.md` -- describe `RuntimeSignatureDecoder`,
`IRuntimeSignatureTypeProvider`, and the runtime-only
`ELEMENT_TYPE_INTERNAL` / `ELEMENT_TYPE_CMOD_INTERNAL` extensions;
refresh the `DecodeFieldSignature` code sample.
* `StackWalk.md` -- new Signature-Based Scanning section covering
`GcSignatureTypeProvider`, the `PromoteCallerStack` algorithm,
reserved-slot table, and limitations vs. native.
Also revert a stray whitespace change in `ISignatureDecoder.cs`.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@max-charlamb
max-charlamb marked this pull request as ready for review May 1, 2026 05:16
CopilotAI review requested due to automatic review settings May 1, 2026 05:16

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.

Comments suppressed due to low confidence (2)

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/SignatureTypeProvider.cs:25

  • The private field _target is assigned in the constructor but never read in this type, which will trigger CS0414 (and in this repo, warnings are treated as errors by default). Please remove _target (and its assignment) or start using it (e.g., for validation) to avoid build breaks.
 private readonly Target _target;
private readonly Contracts.ModuleHandle _moduleHandle;
private readonly Contracts.ILoader _loader;
private readonly Contracts.IRuntimeTypeSystem _runtimeTypeSystem;
public SignatureTypeProvider(Target target, Contracts.ModuleHandle moduleHandle)
{
_target = target;
_moduleHandle = moduleHandle;
_loader = target.Contracts.Loader;
_runtimeTypeSystem = target.Contracts.RuntimeTypeSystem;

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/GC/GcScanner.cs:380

  • isValueTypeThis is always initialized to false and never updated, so this is never reported with GC_CALL_INTERIOR even for value-type instance methods. Since you already resolve the declaring TypeHandle earlier, it seems like this should be computed (e.g., via IRuntimeTypeSystem.IsValueType(typeHandle)) and included in the existing try/catch alongside RequiresInstArg / IsAsyncMethod.
 bool hasThis = methodSig.Header.IsInstance;
bool hasRetBuf = methodSig.ReturnType is GcTypeKind.Other;
bool requiresInstArg = false;
bool isAsync = false;
bool isValueTypeThis = false;
try
{
requiresInstArg = rts.RequiresInstArg(mdh);
isAsync = rts.IsAsyncMethod(mdh);
}
catch
{
}
PromoteCallerStackHelper(transitionBlock, methodSig, hasThis, hasRetBuf,
requiresInstArg, isAsync, isValueTypeThis, scanContext);
}

Comment threaddocs/design/datacontracts/SignatureDecoder.md Outdated
Max Charlamband others added 2 commits May 1, 2026 01:34
…rovider
Match native MethodDesc::GetSig in GcScanner.PromoteCallerStack: try
IsStoredSigMethodDesc first (dynamic, EEImpl, and array method descs) and
read the stored signature via an inline pinned BlobReader before falling
back to the metadata token lookup. Cache the GcSignatureTypeProvider on
the GcScanner so it is allocated once instead of per call.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…mitive
GcSignatureTypeProvider now classifies type parameters and TypeDef/TypeRef
tokens using the loaded TypeHandle, matching native SigTypeContext-driven
PeekElemTypeNormalized behavior:
- VAR/MVAR placeholders are resolved against the method's actual class /
method instantiation via a new GcSignatureContext(TypeHandle, MethodDescHandle)
passed as the SRM generic context.
- TypeDef/TypeRef tokens are resolved via the module's TypeDefToMethodTable /
TypeRefToMethodTable lookup tables; enums collapse to their underlying
primitive (None) via IRuntimeTypeSystem.IsEnum, matching
MethodTable::GetInternalCorElementType.
The provider is now module-scoped, so it is constructed per PromoteCallerStack
call rather than cached on GcScanner.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings May 1, 2026 05:51

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.

Comment threaddocs/design/datacontracts/SignatureDecoder.md Outdated
- IRuntimeSignatureTypeProvider: internal -> public to match SignatureTypeProvider's
visibility (avoids the "public class implements internal interface" warning while
keeping the provider type available to downstream consumers).
- RuntimeSignatureDecoder: drop the unused _target field and the redundant
ArgumentNullException.ThrowIfNull(provider/metadataReader) calls; the file is
in a nullable-enabled context, so the type system already enforces non-null.
- SignatureDecoder.md: drop the bogus `target,` argument from the GetInternalType
/ GetInternalModifiedType bullets, and reword GetTypeFromDefinition /
GetTypeFromReference's "returns null" to "returns a default TypeHandle
(Address == TargetPointer.Null)" since the API returns a struct value.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@max-charlamb
max-charlamb requested a review from rcj1May 1, 2026 15:08
Comment threaddocs/design/datacontracts/SignatureDecoder.md Outdated
Comment threaddocs/design/datacontracts/Signature.md Outdated
Comment threaddocs/design/datacontracts/Signature.md Outdated
Comment threaddocs/design/datacontracts/StackWalk.md Outdated
Comment threaddocs/design/datacontracts/Signature.md Outdated
Comment threaddocs/design/datacontracts/Signature.md Outdated
Comment threaddocs/design/datacontracts/Signature.md Outdated
CopilotAI review requested due to automatic review settings May 4, 2026 17:45

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 4 comments.

Comments suppressed due to low confidence (1)

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ISignature.cs:16

  • Renaming the public abstractions from ISignatureDecoder/SignatureDecoder to ISignature/Signature is a source/binary breaking change for any consumer of Microsoft.Diagnostics.DataContractReader.Abstractions, and this PR doesn't preserve compatibility shims. The PR metadata only links other PRs, not an api-approved issue, so this public surface either needs prior approval or it needs to stay internal until the rename is reviewed.

Comment threadsrc/coreclr/vm/datadescriptor/datadescriptor.inc
Comment threaddocs/design/datacontracts/StackWalk.md
Match native SigTypeContext::InitTypeContext: array MTs use the element
type as their class instantiation. RuntimeTypeSystem.GetInstantiation
returns an empty span for arrays, so signature decoding for array
accessor methods (e.g., int[]::Set with VAR 0) was misclassifying the
element-type slot as Ref. Special-case IsArray and use GetTypeParam,
the managed equivalent of MethodTable::GetArrayInstantiation.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
rcj1
rcj1 approved these changes May 8, 2026
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings May 8, 2026 21:56
@max-charlamb
max-charlamb enabled auto-merge (squash) May 8, 2026 22:01

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ISignature.cs:16

  • Renaming the contract from SignatureDecoder to Signature changes the wire-level lookup key (TContract.Name) used by CachingContractRegistry (it calls _tryGetContractVersion(TContract.Name, ...)). This will break compatibility between newer readers and older runtimes/dumps that only advertise SignatureDecoder (and vice versa), since contract versioning can’t help if the name doesn’t match. Consider keeping the contract name stable (e.g., keep IContract.Name as "SignatureDecoder"), or publish an alias contract on the runtime side and keep the legacy ISignatureDecoder surface as a forwarding shim so existing readers can still resolve the contract.

Comment threaddocs/design/datacontracts/StackWalk.md
@max-charlamb

Copy link
Copy Markdown
MemberAuthor

/ba-g unrelated test failures

@max-charlamb
max-charlamb disabled auto-merge May 10, 2026 02:42
@max-charlamb
max-charlamb enabled auto-merge (squash) May 10, 2026 02:44
@max-charlamb

Copy link
Copy Markdown
MemberAuthor

/ba-g cDAC only change, the only cDAC test failure is a known issue on osx

@steveisok
steveisok disabled auto-merge May 11, 2026 14:53
@steveisok
steveisok merged commit 6f89eaf into dotnet:mainMay 11, 2026
120 of 125 checks passed
jakobbotsch pushed a commit to jakobbotsch/runtime that referenced this pull request May 12, 2026
…5) (dotnet#127636)
## Summary
Part 2 of 5 stacked PRs splitting
[dotnet#126408](dotnet#126408). Builds on
[dotnet#127395](dotnet#127395) (merged).
This PR adds a cDAC ECMA-335 signature decoder that closely mirrors
`System.Reflection.Metadata.SignatureDecoder` and supports the two
runtime-only extensions used in CoreCLR-internal signatures:
`ELEMENT_TYPE_INTERNAL` (`0x21`) and `ELEMENT_TYPE_CMOD_INTERNAL`
(`0x22`). The decoder is then used by both the Signature contract (for
field signatures) and the StackWalk contract (for signature-based GC
reference scanning of transition frames).
### What this PR contains
**`RuntimeSignatureDecoder` (SRM-aligned polyfill):**
- `RuntimeSignatureDecoder<TType, TGenericContext>` -- readonly struct
mirroring SRM's `SignatureDecoder<TType, TGenericContext>`
(`DecodeType`, `DecodeFieldSignature`, `DecodeMethodSignature`,
`DecodeLocalSignature`, all taking `ref BlobReader`).
- `IRuntimeSignatureTypeProvider<TType, TGenericContext>` -- superset of
SRM's `ISignatureTypeProvider` adding `GetInternalType(TargetPointer)`
and `GetInternalModifiedType(TargetPointer, TType, bool)` for the two
runtime-only encodings.
- `SignatureTypeProvider` (the existing field-signature provider)
implements `IRuntimeSignatureTypeProvider` so internal types in field
signatures resolve via `RuntimeTypeSystem.GetTypeHandle`.
**Signature-based GC reference scanning in StackWalk:**
- `GcSignatureTypeProvider` (internal, in `StackWalkHelpers`) classifies
each method-signature parameter as `Ref`, `Interior`, `Other` (value
type or larger-than-slot), or `None`.
- A new `GcSignatureContext(TypeHandle classContext, MethodDescHandle
methodContext)` record struct is plumbed through
`RuntimeSignatureDecoder<GcTypeKind, GcSignatureContext>` so
`ELEMENT_TYPE_VAR` / `ELEMENT_TYPE_MVAR` placeholders resolve to the
method's actual class / method instantiation -- matching native
`SigTypeContext`-driven `PeekElemTypeNormalized` behavior.
- `GcScanner.PromoteCallerStack` constructs the provider per call and
walks the `TransitionBlock` using a reserved-slot count derived from
`IsInstance` / return-buffer / `RequiresInstArg` / `IsAsyncMethod` /
ARM64 `x8`, reporting each parameter slot as a GC reference, interior
pointer, or skip.
- Signature acquisition mirrors native `MethodDesc::GetSig`: prefers
`IsStoredSigMethodDesc` (dynamic, EEImpl, array methods) before falling
back to the metadata token, so caller-stack roots of dynamic and
array-method transition frames are handled. Stored sigs are pinned with
an inline `fixed` block and read via a `BlobReader`, matching the
existing `SigFormat.cs` pattern.
- This is the cDAC equivalent of native
`TransitionFrame::PromoteCallerStack` and is used for
`PrestubMethodFrame`, `CallCountingHelperFrame`, and the
`StubDispatchFrame` / `ExternalMethodFrame` fallback when no GCRefMap is
available.
**Signature contract surface stays minimal:**
- `ISignatureDecoder` continues to expose only
`DecodeFieldSignature(BlobHandle, ModuleHandle, TypeHandle)`.
- GC-specific decoding lives entirely inside the StackWalk contract; the
Signature contract has no GC dependencies.
**cDAC documentation:**
- `docs/design/datacontracts/SignatureDecoder.md` -- describes
`RuntimeSignatureDecoder`, `IRuntimeSignatureTypeProvider`, and the
`ELEMENT_TYPE_INTERNAL` / `CMOD_INTERNAL` extensions; refreshed
`DecodeFieldSignature` code sample.
- `docs/design/datacontracts/StackWalk.md` -- new "Signature-Based
Scanning" section under GC stack reference scanning, covering
`GcSignatureTypeProvider` (with module scoping, `GcSignatureContext`,
and enum normalization), the `PromoteCallerStack` algorithm, the
reserved-slot table, and limitations vs. native.
### Testing
- Build: clean, 0 warnings / 0 errors.
- 1921 / 1921 cDAC unit tests pass.
- Behavioral verification against the legacy DAC happens via the
GC-stress verification harness introduced later in the PR series.
> [!NOTE]
> This PR description was created with AI assistance from Copilot.
---------
Co-authored-by: Max Charlamb <maxcharlamb@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jun 11, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@max-charlamb@jkotas@rcj1@steveisok
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

[cDAC] RuntimeSignatureDecoder and centralized Signature contract (2/5) - #127636

Merged
steveisok merged 13 commits into
dotnet:mainfrom
max-charlamb:cdac-stackrefs-pr2
May 11, 2026
Merged

[cDAC] RuntimeSignatureDecoder and centralized Signature contract (2/5)#127636
steveisok merged 13 commits into
dotnet:mainfrom
max-charlamb:cdac-stackrefs-pr2

Conversation

@max-charlamb

@max-charlambmax-charlamb commented May 1, 2026

Copy link
Copy Markdown
Member

Summary

Part 2 of 5 stacked PRs splitting #126408. Builds on #127395 (merged).

This PR adds a cDAC ECMA-335 signature decoder that closely mirrors System.Reflection.Metadata.SignatureDecoder and supports the two runtime-only extensions used in CoreCLR-internal signatures: ELEMENT_TYPE_INTERNAL (0x21) and ELEMENT_TYPE_CMOD_INTERNAL (0x22). The decoder is then used by both the Signature contract (for field signatures) and the StackWalk contract (for signature-based GC reference scanning of transition frames).

What this PR contains

RuntimeSignatureDecoder (SRM-aligned polyfill):

  • RuntimeSignatureDecoder<TType, TGenericContext> -- readonly struct mirroring SRM's SignatureDecoder<TType, TGenericContext> (DecodeType, DecodeFieldSignature, DecodeMethodSignature, DecodeLocalSignature, all taking ref BlobReader).
  • IRuntimeSignatureTypeProvider<TType, TGenericContext> -- superset of SRM's ISignatureTypeProvider adding GetInternalType(TargetPointer) and GetInternalModifiedType(TargetPointer, TType, bool) for the two runtime-only encodings.
  • SignatureTypeProvider (the existing field-signature provider) implements IRuntimeSignatureTypeProvider so internal types in field signatures resolve via RuntimeTypeSystem.GetTypeHandle.

Signature-based GC reference scanning in StackWalk:

  • GcSignatureTypeProvider (internal, in StackWalkHelpers) classifies each method-signature parameter as Ref, Interior, Other (value type or larger-than-slot), or None.
  • A new GcSignatureContext(TypeHandle classContext, MethodDescHandle methodContext) record struct is plumbed through RuntimeSignatureDecoder<GcTypeKind, GcSignatureContext> so ELEMENT_TYPE_VAR / ELEMENT_TYPE_MVAR placeholders resolve to the method's actual class / method instantiation -- matching native SigTypeContext-driven PeekElemTypeNormalized behavior.
  • GcScanner.PromoteCallerStack constructs the provider per call and walks the TransitionBlock using a reserved-slot count derived from IsInstance / return-buffer / RequiresInstArg / IsAsyncMethod / ARM64 x8, reporting each parameter slot as a GC reference, interior pointer, or skip.
  • Signature acquisition mirrors native MethodDesc::GetSig: prefers IsStoredSigMethodDesc (dynamic, EEImpl, array methods) before falling back to the metadata token, so caller-stack roots of dynamic and array-method transition frames are handled. Stored sigs are pinned with an inline fixed block and read via a BlobReader, matching the existing SigFormat.cs pattern.
  • This is the cDAC equivalent of native TransitionFrame::PromoteCallerStack and is used for PrestubMethodFrame, CallCountingHelperFrame, and the StubDispatchFrame / ExternalMethodFrame fallback when no GCRefMap is available.

Signature contract surface stays minimal:

  • ISignatureDecoder continues to expose only DecodeFieldSignature(BlobHandle, ModuleHandle, TypeHandle).
  • GC-specific decoding lives entirely inside the StackWalk contract; the Signature contract has no GC dependencies.

cDAC documentation:

  • docs/design/datacontracts/SignatureDecoder.md -- describes RuntimeSignatureDecoder, IRuntimeSignatureTypeProvider, and the ELEMENT_TYPE_INTERNAL / CMOD_INTERNAL extensions; refreshed DecodeFieldSignature code sample.
  • docs/design/datacontracts/StackWalk.md -- new "Signature-Based Scanning" section under GC stack reference scanning, covering GcSignatureTypeProvider (with module scoping, GcSignatureContext, and enum normalization), the PromoteCallerStack algorithm, the reserved-slot table, and limitations vs. native.

Testing

  • Build: clean, 0 warnings / 0 errors.
  • 1921 / 1921 cDAC unit tests pass.
  • Behavioral verification against the legacy DAC happens via the GC-stress verification harness introduced later in the PR series.

Note

This PR description was created with AI assistance from Copilot.

- Move RuntimeSignatureDecoder to Contracts/Signature/
- Move GcSignatureTypeProvider to Contracts/Signature/ with module-scoped caching
- Add DecodeMethodSignatureForGC(BlobHandle, ModuleHandle) to ISignatureDecoder
- Add DecodeFieldSignature to RuntimeSignatureDecoder
- Add BlobHandleSignatureReader for lazy blob reading
- SignatureTypeProvider implements IRuntimeSignatureTypeProvider
- Switch DecodeFieldSignature to use RuntimeSignatureDecoder
- GcScanner uses contract API instead of direct decoder construction
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @steveisok, @tommcdon, @dotnet/dotnet-diag
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR is part of the cDAC signature-decoding refactor, introducing a custom RuntimeSignatureDecoder capable of handling runtime-internal signature element types (ELEMENT_TYPE_INTERNAL / ELEMENT_TYPE_CMOD_INTERNAL) and centralizing GC-oriented signature decoding behind the ISignatureDecoder contract.

Changes:

  • Added RuntimeSignatureDecoder plus signature-reader abstractions (ISignatureReader, span/blob-backed readers) to support decoding from different signature sources.
  • Extended the ISignatureDecoder contract with DecodeMethodSignatureForGC(...) and promoted GcTypeKind to a public abstraction.
  • Updated stack GC scanning (GcScanner) and signature type providers to route decoding through the centralized contract API.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Show a summary per file
FileDescription
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/GC/GcScanner.csSwitches caller-signature decoding to the ISignatureDecoder.DecodeMethodSignatureForGC contract API.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/SignatureTypeProvider.csUpdates the type provider to implement IRuntimeSignatureTypeProvider and adds internal-type callbacks.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/SignatureReaders.csIntroduces abstractions for reading signature bytes from spans and metadata blobs.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/SignatureDecoder_1.csAdds module-scoped GC provider caching and implements DecodeMethodSignatureForGC using RuntimeSignatureDecoder.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/RuntimeSignatureDecoder.csNew runtime-aware ECMA-335-ish signature decoder with support for internal runtime element types.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/GcSignatureTypeProvider.csMoves/updates GC classification provider to implement the runtime-aware provider interface and classify internal types via RTS.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ISignatureDecoder.csExtends the public contract surface with GcTypeKind and DecodeMethodSignatureForGC.
Comments suppressed due to low confidence (1)

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/GcSignatureTypeProvider.cs:26

  • GcSignatureTypeProvider stores _target and _moduleHandle but never uses either of them (the implementation uses the target method parameter instead). This is dead state that should be removed, or the implementation should consistently use the stored fields if they're intended for future module/target-scoped behavior.

Max Charlamband others added 2 commits May 1, 2026 00:50
Rewrites RuntimeSignatureDecoder as a readonly struct that mirrors SRM's SignatureDecoder API (ref BlobReader per method, allowTypeSpecifications flag), with only ELEMENT_TYPE_INTERNAL (0x21) and CMOD_INTERNAL (0x22) added on top via the new IRuntimeSignatureTypeProvider interface. Drops the custom ISignatureReader/BlobHandleSignatureReader/SpanSignatureReader abstraction since BlobReader already provides lazy reading.
Fixes two latent bugs vs SRM: TypeDefOrRefOrSpec tag=3 now throws (was incorrectly returning Object), and the leading element type code is read as a compressed integer rather than a raw byte.
Moves GC-specific signature decoding out of the Signature contract into the StackWalk contract (Option B). GcSignatureTypeProvider and GcTypeKind move to Contracts/StackWalk/GC/ under the StackWalkHelpers namespace. ISignatureDecoder no longer exposes DecodeMethodSignatureForGC; the _gcProviders cache is removed from SignatureDecoder_1; GcScanner constructs RuntimeSignatureDecoder<GcTypeKind, object?> directly.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…adataReader
Splits IRuntimeSignatureTypeProvider into its own file (matches cDAC convention for internal interfaces). Makes MetadataReader required on RuntimeSignatureDecoder (matches SRM exactly; both call sites already pass non-null) and removes the null-forgiving operators. Reorders constructor params to (provider, target, metadataReader, genericContext) to mirror SRM's parameter order.
Cleanup: removes unused _mdhProviders dictionary and GetMethodDescHandleProvider from SignatureDecoder_1; removes unused _target/_moduleHandle fields and constructor from GcSignatureTypeProvider; renames _metadataReaderOpt to _metadataReader (Opt suffix is non-standard for a nullable-typed field); converts block comment to line comments.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings May 1, 2026 05:04

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.

Drop the redundant `Target target` parameter from
`IRuntimeSignatureTypeProvider.GetInternalType` and
`GetInternalModifiedType`. Providers now capture the target in their
own constructor when they need it, matching `SignatureTypeProvider<T>`.
`GcSignatureTypeProvider` gains a `Target` constructor parameter.
Update the cDAC documentation:
* `SignatureDecoder.md` -- describe `RuntimeSignatureDecoder`,
`IRuntimeSignatureTypeProvider`, and the runtime-only
`ELEMENT_TYPE_INTERNAL` / `ELEMENT_TYPE_CMOD_INTERNAL` extensions;
refresh the `DecodeFieldSignature` code sample.
* `StackWalk.md` -- new Signature-Based Scanning section covering
`GcSignatureTypeProvider`, the `PromoteCallerStack` algorithm,
reserved-slot table, and limitations vs. native.
Also revert a stray whitespace change in `ISignatureDecoder.cs`.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@max-charlamb
max-charlamb marked this pull request as ready for review May 1, 2026 05:16
CopilotAI review requested due to automatic review settings May 1, 2026 05:16

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.

Comments suppressed due to low confidence (2)

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/SignatureTypeProvider.cs:25

  • The private field _target is assigned in the constructor but never read in this type, which will trigger CS0414 (and in this repo, warnings are treated as errors by default). Please remove _target (and its assignment) or start using it (e.g., for validation) to avoid build breaks.
 private readonly Target _target;
private readonly Contracts.ModuleHandle _moduleHandle;
private readonly Contracts.ILoader _loader;
private readonly Contracts.IRuntimeTypeSystem _runtimeTypeSystem;
public SignatureTypeProvider(Target target, Contracts.ModuleHandle moduleHandle)
{
_target = target;
_moduleHandle = moduleHandle;
_loader = target.Contracts.Loader;
_runtimeTypeSystem = target.Contracts.RuntimeTypeSystem;

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/GC/GcScanner.cs:380

  • isValueTypeThis is always initialized to false and never updated, so this is never reported with GC_CALL_INTERIOR even for value-type instance methods. Since you already resolve the declaring TypeHandle earlier, it seems like this should be computed (e.g., via IRuntimeTypeSystem.IsValueType(typeHandle)) and included in the existing try/catch alongside RequiresInstArg / IsAsyncMethod.
 bool hasThis = methodSig.Header.IsInstance;
bool hasRetBuf = methodSig.ReturnType is GcTypeKind.Other;
bool requiresInstArg = false;
bool isAsync = false;
bool isValueTypeThis = false;
try
{
requiresInstArg = rts.RequiresInstArg(mdh);
isAsync = rts.IsAsyncMethod(mdh);
}
catch
{
}
PromoteCallerStackHelper(transitionBlock, methodSig, hasThis, hasRetBuf,
requiresInstArg, isAsync, isValueTypeThis, scanContext);
}

Comment threaddocs/design/datacontracts/SignatureDecoder.md Outdated
Max Charlamband others added 2 commits May 1, 2026 01:34
…rovider
Match native MethodDesc::GetSig in GcScanner.PromoteCallerStack: try
IsStoredSigMethodDesc first (dynamic, EEImpl, and array method descs) and
read the stored signature via an inline pinned BlobReader before falling
back to the metadata token lookup. Cache the GcSignatureTypeProvider on
the GcScanner so it is allocated once instead of per call.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…mitive
GcSignatureTypeProvider now classifies type parameters and TypeDef/TypeRef
tokens using the loaded TypeHandle, matching native SigTypeContext-driven
PeekElemTypeNormalized behavior:
- VAR/MVAR placeholders are resolved against the method's actual class /
method instantiation via a new GcSignatureContext(TypeHandle, MethodDescHandle)
passed as the SRM generic context.
- TypeDef/TypeRef tokens are resolved via the module's TypeDefToMethodTable /
TypeRefToMethodTable lookup tables; enums collapse to their underlying
primitive (None) via IRuntimeTypeSystem.IsEnum, matching
MethodTable::GetInternalCorElementType.
The provider is now module-scoped, so it is constructed per PromoteCallerStack
call rather than cached on GcScanner.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings May 1, 2026 05:51

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.

Comment threaddocs/design/datacontracts/SignatureDecoder.md Outdated
- IRuntimeSignatureTypeProvider: internal -> public to match SignatureTypeProvider's
visibility (avoids the "public class implements internal interface" warning while
keeping the provider type available to downstream consumers).
- RuntimeSignatureDecoder: drop the unused _target field and the redundant
ArgumentNullException.ThrowIfNull(provider/metadataReader) calls; the file is
in a nullable-enabled context, so the type system already enforces non-null.
- SignatureDecoder.md: drop the bogus `target,` argument from the GetInternalType
/ GetInternalModifiedType bullets, and reword GetTypeFromDefinition /
GetTypeFromReference's "returns null" to "returns a default TypeHandle
(Address == TargetPointer.Null)" since the API returns a struct value.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@max-charlamb
max-charlamb requested a review from rcj1May 1, 2026 15:08
Comment threaddocs/design/datacontracts/SignatureDecoder.md Outdated
Comment threaddocs/design/datacontracts/Signature.md Outdated
Comment threaddocs/design/datacontracts/Signature.md Outdated
Comment threaddocs/design/datacontracts/StackWalk.md Outdated
Comment threaddocs/design/datacontracts/Signature.md Outdated
Comment threaddocs/design/datacontracts/Signature.md Outdated
Comment threaddocs/design/datacontracts/Signature.md Outdated
CopilotAI review requested due to automatic review settings May 4, 2026 17:45

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 4 comments.

Comments suppressed due to low confidence (1)

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ISignature.cs:16

  • Renaming the public abstractions from ISignatureDecoder/SignatureDecoder to ISignature/Signature is a source/binary breaking change for any consumer of Microsoft.Diagnostics.DataContractReader.Abstractions, and this PR doesn't preserve compatibility shims. The PR metadata only links other PRs, not an api-approved issue, so this public surface either needs prior approval or it needs to stay internal until the rename is reviewed.

Comment threadsrc/coreclr/vm/datadescriptor/datadescriptor.inc
Comment threaddocs/design/datacontracts/StackWalk.md
Match native SigTypeContext::InitTypeContext: array MTs use the element
type as their class instantiation. RuntimeTypeSystem.GetInstantiation
returns an empty span for arrays, so signature decoding for array
accessor methods (e.g., int[]::Set with VAR 0) was misclassifying the
element-type slot as Ref. Special-case IsArray and use GetTypeParam,
the managed equivalent of MethodTable::GetArrayInstantiation.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
rcj1
rcj1 approved these changes May 8, 2026
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings May 8, 2026 21:56
@max-charlamb
max-charlamb enabled auto-merge (squash) May 8, 2026 22:01

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ISignature.cs:16

  • Renaming the contract from SignatureDecoder to Signature changes the wire-level lookup key (TContract.Name) used by CachingContractRegistry (it calls _tryGetContractVersion(TContract.Name, ...)). This will break compatibility between newer readers and older runtimes/dumps that only advertise SignatureDecoder (and vice versa), since contract versioning can’t help if the name doesn’t match. Consider keeping the contract name stable (e.g., keep IContract.Name as "SignatureDecoder"), or publish an alias contract on the runtime side and keep the legacy ISignatureDecoder surface as a forwarding shim so existing readers can still resolve the contract.

Comment threaddocs/design/datacontracts/StackWalk.md
@max-charlamb

Copy link
Copy Markdown
MemberAuthor

/ba-g unrelated test failures

@max-charlamb
max-charlamb disabled auto-merge May 10, 2026 02:42
@max-charlamb
max-charlamb enabled auto-merge (squash) May 10, 2026 02:44
@max-charlamb

Copy link
Copy Markdown
MemberAuthor

/ba-g cDAC only change, the only cDAC test failure is a known issue on osx

@steveisok
steveisok disabled auto-merge May 11, 2026 14:53
@steveisok
steveisok merged commit 6f89eaf into dotnet:mainMay 11, 2026
120 of 125 checks passed
jakobbotsch pushed a commit to jakobbotsch/runtime that referenced this pull request May 12, 2026
…5) (dotnet#127636)
## Summary
Part 2 of 5 stacked PRs splitting
[dotnet#126408](dotnet#126408). Builds on
[dotnet#127395](dotnet#127395) (merged).
This PR adds a cDAC ECMA-335 signature decoder that closely mirrors
`System.Reflection.Metadata.SignatureDecoder` and supports the two
runtime-only extensions used in CoreCLR-internal signatures:
`ELEMENT_TYPE_INTERNAL` (`0x21`) and `ELEMENT_TYPE_CMOD_INTERNAL`
(`0x22`). The decoder is then used by both the Signature contract (for
field signatures) and the StackWalk contract (for signature-based GC
reference scanning of transition frames).
### What this PR contains
**`RuntimeSignatureDecoder` (SRM-aligned polyfill):**
- `RuntimeSignatureDecoder<TType, TGenericContext>` -- readonly struct
mirroring SRM's `SignatureDecoder<TType, TGenericContext>`
(`DecodeType`, `DecodeFieldSignature`, `DecodeMethodSignature`,
`DecodeLocalSignature`, all taking `ref BlobReader`).
- `IRuntimeSignatureTypeProvider<TType, TGenericContext>` -- superset of
SRM's `ISignatureTypeProvider` adding `GetInternalType(TargetPointer)`
and `GetInternalModifiedType(TargetPointer, TType, bool)` for the two
runtime-only encodings.
- `SignatureTypeProvider` (the existing field-signature provider)
implements `IRuntimeSignatureTypeProvider` so internal types in field
signatures resolve via `RuntimeTypeSystem.GetTypeHandle`.
**Signature-based GC reference scanning in StackWalk:**
- `GcSignatureTypeProvider` (internal, in `StackWalkHelpers`) classifies
each method-signature parameter as `Ref`, `Interior`, `Other` (value
type or larger-than-slot), or `None`.
- A new `GcSignatureContext(TypeHandle classContext, MethodDescHandle
methodContext)` record struct is plumbed through
`RuntimeSignatureDecoder<GcTypeKind, GcSignatureContext>` so
`ELEMENT_TYPE_VAR` / `ELEMENT_TYPE_MVAR` placeholders resolve to the
method's actual class / method instantiation -- matching native
`SigTypeContext`-driven `PeekElemTypeNormalized` behavior.
- `GcScanner.PromoteCallerStack` constructs the provider per call and
walks the `TransitionBlock` using a reserved-slot count derived from
`IsInstance` / return-buffer / `RequiresInstArg` / `IsAsyncMethod` /
ARM64 `x8`, reporting each parameter slot as a GC reference, interior
pointer, or skip.
- Signature acquisition mirrors native `MethodDesc::GetSig`: prefers
`IsStoredSigMethodDesc` (dynamic, EEImpl, array methods) before falling
back to the metadata token, so caller-stack roots of dynamic and
array-method transition frames are handled. Stored sigs are pinned with
an inline `fixed` block and read via a `BlobReader`, matching the
existing `SigFormat.cs` pattern.
- This is the cDAC equivalent of native
`TransitionFrame::PromoteCallerStack` and is used for
`PrestubMethodFrame`, `CallCountingHelperFrame`, and the
`StubDispatchFrame` / `ExternalMethodFrame` fallback when no GCRefMap is
available.
**Signature contract surface stays minimal:**
- `ISignatureDecoder` continues to expose only
`DecodeFieldSignature(BlobHandle, ModuleHandle, TypeHandle)`.
- GC-specific decoding lives entirely inside the StackWalk contract; the
Signature contract has no GC dependencies.
**cDAC documentation:**
- `docs/design/datacontracts/SignatureDecoder.md` -- describes
`RuntimeSignatureDecoder`, `IRuntimeSignatureTypeProvider`, and the
`ELEMENT_TYPE_INTERNAL` / `CMOD_INTERNAL` extensions; refreshed
`DecodeFieldSignature` code sample.
- `docs/design/datacontracts/StackWalk.md` -- new "Signature-Based
Scanning" section under GC stack reference scanning, covering
`GcSignatureTypeProvider` (with module scoping, `GcSignatureContext`,
and enum normalization), the `PromoteCallerStack` algorithm, the
reserved-slot table, and limitations vs. native.
### Testing
- Build: clean, 0 warnings / 0 errors.
- 1921 / 1921 cDAC unit tests pass.
- Behavioral verification against the legacy DAC happens via the
GC-stress verification harness introduced later in the PR series.
> [!NOTE]
> This PR description was created with AI assistance from Copilot.
---------
Co-authored-by: Max Charlamb <maxcharlamb@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jun 11, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@max-charlamb@jkotas@rcj1@steveisok