Commit 23d2e12
[cDAC] Add IMetaDataImport COM wrapper over MetadataReader for no-fallback mode (#127028)
> [!NOTE]
> This PR description was AI/Copilot-generated.
## Summary
Implements a managed `[GeneratedComClass]` wrapper
(`MetaDataImportImpl`) that adapts
`System.Reflection.Metadata.MetadataReader` to the `IMetaDataImport`,
`IMetaDataImport2`, and `IMetaDataAssemblyImport` COM interfaces. This
enables SOS and ClrMD to query metadata in cDAC mode, with optional
legacy DAC fallback for methods not yet implemented in the managed
layer.
## Motivation
In cDAC no-fallback mode, `ClrDataModule.GetInterface()` returned
`NotHandled` for `IMetaDataImport` QIs when `_legacyModulePointer == 0`,
meaning diagnostic tools couldn't access type/method/field metadata. The
cDAC already has access to `MetadataReader` via the `EcmaMetadata`
contract, so a thin COM wrapper bridges the gap.
When a legacy DAC *is* available, the wrapper uses it for `#if DEBUG`
validation (asserting that cDAC and DAC produce identical results) and
as a fallback for the ~45 methods not yet implemented in managed code.
## Changes
| File | Description |
|------|-------------|
| `IMetaDataImport.cs` | Managed `[GeneratedComInterface]` definitions
for IMetaDataImport (51 methods), IMetaDataImport2 (8 methods),
IMetaDataAssemblyImport (14 methods), ASSEMBLYMETADATA struct, and
internal `CldbHResults` constants |
| `MetaDataImportImpl.cs` | `[GeneratedComClass]` implementation — 28
full cDAC implementations via MetadataReader + ~45 legacy-delegated
stubs. Uses explicit interface notation. |
| `OutputBufferHelpers.cs` | `CopyStringToBuffer` split into two
overloads: `void` (for callers that don't check truncation) and `out
bool truncated` (for MetaDataImportImpl) |
| `ClrDataModule.cs` | Wire up wrapper via `ICustomQueryInterface` —
creates `MetaDataImportImpl` with both MetadataReader and optional
legacy IMetaDataImport |
| `MetaDataImportImplTests.cs` | 50 unit tests using synthetic metadata
built with `MetadataBuilder` |
| `MetaDataImportDumpTests.cs` | 3 dump-based integration tests
verifying semantic parity against real metadata |
| `MultiModule` debuggee | Test debuggee with non-const fields, user
strings, and methods for dump tests |
### Implemented methods (28 cDAC, ~45 legacy fallback)
**Enum (cDAC):** `EnumInterfaceImpls`, `EnumFields`,
`EnumGenericParams`, `CloseEnum`, `CountEnum`, `ResetEnum`
**Properties (cDAC):** `GetTypeDefProps`, `GetTypeRefProps`,
`GetMethodProps`, `GetFieldProps`, `GetMemberProps`,
`GetInterfaceImplProps`, `GetNestedClassProps`, `GetGenericParamProps`,
`GetMemberRefProps`, `GetModuleRefProps`, `GetParamProps`,
`GetClassLayout`, `GetUserString`, `GetParamForMethodIndex`
**Blob/token (cDAC):** `GetRVA`, `GetSigFromToken`,
`GetTypeSpecFromToken`, `GetCustomAttributeByName`, `IsValidToken`,
`FindTypeDefByName`
**Assembly (cDAC):** `GetAssemblyProps`, `GetAssemblyRefProps`,
`GetAssemblyFromScope`
**Legacy fallback:** All remaining methods delegate to
`_legacyImport`/`_legacyImport2`/`_legacyAssemblyImport` or return
`E_NOTIMPL` when no legacy is available.
### Native parity behaviors
- **`<Module>` parent mapping**:
`GetMethodProps`/`GetFieldProps`/`GetMemberRefProps` map TypeDef RID 1
to `mdTypeDefNil` (0x00000000) via `MapGlobalParentToken`, matching
native RegMeta
- **Constant defaults**: `GetFieldProps`/`GetParamProps` return
`ELEMENT_TYPE_VOID` when no constant is present
- **User string heap**: `GetUserString` uses raw `#US` heap byte parsing
to exactly match native blob size validation (odd-length check, terminal
byte stripping)
- **Assembly flags**: `GetAssemblyProps` ORs `afPublicKey` into flags
when public key blob is non-empty
- **Truncation**: All string-returning methods return
`CLDB_S_TRUNCATION` when buffer is too small
- **Record not found**:
`GetNestedClassProps`/`GetClassLayout`/`GetAssemblyProps` return
`CLDB_E_RECORD_NOTFOUND` for missing records
### Design decisions
- **Explicit interface implementation**: All ~73 COM methods use
explicit interface notation (`int IMetaDataImport.Method(...)`) to keep
the public surface clean
- **ICustomQueryInterface**: QI for `IMetaDataImport` returns an
`IMetaDataImport2` vtable so callers always get the full interface
- **HCORENUM pattern**: Uses `GCHandle.Alloc` to box `MetadataEnum`
objects; `ConcurrentDictionary<nint, byte>` tracks ownership for routing
CloseEnum/CountEnum/ResetEnum between cDAC and legacy handles
- **`#if DEBUG` validation**: Every cDAC-implemented method (with 2
justified exceptions) cross-checks its output against the legacy DAC in
debug builds
- **CopyStringToBuffer overloads**: `void` overload for callers that
don't need truncation info; `out bool truncated` overload for
MetaDataImportImpl callers that return `CLDB_S_TRUNCATION`
## Testing
- **1845 unit tests** pass (all cDAC tests including 50
MetaDataImportImpl tests)
- **214 dump-based tests** pass locally (3 are MetaDataImport-specific)
- Tests cover: enum pagination, global/non-global parent mapping,
constant handling, user string char counts, truncation, nested classes,
generic params, assembly properties, invalid tokens, QI vtable
correctness, and E_NOTIMPL fallback behavior
---------
Co-authored-by: Max Charlamb <maxcharlamb@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>1 parent dc94b10 commit 23d2e12
10 files changed
Lines changed: 3411 additions & 7 deletions
File tree
- src/native/managed/cdac
- Microsoft.Diagnostics.DataContractReader.Legacy
- tests
- DumpTests
- Debuggees/MultiModule
Lines changed: 57 additions & 4 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
34 | 34 | | |
35 | 35 | | |
36 | 36 | | |
| 37 | + | |
| 38 | + | |
37 | 39 | | |
38 | 40 | | |
39 | 41 | | |
| |||
49 | 51 | | |
50 | 52 | | |
51 | 53 | | |
52 | | - | |
53 | 54 | | |
54 | 55 | | |
55 | 56 | | |
56 | 57 | | |
57 | | - | |
58 | | - | |
59 | 58 | | |
60 | 59 | | |
61 | 60 | | |
62 | 61 | | |
63 | | - | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
64 | 116 | | |
| 117 | + | |
65 | 118 | | |
66 | 119 | | |
67 | 120 | | |
| |||
0 commit comments