diff --git a/docs/design/datacontracts/RuntimeTypeSystem.md b/docs/design/datacontracts/RuntimeTypeSystem.md index 846eb91c2c3fb4..14150db143a4d9 100644 --- a/docs/design/datacontracts/RuntimeTypeSystem.md +++ b/docs/design/datacontracts/RuntimeTypeSystem.md @@ -117,7 +117,7 @@ partial interface IRuntimeTypeSystem : IContract // return true if the TypeHandle represents an array, and set the rank to either 0 (if the type is not an array), or the rank number if it is. bool IsArray(TypeHandle typeHandle, out uint rank); TypeHandle GetTypeParam(TypeHandle typeHandle); - TypeHandle GetConstructedType(TypeHandle typeHandle, CorElementType corElementType, int rank, ImmutableArray typeArguments); + TypeHandle GetConstructedType(TypeHandle typeHandle, CorElementType corElementType, int rank, ImmutableArray typeArguments, byte callConv = 0); TypeHandle GetPrimitiveType(CorElementType typeCode); bool IsGenericVariable(TypeHandle typeHandle, out TargetPointer module, out uint token); bool IsFunctionPointer(TypeHandle typeHandle, out ReadOnlySpan retAndArgTypes, out byte callConv); @@ -501,6 +501,8 @@ The contract additionally depends on these data descriptors | `FnPtrTypeDesc` | `RetAndArgTypes` | Pointer to an array of TypeHandle addresses. This length of this is 1 more than `NumArgs` | | `GenericsDictInfo` | `NumDicts` | Number of instantiation dictionaries, including inherited ones, in this `GenericsDictInfo` | | `GenericsDictInfo` | `NumTypeArgs` | Number of type arguments in the type or method instantiation described by this `GenericsDictInfo` | +| `LoaderAllocator` | `IsCollectible` | Non-zero if the `LoaderAllocator` is collectible. | +| `LoaderAllocator` | `CreationNumber` | Monotonically-increasing creation number assigned to each collectible `LoaderAllocator`. | The value of the `NativeCodeVersionNode::OptimizationTier` field is one of: ```csharp @@ -1003,6 +1005,22 @@ Contracts used: } + private bool FnPtrMatch(TypeHandle candidate, ImmutableArray retAndArgTypes, byte callConv) + { + if (!IsFunctionPointer(candidate, out ReadOnlySpan candidateRetAndArgs, out byte candidateCallConv)) + return false; + if (candidateCallConv != callConv) + return false; + if (candidateRetAndArgs.Length != retAndArgTypes.Length) + return false; + for (int i = 0; i < candidateRetAndArgs.Length; i++) + { + if (candidateRetAndArgs[i].Address != retAndArgTypes[i].Address) + return false; + } + return true; + } + private bool IsLoaded(TypeHandle typeHandle) { if (typeHandle.Address == TargetPointer.Null) @@ -1018,12 +1036,19 @@ Contracts used: return (flags & (uint)MethodTableAuxiliaryFlags.IsNotFullyLoaded) == 0; } - TypeHandle GetConstructedType(TypeHandle typeHandle, CorElementType corElementType, int rank, ImmutableArray typeArguments) + TypeHandle GetConstructedType(TypeHandle typeHandle, CorElementType corElementType, int rank, ImmutableArray typeArguments, byte callConv) { - if (typeHandle.Address == TargetPointer.Null) + // For function pointers the "parent" type handle is unused. `callConv` discriminates + // overloads in the cache; for all other element types it is zero. + if (corElementType != CorElementType.FnPtr && typeHandle.Address == TargetPointer.Null) return new TypeHandle(TargetPointer.Null); ILoader loaderContract = _target.Contracts.Loader; - TargetPointer loaderModule = GetLoaderModule(typeHandle); + // Function pointer types are not owned by the loader module of any single type argument, + // so they need their own loader-module selection. Every other constructed type is owned + // by the loader module of its "parent" type handle. + TargetPointer loaderModule = corElementType == CorElementType.FnPtr + ? FindFnPtrLoaderModule(typeArguments) + : GetLoaderModule(typeHandle); ModuleHandle moduleHandle = loaderContract.GetModuleHandleFromModulePtr(loaderModule); TypeHandle potentialMatch = new TypeHandle(TargetPointer.Null); foreach (TargetPointer ptr in loaderContract.GetAvailableTypeParams(moduleHandle)) @@ -1033,19 +1058,36 @@ Contracts used: { if (GenericInstantiationMatch(typeHandle, potentialMatch, typeArguments) && IsLoaded(potentialMatch)) { - _ = _typeHandles.TryAdd(new TypeKey(typeHandle, corElementType, rank, typeArguments), potentialMatch); + _ = _typeHandles.TryAdd(new TypeKey(typeHandle, corElementType, rank, typeArguments, callConv), potentialMatch); + return potentialMatch; + } + } + else if (corElementType == CorElementType.FnPtr) + { + if (FnPtrMatch(potentialMatch, typeArguments, callConv) && IsLoaded(potentialMatch)) + { + _ = _typeHandles.TryAdd(new TypeKey(typeHandle, corElementType, rank, typeArguments, callConv), potentialMatch); return potentialMatch; } } else if (ArrayPtrMatch(typeHandle, corElementType, rank, potentialMatch) && IsLoaded(potentialMatch)) { - _ = _typeHandles.TryAdd(new TypeKey(typeHandle, corElementType, rank, typeArguments), potentialMatch); + _ = _typeHandles.TryAdd(new TypeKey(typeHandle, corElementType, rank, typeArguments, callConv), potentialMatch); return potentialMatch; } } return new TypeHandle(TargetPointer.Null); } + // Mirrors `ClassLoader::ComputeLoaderModuleForFunctionPointer`. + private TargetPointer FindFnPtrLoaderModule(ImmutableArray retAndArgTypes) + { + // The loader module of a + // function pointer type is the loader module of the collectible ret/arg type with the + // largest `LoaderAllocator::CreationNumber`, or CoreLib's loader module if none of the + // ret/arg types are collectible. + } + public TypeHandle GetPrimitiveType(CorElementType typeCode) { TargetPointer coreLib = _target.ReadGlobalPointer("CoreLib"); diff --git a/src/coreclr/vm/datadescriptor/datadescriptor.inc b/src/coreclr/vm/datadescriptor/datadescriptor.inc index d8b0ae58896a83..caf0344dc949bd 100644 --- a/src/coreclr/vm/datadescriptor/datadescriptor.inc +++ b/src/coreclr/vm/datadescriptor/datadescriptor.inc @@ -320,6 +320,8 @@ CDAC_TYPE_FIELD(LoaderAllocator, T_POINTER, DynamicHelpersStubHeap, cdac_data::VirtualCallStubManager) CDAC_TYPE_FIELD(LoaderAllocator, TYPE(ObjectHandle), ObjectHandle, cdac_data::ObjectHandle) +CDAC_TYPE_FIELD(LoaderAllocator, T_UINT8, IsCollectible, cdac_data::IsCollectible) +CDAC_TYPE_FIELD(LoaderAllocator, T_UINT64, CreationNumber, cdac_data::CreationNumber) CDAC_TYPE_END(LoaderAllocator) CDAC_TYPE_BEGIN(LoaderHeap) diff --git a/src/coreclr/vm/loaderallocator.hpp b/src/coreclr/vm/loaderallocator.hpp index e8387bcb74b758..525eb06d5ddb8f 100644 --- a/src/coreclr/vm/loaderallocator.hpp +++ b/src/coreclr/vm/loaderallocator.hpp @@ -963,6 +963,8 @@ struct cdac_data #endif // defined(FEATURE_READYTORUN) && defined(FEATURE_STUBPRECODE_DYNAMIC_HELPERS) static constexpr size_t VirtualCallStubManager = offsetof(LoaderAllocator, m_pVirtualCallStubManager); static constexpr size_t ObjectHandle = offsetof(LoaderAllocator, m_hLoaderAllocatorObjectHandle); + static constexpr size_t IsCollectible = offsetof(LoaderAllocator, m_IsCollectible); + static constexpr size_t CreationNumber = offsetof(LoaderAllocator, m_nLoaderAllocator); }; typedef VPTR(LoaderAllocator) PTR_LoaderAllocator; diff --git a/src/coreclr/vm/typedesc.h b/src/coreclr/vm/typedesc.h index da2dffb380209e..767ea61400bbb6 100644 --- a/src/coreclr/vm/typedesc.h +++ b/src/coreclr/vm/typedesc.h @@ -472,7 +472,8 @@ class FnPtrTypeDesc : public TypeDesc LIMITED_METHOD_CONTRACT; SUPPORTS_DAC; - return PTR_TypeHandle(m_RetAndArgTypes); + return dac_cast( + PTR_HOST_MEMBER_TADDR(FnPtrTypeDesc, this, m_RetAndArgTypes)); } BOOL IsSharedByGenericInstantiations(); diff --git a/src/coreclr/vm/typehash.cpp b/src/coreclr/vm/typehash.cpp index 31ba44e3073e21..a03aee31a7d3dc 100644 --- a/src/coreclr/vm/typehash.cpp +++ b/src/coreclr/vm/typehash.cpp @@ -433,8 +433,6 @@ BOOL EETypeHashTable::CompareFnPtrType(TypeHandle t, BYTE callConv, DWORD numArg if (!t.IsFnPtrType()) return FALSE; -#ifndef DACCESS_COMPILE - FnPtrTypeDesc* pTD = t.AsFnPtrType(); if (pTD->GetNumArgs() != numArgs || pTD->GetCallConv() != callConv) @@ -451,11 +449,6 @@ BOOL EETypeHashTable::CompareFnPtrType(TypeHandle t, BYTE callConv, DWORD numArg } return TRUE; - -#else - DacNotImpl(); - return FALSE; -#endif // #ifndef DACCESS_COMPILE } TypeHandle EETypeHashTable::GetValue(const TypeKey *pKey) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs index fdcad78ddd3161..912d626ea66879 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs @@ -190,7 +190,7 @@ public interface IRuntimeTypeSystem : IContract // return true if the TypeHandle represents an array, and set the rank to either 0 (if the type is not an array), or the rank number if it is. bool IsArray(TypeHandle typeHandle, out uint rank) => throw new NotImplementedException(); TypeHandle GetTypeParam(TypeHandle typeHandle) => throw new NotImplementedException(); - TypeHandle GetConstructedType(TypeHandle typeHandle, CorElementType corElementType, int rank, ImmutableArray typeArguments) => throw new NotImplementedException(); + TypeHandle GetConstructedType(TypeHandle typeHandle, CorElementType corElementType, int rank, ImmutableArray typeArguments, byte callConv = 0) => throw new NotImplementedException(); TypeHandle GetPrimitiveType(CorElementType typeCode) => throw new NotImplementedException(); TypeHandle GetTypeByNameAndModule(string name, string nameSpace, ModuleHandle moduleHandle) => throw new NotImplementedException(); bool IsGenericVariable(TypeHandle typeHandle, out TargetPointer module, out uint token) => throw new NotImplementedException(); diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs index 9b7bd0692e0f2f..ef8e7dce8f5dd7 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs @@ -73,21 +73,23 @@ internal MethodTable(Data.MethodTable data) private readonly struct TypeKey : IEquatable { - public TypeKey(TypeHandle typeHandle, CorElementType elementType, int rank, ImmutableArray typeArgs) + public TypeKey(TypeHandle typeHandle, CorElementType elementType, int rank, ImmutableArray typeArgs, byte callConv = 0) { TypeHandle = typeHandle; ElementType = elementType; Rank = rank; TypeArgs = typeArgs; + CallConv = callConv; } public TypeHandle TypeHandle { get; } public CorElementType ElementType { get; } public int Rank { get; } public ImmutableArray TypeArgs { get; } + public byte CallConv { get; } public bool Equals(TypeKey other) { - if (ElementType != other.ElementType || Rank != other.Rank || TypeArgs.Length != other.TypeArgs.Length) + if (ElementType != other.ElementType || Rank != other.Rank || CallConv != other.CallConv || TypeArgs.Length != other.TypeArgs.Length) return false; for (int i = 0; i < TypeArgs.Length; i++) { @@ -101,7 +103,7 @@ public bool Equals(TypeKey other) public override int GetHashCode() { - int hash = HashCode.Combine(TypeHandle.GetHashCode(), (int)ElementType, Rank); + int hash = HashCode.Combine(TypeHandle.GetHashCode(), (int)ElementType, Rank, (int)CallConv); foreach (TypeHandle th in TypeArgs) { hash = HashCode.Combine(hash, th.GetHashCode()); @@ -1038,6 +1040,22 @@ private bool ArrayPtrMatch(TypeHandle elementType, CorElementType corElementType } + private bool FnPtrMatch(TypeHandle candidate, ImmutableArray retAndArgTypes, byte callConv) + { + if (!IsFunctionPointer(candidate, out ReadOnlySpan candidateRetAndArgs, out byte candidateCallConv)) + return false; + if (candidateCallConv != callConv) + return false; + if (candidateRetAndArgs.Length != retAndArgTypes.Length) + return false; + for (int i = 0; i < candidateRetAndArgs.Length; i++) + { + if (candidateRetAndArgs[i].Address != retAndArgTypes[i].Address) + return false; + } + return true; + } + private bool IsLoaded(TypeHandle typeHandle) { if (typeHandle.Address == TargetPointer.Null) @@ -1053,14 +1071,19 @@ private bool IsLoaded(TypeHandle typeHandle) return (auxData.Flags & (uint)MethodTableAuxiliaryFlags.IsNotFullyLoaded) == 0; // IsUnloaded } - TypeHandle IRuntimeTypeSystem.GetConstructedType(TypeHandle typeHandle, CorElementType corElementType, int rank, ImmutableArray typeArguments) + TypeHandle IRuntimeTypeSystem.GetConstructedType(TypeHandle typeHandle, CorElementType corElementType, int rank, ImmutableArray typeArguments, byte callConv) { - if (typeHandle.Address == TargetPointer.Null) + if (typeHandle.Address == TargetPointer.Null && corElementType != CorElementType.FnPtr) return new TypeHandle(TargetPointer.Null); - if (_typeHandles.TryGetValue(new TypeKey(typeHandle, corElementType, rank, typeArguments), out TypeHandle existing)) + if (_typeHandles.TryGetValue(new TypeKey(typeHandle, corElementType, rank, typeArguments, callConv), out TypeHandle existing)) return existing; ILoader loaderContract = _target.Contracts.Loader; - TargetPointer loaderModule = GetLoaderModule(typeHandle); + TargetPointer loaderModule; + if (corElementType == CorElementType.FnPtr) + loaderModule = FindFnPtrLoaderModule(typeArguments); + else + loaderModule = GetLoaderModule(typeHandle); + ModuleHandle moduleHandle = loaderContract.GetModuleHandleFromModulePtr(loaderModule); TypeHandle potentialMatch; foreach (TargetPointer ptr in loaderContract.GetAvailableTypeParams(moduleHandle)) @@ -1074,6 +1097,14 @@ TypeHandle IRuntimeTypeSystem.GetConstructedType(TypeHandle typeHandle, CorEleme return potentialMatch; } } + else if (corElementType == CorElementType.FnPtr) + { + if (FnPtrMatch(potentialMatch, typeArguments, callConv) && IsLoaded(potentialMatch)) + { + _ = _typeHandles.TryAdd(new TypeKey(typeHandle, corElementType, rank, typeArguments, callConv), potentialMatch); + return potentialMatch; + } + } else if (ArrayPtrMatch(typeHandle, corElementType, rank, potentialMatch) && IsLoaded(potentialMatch)) { _ = _typeHandles.TryAdd(new TypeKey(typeHandle, corElementType, rank, typeArguments), potentialMatch); @@ -1083,6 +1114,54 @@ TypeHandle IRuntimeTypeSystem.GetConstructedType(TypeHandle typeHandle, CorEleme return new TypeHandle(TargetPointer.Null); } + // Mirrors the native algorithm + // ClassLoader::ComputeLoaderModuleForFunctionPointer: the loader module is the collectible + // ret/arg type's loader module with the largest LoaderAllocator creation number, or CoreLib's + // loader module if none of the ret/arg types are collectible. + private TargetPointer FindFnPtrLoaderModule(ImmutableArray retAndArgTypes) + { + ILoader loaderContract = _target.Contracts.Loader; + + TargetPointer loaderModulePtr = TargetPointer.Null; + ulong latestFoundNumber = 0; + bool anyCollectible = false; + + foreach (TypeHandle arg in retAndArgTypes) + { + if (arg.Address == TargetPointer.Null) + continue; + + TargetPointer argModulePtr = GetLoaderModule(arg); + if (argModulePtr == TargetPointer.Null) + continue; + + ModuleHandle argModuleHandle = loaderContract.GetModuleHandleFromModulePtr(argModulePtr); + TargetPointer argLoaderAllocator = loaderContract.GetLoaderAllocator(argModuleHandle); + if (argLoaderAllocator == TargetPointer.Null) + continue; + + Data.LoaderAllocator loaderAllocator = _target.ProcessedData.GetOrAdd(argLoaderAllocator); + if (!loaderAllocator.IsCollectible) + continue; + + if (!anyCollectible || loaderAllocator.CreationNumber > latestFoundNumber) + { + anyCollectible = true; + latestFoundNumber = loaderAllocator.CreationNumber; + loaderModulePtr = argModulePtr; + } + } + + if (!anyCollectible) + { + TargetPointer systemAssembly = loaderContract.GetSystemAssembly(); + ModuleHandle coreLibModuleHandle = loaderContract.GetModuleHandleFromAssemblyPtr(systemAssembly); + loaderModulePtr = loaderContract.GetModule(coreLibModuleHandle); + } + + return loaderModulePtr; + } + TypeHandle IRuntimeTypeSystem.GetPrimitiveType(CorElementType typeCode) { TargetPointer coreLib = _target.ReadGlobalPointer(Constants.Globals.CoreLib); diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/LoaderAllocator.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/LoaderAllocator.cs index ae3abac044464d..17793a6d9eaec7 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/LoaderAllocator.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/LoaderAllocator.cs @@ -29,6 +29,9 @@ public LoaderAllocator(Target target, TargetPointer address) VirtualCallStubManager = target.ReadPointerField(address, type, nameof(VirtualCallStubManager)); ObjectHandle = target.ReadDataField(address, type, nameof(ObjectHandle)); + + IsCollectible = target.ReadField(address, type, nameof(IsCollectible)) != 0; + CreationNumber = target.ReadField(address, type, nameof(CreationNumber)); } public uint ReferenceCount { get; init; } @@ -42,6 +45,8 @@ public LoaderAllocator(Target target, TargetPointer address) public TargetPointer? DynamicHelpersStubHeap { get; init; } public TargetPointer VirtualCallStubManager { get; init; } public ObjectHandle ObjectHandle { get; init; } + public bool IsCollectible { get; init; } + public ulong CreationNumber { get; init; } public bool IsAlive => ReferenceCount != 0; } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs index 61d77ad15202ed..7e1d5bc5dd83e9 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs @@ -4,6 +4,7 @@ using System; using System.Buffers.Binary; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics; using System.Linq; using System.Reflection.Metadata; @@ -1869,8 +1870,166 @@ public int GetTypeHandle(ulong vmModule, uint metadataToken, ulong* pRetVal) public int GetApproxTypeHandle(nint pTypeData, ulong* pRetVal) => LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.GetApproxTypeHandle(pTypeData, pRetVal) : HResults.E_NOTIMPL; - public int GetExactTypeHandle(nint pTypeData, nint pArgInfo, ulong* pVmTypeHandle) - => LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.GetExactTypeHandle(pTypeData, pArgInfo, pVmTypeHandle) : HResults.E_NOTIMPL; + public int GetExactTypeHandle(DebuggerIPCE_ExpandedTypeData* pTypeData, ArgInfoList* pArgInfo, ulong* pVmTypeHandle) + { + if (pVmTypeHandle == null) + return HResults.E_POINTER; + *pVmTypeHandle = 0; + int hr = HResults.S_OK; + try + { + IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; + TypeHandle th = default; + CorElementType et = (CorElementType)ReadLittleEndian(pTypeData->elementType); + switch (et) + { + case CorElementType.Array: + case CorElementType.SzArray: + th = GetExactArrayTypeHandle(rts, pTypeData, pArgInfo); + break; + case CorElementType.Ptr: + case CorElementType.Byref: + th = GetExactPtrOrByRefTypeHandle(rts, pTypeData, pArgInfo); + break; + case CorElementType.Class: + case CorElementType.ValueType: + th = GetExactClassTypeHandle(rts, pTypeData, pArgInfo); + break; + case CorElementType.FnPtr: + th = GetExactFnPtrTypeHandle(rts, pArgInfo); + break; + default: + th = rts.GetPrimitiveType(et); + break; + } + if (th.Address == TargetPointer.Null) + throw Marshal.GetExceptionForHR(CorDbgHResults.CORDBG_E_CLASS_NOT_LOADED)!; + *pVmTypeHandle = th.Address.Value; + } + catch (System.Exception ex) + { + hr = ex.HResult; + } +#if DEBUG + if (_legacy is not null) + { + ulong vmLocal; + int hrLocal = _legacy.GetExactTypeHandle(pTypeData, pArgInfo, &vmLocal); + Debug.ValidateHResult(hr, hrLocal); + if (hr == HResults.S_OK) + Debug.Assert(*pVmTypeHandle == vmLocal, $"cDAC: {*pVmTypeHandle:x}, DAC: {vmLocal:x}"); + } +#endif + return hr; + } + + private TypeHandle BasicTypeInfoToTypeHandle(IRuntimeTypeSystem rts, DebuggerIPCE_BasicTypeData* pData) + { + CorElementType et = (CorElementType)ReadLittleEndian(pData->elementType); + TypeHandle th; + switch (et) + { + case CorElementType.Array: + case CorElementType.SzArray: + case CorElementType.Ptr: + case CorElementType.Byref: + case CorElementType.FnPtr: + ulong vmTh = ReadLittleEndian(pData->vmTypeHandle); + if (vmTh == 0) + throw Marshal.GetExceptionForHR(CorDbgHResults.CORDBG_E_CLASS_NOT_LOADED)!; + th = rts.GetTypeHandle(new TargetPointer(vmTh)); + break; + case CorElementType.Class: + case CorElementType.ValueType: + th = GetClassOrValueTypeHandle(rts, pData); + break; + default: + th = rts.GetPrimitiveType(et); + break; + } + if (th.Address == TargetPointer.Null) + throw Marshal.GetExceptionForHR(CorDbgHResults.CORDBG_E_CLASS_NOT_LOADED)!; + return th; + } + + private TypeHandle GetClassOrValueTypeHandle(IRuntimeTypeSystem rts, DebuggerIPCE_BasicTypeData* pData) + { + ulong vmTh = ReadLittleEndian(pData->vmTypeHandle); + if (vmTh != 0) + return rts.GetTypeHandle(new TargetPointer(vmTh)); + + ulong vmAssembly = ReadLittleEndian(pData->vmAssembly); + uint metadataToken = ReadLittleEndian(pData->metadataToken); + return LookupTypeDefOrRefInAssembly(rts, vmAssembly, metadataToken); + } + + private TypeHandle LookupTypeDefOrRefInAssembly(IRuntimeTypeSystem rts, ulong vmAssembly, uint metadataToken) + { + Contracts.ILoader loader = _target.Contracts.Loader; + Contracts.ModuleHandle moduleHandle = loader.GetModuleHandleFromAssemblyPtr(new TargetPointer(vmAssembly)); + Contracts.ModuleLookupTables lookupTables = loader.GetLookupTables(moduleHandle); + TargetPointer mt; + switch ((EcmaMetadataUtils.TokenType)(metadataToken & EcmaMetadataUtils.TokenTypeMask)) + { + case EcmaMetadataUtils.TokenType.mdtTypeDef: + mt = loader.GetModuleLookupMapElement(lookupTables.TypeDefToMethodTable, metadataToken, out _); + break; + case EcmaMetadataUtils.TokenType.mdtTypeRef: + mt = loader.GetModuleLookupMapElement(lookupTables.TypeRefToMethodTable, metadataToken, out _); + break; + default: + throw Marshal.GetExceptionForHR(CorDbgHResults.CORDBG_E_CLASS_NOT_LOADED)!; + } + if (mt == TargetPointer.Null) + throw Marshal.GetExceptionForHR(CorDbgHResults.CORDBG_E_CLASS_NOT_LOADED)!; + return rts.GetTypeHandle(mt); + } + + private TypeHandle GetExactArrayTypeHandle(IRuntimeTypeSystem rts, DebuggerIPCE_ExpandedTypeData* pTopLevel, ArgInfoList* pArgInfo) + { + Debug.Assert(pArgInfo->m_nEntries == 1, $"array arg count: {pArgInfo->m_nEntries}"); + TypeHandle elementType = BasicTypeInfoToTypeHandle(rts, &pArgInfo->m_pList[0]); + CorElementType et = (CorElementType)ReadLittleEndian(pTopLevel->elementType); + int rank = (int)ReadLittleEndian(pTopLevel->ArrayTypeData_arrayRank); + return rts.GetConstructedType(elementType, et, rank, ImmutableArray.Empty); + } + + private TypeHandle GetExactPtrOrByRefTypeHandle(IRuntimeTypeSystem rts, DebuggerIPCE_ExpandedTypeData* pTopLevel, ArgInfoList* pArgInfo) + { + Debug.Assert(pArgInfo->m_nEntries == 1, $"ptr/byref arg count: {pArgInfo->m_nEntries}"); + TypeHandle referent = BasicTypeInfoToTypeHandle(rts, &pArgInfo->m_pList[0]); + CorElementType et = (CorElementType)ReadLittleEndian(pTopLevel->elementType); + return rts.GetConstructedType(referent, et, 0, ImmutableArray.Empty); + } + + private TypeHandle GetExactClassTypeHandle(IRuntimeTypeSystem rts, DebuggerIPCE_ExpandedTypeData* pTopLevel, ArgInfoList* pArgInfo) + { + ulong vmAssembly = ReadLittleEndian(pTopLevel->ClassTypeData_vmAssembly); + uint metadataToken = ReadLittleEndian(pTopLevel->ClassTypeData_metadataToken); + TypeHandle typeConstructor = LookupTypeDefOrRefInAssembly(rts, vmAssembly, metadataToken); + + int argCount = pArgInfo->m_nEntries; + if (argCount == 0) + return typeConstructor; + + ImmutableArray.Builder builder = ImmutableArray.CreateBuilder(argCount); + for (int i = 0; i < argCount; i++) + builder.Add(BasicTypeInfoToTypeHandle(rts, &pArgInfo->m_pList[i])); + + return rts.GetConstructedType(typeConstructor, CorElementType.GenericInst, 0, builder.MoveToImmutable()); + } + + private TypeHandle GetExactFnPtrTypeHandle(IRuntimeTypeSystem rts, ArgInfoList* pArgInfo) + { + int argCount = pArgInfo->m_nEntries; + ImmutableArray.Builder builder = ImmutableArray.CreateBuilder(argCount); + for (int i = 0; i < argCount; i++) + builder.Add(BasicTypeInfoToTypeHandle(rts, &pArgInfo->m_pList[i])); + + // TODO: support non-default calling conventions. + // Currently passes callConv=0 to match native DAC. + return rts.GetConstructedType(default, CorElementType.FnPtr, 0, builder.MoveToImmutable()); + } public int GetMethodDescParams(ulong vmMethodDesc, ulong genericsToken, uint* pcGenericClassTypeParams, nint pGenericTypeParams) => LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.GetMethodDescParams(vmMethodDesc, genericsToken, pcGenericClassTypeParams, pGenericTypeParams) : HResults.E_NOTIMPL; diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/IDacDbiInterface.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/IDacDbiInterface.cs index c9b59b7d067e80..0dcc76f90c4186 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/IDacDbiInterface.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/IDacDbiInterface.cs @@ -280,6 +280,13 @@ public struct DebuggerIPCE_ExpandedTypeData [FieldOffset(8)] public ulong NaryTypeData_typeHandle; // VMPTR_TypeHandle } +[StructLayout(LayoutKind.Sequential)] +public unsafe struct ArgInfoList +{ + public DebuggerIPCE_BasicTypeData* m_pList; + public int m_nEntries; +} + public enum DynamicMethodType { kNone = 0, @@ -550,7 +557,7 @@ public unsafe partial interface IDacDbiInterface int GetApproxTypeHandle(nint pTypeData, ulong* pRetVal); [PreserveSig] - int GetExactTypeHandle(nint pTypeData, nint pArgInfo, ulong* pVmTypeHandle); + int GetExactTypeHandle(DebuggerIPCE_ExpandedTypeData* pTypeData, ArgInfoList* pArgInfo, ulong* pVmTypeHandle); [PreserveSig] int GetMethodDescParams(ulong vmMethodDesc, ulong genericsToken, uint* pcGenericClassTypeParams, nint pGenericTypeParams);