diff --git a/docs/design/datacontracts/RuntimeTypeSystem.md b/docs/design/datacontracts/RuntimeTypeSystem.md index 0ce6cca2e5a141..d267b5746b065d 100644 --- a/docs/design/datacontracts/RuntimeTypeSystem.md +++ b/docs/design/datacontracts/RuntimeTypeSystem.md @@ -51,6 +51,8 @@ partial interface IRuntimeTypeSystem : IContract // True if the MethodTable is the sentinel value associated with unallocated space in the managed heap public virtual bool IsFreeObjectMethodTable(TypeHandle typeHandle); + // True if the MethodTable is the System.Object MethodTable (g_pObjectClass) + public virtual bool IsObject(TypeHandle typeHandle); public virtual bool IsString(TypeHandle typeHandle); // True if the type is a GC-collectable object reference. public virtual bool IsObjRef(TypeHandle typeHandle); @@ -439,7 +441,8 @@ The contract depends on the following globals | Global name | Meaning | | --- | --- | | `ContinuationMethodTable` | A pointer to the address of the base `Continuation` `MethodTable`, or null if no continuations have been created -| `FreeObjectMethodTablePointer` | A pointer to the address of a `MethodTable` used by the GC to indicate reclaimed memory +| `FreeObjectMethodTable` | A pointer to the address of a `MethodTable` used by the GC to indicate reclaimed memory +| `ObjectMethodTable` | A pointer to the address of the `System.Object` `MethodTable` (`g_pObjectClass`) | `StaticsPointerMask` | For masking out a bit of DynamicStaticsInfo pointer fields | `ArrayBaseSize` | The base size of an array object; used to compute multidimensional array rank from `MethodTable::BaseSize` @@ -512,6 +515,7 @@ Contracts used: private readonly Dictionary _methodTables; internal TargetPointer FreeObjectMethodTablePointer {get; } + internal TargetPointer ObjectMethodTablePointer {get; } internal TargetPointer ContinuationMethodTablePointer {get; } public TypeHandle GetTypeHandle(TargetPointer typeHandlePointer) @@ -572,6 +576,8 @@ Contracts used: public bool IsFreeObjectMethodTable(TypeHandle TypeHandle) => FreeObjectMethodTablePointer == TypeHandle.Address; + public bool IsObject(TypeHandle TypeHandle) => ObjectMethodTablePointer != TargetPointer.Null && ObjectMethodTablePointer == TypeHandle.Address; + public bool IsString(TypeHandle TypeHandle) => !typeHandle.IsMethodTable() ? false : _methodTables[TypeHandle.Address].Flags.IsString; public bool IsObjRef(TypeHandle typeHandle) => // Returns true if GetSignatureCorElementType returns Class, Array, or SzArray. diff --git a/src/coreclr/debug/daccess/dacdbiimpl.cpp b/src/coreclr/debug/daccess/dacdbiimpl.cpp index 478b6ae9155f10..00c80b7eca0403 100644 --- a/src/coreclr/debug/daccess/dacdbiimpl.cpp +++ b/src/coreclr/debug/daccess/dacdbiimpl.cpp @@ -2513,20 +2513,6 @@ void DacDbiInterfaceImpl::TypeHandleToBasicTypeInfo(TypeHandle } // DacDbiInterfaceImpl::TypeHandleToBasicTypeInfo -HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetObjectExpandedTypeInfoFromID(AreValueTypesBoxed boxed, COR_TYPEID id, OUT DebuggerIPCE_ExpandedTypeData * pTypeInfo) -{ - DD_ENTER_MAY_THROW; - - HRESULT hr = S_OK; - EX_TRY - { - - TypeHandleToExpandedTypeInfoImpl(boxed, TypeHandle::FromPtr(TO_TADDR(id.token1)), pTypeInfo); - } - EX_CATCH_HRESULT(hr); - return hr; -} - HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetObjectExpandedTypeInfo(AreValueTypesBoxed boxed, CORDB_ADDRESS addr, OUT DebuggerIPCE_ExpandedTypeData * pTypeInfo) { DD_ENTER_MAY_THROW; @@ -2534,7 +2520,6 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetObjectExpandedTypeInfo(AreValu HRESULT hr = S_OK; EX_TRY { - PTR_Object obj(TO_TADDR(addr)); TypeHandleToExpandedTypeInfoImpl(boxed, obj->GetGCSafeTypeHandle(), pTypeInfo); } @@ -2543,16 +2528,14 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetObjectExpandedTypeInfo(AreValu } // DacDbi API: use a type handle to get the information needed to create the corresponding RS CordbType instance -HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::TypeHandleToExpandedTypeInfo(AreValueTypesBoxed boxed, VMPTR_TypeHandle vmTypeHandle, DebuggerIPCE_ExpandedTypeData * pTypeInfo) +HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::TypeHandleToExpandedTypeInfo(AreValueTypesBoxed boxed, CORDB_ADDRESS vmTypeHandle, DebuggerIPCE_ExpandedTypeData * pTypeInfo) { DD_ENTER_MAY_THROW; HRESULT hr = S_OK; EX_TRY { - - - TypeHandle typeHandle = TypeHandle::FromPtr(vmTypeHandle.GetDacPtr()); + TypeHandle typeHandle = TypeHandle::FromPtr((TADDR)vmTypeHandle); TypeHandleToExpandedTypeInfoImpl(boxed, typeHandle, pTypeInfo); } EX_CATCH_HRESULT(hr); @@ -2564,6 +2547,7 @@ void DacDbiInterfaceImpl::TypeHandleToExpandedTypeInfoImpl(AreValueTypesBoxed TypeHandle typeHandle, DebuggerIPCE_ExpandedTypeData * pTypeInfo) { + *pTypeInfo = {}; pTypeInfo->elementType = GetElementType(typeHandle); switch (pTypeInfo->elementType) @@ -2776,20 +2760,16 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetMethodDescParams(VMPTR_MethodD EX_TRY_ALLOW_DATATARGET_MISSING_MEMORY_WITH_HANDLER { // Fill in the struct using the TypeHandle of the current type parameter if we can. - VMPTR_TypeHandle vmTypeHandle = VMPTR_TypeHandle::NullPtr(); - vmTypeHandle.SetDacTargetPtr(thCurrent.AsTAddr()); IfFailThrow(TypeHandleToExpandedTypeInfo(NoValueTypeBoxing, - vmTypeHandle, + (CORDB_ADDRESS)thCurrent.AsTAddr(), &((*pGenericTypeParams)[i]))); } EX_CATCH_ALLOW_DATATARGET_MISSING_MEMORY_WITH_HANDLER { // On failure for a particular type, default it back to System.__Canon. - VMPTR_TypeHandle vmTHCanon = VMPTR_TypeHandle::NullPtr(); TypeHandle thCanon = TypeHandle(g_pCanonMethodTableClass); - vmTHCanon.SetDacTargetPtr(thCanon.AsTAddr()); IfFailThrow(TypeHandleToExpandedTypeInfo(NoValueTypeBoxing, - vmTHCanon, + (CORDB_ADDRESS)thCanon.AsTAddr(), &((*pGenericTypeParams)[i]))); } EX_END_CATCH_ALLOW_DATATARGET_MISSING_MEMORY_WITH_HANDLER @@ -3214,11 +3194,8 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetTypeHandleParams(VMPTR_TypeHan // collect type information for each type parameter for (unsigned int i = 0; i < pParams->Count(); ++i) { - VMPTR_TypeHandle thInst = VMPTR_TypeHandle::NullPtr(); - thInst.SetDacTargetPtr(typeHandle.GetInstantiation()[i].AsTAddr()); - IfFailThrow(TypeHandleToExpandedTypeInfo(NoValueTypeBoxing, - thInst, + (CORDB_ADDRESS)typeHandle.GetInstantiation()[i].AsTAddr(), &((*pParams)[i]))); } @@ -6092,15 +6069,11 @@ void DacDbiInterfaceImpl::InitObjectData(PTR_Object objPtr, DebuggerIPCE_ObjectData * pObjectData) { _ASSERTE(pObjectData != NULL); - // @todo - this is still dangerous because the object may still be invalid. - VMPTR_TypeHandle vmTypeHandle = VMPTR_TypeHandle::NullPtr(); - vmTypeHandle.SetDacTargetPtr(objPtr->GetGCSafeTypeHandle().AsTAddr()); - // Save basic object info. pObjectData->objSize = objPtr->GetSize(); pObjectData->objOffsetToVars = dac_cast((objPtr)->GetData()) - dac_cast(objPtr); - IfFailThrow(TypeHandleToExpandedTypeInfo(AllBoxed, vmTypeHandle, &(pObjectData->objTypeData))); + IfFailThrow(TypeHandleToExpandedTypeInfo(AllBoxed, (CORDB_ADDRESS)objPtr->GetGCSafeTypeHandle().AsTAddr(), &(pObjectData->objTypeData))); // If this is a string object, set the type to ELEMENT_TYPE_STRING. if (objPtr->GetGCSafeMethodTable() == g_pStringClass) diff --git a/src/coreclr/debug/daccess/dacdbiimpl.h b/src/coreclr/debug/daccess/dacdbiimpl.h index 36a077548987cd..3e184aa277a008 100644 --- a/src/coreclr/debug/daccess/dacdbiimpl.h +++ b/src/coreclr/debug/daccess/dacdbiimpl.h @@ -244,13 +244,10 @@ class DacDbiInterfaceImpl : HRESULT STDMETHODCALLTYPE GetObjectExpandedTypeInfo(AreValueTypesBoxed boxed, CORDB_ADDRESS addr, OUT DebuggerIPCE_ExpandedTypeData * pTypeInfo); - HRESULT STDMETHODCALLTYPE GetObjectExpandedTypeInfoFromID(AreValueTypesBoxed boxed, COR_TYPEID id, OUT DebuggerIPCE_ExpandedTypeData * pTypeInfo); - - // @dbgtodo Microsoft inspection: change DebuggerIPCE_ExpandedTypeData to DacDbiStructures type hierarchy // once ICorDebugType and ICorDebugClass are DACized // use a type handle to get the information needed to create the corresponding RS CordbType instance - HRESULT STDMETHODCALLTYPE TypeHandleToExpandedTypeInfo(AreValueTypesBoxed boxed, VMPTR_TypeHandle vmTypeHandle, DebuggerIPCE_ExpandedTypeData * pTypeInfo); + HRESULT STDMETHODCALLTYPE TypeHandleToExpandedTypeInfo(AreValueTypesBoxed boxed, CORDB_ADDRESS vmTypeHandle, DebuggerIPCE_ExpandedTypeData * pTypeInfo); // Get type handle for a TypeDef token, if one exists. For generics this returns the open type. HRESULT STDMETHODCALLTYPE GetTypeHandle(VMPTR_Module vmModule, mdTypeDef metadataToken, OUT VMPTR_TypeHandle * pRetVal); diff --git a/src/coreclr/debug/di/process.cpp b/src/coreclr/debug/di/process.cpp index f8b5a443e3cb9f..e81c242d69b8e3 100644 --- a/src/coreclr/debug/di/process.cpp +++ b/src/coreclr/debug/di/process.cpp @@ -2370,7 +2370,7 @@ HRESULT CordbProcess::GetTypeForTypeID(COR_TYPEID id, ICorDebugType **ppType) EX_TRY { DebuggerIPCE_ExpandedTypeData data; - IfFailThrow(GetDAC()->GetObjectExpandedTypeInfoFromID(AllBoxed, id, &data)); + IfFailThrow(GetDAC()->TypeHandleToExpandedTypeInfo(AllBoxed, id.token1, &data)); CordbType *type = 0; hr = CordbType::TypeDataToType(GetAppDomain(), &data, &type); diff --git a/src/coreclr/debug/di/rstype.cpp b/src/coreclr/debug/di/rstype.cpp index 7cfe5f9f11bc11..79ea8dabe419c5 100644 --- a/src/coreclr/debug/di/rstype.cpp +++ b/src/coreclr/debug/di/rstype.cpp @@ -1163,8 +1163,11 @@ HRESULT CordbType::TypeDataToType(CordbAppDomain *pAppDomain, DebuggerIPCE_Basic { RSLockHolder lockHolder(pProcess->GetProcessLock()); + CORDB_ADDRESS vmTypeHandleRaw = 0; + static_assert(sizeof(data->vmTypeHandle) <= sizeof(vmTypeHandleRaw), "VMPTR_TypeHandle is larger than CORDB_ADDRESS"); + memcpy(&vmTypeHandleRaw, &data->vmTypeHandle, sizeof(data->vmTypeHandle)); IfFailThrow(pProcess->GetDAC()->TypeHandleToExpandedTypeInfo(NoValueTypeBoxing, // could be generics which are never boxed - data->vmTypeHandle, + vmTypeHandleRaw, &typeInfo)); } diff --git a/src/coreclr/debug/inc/dacdbiinterface.h b/src/coreclr/debug/inc/dacdbiinterface.h index c024524724e633..ec2217eb7905a6 100644 --- a/src/coreclr/debug/inc/dacdbiinterface.h +++ b/src/coreclr/debug/inc/dacdbiinterface.h @@ -1549,14 +1549,11 @@ IDacDbiInterface : public IUnknown // vmTypeHandle - type handle for the type // output: pTypeInfo - holds information needed to build the corresponding CordbType // - virtual HRESULT STDMETHODCALLTYPE TypeHandleToExpandedTypeInfo(AreValueTypesBoxed boxed, VMPTR_TypeHandle vmTypeHandle, DebuggerIPCE_ExpandedTypeData * pTypeInfo) = 0; + virtual HRESULT STDMETHODCALLTYPE TypeHandleToExpandedTypeInfo(AreValueTypesBoxed boxed, CORDB_ADDRESS vmTypeHandle, DebuggerIPCE_ExpandedTypeData * pTypeInfo) = 0; virtual HRESULT STDMETHODCALLTYPE GetObjectExpandedTypeInfo(AreValueTypesBoxed boxed, CORDB_ADDRESS addr, OUT DebuggerIPCE_ExpandedTypeData * pTypeInfo) = 0; - virtual HRESULT STDMETHODCALLTYPE GetObjectExpandedTypeInfoFromID(AreValueTypesBoxed boxed, COR_TYPEID id, OUT DebuggerIPCE_ExpandedTypeData * pTypeInfo) = 0; - - // Get type handle for a TypeDef token, if one exists. For generics this returns the open type. // Note there is no guarantee the returned handle will be fully restored (in pre-jit scenarios), // only that it exists. Later functions that use this type handle should fail if they require diff --git a/src/coreclr/debug/inc/dacdbistructures.h b/src/coreclr/debug/inc/dacdbistructures.h index aff80317c59b32..5cc56a2b8b981d 100644 --- a/src/coreclr/debug/inc/dacdbistructures.h +++ b/src/coreclr/debug/inc/dacdbistructures.h @@ -648,7 +648,12 @@ class MSLAYOUT EnCHangingFieldInfo // NoValueTypeBoxing: // TypeHandleToExpandedTypeInfo is also used to report type parameters, // and in this case none of the types are considered boxed ( -enum AreValueTypesBoxed { NoValueTypeBoxing, OnlyPrimitivesUnboxed, AllBoxed }; +enum AreValueTypesBoxed +{ + NoValueTypeBoxing = 0, + OnlyPrimitivesUnboxed = 1, + AllBoxed = 2 +}; // TypeRefData is used for resolving a type reference (see code:CordbModule::ResolveTypeRef and // code:DacDbiInterfaceImpl::ResolveTypeReference) to store relevant information about the type diff --git a/src/coreclr/inc/dacdbi.idl b/src/coreclr/inc/dacdbi.idl index edbeec2e00e1b4..2ea64861a02022 100644 --- a/src/coreclr/inc/dacdbi.idl +++ b/src/coreclr/inc/dacdbi.idl @@ -310,9 +310,8 @@ interface IDacDbiInterface : IUnknown [in] VMPTR_TypeHandle vmThApprox, [out] DacDbiArrayList_FieldData * pFieldList, [out] SIZE_T * pObjectSize); - HRESULT TypeHandleToExpandedTypeInfo([in] AreValueTypesBoxed boxed, [in] VMPTR_TypeHandle vmTypeHandle, [out] struct DebuggerIPCE_ExpandedTypeData * pTypeInfo); + HRESULT TypeHandleToExpandedTypeInfo([in] AreValueTypesBoxed boxed, [in] CORDB_ADDRESS vmTypeHandle, [out] struct DebuggerIPCE_ExpandedTypeData * pTypeInfo); HRESULT GetObjectExpandedTypeInfo([in] AreValueTypesBoxed boxed, [in] CORDB_ADDRESS addr, [out] struct DebuggerIPCE_ExpandedTypeData * pTypeInfo); - HRESULT GetObjectExpandedTypeInfoFromID([in] AreValueTypesBoxed boxed, [in] COR_TYPEID id, [out] struct DebuggerIPCE_ExpandedTypeData * pTypeInfo); HRESULT GetTypeHandle([in] VMPTR_Module vmModule, [in] mdTypeDef metadataToken, [out] VMPTR_TypeHandle * pRetVal); HRESULT GetApproxTypeHandle([in] TypeInfoList * pTypeData, [out] VMPTR_TypeHandle * pRetVal); HRESULT GetExactTypeHandle([in] struct DebuggerIPCE_ExpandedTypeData * pTypeData, [in] ArgInfoList * pArgInfo, [out] VMPTR_TypeHandle * pVmTypeHandle); 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 3d1c8cf60fbaaf..03405e77b283c9 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 @@ -114,6 +114,8 @@ public interface IRuntimeTypeSystem : IContract // True if the MethodTable is the sentinel value associated with unallocated space in the managed heap bool IsFreeObjectMethodTable(TypeHandle typeHandle) => throw new NotImplementedException(); + // True if the MethodTable is the System.Object MethodTable (g_pObjectClass) + bool IsObject(TypeHandle typeHandle) => throw new NotImplementedException(); bool IsString(TypeHandle typeHandle) => throw new NotImplementedException(); bool IsObjRef(TypeHandle typeHandle) => throw new NotImplementedException(); // True if the MethodTable represents a type that contains managed references 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 cd9fb666c51962..5c76443d860c22 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 @@ -18,6 +18,7 @@ internal partial struct RuntimeTypeSystem_1 : IRuntimeTypeSystem private const int TYPE_MASK_OFFSET = 27; // offset of type in field desc flags2 private readonly Target _target; private readonly TargetPointer _freeObjectMethodTablePointer; + private readonly TargetPointer _objectMethodTablePointer; private readonly TargetPointer _continuationMethodTablePointer; private readonly ulong _methodDescAlignment; private readonly TypeValidation _typeValidation; @@ -433,6 +434,8 @@ internal RuntimeTypeSystem_1(Target target) _target = target; _freeObjectMethodTablePointer = target.ReadPointer( target.ReadGlobalPointer(Constants.Globals.FreeObjectMethodTable)); + _objectMethodTablePointer = target.ReadPointer( + target.ReadGlobalPointer(Constants.Globals.ObjectMethodTable)); _continuationMethodTablePointer = target.ReadPointer( target.ReadGlobalPointer(Constants.Globals.ContinuationMethodTable)); _methodDescAlignment = target.ReadGlobal(Constants.Globals.MethodDescAlignment); @@ -442,6 +445,7 @@ internal RuntimeTypeSystem_1(Target target) } internal TargetPointer FreeObjectMethodTablePointer => _freeObjectMethodTablePointer; + internal TargetPointer ObjectMethodTablePointer => _objectMethodTablePointer; internal TargetPointer ContinuationMethodTablePointer => _continuationMethodTablePointer; internal ulong MethodDescAlignment => _methodDescAlignment; @@ -560,6 +564,8 @@ private Data.EEClass GetClassData(TypeHandle typeHandle) public bool IsFreeObjectMethodTable(TypeHandle typeHandle) => FreeObjectMethodTablePointer == typeHandle.Address; + public bool IsObject(TypeHandle typeHandle) => ObjectMethodTablePointer != TargetPointer.Null && ObjectMethodTablePointer == typeHandle.Address; + public bool IsString(TypeHandle typeHandle) => !typeHandle.IsMethodTable() ? false : _methodTables[typeHandle.Address].Flags.IsString; public bool IsObjRef(TypeHandle typeHandle) { 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 508aaeb440d322..a2e3b96b044791 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 @@ -2,9 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Buffers.Binary; using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using System.Numerics; using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; using Microsoft.Diagnostics.DataContractReader.Contracts; @@ -1277,14 +1279,110 @@ public int GetClassInfo(ulong thExact, nint pData) public int GetInstantiationFieldInfo(ulong vmAssembly, ulong vmTypeHandle, ulong vmExactMethodTable, nint pFieldList, nuint* pObjectSize) => LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.GetInstantiationFieldInfo(vmAssembly, vmTypeHandle, vmExactMethodTable, pFieldList, pObjectSize) : HResults.E_NOTIMPL; - public int TypeHandleToExpandedTypeInfo(int boxed, ulong vmTypeHandle, nint pData) - => LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.TypeHandleToExpandedTypeInfo(boxed, vmTypeHandle, pData) : HResults.E_NOTIMPL; + public int TypeHandleToExpandedTypeInfo(AreValueTypesBoxed boxed, ulong vmTypeHandle, DebuggerIPCE_ExpandedTypeData* pTypeInfo) + { + int hr = HResults.S_OK; + try + { + IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; + TypeHandle th = rts.GetTypeHandle(new TargetPointer(vmTypeHandle)); + TypeHandleToExpandedTypeInfoImpl(rts, boxed, th, pTypeInfo); + } + catch (System.Exception ex) + { + hr = ex.HResult; + } +#if DEBUG + if (_legacy is not null) + { + DebuggerIPCE_ExpandedTypeData dataLocal; + int hrLocal = _legacy.TypeHandleToExpandedTypeInfo(boxed, vmTypeHandle, &dataLocal); + Debug.ValidateHResult(hr, hrLocal); + if (hr == HResults.S_OK) + { + ValidateExpandedTypeData(pTypeInfo, &dataLocal); + } + } +#endif + return hr; + } - public int GetObjectExpandedTypeInfo(int boxed, ulong addr, nint pTypeInfo) - => LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.GetObjectExpandedTypeInfo(boxed, addr, pTypeInfo) : HResults.E_NOTIMPL; + public int GetObjectExpandedTypeInfo(AreValueTypesBoxed boxed, ulong addr, DebuggerIPCE_ExpandedTypeData* pTypeInfo) + { + int hr = HResults.S_OK; + try + { + IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; + TargetPointer mtAddr = _target.Contracts.Object.GetMethodTableAddress(new TargetPointer(addr)); + TypeHandle th = rts.GetTypeHandle(mtAddr); + TypeHandleToExpandedTypeInfoImpl(rts, boxed, th, pTypeInfo); + } + catch (System.Exception ex) + { + hr = ex.HResult; + } +#if DEBUG + if (_legacy is not null) + { + DebuggerIPCE_ExpandedTypeData dataLocal; + int hrLocal = _legacy.GetObjectExpandedTypeInfo(boxed, addr, &dataLocal); + Debug.ValidateHResult(hr, hrLocal); + if (hr == HResults.S_OK) + { + ValidateExpandedTypeData(pTypeInfo, &dataLocal); + } + } +#endif + return hr; + } - public int GetObjectExpandedTypeInfoFromID(int boxed, COR_TYPEID id, nint pTypeInfo) - => LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.GetObjectExpandedTypeInfoFromID(boxed, id, pTypeInfo) : HResults.E_NOTIMPL; +#if DEBUG + private static void ValidateExpandedTypeData(DebuggerIPCE_ExpandedTypeData* cdac, DebuggerIPCE_ExpandedTypeData* dac) + { + Debug.Assert(cdac->elementType == dac->elementType, + $"cDAC elementType: {cdac->elementType}, DAC: {dac->elementType}"); + switch ((CorElementType)ReadLittleEndian(cdac->elementType)) + { + case CorElementType.Class: + case CorElementType.ValueType: + Debug.Assert(cdac->ClassTypeData_metadataToken == dac->ClassTypeData_metadataToken, + $"cDAC ClassTypeData.metadataToken: {cdac->ClassTypeData_metadataToken:x}, DAC: {dac->ClassTypeData_metadataToken:x}"); + Debug.Assert(cdac->ClassTypeData_vmAssembly == dac->ClassTypeData_vmAssembly, + $"cDAC ClassTypeData.vmAssembly: {cdac->ClassTypeData_vmAssembly:x}, DAC: {dac->ClassTypeData_vmAssembly:x}"); + Debug.Assert(cdac->ClassTypeData_typeHandle == dac->ClassTypeData_typeHandle, + $"cDAC ClassTypeData.typeHandle: {cdac->ClassTypeData_typeHandle:x}, DAC: {dac->ClassTypeData_typeHandle:x}"); + break; + case CorElementType.Array: + case CorElementType.SzArray: + Debug.Assert(cdac->ArrayTypeData_arrayRank == dac->ArrayTypeData_arrayRank, + $"cDAC ArrayTypeData.arrayRank: {cdac->ArrayTypeData_arrayRank}, DAC: {dac->ArrayTypeData_arrayRank}"); + Debug.Assert(cdac->ArrayTypeData_arrayTypeArg.elementType == dac->ArrayTypeData_arrayTypeArg.elementType, + $"cDAC ArrayTypeData.arrayTypeArg.elementType: {cdac->ArrayTypeData_arrayTypeArg.elementType}, DAC: {dac->ArrayTypeData_arrayTypeArg.elementType}"); + Debug.Assert(cdac->ArrayTypeData_arrayTypeArg.metadataToken == dac->ArrayTypeData_arrayTypeArg.metadataToken, + $"cDAC ArrayTypeData.arrayTypeArg.metadataToken: {cdac->ArrayTypeData_arrayTypeArg.metadataToken:x}, DAC: {dac->ArrayTypeData_arrayTypeArg.metadataToken:x}"); + Debug.Assert(cdac->ArrayTypeData_arrayTypeArg.vmAssembly == dac->ArrayTypeData_arrayTypeArg.vmAssembly, + $"cDAC ArrayTypeData.arrayTypeArg.vmAssembly: {cdac->ArrayTypeData_arrayTypeArg.vmAssembly:x}, DAC: {dac->ArrayTypeData_arrayTypeArg.vmAssembly:x}"); + Debug.Assert(cdac->ArrayTypeData_arrayTypeArg.vmTypeHandle == dac->ArrayTypeData_arrayTypeArg.vmTypeHandle, + $"cDAC ArrayTypeData.arrayTypeArg.vmTypeHandle: {cdac->ArrayTypeData_arrayTypeArg.vmTypeHandle:x}, DAC: {dac->ArrayTypeData_arrayTypeArg.vmTypeHandle:x}"); + break; + case CorElementType.Ptr: + case CorElementType.Byref: + Debug.Assert(cdac->UnaryTypeData_unaryTypeArg.elementType == dac->UnaryTypeData_unaryTypeArg.elementType, + $"cDAC UnaryTypeData.unaryTypeArg.elementType: {cdac->UnaryTypeData_unaryTypeArg.elementType}, DAC: {dac->UnaryTypeData_unaryTypeArg.elementType}"); + Debug.Assert(cdac->UnaryTypeData_unaryTypeArg.metadataToken == dac->UnaryTypeData_unaryTypeArg.metadataToken, + $"cDAC UnaryTypeData.unaryTypeArg.metadataToken: {cdac->UnaryTypeData_unaryTypeArg.metadataToken:x}, DAC: {dac->UnaryTypeData_unaryTypeArg.metadataToken:x}"); + Debug.Assert(cdac->UnaryTypeData_unaryTypeArg.vmAssembly == dac->UnaryTypeData_unaryTypeArg.vmAssembly, + $"cDAC UnaryTypeData.unaryTypeArg.vmAssembly: {cdac->UnaryTypeData_unaryTypeArg.vmAssembly:x}, DAC: {dac->UnaryTypeData_unaryTypeArg.vmAssembly:x}"); + Debug.Assert(cdac->UnaryTypeData_unaryTypeArg.vmTypeHandle == dac->UnaryTypeData_unaryTypeArg.vmTypeHandle, + $"cDAC UnaryTypeData.unaryTypeArg.vmTypeHandle: {cdac->UnaryTypeData_unaryTypeArg.vmTypeHandle:x}, DAC: {dac->UnaryTypeData_unaryTypeArg.vmTypeHandle:x}"); + break; + case CorElementType.FnPtr: + Debug.Assert(cdac->NaryTypeData_typeHandle == dac->NaryTypeData_typeHandle, + $"cDAC NaryTypeData.typeHandle: {cdac->NaryTypeData_typeHandle:x}, DAC: {dac->NaryTypeData_typeHandle:x}"); + break; + } + } +#endif public int GetTypeHandle(ulong vmModule, uint metadataToken, ulong* pRetVal) { @@ -2203,4 +2301,211 @@ public int GetAsyncLocals(ulong vmMethod, ulong codeAddr, uint state, nint pAsyn public int GetGenericArgTokenIndex(ulong vmMethod, uint* pIndex) => LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.GetGenericArgTokenIndex(vmMethod, pIndex) : HResults.E_NOTIMPL; + // Shared core implementation for TypeHandleToExpandedTypeInfo and GetObjectExpandedTypeInfo. + private void TypeHandleToExpandedTypeInfoImpl(IRuntimeTypeSystem rts, AreValueTypesBoxed boxed, TypeHandle typeHandle, DebuggerIPCE_ExpandedTypeData* pTypeInfo) + { + *pTypeInfo = default; + CorElementType elementType = GetElementType(rts, typeHandle); + WriteLittleEndian(ref pTypeInfo->elementType, (int)elementType); + + switch (elementType) + { + case CorElementType.Array: + case CorElementType.SzArray: + FillArrayTypeInfo(rts, typeHandle, pTypeInfo); + break; + + case CorElementType.Ptr: + case CorElementType.Byref: + FillPtrTypeInfo(rts, boxed, typeHandle, pTypeInfo); + break; + + case CorElementType.ValueType: + if (boxed == AreValueTypesBoxed.OnlyPrimitivesUnboxed || boxed == AreValueTypesBoxed.AllBoxed) + { + WriteLittleEndian(ref pTypeInfo->elementType, (int)CorElementType.Class); + } + FillClassTypeInfo(rts, typeHandle, pTypeInfo); + break; + + case CorElementType.Class: + FillClassTypeInfo(rts, typeHandle, pTypeInfo); + break; + + case CorElementType.FnPtr: + FillFnPtrTypeInfo(rts, boxed, typeHandle, pTypeInfo); + break; + + default: + if (boxed == AreValueTypesBoxed.AllBoxed) + { + WriteLittleEndian(ref pTypeInfo->elementType, (int)CorElementType.Class); + FillClassTypeInfo(rts, typeHandle, pTypeInfo); + } + break; + } + } + + // Determines the CorElementType for a type handle, mapping System.Object and System.String + // to their specific element types (the runtime's GetSignatureCorElementType returns E_T_CLASS + // for both Object and String). + private static CorElementType GetElementType(IRuntimeTypeSystem rts, TypeHandle typeHandle) + { + if (typeHandle.IsNull) + return CorElementType.Void; + + if (rts.IsString(typeHandle)) + return CorElementType.String; + + if (rts.IsObject(typeHandle)) + return CorElementType.Object; + + return rts.GetSignatureCorElementType(typeHandle); + } + + // Mirrors native TypeHandle::UpCastTypeIfNeeded — for continuation types, returns the + // parent (continuation base) type handle instead. + private static TypeHandle UpCastTypeIfNeeded(IRuntimeTypeSystem rts, TypeHandle typeHandle) + { + if (rts.IsContinuation(typeHandle)) + { + TargetPointer parentMT = rts.GetParentMethodTable(typeHandle); + if (parentMT != TargetPointer.Null) + return rts.GetTypeHandle(parentMT); + } + return typeHandle; + } + + // Fills ArrayTypeData for E_T_ARRAY and E_T_SZARRAY. + // Mirrors native DacDbiInterfaceImpl::GetArrayTypeInfo. + private void FillArrayTypeInfo(IRuntimeTypeSystem rts, TypeHandle typeHandle, DebuggerIPCE_ExpandedTypeData* pTypeInfo) + { + Debug.Assert(rts.IsArray(typeHandle, out _)); + rts.IsArray(typeHandle, out uint rank); + WriteLittleEndian(ref pTypeInfo->ArrayTypeData_arrayRank, rank); + TypeHandle elemTypeHandle = rts.GetTypeParam(typeHandle); + FillBasicTypeInfo(rts, elemTypeHandle, out pTypeInfo->ArrayTypeData_arrayTypeArg); + } + + // Fills UnaryTypeData for E_T_PTR and E_T_BYREF (or ClassTypeData if AllBoxed). + private void FillPtrTypeInfo(IRuntimeTypeSystem rts, AreValueTypesBoxed boxed, TypeHandle typeHandle, DebuggerIPCE_ExpandedTypeData* pTypeInfo) + { + if (boxed == AreValueTypesBoxed.AllBoxed) + { + FillClassTypeInfo(rts, typeHandle, pTypeInfo); + } + else + { + TypeHandle paramTypeHandle = rts.GetTypeParam(typeHandle); + FillBasicTypeInfo(rts, paramTypeHandle, out pTypeInfo->UnaryTypeData_unaryTypeArg); + } + } + + // Fills ClassTypeData for E_T_CLASS and E_T_VALUETYPE. + private void FillClassTypeInfo(IRuntimeTypeSystem rts, TypeHandle typeHandle, DebuggerIPCE_ExpandedTypeData* pTypeInfo) + { + typeHandle = UpCastTypeIfNeeded(rts, typeHandle); + + TargetPointer modulePtr = rts.GetModule(typeHandle); + Contracts.ILoader loader = _target.Contracts.Loader; + Contracts.ModuleHandle moduleHandle = loader.GetModuleHandleFromModulePtr(modulePtr); + + ReadOnlySpan instantiation = rts.GetInstantiation(typeHandle); + if (instantiation.Length > 0) + { + // Generic instantiation — set the type handle so the debugger can fetch type arguments + WriteLittleEndian(ref pTypeInfo->ClassTypeData_typeHandle, typeHandle.Address.Value); + } + // else: non-generic — typeHandle stays null + + WriteLittleEndian(ref pTypeInfo->ClassTypeData_metadataToken, rts.GetTypeDefToken(typeHandle)); + + Debug.Assert(modulePtr != TargetPointer.Null); + WriteLittleEndian(ref pTypeInfo->ClassTypeData_vmAssembly, loader.GetAssembly(moduleHandle).Value); + } + + // Fills NaryTypeData for E_T_FNPTR (or ClassTypeData if AllBoxed). + private void FillFnPtrTypeInfo(IRuntimeTypeSystem rts, AreValueTypesBoxed boxed, TypeHandle typeHandle, DebuggerIPCE_ExpandedTypeData* pTypeInfo) + { + if (boxed == AreValueTypesBoxed.AllBoxed) + { + FillClassTypeInfo(rts, typeHandle, pTypeInfo); + } + else + { + WriteLittleEndian(ref pTypeInfo->NaryTypeData_typeHandle, typeHandle.Address.Value); + } + } + + // Fills a DebuggerIPCE_BasicTypeData for a type handle — used for array element types + // and ptr/byref referent types. + private void FillBasicTypeInfo(IRuntimeTypeSystem rts, TypeHandle typeHandle, out DebuggerIPCE_BasicTypeData typeInfo) + { + typeInfo = default; + CorElementType elementType = GetElementType(rts, typeHandle); + WriteLittleEndian(ref typeInfo.elementType, (int)elementType); + + switch (elementType) + { + case CorElementType.Array: + case CorElementType.SzArray: + case CorElementType.FnPtr: + case CorElementType.Ptr: + case CorElementType.Byref: + WriteLittleEndian(ref typeInfo.vmTypeHandle, typeHandle.Address.Value); + // metadataToken and vmAssembly stay zero + break; + + case CorElementType.Class: + case CorElementType.ValueType: + { + typeHandle = UpCastTypeIfNeeded(rts, typeHandle); + + TargetPointer modulePtr = rts.GetModule(typeHandle); + Contracts.ILoader loader = _target.Contracts.Loader; + Contracts.ModuleHandle moduleHandle = loader.GetModuleHandleFromModulePtr(modulePtr); + + ReadOnlySpan instantiation = rts.GetInstantiation(typeHandle); + if (instantiation.Length > 0) + { + WriteLittleEndian(ref typeInfo.vmTypeHandle, typeHandle.Address.Value); + } + // else: vmTypeHandle stays null + + WriteLittleEndian(ref typeInfo.metadataToken, rts.GetTypeDefToken(typeHandle)); + Debug.Assert(modulePtr != TargetPointer.Null); + WriteLittleEndian(ref typeInfo.vmAssembly, loader.GetAssembly(moduleHandle).Value); + break; + } + + default: + // All fields zero + break; + } + } + + // Little-endian read/write helpers for IPCE structs. + // Native IPCE structs use Portable, which stores data in little-endian format. + // These helpers ensure managed reads/writes match that convention. + private static void WriteLittleEndian(ref T dest, T value) where T : unmanaged, IBinaryInteger + { + if (BitConverter.IsLittleEndian) + { + dest = value; + } + else + { + Span destBytes = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref dest, 1)); + value.WriteLittleEndian(destBytes); + } + } + + private static T ReadLittleEndian(T value) where T : unmanaged, IBinaryInteger + { + if (BitConverter.IsLittleEndian) + return value; + MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref value, 1)).Reverse(); + return value; + } + } 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 e3a2e87bd70421..ee33f5ff5e659b 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 @@ -153,6 +153,47 @@ public struct COR_FIELD #pragma warning restore CS0649 +public enum AreValueTypesBoxed : int +{ + NoValueTypeBoxing = 0, + OnlyPrimitivesUnboxed = 1, + AllBoxed = 2 +} +// Matches native DebuggerIPCE_BasicTypeData layout (24 bytes). +// All fields are stored in little-endian format (Portable in native). +[StructLayout(LayoutKind.Explicit, Size = 24)] +public struct DebuggerIPCE_BasicTypeData +{ + [FieldOffset(0)] public int elementType; // Portable + [FieldOffset(4)] public uint metadataToken; // Portable + [FieldOffset(8)] public ulong vmAssembly; // VMPTR_Assembly (Portable) + [FieldOffset(16)] public ulong vmTypeHandle; // VMPTR_TypeHandle (Portable) +} + +// Matches native DebuggerIPCE_ExpandedTypeData layout (40 bytes). +// Contains a union at offset 8 (4 bytes of padding after elementType to align the +// 8-byte VMPTR fields inside the union). All fields are stored in little-endian format. +[StructLayout(LayoutKind.Explicit, Size = 40)] +public struct DebuggerIPCE_ExpandedTypeData +{ + [FieldOffset(0)] public int elementType; // Portable + + // ClassTypeData (used for E_T_CLASS, E_T_VALUETYPE) + [FieldOffset(8)] public uint ClassTypeData_metadataToken; // Portable + [FieldOffset(16)] public ulong ClassTypeData_vmAssembly; // VMPTR_Assembly + [FieldOffset(24)] public ulong ClassTypeData_typeHandle; // VMPTR_TypeHandle + + // UnaryTypeData (used for E_T_PTR, E_T_BYREF) — overlaps union at offset 8 + [FieldOffset(8)] public DebuggerIPCE_BasicTypeData UnaryTypeData_unaryTypeArg; + + // ArrayTypeData (used for E_T_ARRAY, E_T_SZARRAY) — overlaps union at offset 8 + [FieldOffset(8)] public DebuggerIPCE_BasicTypeData ArrayTypeData_arrayTypeArg; + [FieldOffset(32)] public uint ArrayTypeData_arrayRank; // Portable + + // NaryTypeData (used for E_T_FNPTR) — overlaps union at offset 8 + [FieldOffset(8)] public ulong NaryTypeData_typeHandle; // VMPTR_TypeHandle +} + public enum DynamicMethodType { kNone = 0, @@ -384,13 +425,10 @@ public unsafe partial interface IDacDbiInterface int GetInstantiationFieldInfo(ulong vmAssembly, ulong vmTypeHandle, ulong vmExactMethodTable, nint pFieldList, nuint* pObjectSize); [PreserveSig] - int TypeHandleToExpandedTypeInfo(int boxed, ulong vmTypeHandle, nint pData); - - [PreserveSig] - int GetObjectExpandedTypeInfo(int boxed, ulong addr, nint pTypeInfo); + int TypeHandleToExpandedTypeInfo(AreValueTypesBoxed boxed, ulong vmTypeHandle, DebuggerIPCE_ExpandedTypeData* pData); [PreserveSig] - int GetObjectExpandedTypeInfoFromID(int boxed, COR_TYPEID id, nint pTypeInfo); + int GetObjectExpandedTypeInfo(AreValueTypesBoxed boxed, ulong addr, DebuggerIPCE_ExpandedTypeData* pTypeInfo); [PreserveSig] int GetTypeHandle(ulong vmModule, uint metadataToken, ulong* pRetVal); diff --git a/src/native/managed/cdac/tests/DumpTests/RuntimeTypeSystemDumpTests.cs b/src/native/managed/cdac/tests/DumpTests/RuntimeTypeSystemDumpTests.cs index 8a049c6a436af8..a587c129edd6dd 100644 --- a/src/native/managed/cdac/tests/DumpTests/RuntimeTypeSystemDumpTests.cs +++ b/src/native/managed/cdac/tests/DumpTests/RuntimeTypeSystemDumpTests.cs @@ -49,6 +49,7 @@ public void RuntimeTypeSystem_ObjectMethodTableIsValid(TestConfiguration config) TypeHandle handle = rts.GetTypeHandle(objectMT); Assert.False(rts.IsFreeObjectMethodTable(handle)); + Assert.True(rts.IsObject(handle)); } [ConditionalTheory] @@ -65,6 +66,7 @@ public void RuntimeTypeSystem_FreeObjectMethodTableIsValid(TestConfiguration con TypeHandle handle = rts.GetTypeHandle(freeObjMT); Assert.True(rts.IsFreeObjectMethodTable(handle)); + Assert.False(rts.IsObject(handle)); } [ConditionalTheory] @@ -81,6 +83,7 @@ public void RuntimeTypeSystem_StringMethodTableIsString(TestConfiguration config TypeHandle handle = rts.GetTypeHandle(stringMT); Assert.True(rts.IsString(handle)); + Assert.False(rts.IsObject(handle)); } [ConditionalTheory] diff --git a/src/native/managed/cdac/tests/MethodTableTests.cs b/src/native/managed/cdac/tests/MethodTableTests.cs index 6ceafece039de6..6206d761650ffa 100644 --- a/src/native/managed/cdac/tests/MethodTableTests.cs +++ b/src/native/managed/cdac/tests/MethodTableTests.cs @@ -35,6 +35,7 @@ internal static (string Name, ulong Value)[] CreateContractGlobals(MockRTS rtsBu [ (nameof(Constants.Globals.FreeObjectMethodTable), rtsBuilder.FreeObjectMethodTableGlobalAddress), (nameof(Constants.Globals.ContinuationMethodTable), rtsBuilder.ContinuationMethodTableGlobalAddress), + (nameof(Constants.Globals.ObjectMethodTable), rtsBuilder.ObjectMethodTableGlobalAddress), (nameof(Constants.Globals.MethodDescAlignment), rtsBuilder.MethodDescAlignment), (nameof(Constants.Globals.ArrayBaseSize), rtsBuilder.ArrayBaseSize), ]; @@ -76,6 +77,7 @@ public void HasRuntimeTypeSystemContract(MockTarget.Architecture arch) Contracts.TypeHandle handle = contract.GetTypeHandle(freeObjectMethodTableAddress); Assert.NotEqual(TargetPointer.Null, handle.Address); Assert.True(contract.IsFreeObjectMethodTable(handle)); + Assert.False(contract.IsObject(handle)); } [Theory] @@ -91,6 +93,7 @@ public void ValidateSystemObjectMethodTable(MockTarget.Architecture arch) Contracts.TypeHandle systemObjectTypeHandle = contract.GetTypeHandle(systemObjectMethodTablePtr); Assert.Equal(systemObjectMethodTablePtr.Value, systemObjectTypeHandle.Address.Value); Assert.False(contract.IsFreeObjectMethodTable(systemObjectTypeHandle)); + Assert.True(contract.IsObject(systemObjectTypeHandle)); } [Theory] diff --git a/src/native/managed/cdac/tests/MockDescriptors/MockDescriptors.RuntimeTypeSystem.cs b/src/native/managed/cdac/tests/MockDescriptors/MockDescriptors.RuntimeTypeSystem.cs index 6ad4ed4996405a..ca3b4198536a2b 100644 --- a/src/native/managed/cdac/tests/MockDescriptors/MockDescriptors.RuntimeTypeSystem.cs +++ b/src/native/managed/cdac/tests/MockDescriptors/MockDescriptors.RuntimeTypeSystem.cs @@ -300,6 +300,7 @@ public class RuntimeTypeSystem { internal const ulong TestFreeObjectMethodTableGlobalAddress = 0x00000000_7a0000a0; internal const ulong TestContinuationMethodTableGlobalAddress = 0x00000000_7a0000b0; + internal const ulong TestObjectMethodTableGlobalAddress = 0x00000000_7a0000c0; private const ulong DefaultAllocationRangeStart = 0x00000000_4a000000; private const ulong DefaultAllocationRangeEnd = 0x00000000_4b000000; @@ -326,6 +327,7 @@ public class RuntimeTypeSystem internal ulong FreeObjectMethodTableAddress { get; private set; } internal ulong FreeObjectMethodTableGlobalAddress => TestFreeObjectMethodTableGlobalAddress; internal ulong ContinuationMethodTableGlobalAddress => TestContinuationMethodTableGlobalAddress; + internal ulong ObjectMethodTableGlobalAddress => TestObjectMethodTableGlobalAddress; internal ulong MethodDescAlignment => GetMethodDescAlignment(Builder.TargetTestHelpers); internal ulong ArrayBaseSize => Builder.TargetTestHelpers.ArrayBaseBaseSize; // sizeof(ContinuationObject) = sizeof(MT*) + sizeof(Next*) + sizeof(Resume*) + sizeof(Flags) + sizeof(State) @@ -358,6 +360,7 @@ private void AddGlobalPointers() { AddFreeObjectMethodTable(); AddContinuationMethodTableGlobal(); + AddObjectMethodTableGlobal(); } private void AddDefaultTypes() @@ -378,6 +381,12 @@ private void AddContinuationMethodTableGlobal() AddPointerGlobal("Address of Continuation Method Table", TestContinuationMethodTableGlobalAddress, 0); } + private void AddObjectMethodTableGlobal() + { + // Initialized to 0; patched to point to SystemObjectMethodTable in AddSystemObjectType. + AddPointerGlobal("Address of Object Method Table", TestObjectMethodTableGlobalAddress, 0); + } + private void AddSystemObjectType() { const int NumMethods = 8; @@ -392,6 +401,10 @@ private void AddSystemObjectType() SystemObjectMethodTable.NumVirtuals = NumVirtuals; SystemObjectEEClass.MethodTable = SystemObjectMethodTable.Address; SystemObjectMethodTable.EEClassOrCanonMT = SystemObjectEEClass.Address; + + // Patch the ObjectMethodTable global to point to System.Object's MethodTable. + Span globalAddrBytes = Builder.BorrowAddressRange(TestObjectMethodTableGlobalAddress, Builder.TargetTestHelpers.PointerSize); + Builder.TargetTestHelpers.WritePointer(globalAddrBytes, SystemObjectMethodTable.Address); } private void AddContinuationType()