From 6af5de21b2c511094d40ee912acf18978a787eb3 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Mon, 27 Apr 2026 16:50:18 -0400 Subject: [PATCH 1/4] Implement EnumTypeDefs, EnumMethods, GetExportedTypeProps, FindExportedTypeByName Add cDAC implementations for 4 IMetaDataImport/IMetaDataAssemblyImport methods that were previously legacy-only stubs. These are used by an internal debugger. - EnumTypeDefs: iterates MetadataReader.TypeDefinitions - EnumMethods: iterates TypeDefinition.GetMethods() for a given TypeDef - GetExportedTypeProps: returns name, implementation, typeDef, and flags - FindExportedTypeByName: searches ExportedTypes by name with nested type support Includes #if DEBUG validation against legacy DAC and 11 new unit tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../MetaDataImportImpl.cs | 163 ++++++++++++- .../DumpTests/MetaDataImportDumpTests.cs | 80 +++++++ .../cdac/tests/MetaDataImportImplTests.cs | 226 +++++++++++++++++- 3 files changed, 464 insertions(+), 5 deletions(-) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/MetaDataImportImpl.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/MetaDataImportImpl.cs index 2a2711751d6efa..a2a8e200fbf377 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/MetaDataImportImpl.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/MetaDataImportImpl.cs @@ -156,7 +156,29 @@ int IMetaDataImport.ResetEnum(nint hEnum, uint ulPos) } int IMetaDataImport.EnumTypeDefs(nint* phEnum, uint* rTypeDefs, uint cMax, uint* pcTypeDefs) - => _legacyImport is not null ? _legacyImport.EnumTypeDefs(phEnum, rTypeDefs, cMax, pcTypeDefs) : HResults.E_NOTIMPL; + { + int hr = HResults.S_OK; + try + { + if (phEnum is not null && *phEnum != 0) + { + hr = FillEnum(phEnum, GetEnum(*phEnum).Tokens, rTypeDefs, cMax, pcTypeDefs); + } + else + { + List tokens = new(); + foreach (TypeDefinitionHandle h in _reader.TypeDefinitions) + tokens.Add((uint)MetadataTokens.GetToken(h)); + hr = FillEnum(phEnum, tokens, rTypeDefs, cMax, pcTypeDefs); + } + } + catch (System.Exception ex) + { + hr = ex.HResult; + } + + return hr; + } int IMetaDataImport.EnumInterfaceImpls(nint* phEnum, uint td, uint* rImpls, uint cMax, uint* pcImpls) { @@ -192,7 +214,31 @@ int IMetaDataImport.EnumMembers(nint* phEnum, uint cl, uint* rMembers, uint cMax => _legacyImport is not null ? _legacyImport.EnumMembers(phEnum, cl, rMembers, cMax, pcTokens) : HResults.E_NOTIMPL; int IMetaDataImport.EnumMethods(nint* phEnum, uint cl, uint* rMethods, uint cMax, uint* pcTokens) - => _legacyImport is not null ? _legacyImport.EnumMethods(phEnum, cl, rMethods, cMax, pcTokens) : HResults.E_NOTIMPL; + { + int hr = HResults.S_OK; + try + { + if (phEnum is not null && *phEnum != 0) + { + hr = FillEnum(phEnum, GetEnum(*phEnum).Tokens, rMethods, cMax, pcTokens); + } + else + { + TypeDefinitionHandle typeHandle = MetadataTokens.TypeDefinitionHandle((int)(cl & 0x00FFFFFF)); + TypeDefinition typeDef = _reader.GetTypeDefinition(typeHandle); + List tokens = new(); + foreach (MethodDefinitionHandle h in typeDef.GetMethods()) + tokens.Add((uint)MetadataTokens.GetToken(h)); + hr = FillEnum(phEnum, tokens, rMethods, cMax, pcTokens); + } + } + catch (System.Exception ex) + { + hr = ex.HResult; + } + + return hr; + } int IMetaDataImport.EnumFields(nint* phEnum, uint cl, uint* rFields, uint cMax, uint* pcTokens) { @@ -1633,7 +1679,63 @@ int IMetaDataAssemblyImport.GetFileProps(uint mdf, char* szName, uint cchName, u int IMetaDataAssemblyImport.GetExportedTypeProps(uint mdct, char* szName, uint cchName, uint* pchName, uint* ptkImplementation, uint* ptkTypeDef, uint* pdwExportedTypeFlags) - => _legacyAssemblyImport is not null ? _legacyAssemblyImport.GetExportedTypeProps(mdct, szName, cchName, pchName, ptkImplementation, ptkTypeDef, pdwExportedTypeFlags) : HResults.E_NOTIMPL; + { + int hr = HResults.S_OK; + try + { + ExportedTypeHandle handle = MetadataTokens.ExportedTypeHandle((int)(mdct & 0x00FFFFFF)); + ExportedType exportedType = _reader.GetExportedType(handle); + + string name = _reader.GetString(exportedType.Name); + string ns = _reader.GetString(exportedType.Namespace); + string fullName = string.IsNullOrEmpty(ns) ? name : $"{ns}.{name}"; + OutputBufferHelpers.CopyStringToBuffer(szName, cchName, pchName, fullName, out bool truncated); + + if (ptkImplementation is not null) + { + EntityHandle impl = exportedType.Implementation; + *ptkImplementation = impl.IsNil ? 0 : (uint)MetadataTokens.GetToken(impl); + } + + if (ptkTypeDef is not null) + *ptkTypeDef = (uint)exportedType.GetTypeDefinitionId(); + + if (pdwExportedTypeFlags is not null) + *pdwExportedTypeFlags = (uint)exportedType.Attributes; + + hr = truncated ? CldbHResults.CLDB_S_TRUNCATION : HResults.S_OK; + } + catch (System.Exception ex) + { + hr = ex.HResult; + } + +#if DEBUG + if (_legacyAssemblyImport is not null) + { + char* szNameLocal = stackalloc char[(int)cchName]; + uint pchNameLocal = 0; + uint tkImplementationLocal = 0; + uint tkTypeDefLocal = 0; + uint dwExportedTypeFlagsLocal = 0; + int hrLegacy = _legacyAssemblyImport.GetExportedTypeProps(mdct, szNameLocal, cchName, &pchNameLocal, + &tkImplementationLocal, &tkTypeDefLocal, &dwExportedTypeFlagsLocal); + Debug.ValidateHResult(hr, hrLegacy); + if (hr >= 0 && hrLegacy >= 0) + { + if (pchName is not null) + Debug.Assert(*pchName == pchNameLocal, $"ExportedType name length mismatch: cDAC={*pchName}, DAC={pchNameLocal}"); + if (ptkImplementation is not null) + Debug.Assert(*ptkImplementation == tkImplementationLocal, $"ExportedType implementation mismatch: cDAC=0x{*ptkImplementation:X}, DAC=0x{tkImplementationLocal:X}"); + if (ptkTypeDef is not null) + Debug.Assert(*ptkTypeDef == tkTypeDefLocal, $"ExportedType typeDef mismatch: cDAC=0x{*ptkTypeDef:X}, DAC=0x{tkTypeDefLocal:X}"); + if (pdwExportedTypeFlags is not null) + Debug.Assert(*pdwExportedTypeFlags == dwExportedTypeFlagsLocal, $"ExportedType flags mismatch: cDAC=0x{*pdwExportedTypeFlags:X}, DAC=0x{dwExportedTypeFlagsLocal:X}"); + } + } +#endif + return hr; + } int IMetaDataAssemblyImport.GetManifestResourceProps(uint mdmr, char* szName, uint cchName, uint* pchName, uint* ptkImplementation, uint* pdwOffset, uint* pdwResourceFlags) @@ -1671,7 +1773,60 @@ int IMetaDataAssemblyImport.GetAssemblyFromScope(uint* ptkAssembly) } int IMetaDataAssemblyImport.FindExportedTypeByName(char* szName, uint mdtExportedType, uint* ptkExportedType) - => _legacyAssemblyImport is not null ? _legacyAssemblyImport.FindExportedTypeByName(szName, mdtExportedType, ptkExportedType) : HResults.E_NOTIMPL; + { + int hr = HResults.S_OK; + try + { + if (ptkExportedType is not null) + *ptkExportedType = 0; + + string targetName = new string(szName); + + bool found = false; + foreach (ExportedTypeHandle eth in _reader.ExportedTypes) + { + ExportedType exportedType = _reader.GetExportedType(eth); + string name = _reader.GetString(exportedType.Name); + string ns = _reader.GetString(exportedType.Namespace); + string fullName = string.IsNullOrEmpty(ns) ? name : $"{ns}.{name}"; + + if (!string.Equals(fullName, targetName, StringComparison.Ordinal)) + continue; + + if (mdtExportedType != 0) + { + EntityHandle impl = exportedType.Implementation; + if (impl.IsNil || (uint)MetadataTokens.GetToken(impl) != mdtExportedType) + continue; + } + + if (ptkExportedType is not null) + *ptkExportedType = (uint)MetadataTokens.GetToken(eth); + + found = true; + break; + } + + if (!found) + hr = CldbHResults.CLDB_E_RECORD_NOTFOUND; + } + catch (System.Exception ex) + { + hr = ex.HResult; + } + +#if DEBUG + if (_legacyAssemblyImport is not null) + { + uint tkExportedTypeLocal = 0; + int hrLegacy = _legacyAssemblyImport.FindExportedTypeByName(szName, mdtExportedType, &tkExportedTypeLocal); + Debug.ValidateHResult(hr, hrLegacy); + if (hr >= 0 && hrLegacy >= 0 && ptkExportedType is not null) + Debug.Assert(*ptkExportedType == tkExportedTypeLocal, $"ExportedType mismatch: cDAC=0x{*ptkExportedType:X}, DAC=0x{tkExportedTypeLocal:X}"); + } +#endif + return hr; + } int IMetaDataAssemblyImport.FindManifestResourceByName(char* szName, uint* ptkManifestResource) => _legacyAssemblyImport is not null ? _legacyAssemblyImport.FindManifestResourceByName(szName, ptkManifestResource) : HResults.E_NOTIMPL; diff --git a/src/native/managed/cdac/tests/DumpTests/MetaDataImportDumpTests.cs b/src/native/managed/cdac/tests/DumpTests/MetaDataImportDumpTests.cs index 715f13c1a6069e..f7092cb537d4e9 100644 --- a/src/native/managed/cdac/tests/DumpTests/MetaDataImportDumpTests.cs +++ b/src/native/managed/cdac/tests/DumpTests/MetaDataImportDumpTests.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Collections.Generic; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using Microsoft.Diagnostics.DataContractReader.Contracts; @@ -143,4 +144,83 @@ public unsafe void GetUserString_ReturnsCharCountWithoutNull(TestConfiguration c // Native returns character count WITHOUT null terminator Assert.Equal((uint)expectedCharCount, pchString); } + + [ConditionalTheory] + [MemberData(nameof(TestConfigurations))] + [SkipOnVersion("net10.0", "Assembly type does not include IsDynamic/IsLoaded fields in .NET 10")] + public unsafe void EnumTypeDefs_MatchesMetadataReader(TestConfiguration config) + { + InitializeDumpTest(config); + var (reader, mdi) = GetRootModuleImport(); + + List enumTokens = new(); + nint hEnum = 0; + uint* rTokens = stackalloc uint[16]; + + while (true) + { + uint count; + int hr = mdi.EnumTypeDefs(&hEnum, rTokens, 16, &count); + Assert.True(hr >= 0); + if (count == 0) + break; + for (uint i = 0; i < count; i++) + enumTokens.Add(rTokens[i]); + } + + List readerTokens = new(); + foreach (TypeDefinitionHandle h in reader.TypeDefinitions) + readerTokens.Add((uint)MetadataTokens.GetToken(h)); + + Assert.Equal(readerTokens, enumTokens); + mdi.CloseEnum(hEnum); + } + + [ConditionalTheory] + [MemberData(nameof(TestConfigurations))] + [SkipOnVersion("net10.0", "Assembly type does not include IsDynamic/IsLoaded fields in .NET 10")] + public unsafe void EnumMethods_MatchesMetadataReader(TestConfiguration config) + { + InitializeDumpTest(config); + var (reader, mdi) = GetRootModuleImport(); + + // Find a type with methods (skip ) + uint classToken = 0; + TypeDefinitionHandle targetType = default; + foreach (TypeDefinitionHandle tdh in reader.TypeDefinitions) + { + if (MetadataTokens.GetRowNumber(tdh) == 1) continue; + TypeDefinition td = reader.GetTypeDefinition(tdh); + if (td.GetMethods().Count > 0) + { + classToken = (uint)MetadataTokens.GetToken(tdh); + targetType = tdh; + break; + } + } + + Assert.True(classToken != 0, "Expected at least one non-global type with methods"); + + List enumTokens = new(); + nint hEnum = 0; + uint* rMethods = stackalloc uint[16]; + + while (true) + { + uint count; + int hr = mdi.EnumMethods(&hEnum, classToken, rMethods, 16, &count); + Assert.True(hr >= 0); + if (count == 0) + break; + for (uint i = 0; i < count; i++) + enumTokens.Add(rMethods[i]); + } + + List readerTokens = new(); + foreach (MethodDefinitionHandle mdh in reader.GetTypeDefinition(targetType).GetMethods()) + readerTokens.Add((uint)MetadataTokens.GetToken(mdh)); + + Assert.Equal(readerTokens, enumTokens); + mdi.CloseEnum(hEnum); + } } diff --git a/src/native/managed/cdac/tests/MetaDataImportImplTests.cs b/src/native/managed/cdac/tests/MetaDataImportImplTests.cs index 9983512542c64f..c69b9fcd4a97b2 100644 --- a/src/native/managed/cdac/tests/MetaDataImportImplTests.cs +++ b/src/native/managed/cdac/tests/MetaDataImportImplTests.cs @@ -146,6 +146,15 @@ private static (MetadataReader reader, MetadataReaderProvider provider) CreateTe mb.AddCustomAttribute(testClassHandle, obsoleteCtor, mb.GetOrAddBlob(new byte[] { 0x01, 0x00, 0x0C, 0x74, 0x65, 0x73, 0x74, 0x20, 0x6D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x00, 0x00 })); + // Exported types (for GetExportedTypeProps and FindExportedTypeByName testing) + ExportedTypeHandle exportedType1 = mb.AddExportedType( + TypeAttributes.Public, mb.GetOrAddString("TestNamespace"), mb.GetOrAddString("ExportedClass"), + mscorlibRef, 0x02000002); + + mb.AddExportedType( + TypeAttributes.NestedPublic, default, mb.GetOrAddString("NestedExported"), + exportedType1, 0); + // Serialize BlobBuilder metadataBlob = new(); MetadataRootBuilder root = new(mb); @@ -448,7 +457,6 @@ public void NotImplementedMethods_ReturnENotImpl() Assert.Equal(HResults.E_NOTIMPL, wrapper.GetScopeProps(null, 0, null, null)); Assert.Equal(HResults.E_NOTIMPL, wrapper.ResolveTypeRef(0, null, null, null)); - Assert.Equal(HResults.E_NOTIMPL, wrapper.EnumTypeDefs(null, null, 0, null)); Assert.Equal(HResults.E_NOTIMPL, wrapper.EnumTypeRefs(null, null, 0, null)); } @@ -1099,4 +1107,220 @@ public void ResetEnum_NullHandle_ReturnsOk() int hr = wrapper.ResetEnum(0, 0); Assert.Equal(HResults.S_OK, hr); } + + [Fact] + public void EnumTypeDefs_ReturnsAllTypes() + { + IMetaDataImport2 wrapper = CreateWrapper(); + + // Enumerate all type defs at once (metadata has 4 TypeDefs: , TestClass, NestedType, LayoutClass) + nint hEnum = 0; + uint* tokens = stackalloc uint[10]; + uint count; + + int hr = wrapper.EnumTypeDefs(&hEnum, tokens, 10, &count); + Assert.True(hr >= 0); + Assert.Equal(4u, count); + + // Verify first token is (TypeDef RID 1 = 0x02000001) + Assert.Equal(0x02000001u, tokens[0]); + + wrapper.CloseEnum(hEnum); + } + + [Fact] + public void EnumTypeDefs_Pagination() + { + IMetaDataImport2 wrapper = CreateWrapper(); + + nint hEnum = 0; + uint token; + uint count; + int totalCount = 0; + + // Enumerate one at a time + while (true) + { + int hr = wrapper.EnumTypeDefs(&hEnum, &token, 1, &count); + Assert.True(hr >= 0); + if (count == 0) + break; + totalCount++; + } + + Assert.Equal(4, totalCount); + wrapper.CloseEnum(hEnum); + } + + [Fact] + public void EnumMethods_ReturnsMethodsForType() + { + IMetaDataImport2 wrapper = CreateWrapper(); + + // TestClass (TypeDef RID 2 = 0x02000002) has 1 method: DoWork + nint hEnum = 0; + uint token; + uint count; + + int hr = wrapper.EnumMethods(&hEnum, 0x02000002, &token, 1, &count); + Assert.Equal(HResults.S_OK, hr); + Assert.Equal(1u, count); + // DoWork is method row 2 = 0x06000002 + Assert.Equal(0x06000002u, token); + + // No more methods + hr = wrapper.EnumMethods(&hEnum, 0x02000002, &token, 1, &count); + Assert.True(hr >= 0); + Assert.Equal(0u, count); + + wrapper.CloseEnum(hEnum); + } + + [Fact] + public void EnumMethods_ModuleType_ReturnsGlobalMethod() + { + IMetaDataImport2 wrapper = CreateWrapper(); + + // (TypeDef RID 1 = 0x02000001) has 1 method: GlobalHelper + nint hEnum = 0; + uint token; + uint count; + + int hr = wrapper.EnumMethods(&hEnum, 0x02000001, &token, 1, &count); + Assert.Equal(HResults.S_OK, hr); + Assert.Equal(1u, count); + // GlobalHelper is method row 1 = 0x06000001 + Assert.Equal(0x06000001u, token); + + wrapper.CloseEnum(hEnum); + } + + [Fact] + public void EnumMethods_NoMethods_ReturnsFalse() + { + IMetaDataImport2 wrapper = CreateWrapper(); + + // NestedType (TypeDef RID 3 = 0x02000003) has no methods + nint hEnum = 0; + uint token; + uint count; + + int hr = wrapper.EnumMethods(&hEnum, 0x02000003, &token, 1, &count); + Assert.True(hr >= 0); + Assert.Equal(0u, count); + + wrapper.CloseEnum(hEnum); + } + + [Fact] + public void GetExportedTypeProps_ReturnsCorrectProperties() + { + IMetaDataImport2 wrapper = CreateWrapper(); + IMetaDataAssemblyImport assemblyImport = (IMetaDataAssemblyImport)wrapper; + + // ExportedType row 1 = 0x27000001 (TestNamespace.ExportedClass) + char* szName = stackalloc char[256]; + uint pchName; + uint tkImplementation; + uint tkTypeDef; + uint dwFlags; + + int hr = assemblyImport.GetExportedTypeProps(0x27000001, szName, 256, &pchName, + &tkImplementation, &tkTypeDef, &dwFlags); + Assert.True(hr >= 0); + + string name = new string(szName); + Assert.Equal("TestNamespace.ExportedClass", name); + Assert.Equal((uint)TypeAttributes.Public, dwFlags); + Assert.Equal(0x02000002u, tkTypeDef); + // Implementation should be the mscorlib AssemblyRef + Assert.NotEqual(0u, tkImplementation); + } + + [Fact] + public void GetExportedTypeProps_NestedType() + { + IMetaDataImport2 wrapper = CreateWrapper(); + IMetaDataAssemblyImport assemblyImport = (IMetaDataAssemblyImport)wrapper; + + // ExportedType row 2 = 0x27000002 (NestedExported, nested in ExportedClass) + char* szName = stackalloc char[256]; + uint pchName; + uint tkImplementation; + uint tkTypeDef; + uint dwFlags; + + int hr = assemblyImport.GetExportedTypeProps(0x27000002, szName, 256, &pchName, + &tkImplementation, &tkTypeDef, &dwFlags); + Assert.True(hr >= 0); + + string name = new string(szName); + Assert.Equal("NestedExported", name); + Assert.Equal((uint)TypeAttributes.NestedPublic, dwFlags); + // Implementation should point to the parent ExportedType (0x27000001) + Assert.Equal(0x27000001u, tkImplementation); + } + + [Fact] + public void GetExportedTypeProps_Truncation() + { + IMetaDataImport2 wrapper = CreateWrapper(); + IMetaDataAssemblyImport assemblyImport = (IMetaDataAssemblyImport)wrapper; + + // Use a small buffer to trigger truncation + char* szName = stackalloc char[5]; + uint pchName; + uint tkImplementation; + uint tkTypeDef; + uint dwFlags; + + int hr = assemblyImport.GetExportedTypeProps(0x27000001, szName, 5, &pchName, + &tkImplementation, &tkTypeDef, &dwFlags); + Assert.Equal(0x00131106, hr); // CLDB_S_TRUNCATION + } + + [Fact] + public void FindExportedTypeByName_FindsType() + { + IMetaDataImport2 wrapper = CreateWrapper(); + IMetaDataAssemblyImport assemblyImport = (IMetaDataAssemblyImport)wrapper; + + uint tkExportedType; + fixed (char* szName = "TestNamespace.ExportedClass") + { + int hr = assemblyImport.FindExportedTypeByName(szName, 0, &tkExportedType); + Assert.True(hr >= 0); + Assert.Equal(0x27000001u, tkExportedType); + } + } + + [Fact] + public void FindExportedTypeByName_NestedType() + { + IMetaDataImport2 wrapper = CreateWrapper(); + IMetaDataAssemblyImport assemblyImport = (IMetaDataAssemblyImport)wrapper; + + uint tkExportedType; + fixed (char* szName = "NestedExported") + { + // Find nested type with parent = ExportedType row 1 + int hr = assemblyImport.FindExportedTypeByName(szName, 0x27000001, &tkExportedType); + Assert.True(hr >= 0); + Assert.Equal(0x27000002u, tkExportedType); + } + } + + [Fact] + public void FindExportedTypeByName_NotFound() + { + IMetaDataImport2 wrapper = CreateWrapper(); + IMetaDataAssemblyImport assemblyImport = (IMetaDataAssemblyImport)wrapper; + + uint tkExportedType; + fixed (char* szName = "NonExistent.Type") + { + int hr = assemblyImport.FindExportedTypeByName(szName, 0, &tkExportedType); + Assert.True(hr < 0); // CLDB_E_RECORD_NOTFOUND + } + } } From be1230099013448fda3af2bd288944ce5cf6769c Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Tue, 28 Apr 2026 13:53:50 -0400 Subject: [PATCH 2/4] Use named CLDB HRESULT constants in tests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/native/managed/cdac/tests/MetaDataImportImplTests.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/native/managed/cdac/tests/MetaDataImportImplTests.cs b/src/native/managed/cdac/tests/MetaDataImportImplTests.cs index c69b9fcd4a97b2..1db47d31236ed6 100644 --- a/src/native/managed/cdac/tests/MetaDataImportImplTests.cs +++ b/src/native/managed/cdac/tests/MetaDataImportImplTests.cs @@ -781,7 +781,7 @@ public void GetAssemblyProps_SmallBuffer_Truncates() uint nameLen; int hr = assemblyImport.GetAssemblyProps(0x20000001, null, null, null, nameBuf, 5, &nameLen, null, null); - Assert.Equal(0x00131106, hr); // CLDB_S_TRUNCATION + Assert.Equal(CldbHResults.CLDB_S_TRUNCATION, hr); // Full name is "TestAssembly" (12 chars + null = 13) Assert.Equal(13u, nameLen); // Buffer should contain "Test\0" @@ -814,7 +814,7 @@ public void GetAssemblyProps_InvalidToken_ReturnsRecordNotFound() // Pass an invalid assembly token (wrong RID) int hr = assemblyImport.GetAssemblyProps(0x20000002, null, null, null, null, 0, null, null, null); - Assert.Equal(unchecked((int)0x80131130), hr); // CLDB_E_RECORD_NOTFOUND + Assert.Equal(CldbHResults.CLDB_E_RECORD_NOTFOUND, hr); } } @@ -1023,7 +1023,7 @@ public void QueryInterfaceForIMetaDataImport_ReturnsIMetaDataImport2VtableWithEx var fn = (delegate* unmanaged[Stdcall])enumGenericParams; hr = fn(pImportAgain, &hEnum, 0x02000002, null, 0, &count); // Should succeed (or return no results) without AV - Assert.True(hr >= 0 || hr == unchecked((int)0x80131130)); // S_OK or CLDB_E_RECORD_NOTFOUND + Assert.True(hr >= 0 || hr == CldbHResults.CLDB_E_RECORD_NOTFOUND); } finally { @@ -1276,7 +1276,7 @@ public void GetExportedTypeProps_Truncation() int hr = assemblyImport.GetExportedTypeProps(0x27000001, szName, 5, &pchName, &tkImplementation, &tkTypeDef, &dwFlags); - Assert.Equal(0x00131106, hr); // CLDB_S_TRUNCATION + Assert.Equal(CldbHResults.CLDB_S_TRUNCATION, hr); } [Fact] From da0d10f86e997a6f6f09052b04f7d05be1e26e55 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Tue, 28 Apr 2026 17:41:57 -0400 Subject: [PATCH 3/4] Use EcmaMetadataUtils.GetRowId instead of raw RID mask Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../EcmaMetadataUtils.cs | 2 +- .../MetaDataImportImpl.cs | 48 +++++++++---------- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/EcmaMetadataUtils.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/EcmaMetadataUtils.cs index 75657a4bb4d2f6..6ecbff7791bfad 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/EcmaMetadataUtils.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/EcmaMetadataUtils.cs @@ -10,7 +10,7 @@ public static class EcmaMetadataUtils internal const int RowIdBitCount = 24; internal const uint RIDMask = (1 << RowIdBitCount) - 1; - internal static uint GetRowId(uint token) => token & RIDMask; + public static uint GetRowId(uint token) => token & RIDMask; internal static uint MakeToken(uint rid, uint table) => rid | (table << RowIdBitCount); diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/MetaDataImportImpl.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/MetaDataImportImpl.cs index a2a8e200fbf377..d99421a1ed33d0 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/MetaDataImportImpl.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/MetaDataImportImpl.cs @@ -191,7 +191,7 @@ int IMetaDataImport.EnumInterfaceImpls(nint* phEnum, uint td, uint* rImpls, uint } else { - TypeDefinitionHandle typeHandle = MetadataTokens.TypeDefinitionHandle((int)(td & 0x00FFFFFF)); + TypeDefinitionHandle typeHandle = MetadataTokens.TypeDefinitionHandle((int)EcmaMetadataUtils.GetRowId(td)); TypeDefinition typeDef = _reader.GetTypeDefinition(typeHandle); List tokens = new(); foreach (InterfaceImplementationHandle h in typeDef.GetInterfaceImplementations()) @@ -224,7 +224,7 @@ int IMetaDataImport.EnumMethods(nint* phEnum, uint cl, uint* rMethods, uint cMax } else { - TypeDefinitionHandle typeHandle = MetadataTokens.TypeDefinitionHandle((int)(cl & 0x00FFFFFF)); + TypeDefinitionHandle typeHandle = MetadataTokens.TypeDefinitionHandle((int)EcmaMetadataUtils.GetRowId(cl)); TypeDefinition typeDef = _reader.GetTypeDefinition(typeHandle); List tokens = new(); foreach (MethodDefinitionHandle h in typeDef.GetMethods()) @@ -251,7 +251,7 @@ int IMetaDataImport.EnumFields(nint* phEnum, uint cl, uint* rFields, uint cMax, } else { - TypeDefinitionHandle typeHandle = MetadataTokens.TypeDefinitionHandle((int)(cl & 0x00FFFFFF)); + TypeDefinitionHandle typeHandle = MetadataTokens.TypeDefinitionHandle((int)EcmaMetadataUtils.GetRowId(cl)); TypeDefinition typeDef = _reader.GetTypeDefinition(typeHandle); List tokens = new(); foreach (FieldDefinitionHandle h in typeDef.GetFields()) @@ -312,7 +312,7 @@ int IMetaDataImport.GetTypeDefProps(uint td, char* szTypeDef, uint cchTypeDef, u int hr = HResults.S_OK; try { - TypeDefinitionHandle typeHandle = MetadataTokens.TypeDefinitionHandle((int)(td & 0x00FFFFFF)); + TypeDefinitionHandle typeHandle = MetadataTokens.TypeDefinitionHandle((int)EcmaMetadataUtils.GetRowId(td)); TypeDefinition typeDef = _reader.GetTypeDefinition(typeHandle); string fullName = GetTypeDefFullName(typeDef); @@ -359,7 +359,7 @@ int IMetaDataImport.GetTypeRefProps(uint tr, uint* ptkResolutionScope, char* szN int hr = HResults.S_OK; try { - TypeReferenceHandle refHandle = MetadataTokens.TypeReferenceHandle((int)(tr & 0x00FFFFFF)); + TypeReferenceHandle refHandle = MetadataTokens.TypeReferenceHandle((int)EcmaMetadataUtils.GetRowId(tr)); TypeReference typeRef = _reader.GetTypeReference(refHandle); string fullName = GetTypeRefFullName(typeRef); @@ -402,7 +402,7 @@ int IMetaDataImport.GetMethodProps(uint mb, uint* pClass, char* szMethod, uint c int hr = HResults.S_OK; try { - MethodDefinitionHandle methodHandle = MetadataTokens.MethodDefinitionHandle((int)(mb & 0x00FFFFFF)); + MethodDefinitionHandle methodHandle = MetadataTokens.MethodDefinitionHandle((int)EcmaMetadataUtils.GetRowId(mb)); MethodDefinition methodDef = _reader.GetMethodDefinition(methodHandle); string name = _reader.GetString(methodDef.Name); @@ -473,7 +473,7 @@ int IMetaDataImport.GetFieldProps(uint mb, uint* pClass, char* szField, uint cch int hr = HResults.S_OK; try { - FieldDefinitionHandle fieldHandle = MetadataTokens.FieldDefinitionHandle((int)(mb & 0x00FFFFFF)); + FieldDefinitionHandle fieldHandle = MetadataTokens.FieldDefinitionHandle((int)EcmaMetadataUtils.GetRowId(mb)); FieldDefinition fieldDef = _reader.GetFieldDefinition(fieldHandle); string name = _reader.GetString(fieldDef.Name); @@ -592,13 +592,13 @@ int IMetaDataImport.GetInterfaceImplProps(uint iiImpl, uint* pClass, uint* ptkIf int hr = HResults.S_OK; try { - InterfaceImplementationHandle implHandle = MetadataTokens.InterfaceImplementationHandle((int)(iiImpl & 0x00FFFFFF)); + InterfaceImplementationHandle implHandle = MetadataTokens.InterfaceImplementationHandle((int)EcmaMetadataUtils.GetRowId(iiImpl)); InterfaceImplementation impl = _reader.GetInterfaceImplementation(implHandle); if (pClass is not null) { _interfaceImplToTypeDef ??= BuildInterfaceImplLookup(); - *pClass = _interfaceImplToTypeDef.TryGetValue((int)(iiImpl & 0x00FFFFFF), out uint ownerToken) + *pClass = _interfaceImplToTypeDef.TryGetValue((int)EcmaMetadataUtils.GetRowId(iiImpl), out uint ownerToken) ? ownerToken : 0; } @@ -635,7 +635,7 @@ int IMetaDataImport.GetNestedClassProps(uint tdNestedClass, uint* ptdEnclosingCl int hr = HResults.S_OK; try { - TypeDefinitionHandle typeHandle = MetadataTokens.TypeDefinitionHandle((int)(tdNestedClass & 0x00FFFFFF)); + TypeDefinitionHandle typeHandle = MetadataTokens.TypeDefinitionHandle((int)EcmaMetadataUtils.GetRowId(tdNestedClass)); TypeDefinition typeDef = _reader.GetTypeDefinition(typeHandle); TypeDefinitionHandle declaringType = typeDef.GetDeclaringType(); @@ -668,7 +668,7 @@ int IMetaDataImport2.GetGenericParamProps(uint gp, uint* pulParamSeq, uint* pdwP int hr = HResults.S_OK; try { - GenericParameterHandle gpHandle = MetadataTokens.GenericParameterHandle((int)(gp & 0x00FFFFFF)); + GenericParameterHandle gpHandle = MetadataTokens.GenericParameterHandle((int)EcmaMetadataUtils.GetRowId(gp)); GenericParameter genericParam = _reader.GetGenericParameter(gpHandle); if (pulParamSeq is not null) @@ -721,7 +721,7 @@ int IMetaDataImport.GetRVA(uint tk, uint* pulCodeRVA, uint* pdwImplFlags) uint tableIndex = tk >> 24; if (tableIndex == 0x06) // MethodDef { - MethodDefinitionHandle methodHandle = MetadataTokens.MethodDefinitionHandle((int)(tk & 0x00FFFFFF)); + MethodDefinitionHandle methodHandle = MetadataTokens.MethodDefinitionHandle((int)EcmaMetadataUtils.GetRowId(tk)); MethodDefinition methodDef = _reader.GetMethodDefinition(methodHandle); if (pulCodeRVA is not null) *pulCodeRVA = (uint)methodDef.RelativeVirtualAddress; @@ -730,7 +730,7 @@ int IMetaDataImport.GetRVA(uint tk, uint* pulCodeRVA, uint* pdwImplFlags) } else if (tableIndex == 0x04) // FieldDef { - FieldDefinitionHandle fieldHandle = MetadataTokens.FieldDefinitionHandle((int)(tk & 0x00FFFFFF)); + FieldDefinitionHandle fieldHandle = MetadataTokens.FieldDefinitionHandle((int)EcmaMetadataUtils.GetRowId(tk)); FieldDefinition fieldDef = _reader.GetFieldDefinition(fieldHandle); if (pulCodeRVA is not null) *pulCodeRVA = (uint)fieldDef.GetRelativeVirtualAddress(); @@ -770,7 +770,7 @@ int IMetaDataImport.GetSigFromToken(uint mdSig, byte** ppvSig, uint* pcbSig) int hr = HResults.S_OK; try { - StandaloneSignatureHandle sigHandle = MetadataTokens.StandaloneSignatureHandle((int)(mdSig & 0x00FFFFFF)); + StandaloneSignatureHandle sigHandle = MetadataTokens.StandaloneSignatureHandle((int)EcmaMetadataUtils.GetRowId(mdSig)); StandaloneSignature sig = _reader.GetStandaloneSignature(sigHandle); BlobReader blobReader = _reader.GetBlobReader(sig.Signature); @@ -864,7 +864,7 @@ int IMetaDataImport.GetCustomAttributeByName(uint tkObj, char* szName, void** pp int IMetaDataImport.IsValidToken(uint tk) { - int rid = (int)(tk & 0x00FFFFFF); + int rid = (int)EcmaMetadataUtils.GetRowId(tk); int tokenType = (int)(tk >> 24); if (rid == 0) @@ -1012,7 +1012,7 @@ int IMetaDataImport.GetMemberRefProps(uint mr, uint* ptk, char* szMember, uint c int hr = HResults.S_OK; try { - MemberReferenceHandle refHandle = MetadataTokens.MemberReferenceHandle((int)(mr & 0x00FFFFFF)); + MemberReferenceHandle refHandle = MetadataTokens.MemberReferenceHandle((int)EcmaMetadataUtils.GetRowId(mr)); MemberReference memberRef = _reader.GetMemberReference(refHandle); string name = _reader.GetString(memberRef.Name); @@ -1082,7 +1082,7 @@ int IMetaDataImport.GetClassLayout(uint td, uint* pdwPackSize, void* rFieldOffse int hr = HResults.S_OK; try { - TypeDefinitionHandle typeHandle = MetadataTokens.TypeDefinitionHandle((int)(td & 0x00FFFFFF)); + TypeDefinitionHandle typeHandle = MetadataTokens.TypeDefinitionHandle((int)EcmaMetadataUtils.GetRowId(td)); TypeDefinition typeDef = _reader.GetTypeDefinition(typeHandle); TypeLayout layout = typeDef.GetLayout(); @@ -1156,7 +1156,7 @@ int IMetaDataImport.GetModuleRefProps(uint mur, char* szName, uint cchName, uint int hr = HResults.S_OK; try { - ModuleReferenceHandle modRefHandle = MetadataTokens.ModuleReferenceHandle((int)(mur & 0x00FFFFFF)); + ModuleReferenceHandle modRefHandle = MetadataTokens.ModuleReferenceHandle((int)EcmaMetadataUtils.GetRowId(mur)); ModuleReference modRef = _reader.GetModuleReference(modRefHandle); string name = _reader.GetString(modRef.Name); @@ -1190,7 +1190,7 @@ int IMetaDataImport.GetTypeSpecFromToken(uint typespec, byte** ppvSig, uint* pcb int hr = HResults.S_OK; try { - TypeSpecificationHandle tsHandle = MetadataTokens.TypeSpecificationHandle((int)(typespec & 0x00FFFFFF)); + TypeSpecificationHandle tsHandle = MetadataTokens.TypeSpecificationHandle((int)EcmaMetadataUtils.GetRowId(typespec)); TypeSpecification typeSpec = _reader.GetTypeSpecification(tsHandle); BlobReader blobReader = _reader.GetBlobReader(typeSpec.Signature); @@ -1241,7 +1241,7 @@ int IMetaDataImport.GetUserString(uint stk, char* szString, uint cchString, uint // Using raw bytes avoids potential discrepancies with MetadataReader.GetUserString(). int heapMetadataOffset = _reader.GetHeapMetadataOffset(HeapIndex.UserString); int heapSize = _reader.GetHeapSize(HeapIndex.UserString); - int handleOffset = (int)(stk & 0x00FFFFFF); + int handleOffset = (int)EcmaMetadataUtils.GetRowId(stk); byte* heapBase = _reader.MetadataPointer + heapMetadataOffset; int remaining = heapSize - handleOffset; @@ -1316,7 +1316,7 @@ int IMetaDataImport.GetParamForMethodIndex(uint md, uint ulParamSeq, uint* ppd) if (ppd is not null) *ppd = 0; - MethodDefinitionHandle methodHandle = MetadataTokens.MethodDefinitionHandle((int)(md & 0x00FFFFFF)); + MethodDefinitionHandle methodHandle = MetadataTokens.MethodDefinitionHandle((int)EcmaMetadataUtils.GetRowId(md)); MethodDefinition methodDef = _reader.GetMethodDefinition(methodHandle); bool found = false; @@ -1371,7 +1371,7 @@ int IMetaDataImport.GetParamProps(uint tk, uint* pmd, uint* pulSequence, char* s int hr = HResults.S_OK; try { - ParameterHandle paramHandle = MetadataTokens.ParameterHandle((int)(tk & 0x00FFFFFF)); + ParameterHandle paramHandle = MetadataTokens.ParameterHandle((int)EcmaMetadataUtils.GetRowId(tk)); Parameter param = _reader.GetParameter(paramHandle); string name = _reader.GetString(param.Name); @@ -1574,7 +1574,7 @@ int IMetaDataAssemblyImport.GetAssemblyRefProps(uint mdar, byte** ppbPublicKeyOr int hr = HResults.S_OK; try { - AssemblyReferenceHandle refHandle = MetadataTokens.AssemblyReferenceHandle((int)(mdar & 0x00FFFFFF)); + AssemblyReferenceHandle refHandle = MetadataTokens.AssemblyReferenceHandle((int)EcmaMetadataUtils.GetRowId(mdar)); AssemblyReference assemblyRef = _reader.GetAssemblyReference(refHandle); string name = _reader.GetString(assemblyRef.Name); @@ -1683,7 +1683,7 @@ int IMetaDataAssemblyImport.GetExportedTypeProps(uint mdct, char* szName, uint c int hr = HResults.S_OK; try { - ExportedTypeHandle handle = MetadataTokens.ExportedTypeHandle((int)(mdct & 0x00FFFFFF)); + ExportedTypeHandle handle = MetadataTokens.ExportedTypeHandle((int)EcmaMetadataUtils.GetRowId(mdct)); ExportedType exportedType = _reader.GetExportedType(handle); string name = _reader.GetString(exportedType.Name); From a05475640bbbffc47cd35d98a215bc17bd14ea3c Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Wed, 29 Apr 2026 10:08:26 -0400 Subject: [PATCH 4/4] Add comprehensive DEBUG parity validation for all cDAC methods - Add DEBUG enum validation blocks for EnumInterfaceImpls, EnumFields, and EnumGenericParams (previously had no DEBUG validation) - Add name/string content validation to all Get methods that previously only validated name length: GetTypeDefProps, GetTypeRefProps, GetMethodProps, GetFieldProps, GetMemberRefProps, GetModuleRefProps, GetUserString, GetAssemblyProps, GetAssemblyRefProps - Add name length + string validation to GetGenericParamProps and GetParamProps (previously missing name validation entirely) - Add private GetRID helper to simplify EcmaMetadataUtils.GetRowId usage Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../MetaDataImportImpl.cs | 292 +++++++++++++++--- 1 file changed, 246 insertions(+), 46 deletions(-) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/MetaDataImportImpl.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/MetaDataImportImpl.cs index d99421a1ed33d0..2ee6c19c075907 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/MetaDataImportImpl.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/MetaDataImportImpl.cs @@ -23,6 +23,8 @@ internal sealed unsafe partial class MetaDataImportImpl : ICustomQueryInterface, private Dictionary? _interfaceImplToTypeDef; private Dictionary? _paramToMethod; + private static int GetRID(uint token) => (int)EcmaMetadataUtils.GetRowId(token); + // Tracks GCHandle values allocated by AllocEnum so that CountEnum, ResetEnum, // and CloseEnum can distinguish cDAC-created enum handles from legacy HENUMInternal*. // ConcurrentDictionary is used because COM objects may be called from multiple threads. @@ -158,6 +160,7 @@ int IMetaDataImport.ResetEnum(nint hEnum, uint ulPos) int IMetaDataImport.EnumTypeDefs(nint* phEnum, uint* rTypeDefs, uint cMax, uint* pcTypeDefs) { int hr = HResults.S_OK; + List? tokens = null; try { if (phEnum is not null && *phEnum != 0) @@ -166,7 +169,7 @@ int IMetaDataImport.EnumTypeDefs(nint* phEnum, uint* rTypeDefs, uint cMax, uint* } else { - List tokens = new(); + tokens = new(); foreach (TypeDefinitionHandle h in _reader.TypeDefinitions) tokens.Add((uint)MetadataTokens.GetToken(h)); hr = FillEnum(phEnum, tokens, rTypeDefs, cMax, pcTypeDefs); @@ -177,12 +180,33 @@ int IMetaDataImport.EnumTypeDefs(nint* phEnum, uint* rTypeDefs, uint cMax, uint* hr = ex.HResult; } +#if DEBUG + if (tokens is not null && _legacyImport is not null) + { + nint hEnumLocal = 0; + List legacyTokens = new(); + uint* buf = stackalloc uint[64]; + while (true) + { + uint count; + int hrLegacy = _legacyImport.EnumTypeDefs(&hEnumLocal, buf, 64, &count); + if (hrLegacy < 0 || count == 0) break; + for (uint i = 0; i < count; i++) + legacyTokens.Add(buf[i]); + } + _legacyImport.CloseEnum(hEnumLocal); + Debug.Assert(tokens.Count == legacyTokens.Count, $"EnumTypeDefs count mismatch: cDAC={tokens.Count}, DAC={legacyTokens.Count}"); + for (int i = 0; i < Math.Min(tokens.Count, legacyTokens.Count); i++) + Debug.Assert(tokens[i] == legacyTokens[i], $"EnumTypeDefs token mismatch at [{i}]: cDAC=0x{tokens[i]:X}, DAC=0x{legacyTokens[i]:X}"); + } +#endif return hr; } int IMetaDataImport.EnumInterfaceImpls(nint* phEnum, uint td, uint* rImpls, uint cMax, uint* pcImpls) { int hr = HResults.S_OK; + List? tokens = null; try { if (phEnum is not null && *phEnum != 0) @@ -191,9 +215,9 @@ int IMetaDataImport.EnumInterfaceImpls(nint* phEnum, uint td, uint* rImpls, uint } else { - TypeDefinitionHandle typeHandle = MetadataTokens.TypeDefinitionHandle((int)EcmaMetadataUtils.GetRowId(td)); + TypeDefinitionHandle typeHandle = MetadataTokens.TypeDefinitionHandle(GetRID(td)); TypeDefinition typeDef = _reader.GetTypeDefinition(typeHandle); - List tokens = new(); + tokens = new(); foreach (InterfaceImplementationHandle h in typeDef.GetInterfaceImplementations()) tokens.Add((uint)MetadataTokens.GetToken(h)); hr = FillEnum(phEnum, tokens, rImpls, cMax, pcImpls); @@ -204,6 +228,26 @@ int IMetaDataImport.EnumInterfaceImpls(nint* phEnum, uint td, uint* rImpls, uint hr = ex.HResult; } +#if DEBUG + if (tokens is not null && _legacyImport is not null) + { + nint hEnumLocal = 0; + List legacyTokens = new(); + uint* buf = stackalloc uint[64]; + while (true) + { + uint count; + int hrLegacy = _legacyImport.EnumInterfaceImpls(&hEnumLocal, td, buf, 64, &count); + if (hrLegacy < 0 || count == 0) break; + for (uint i = 0; i < count; i++) + legacyTokens.Add(buf[i]); + } + _legacyImport.CloseEnum(hEnumLocal); + Debug.Assert(tokens.Count == legacyTokens.Count, $"EnumInterfaceImpls count mismatch for 0x{td:X}: cDAC={tokens.Count}, DAC={legacyTokens.Count}"); + for (int i = 0; i < Math.Min(tokens.Count, legacyTokens.Count); i++) + Debug.Assert(tokens[i] == legacyTokens[i], $"EnumInterfaceImpls token mismatch at [{i}] for 0x{td:X}: cDAC=0x{tokens[i]:X}, DAC=0x{legacyTokens[i]:X}"); + } +#endif return hr; } @@ -216,6 +260,7 @@ int IMetaDataImport.EnumMembers(nint* phEnum, uint cl, uint* rMembers, uint cMax int IMetaDataImport.EnumMethods(nint* phEnum, uint cl, uint* rMethods, uint cMax, uint* pcTokens) { int hr = HResults.S_OK; + List? tokens = null; try { if (phEnum is not null && *phEnum != 0) @@ -224,9 +269,9 @@ int IMetaDataImport.EnumMethods(nint* phEnum, uint cl, uint* rMethods, uint cMax } else { - TypeDefinitionHandle typeHandle = MetadataTokens.TypeDefinitionHandle((int)EcmaMetadataUtils.GetRowId(cl)); + TypeDefinitionHandle typeHandle = MetadataTokens.TypeDefinitionHandle(GetRID(cl)); TypeDefinition typeDef = _reader.GetTypeDefinition(typeHandle); - List tokens = new(); + tokens = new(); foreach (MethodDefinitionHandle h in typeDef.GetMethods()) tokens.Add((uint)MetadataTokens.GetToken(h)); hr = FillEnum(phEnum, tokens, rMethods, cMax, pcTokens); @@ -237,12 +282,33 @@ int IMetaDataImport.EnumMethods(nint* phEnum, uint cl, uint* rMethods, uint cMax hr = ex.HResult; } +#if DEBUG + if (tokens is not null && _legacyImport is not null) + { + nint hEnumLocal = 0; + List legacyTokens = new(); + uint* buf = stackalloc uint[64]; + while (true) + { + uint count; + int hrLegacy = _legacyImport.EnumMethods(&hEnumLocal, cl, buf, 64, &count); + if (hrLegacy < 0 || count == 0) break; + for (uint i = 0; i < count; i++) + legacyTokens.Add(buf[i]); + } + _legacyImport.CloseEnum(hEnumLocal); + Debug.Assert(tokens.Count == legacyTokens.Count, $"EnumMethods count mismatch for 0x{cl:X}: cDAC={tokens.Count}, DAC={legacyTokens.Count}"); + for (int i = 0; i < Math.Min(tokens.Count, legacyTokens.Count); i++) + Debug.Assert(tokens[i] == legacyTokens[i], $"EnumMethods token mismatch at [{i}] for 0x{cl:X}: cDAC=0x{tokens[i]:X}, DAC=0x{legacyTokens[i]:X}"); + } +#endif return hr; } int IMetaDataImport.EnumFields(nint* phEnum, uint cl, uint* rFields, uint cMax, uint* pcTokens) { int hr = HResults.S_OK; + List? tokens = null; try { if (phEnum is not null && *phEnum != 0) @@ -251,9 +317,9 @@ int IMetaDataImport.EnumFields(nint* phEnum, uint cl, uint* rFields, uint cMax, } else { - TypeDefinitionHandle typeHandle = MetadataTokens.TypeDefinitionHandle((int)EcmaMetadataUtils.GetRowId(cl)); + TypeDefinitionHandle typeHandle = MetadataTokens.TypeDefinitionHandle(GetRID(cl)); TypeDefinition typeDef = _reader.GetTypeDefinition(typeHandle); - List tokens = new(); + tokens = new(); foreach (FieldDefinitionHandle h in typeDef.GetFields()) tokens.Add((uint)MetadataTokens.GetToken(h)); hr = FillEnum(phEnum, tokens, rFields, cMax, pcTokens); @@ -264,6 +330,26 @@ int IMetaDataImport.EnumFields(nint* phEnum, uint cl, uint* rFields, uint cMax, hr = ex.HResult; } +#if DEBUG + if (tokens is not null && _legacyImport is not null) + { + nint hEnumLocal = 0; + List legacyTokens = new(); + uint* buf = stackalloc uint[64]; + while (true) + { + uint count; + int hrLegacy = _legacyImport.EnumFields(&hEnumLocal, cl, buf, 64, &count); + if (hrLegacy < 0 || count == 0) break; + for (uint i = 0; i < count; i++) + legacyTokens.Add(buf[i]); + } + _legacyImport.CloseEnum(hEnumLocal); + Debug.Assert(tokens.Count == legacyTokens.Count, $"EnumFields count mismatch for 0x{cl:X}: cDAC={tokens.Count}, DAC={legacyTokens.Count}"); + for (int i = 0; i < Math.Min(tokens.Count, legacyTokens.Count); i++) + Debug.Assert(tokens[i] == legacyTokens[i], $"EnumFields token mismatch at [{i}] for 0x{cl:X}: cDAC=0x{tokens[i]:X}, DAC=0x{legacyTokens[i]:X}"); + } +#endif return hr; } @@ -273,6 +359,7 @@ int IMetaDataImport.EnumCustomAttributes(nint* phEnum, uint tk, uint tkType, uin int IMetaDataImport2.EnumGenericParams(nint* phEnum, uint tk, uint* rGenericParams, uint cMax, uint* pcGenericParams) { int hr = HResults.S_OK; + List? tokens = null; try { if (phEnum is not null && *phEnum != 0) @@ -293,7 +380,7 @@ int IMetaDataImport2.EnumGenericParams(nint* phEnum, uint tk, uint* rGenericPara throw new ArgumentException(null, nameof(tk)); } - List tokens = new(); + tokens = new(); foreach (GenericParameterHandle h in genericParams) tokens.Add((uint)MetadataTokens.GetToken(h)); hr = FillEnum(phEnum, tokens, rGenericParams, cMax, pcGenericParams); @@ -304,6 +391,26 @@ int IMetaDataImport2.EnumGenericParams(nint* phEnum, uint tk, uint* rGenericPara hr = ex.HResult; } +#if DEBUG + if (tokens is not null && _legacyImport2 is not null) + { + nint hEnumLocal = 0; + List legacyTokens = new(); + uint* buf = stackalloc uint[64]; + while (true) + { + uint count; + int hrLegacy = _legacyImport2.EnumGenericParams(&hEnumLocal, tk, buf, 64, &count); + if (hrLegacy < 0 || count == 0) break; + for (uint i = 0; i < count; i++) + legacyTokens.Add(buf[i]); + } + _legacyImport2.CloseEnum(hEnumLocal); + Debug.Assert(tokens.Count == legacyTokens.Count, $"EnumGenericParams count mismatch for 0x{tk:X}: cDAC={tokens.Count}, DAC={legacyTokens.Count}"); + for (int i = 0; i < Math.Min(tokens.Count, legacyTokens.Count); i++) + Debug.Assert(tokens[i] == legacyTokens[i], $"EnumGenericParams token mismatch at [{i}] for 0x{tk:X}: cDAC=0x{tokens[i]:X}, DAC=0x{legacyTokens[i]:X}"); + } +#endif return hr; } @@ -312,7 +419,7 @@ int IMetaDataImport.GetTypeDefProps(uint td, char* szTypeDef, uint cchTypeDef, u int hr = HResults.S_OK; try { - TypeDefinitionHandle typeHandle = MetadataTokens.TypeDefinitionHandle((int)EcmaMetadataUtils.GetRowId(td)); + TypeDefinitionHandle typeHandle = MetadataTokens.TypeDefinitionHandle(GetRID(td)); TypeDefinition typeDef = _reader.GetTypeDefinition(typeHandle); string fullName = GetTypeDefFullName(typeDef); @@ -338,7 +445,8 @@ int IMetaDataImport.GetTypeDefProps(uint td, char* szTypeDef, uint cchTypeDef, u if (_legacyImport is not null) { uint flagsLocal = 0, extendsLocal = 0, pchLocal = 0; - int hrLegacy = _legacyImport.GetTypeDefProps(td, null, 0, &pchLocal, &flagsLocal, &extendsLocal); + char* szLocal = stackalloc char[(int)cchTypeDef]; + int hrLegacy = _legacyImport.GetTypeDefProps(td, szLocal, cchTypeDef, &pchLocal, &flagsLocal, &extendsLocal); Debug.ValidateHResult(hr, hrLegacy); if (hr >= 0 && hrLegacy >= 0) { @@ -348,6 +456,12 @@ int IMetaDataImport.GetTypeDefProps(uint td, char* szTypeDef, uint cchTypeDef, u Debug.Assert(*ptkExtends == extendsLocal, $"Extends mismatch: cDAC=0x{*ptkExtends:X}, DAC=0x{extendsLocal:X}"); if (pchTypeDef is not null) Debug.Assert(*pchTypeDef == pchLocal, $"Name length mismatch: cDAC={*pchTypeDef}, DAC={pchLocal}"); + if (szTypeDef is not null && cchTypeDef > 0) + { + string cdacName = new string(szTypeDef); + string dacName = new string(szLocal); + Debug.Assert(cdacName == dacName, $"TypeDef name mismatch: cDAC='{cdacName}', DAC='{dacName}'"); + } } } #endif @@ -359,7 +473,7 @@ int IMetaDataImport.GetTypeRefProps(uint tr, uint* ptkResolutionScope, char* szN int hr = HResults.S_OK; try { - TypeReferenceHandle refHandle = MetadataTokens.TypeReferenceHandle((int)EcmaMetadataUtils.GetRowId(tr)); + TypeReferenceHandle refHandle = MetadataTokens.TypeReferenceHandle(GetRID(tr)); TypeReference typeRef = _reader.GetTypeReference(refHandle); string fullName = GetTypeRefFullName(typeRef); @@ -382,7 +496,8 @@ int IMetaDataImport.GetTypeRefProps(uint tr, uint* ptkResolutionScope, char* szN if (_legacyImport is not null) { uint scopeLocal = 0, pchLocal = 0; - int hrLegacy = _legacyImport.GetTypeRefProps(tr, &scopeLocal, null, 0, &pchLocal); + char* szLocal = stackalloc char[(int)cchName]; + int hrLegacy = _legacyImport.GetTypeRefProps(tr, &scopeLocal, szLocal, cchName, &pchLocal); Debug.ValidateHResult(hr, hrLegacy); if (hr >= 0 && hrLegacy >= 0) { @@ -390,6 +505,12 @@ int IMetaDataImport.GetTypeRefProps(uint tr, uint* ptkResolutionScope, char* szN Debug.Assert(*ptkResolutionScope == scopeLocal, $"ResolutionScope mismatch: cDAC=0x{*ptkResolutionScope:X}, DAC=0x{scopeLocal:X}"); if (pchName is not null) Debug.Assert(*pchName == pchLocal, $"Name length mismatch: cDAC={*pchName}, DAC={pchLocal}"); + if (szName is not null && cchName > 0) + { + string cdacName = new string(szName); + string dacName = new string(szLocal); + Debug.Assert(cdacName == dacName, $"TypeRef name mismatch: cDAC='{cdacName}', DAC='{dacName}'"); + } } } #endif @@ -402,7 +523,7 @@ int IMetaDataImport.GetMethodProps(uint mb, uint* pClass, char* szMethod, uint c int hr = HResults.S_OK; try { - MethodDefinitionHandle methodHandle = MetadataTokens.MethodDefinitionHandle((int)EcmaMetadataUtils.GetRowId(mb)); + MethodDefinitionHandle methodHandle = MetadataTokens.MethodDefinitionHandle(GetRID(mb)); MethodDefinition methodDef = _reader.GetMethodDefinition(methodHandle); string name = _reader.GetString(methodDef.Name); @@ -442,7 +563,8 @@ int IMetaDataImport.GetMethodProps(uint mb, uint* pClass, char* szMethod, uint c { uint classLocal = 0, attrLocal = 0, rvaLocal = 0, implLocal = 0, pchLocal = 0, cbSigLocal = 0; byte* sigLocal = null; - int hrLegacy = _legacyImport.GetMethodProps(mb, &classLocal, null, 0, &pchLocal, &attrLocal, &sigLocal, &cbSigLocal, &rvaLocal, &implLocal); + char* szLocal = stackalloc char[(int)cchMethod]; + int hrLegacy = _legacyImport.GetMethodProps(mb, &classLocal, szLocal, cchMethod, &pchLocal, &attrLocal, &sigLocal, &cbSigLocal, &rvaLocal, &implLocal); Debug.ValidateHResult(hr, hrLegacy); if (hr >= 0 && hrLegacy >= 0) { @@ -452,6 +574,12 @@ int IMetaDataImport.GetMethodProps(uint mb, uint* pClass, char* szMethod, uint c Debug.Assert(*pdwAttr == attrLocal, $"Attr mismatch: cDAC=0x{*pdwAttr:X}, DAC=0x{attrLocal:X}"); if (pchMethod is not null) Debug.Assert(*pchMethod == pchLocal, $"Name length mismatch: cDAC={*pchMethod}, DAC={pchLocal}"); + if (szMethod is not null && cchMethod > 0) + { + string cdacName = new string(szMethod); + string dacName = new string(szLocal); + Debug.Assert(cdacName == dacName, $"Method name mismatch: cDAC='{cdacName}', DAC='{dacName}'"); + } if (pulCodeRVA is not null) Debug.Assert(*pulCodeRVA == rvaLocal, $"RVA mismatch: cDAC=0x{*pulCodeRVA:X}, DAC=0x{rvaLocal:X}"); if (pdwImplFlags is not null) @@ -473,7 +601,7 @@ int IMetaDataImport.GetFieldProps(uint mb, uint* pClass, char* szField, uint cch int hr = HResults.S_OK; try { - FieldDefinitionHandle fieldHandle = MetadataTokens.FieldDefinitionHandle((int)EcmaMetadataUtils.GetRowId(mb)); + FieldDefinitionHandle fieldHandle = MetadataTokens.FieldDefinitionHandle(GetRID(mb)); FieldDefinition fieldDef = _reader.GetFieldDefinition(fieldHandle); string name = _reader.GetString(fieldDef.Name); @@ -531,7 +659,8 @@ int IMetaDataImport.GetFieldProps(uint mb, uint* pClass, char* szField, uint cch uint classLocal = 0, attrLocal = 0, pchLocal = 0, cbSigLocal = 0, cpTypeLocal = 0, cchValueLocal = 0; byte* sigLocal = null; void* valueLocal = null; - int hrLegacy = _legacyImport.GetFieldProps(mb, &classLocal, null, 0, &pchLocal, &attrLocal, &sigLocal, &cbSigLocal, &cpTypeLocal, &valueLocal, &cchValueLocal); + char* szLocal = stackalloc char[(int)cchField]; + int hrLegacy = _legacyImport.GetFieldProps(mb, &classLocal, szLocal, cchField, &pchLocal, &attrLocal, &sigLocal, &cbSigLocal, &cpTypeLocal, &valueLocal, &cchValueLocal); Debug.ValidateHResult(hr, hrLegacy); if (hr >= 0 && hrLegacy >= 0) { @@ -541,6 +670,12 @@ int IMetaDataImport.GetFieldProps(uint mb, uint* pClass, char* szField, uint cch Debug.Assert(*pdwAttr == attrLocal, $"Attr mismatch: cDAC=0x{*pdwAttr:X}, DAC=0x{attrLocal:X}"); if (pchField is not null) Debug.Assert(*pchField == pchLocal, $"Name length mismatch: cDAC={*pchField}, DAC={pchLocal}"); + if (szField is not null && cchField > 0) + { + string cdacName = new string(szField); + string dacName = new string(szLocal); + Debug.Assert(cdacName == dacName, $"Field name mismatch: cDAC='{cdacName}', DAC='{dacName}'"); + } if (pdwCPlusTypeFlag is not null) Debug.Assert(*pdwCPlusTypeFlag == cpTypeLocal, $"CPlusTypeFlag mismatch: cDAC=0x{*pdwCPlusTypeFlag:X}, DAC=0x{cpTypeLocal:X}"); if (ppvSigBlob is not null) @@ -592,13 +727,13 @@ int IMetaDataImport.GetInterfaceImplProps(uint iiImpl, uint* pClass, uint* ptkIf int hr = HResults.S_OK; try { - InterfaceImplementationHandle implHandle = MetadataTokens.InterfaceImplementationHandle((int)EcmaMetadataUtils.GetRowId(iiImpl)); + InterfaceImplementationHandle implHandle = MetadataTokens.InterfaceImplementationHandle(GetRID(iiImpl)); InterfaceImplementation impl = _reader.GetInterfaceImplementation(implHandle); if (pClass is not null) { _interfaceImplToTypeDef ??= BuildInterfaceImplLookup(); - *pClass = _interfaceImplToTypeDef.TryGetValue((int)EcmaMetadataUtils.GetRowId(iiImpl), out uint ownerToken) + *pClass = _interfaceImplToTypeDef.TryGetValue(GetRID(iiImpl), out uint ownerToken) ? ownerToken : 0; } @@ -635,7 +770,7 @@ int IMetaDataImport.GetNestedClassProps(uint tdNestedClass, uint* ptdEnclosingCl int hr = HResults.S_OK; try { - TypeDefinitionHandle typeHandle = MetadataTokens.TypeDefinitionHandle((int)EcmaMetadataUtils.GetRowId(tdNestedClass)); + TypeDefinitionHandle typeHandle = MetadataTokens.TypeDefinitionHandle(GetRID(tdNestedClass)); TypeDefinition typeDef = _reader.GetTypeDefinition(typeHandle); TypeDefinitionHandle declaringType = typeDef.GetDeclaringType(); @@ -668,7 +803,7 @@ int IMetaDataImport2.GetGenericParamProps(uint gp, uint* pulParamSeq, uint* pdwP int hr = HResults.S_OK; try { - GenericParameterHandle gpHandle = MetadataTokens.GenericParameterHandle((int)EcmaMetadataUtils.GetRowId(gp)); + GenericParameterHandle gpHandle = MetadataTokens.GenericParameterHandle(GetRID(gp)); GenericParameter genericParam = _reader.GetGenericParameter(gpHandle); if (pulParamSeq is not null) @@ -696,8 +831,9 @@ int IMetaDataImport2.GetGenericParamProps(uint gp, uint* pulParamSeq, uint* pdwP #if DEBUG if (_legacyImport2 is not null) { - uint seqLocal = 0, flagsLocal = 0, ownerLocal = 0; - int hrLegacy = _legacyImport2.GetGenericParamProps(gp, &seqLocal, &flagsLocal, &ownerLocal, null, null, 0, null); + uint seqLocal = 0, flagsLocal = 0, ownerLocal = 0, pchLocal = 0; + char* szLocal = stackalloc char[(int)cchName]; + int hrLegacy = _legacyImport2.GetGenericParamProps(gp, &seqLocal, &flagsLocal, &ownerLocal, null, szLocal, cchName, &pchLocal); Debug.ValidateHResult(hr, hrLegacy); if (hr >= 0 && hrLegacy >= 0) { @@ -707,6 +843,14 @@ int IMetaDataImport2.GetGenericParamProps(uint gp, uint* pulParamSeq, uint* pdwP Debug.Assert(*pdwParamFlags == flagsLocal, $"ParamFlags mismatch: cDAC=0x{*pdwParamFlags:X}, DAC=0x{flagsLocal:X}"); if (ptOwner is not null) Debug.Assert(*ptOwner == ownerLocal, $"Owner mismatch: cDAC=0x{*ptOwner:X}, DAC=0x{ownerLocal:X}"); + if (pchName is not null) + Debug.Assert(*pchName == pchLocal, $"Name length mismatch: cDAC={*pchName}, DAC={pchLocal}"); + if (wzname is not null && cchName > 0) + { + string cdacName = new string(wzname); + string dacName = new string(szLocal); + Debug.Assert(cdacName == dacName, $"GenericParam name mismatch: cDAC='{cdacName}', DAC='{dacName}'"); + } } } #endif @@ -721,7 +865,7 @@ int IMetaDataImport.GetRVA(uint tk, uint* pulCodeRVA, uint* pdwImplFlags) uint tableIndex = tk >> 24; if (tableIndex == 0x06) // MethodDef { - MethodDefinitionHandle methodHandle = MetadataTokens.MethodDefinitionHandle((int)EcmaMetadataUtils.GetRowId(tk)); + MethodDefinitionHandle methodHandle = MetadataTokens.MethodDefinitionHandle(GetRID(tk)); MethodDefinition methodDef = _reader.GetMethodDefinition(methodHandle); if (pulCodeRVA is not null) *pulCodeRVA = (uint)methodDef.RelativeVirtualAddress; @@ -730,7 +874,7 @@ int IMetaDataImport.GetRVA(uint tk, uint* pulCodeRVA, uint* pdwImplFlags) } else if (tableIndex == 0x04) // FieldDef { - FieldDefinitionHandle fieldHandle = MetadataTokens.FieldDefinitionHandle((int)EcmaMetadataUtils.GetRowId(tk)); + FieldDefinitionHandle fieldHandle = MetadataTokens.FieldDefinitionHandle(GetRID(tk)); FieldDefinition fieldDef = _reader.GetFieldDefinition(fieldHandle); if (pulCodeRVA is not null) *pulCodeRVA = (uint)fieldDef.GetRelativeVirtualAddress(); @@ -770,7 +914,7 @@ int IMetaDataImport.GetSigFromToken(uint mdSig, byte** ppvSig, uint* pcbSig) int hr = HResults.S_OK; try { - StandaloneSignatureHandle sigHandle = MetadataTokens.StandaloneSignatureHandle((int)EcmaMetadataUtils.GetRowId(mdSig)); + StandaloneSignatureHandle sigHandle = MetadataTokens.StandaloneSignatureHandle(GetRID(mdSig)); StandaloneSignature sig = _reader.GetStandaloneSignature(sigHandle); BlobReader blobReader = _reader.GetBlobReader(sig.Signature); @@ -864,7 +1008,7 @@ int IMetaDataImport.GetCustomAttributeByName(uint tkObj, char* szName, void** pp int IMetaDataImport.IsValidToken(uint tk) { - int rid = (int)EcmaMetadataUtils.GetRowId(tk); + int rid = GetRID(tk); int tokenType = (int)(tk >> 24); if (rid == 0) @@ -1012,7 +1156,7 @@ int IMetaDataImport.GetMemberRefProps(uint mr, uint* ptk, char* szMember, uint c int hr = HResults.S_OK; try { - MemberReferenceHandle refHandle = MetadataTokens.MemberReferenceHandle((int)EcmaMetadataUtils.GetRowId(mr)); + MemberReferenceHandle refHandle = MetadataTokens.MemberReferenceHandle(GetRID(mr)); MemberReference memberRef = _reader.GetMemberReference(refHandle); string name = _reader.GetString(memberRef.Name); @@ -1042,7 +1186,8 @@ int IMetaDataImport.GetMemberRefProps(uint mr, uint* ptk, char* szMember, uint c { uint tkLocal = 0, pchLocal = 0, cbSigLocal = 0; byte* sigLocal = null; - int hrLegacy = _legacyImport.GetMemberRefProps(mr, &tkLocal, null, 0, &pchLocal, &sigLocal, &cbSigLocal); + char* szLocal = stackalloc char[(int)cchMember]; + int hrLegacy = _legacyImport.GetMemberRefProps(mr, &tkLocal, szLocal, cchMember, &pchLocal, &sigLocal, &cbSigLocal); Debug.ValidateHResult(hr, hrLegacy); if (hr >= 0 && hrLegacy >= 0) { @@ -1050,6 +1195,12 @@ int IMetaDataImport.GetMemberRefProps(uint mr, uint* ptk, char* szMember, uint c Debug.Assert(*ptk == tkLocal, $"Parent mismatch: cDAC=0x{*ptk:X}, DAC=0x{tkLocal:X}"); if (pchMember is not null) Debug.Assert(*pchMember == pchLocal, $"Name length mismatch: cDAC={*pchMember}, DAC={pchLocal}"); + if (szMember is not null && cchMember > 0) + { + string cdacName = new string(szMember); + string dacName = new string(szLocal); + Debug.Assert(cdacName == dacName, $"MemberRef name mismatch: cDAC='{cdacName}', DAC='{dacName}'"); + } if (ppvSigBlob is not null) ValidateBlobsEqual(*ppvSigBlob, pbSig is not null ? *pbSig : cbSigLocal, sigLocal, cbSigLocal, "MemberRefSig"); else if (pbSig is not null) @@ -1082,7 +1233,7 @@ int IMetaDataImport.GetClassLayout(uint td, uint* pdwPackSize, void* rFieldOffse int hr = HResults.S_OK; try { - TypeDefinitionHandle typeHandle = MetadataTokens.TypeDefinitionHandle((int)EcmaMetadataUtils.GetRowId(td)); + TypeDefinitionHandle typeHandle = MetadataTokens.TypeDefinitionHandle(GetRID(td)); TypeDefinition typeDef = _reader.GetTypeDefinition(typeHandle); TypeLayout layout = typeDef.GetLayout(); @@ -1156,7 +1307,7 @@ int IMetaDataImport.GetModuleRefProps(uint mur, char* szName, uint cchName, uint int hr = HResults.S_OK; try { - ModuleReferenceHandle modRefHandle = MetadataTokens.ModuleReferenceHandle((int)EcmaMetadataUtils.GetRowId(mur)); + ModuleReferenceHandle modRefHandle = MetadataTokens.ModuleReferenceHandle(GetRID(mur)); ModuleReference modRef = _reader.GetModuleReference(modRefHandle); string name = _reader.GetString(modRef.Name); @@ -1173,10 +1324,20 @@ int IMetaDataImport.GetModuleRefProps(uint mur, char* szName, uint cchName, uint if (_legacyImport is not null) { uint pchLocal = 0; - int hrLegacy = _legacyImport.GetModuleRefProps(mur, null, 0, &pchLocal); + char* szLocal = stackalloc char[(int)cchName]; + int hrLegacy = _legacyImport.GetModuleRefProps(mur, szLocal, cchName, &pchLocal); Debug.ValidateHResult(hr, hrLegacy); - if (hr >= 0 && hrLegacy >= 0 && pchName is not null) - Debug.Assert(*pchName == pchLocal, $"Name length mismatch: cDAC={*pchName}, DAC={pchLocal}"); + if (hr >= 0 && hrLegacy >= 0) + { + if (pchName is not null) + Debug.Assert(*pchName == pchLocal, $"Name length mismatch: cDAC={*pchName}, DAC={pchLocal}"); + if (szName is not null && cchName > 0) + { + string cdacName = new string(szName); + string dacName = new string(szLocal); + Debug.Assert(cdacName == dacName, $"ModuleRef name mismatch: cDAC='{cdacName}', DAC='{dacName}'"); + } + } } #endif return hr; @@ -1190,7 +1351,7 @@ int IMetaDataImport.GetTypeSpecFromToken(uint typespec, byte** ppvSig, uint* pcb int hr = HResults.S_OK; try { - TypeSpecificationHandle tsHandle = MetadataTokens.TypeSpecificationHandle((int)EcmaMetadataUtils.GetRowId(typespec)); + TypeSpecificationHandle tsHandle = MetadataTokens.TypeSpecificationHandle(GetRID(typespec)); TypeSpecification typeSpec = _reader.GetTypeSpecification(tsHandle); BlobReader blobReader = _reader.GetBlobReader(typeSpec.Signature); @@ -1241,7 +1402,7 @@ int IMetaDataImport.GetUserString(uint stk, char* szString, uint cchString, uint // Using raw bytes avoids potential discrepancies with MetadataReader.GetUserString(). int heapMetadataOffset = _reader.GetHeapMetadataOffset(HeapIndex.UserString); int heapSize = _reader.GetHeapSize(HeapIndex.UserString); - int handleOffset = (int)EcmaMetadataUtils.GetRowId(stk); + int handleOffset = GetRID(stk); byte* heapBase = _reader.MetadataPointer + heapMetadataOffset; int remaining = heapSize - handleOffset; @@ -1286,10 +1447,20 @@ int IMetaDataImport.GetUserString(uint stk, char* szString, uint cchString, uint if (_legacyImport is not null) { uint pchLocal = 0; - int hrLegacy = _legacyImport.GetUserString(stk, null, 0, &pchLocal); + char* szLocal = stackalloc char[(int)cchString]; + int hrLegacy = _legacyImport.GetUserString(stk, szLocal, cchString, &pchLocal); Debug.ValidateHResult(hr, hrLegacy); - if (hr >= 0 && hrLegacy >= 0 && pchString is not null) - Debug.Assert(*pchString == pchLocal, $"String length mismatch: cDAC={*pchString}, DAC={pchLocal}"); + if (hr >= 0 && hrLegacy >= 0) + { + if (pchString is not null) + Debug.Assert(*pchString == pchLocal, $"String length mismatch: cDAC={*pchString}, DAC={pchLocal}"); + if (szString is not null && cchString > 0) + { + string cdacStr = new string(szString); + string dacStr = new string(szLocal); + Debug.Assert(cdacStr == dacStr, $"UserString content mismatch: cDAC='{cdacStr}', DAC='{dacStr}'"); + } + } } #endif return hr; @@ -1316,7 +1487,7 @@ int IMetaDataImport.GetParamForMethodIndex(uint md, uint ulParamSeq, uint* ppd) if (ppd is not null) *ppd = 0; - MethodDefinitionHandle methodHandle = MetadataTokens.MethodDefinitionHandle((int)EcmaMetadataUtils.GetRowId(md)); + MethodDefinitionHandle methodHandle = MetadataTokens.MethodDefinitionHandle(GetRID(md)); MethodDefinition methodDef = _reader.GetMethodDefinition(methodHandle); bool found = false; @@ -1371,7 +1542,7 @@ int IMetaDataImport.GetParamProps(uint tk, uint* pmd, uint* pulSequence, char* s int hr = HResults.S_OK; try { - ParameterHandle paramHandle = MetadataTokens.ParameterHandle((int)EcmaMetadataUtils.GetRowId(tk)); + ParameterHandle paramHandle = MetadataTokens.ParameterHandle(GetRID(tk)); Parameter param = _reader.GetParameter(paramHandle); string name = _reader.GetString(param.Name); @@ -1422,8 +1593,9 @@ int IMetaDataImport.GetParamProps(uint tk, uint* pmd, uint* pulSequence, char* s #if DEBUG if (_legacyImport is not null) { - uint mdLocal = 0, seqLocal = 0, attrLocal = 0; - int hrLegacy = _legacyImport.GetParamProps(tk, &mdLocal, &seqLocal, null, 0, null, &attrLocal, null, null, null); + uint mdLocal = 0, seqLocal = 0, attrLocal = 0, pchLocal = 0; + char* szLocal = stackalloc char[(int)cchName]; + int hrLegacy = _legacyImport.GetParamProps(tk, &mdLocal, &seqLocal, szLocal, cchName, &pchLocal, &attrLocal, null, null, null); Debug.ValidateHResult(hr, hrLegacy); if (hr >= 0 && hrLegacy >= 0) { @@ -1433,6 +1605,14 @@ int IMetaDataImport.GetParamProps(uint tk, uint* pmd, uint* pulSequence, char* s Debug.Assert(*pulSequence == seqLocal, $"Sequence mismatch: cDAC={*pulSequence}, DAC={seqLocal}"); if (pdwAttr is not null) Debug.Assert(*pdwAttr == attrLocal, $"Attr mismatch: cDAC=0x{*pdwAttr:X}, DAC=0x{attrLocal:X}"); + if (pchName is not null) + Debug.Assert(*pchName == pchLocal, $"Name length mismatch: cDAC={*pchName}, DAC={pchLocal}"); + if (szName is not null && cchName > 0) + { + string cdacName = new string(szName); + string dacName = new string(szLocal); + Debug.Assert(cdacName == dacName, $"Param name mismatch: cDAC='{cdacName}', DAC='{dacName}'"); + } } } #endif @@ -1540,12 +1720,19 @@ int IMetaDataAssemblyImport.GetAssemblyProps(uint mda, byte** ppbPublicKey, uint uint pchLocal = 0, hashAlgLocal = 0, flagsLocal = 0, cbPublicKeyLocal = 0; byte* publicKeyLocal = null; ASSEMBLYMETADATA metaLocal = default; - int hrLegacy = _legacyAssemblyImport.GetAssemblyProps(mda, &publicKeyLocal, &cbPublicKeyLocal, &hashAlgLocal, null, 0, &pchLocal, &metaLocal, &flagsLocal); + char* szLocal = stackalloc char[(int)cchName]; + int hrLegacy = _legacyAssemblyImport.GetAssemblyProps(mda, &publicKeyLocal, &cbPublicKeyLocal, &hashAlgLocal, szLocal, cchName, &pchLocal, &metaLocal, &flagsLocal); Debug.ValidateHResult(hr, hrLegacy); if (hr >= 0 && hrLegacy >= 0) { if (pchName is not null) Debug.Assert(*pchName == pchLocal, $"Name length mismatch: cDAC={*pchName}, DAC={pchLocal}"); + if (szName is not null && cchName > 0) + { + string cdacName = new string(szName); + string dacName = new string(szLocal); + Debug.Assert(cdacName == dacName, $"Assembly name mismatch: cDAC='{cdacName}', DAC='{dacName}'"); + } if (pulHashAlgId is not null) Debug.Assert(*pulHashAlgId == hashAlgLocal, $"HashAlgId mismatch: cDAC=0x{*pulHashAlgId:X}, DAC=0x{hashAlgLocal:X}"); if (pdwAssemblyFlags is not null) @@ -1574,7 +1761,7 @@ int IMetaDataAssemblyImport.GetAssemblyRefProps(uint mdar, byte** ppbPublicKeyOr int hr = HResults.S_OK; try { - AssemblyReferenceHandle refHandle = MetadataTokens.AssemblyReferenceHandle((int)EcmaMetadataUtils.GetRowId(mdar)); + AssemblyReferenceHandle refHandle = MetadataTokens.AssemblyReferenceHandle(GetRID(mdar)); AssemblyReference assemblyRef = _reader.GetAssemblyReference(refHandle); string name = _reader.GetString(assemblyRef.Name); @@ -1644,12 +1831,19 @@ int IMetaDataAssemblyImport.GetAssemblyRefProps(uint mdar, byte** ppbPublicKeyOr uint pchLocal = 0, flagsLocal = 0, cbPublicKeyLocal = 0, cbHashLocal = 0; byte* publicKeyLocal = null, hashLocal = null; ASSEMBLYMETADATA metaLocal = default; - int hrLegacy = _legacyAssemblyImport.GetAssemblyRefProps(mdar, &publicKeyLocal, &cbPublicKeyLocal, null, 0, &pchLocal, &metaLocal, &hashLocal, &cbHashLocal, &flagsLocal); + char* szLocal = stackalloc char[(int)cchName]; + int hrLegacy = _legacyAssemblyImport.GetAssemblyRefProps(mdar, &publicKeyLocal, &cbPublicKeyLocal, szLocal, cchName, &pchLocal, &metaLocal, &hashLocal, &cbHashLocal, &flagsLocal); Debug.ValidateHResult(hr, hrLegacy); if (hr >= 0 && hrLegacy >= 0) { if (pchName is not null) Debug.Assert(*pchName == pchLocal, $"Name length mismatch: cDAC={*pchName}, DAC={pchLocal}"); + if (szName is not null && cchName > 0) + { + string cdacName = new string(szName); + string dacName = new string(szLocal); + Debug.Assert(cdacName == dacName, $"AssemblyRef name mismatch: cDAC='{cdacName}', DAC='{dacName}'"); + } if (pdwAssemblyRefFlags is not null) Debug.Assert(*pdwAssemblyRefFlags == flagsLocal, $"Flags mismatch: cDAC=0x{*pdwAssemblyRefFlags:X}, DAC=0x{flagsLocal:X}"); if (ppbPublicKeyOrToken is not null) @@ -1683,7 +1877,7 @@ int IMetaDataAssemblyImport.GetExportedTypeProps(uint mdct, char* szName, uint c int hr = HResults.S_OK; try { - ExportedTypeHandle handle = MetadataTokens.ExportedTypeHandle((int)EcmaMetadataUtils.GetRowId(mdct)); + ExportedTypeHandle handle = MetadataTokens.ExportedTypeHandle(GetRID(mdct)); ExportedType exportedType = _reader.GetExportedType(handle); string name = _reader.GetString(exportedType.Name); @@ -1723,6 +1917,12 @@ int IMetaDataAssemblyImport.GetExportedTypeProps(uint mdct, char* szName, uint c Debug.ValidateHResult(hr, hrLegacy); if (hr >= 0 && hrLegacy >= 0) { + if (szName is not null && szNameLocal is not null && cchName > 0) + { + string cdacName = new string(szName); + string dacName = new string(szNameLocal); + Debug.Assert(cdacName == dacName, $"ExportedType name mismatch: cDAC='{cdacName}', DAC='{dacName}'"); + } if (pchName is not null) Debug.Assert(*pchName == pchNameLocal, $"ExportedType name length mismatch: cDAC={*pchName}, DAC={pchNameLocal}"); if (ptkImplementation is not null)