diff --git a/docs/design/datacontracts/RuntimeTypeSystem.md b/docs/design/datacontracts/RuntimeTypeSystem.md index 6a3b9544bbdc50..86fe79087fb690 100644 --- a/docs/design/datacontracts/RuntimeTypeSystem.md +++ b/docs/design/datacontracts/RuntimeTypeSystem.md @@ -70,6 +70,8 @@ partial interface IRuntimeTypeSystem : IContract public TargetPointer GetGCStaticsBasePointer(TypeHandle typeHandle); public TargetPointer GetNonGCStaticsBasePointer(TypeHandle typeHandle); public virtual ReadOnlySpan GetInstantiation(TypeHandle typeHandle); + public bool IsClassInited(TypeHandle typeHandle); + public bool IsInitError(TypeHandle typeHandle); public virtual bool IsGenericTypeDefinition(TypeHandle typeHandle); public virtual bool HasTypeParam(TypeHandle typeHandle); @@ -354,6 +356,7 @@ The contract additionally depends on these data descriptors | `MethodTable` | `NumInterfaces` | Number of interfaces of `MethodTable` | | `MethodTable` | `NumVirtuals` | Number of virtual methods in `MethodTable` | | `MethodTable` | `PerInstInfo` | Either the array element type, or pointer to generic information for `MethodTable` | +| `MethodTableAuxiliaryData` | `Flags` | Flags of `MethodTableAuxiliaryData` | | `MethodTable` | `AuxiliaryData` | Pointer to the AuxiliaryData of a method table | | `DynamicStaticsInfo` | `NonGCStatics` | Pointer to non-GC statics | | `DynamicStaticsInfo` | `GCStatics` | Pointer to the GC statics | @@ -556,6 +559,26 @@ Contracts used: return instantiation; } + public bool IsClassInited(TypeHandle typeHandle) + { + if (!typeHandle.IsMethodTable()) + return false; + TargetPointer auxiliaryDataPtr = target.ReadPointer(typeHandle.Address + /* MethodTable.AuxiliaryData offset */); + TargetPointer flagsPtr = target.ReadPointer(auxiliaryDataPtr + /* MethodTableAuxiliaryData::Flags offset */); + uint flags = target.Read(flagsPtr); + return (flags & (uint)MethodTableAuxiliaryFlags.Initialized) != 0; + } + + public bool IsInitError(TypeHandle typeHandle) + { + if (!typeHandle.IsMethodTable()) + return false; + TargetPointer auxiliaryDataPtr = target.ReadPointer(typeHandle.Address + /* MethodTable.AuxiliaryData offset */); + TargetPointer flagsPtr = target.ReadPointer(auxiliaryDataPtr + /* MethodTableAuxiliaryData::Flags offset */); + uint flags = target.Read(flagsPtr); + return (flags & (uint)MethodTableAuxiliaryFlags.IsInitError) != 0; + } + public bool IsDynamicStatics(TypeHandle TypeHandle) => !typeHandle.IsMethodTable() ? false : _methodTables[TypeHandle.Address].Flags.IsDynamicStatics; public bool HasTypeParam(TypeHandle typeHandle) @@ -827,6 +850,12 @@ And the following enumeration definitions TemporaryEntryPointAssigned = 0x04, } + internal enum MethodTableAuxiliaryFlags : uint + { + Initialized = 0x0001, + IsInitError = 0x0100, + } + ``` Internal to the contract in order to answer queries about method descriptors, diff --git a/src/coreclr/vm/datadescriptor/datadescriptor.inc b/src/coreclr/vm/datadescriptor/datadescriptor.inc index 31a52f9571e81d..5a5755306a4ebd 100644 --- a/src/coreclr/vm/datadescriptor/datadescriptor.inc +++ b/src/coreclr/vm/datadescriptor/datadescriptor.inc @@ -286,6 +286,7 @@ CDAC_TYPE_BEGIN(MethodTableAuxiliaryData) CDAC_TYPE_INDETERMINATE(MethodTableAuxiliaryData) CDAC_TYPE_FIELD(MethodTableAuxiliaryData, /*pointer*/, LoaderModule, offsetof(MethodTableAuxiliaryData, m_pLoaderModule)) CDAC_TYPE_FIELD(MethodTableAuxiliaryData, /*int16*/, OffsetToNonVirtualSlots, offsetof(MethodTableAuxiliaryData, m_offsetToNonVirtualSlots)) +CDAC_TYPE_FIELD(MethodTableAuxiliaryData, /*uint32*/, Flags, offsetof(MethodTableAuxiliaryData, m_dwFlags)) CDAC_TYPE_END(MethodTableAuxiliaryData) CDAC_TYPE_BEGIN(EEClass) 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 57016478c8a312..d0f4476fbbca1a 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 @@ -119,6 +119,8 @@ public interface IRuntimeTypeSystem : IContract ReadOnlySpan GetInstantiation(TypeHandle typeHandle) => throw new NotImplementedException(); + public bool IsClassInited(TypeHandle typeHandle) => throw new NotImplementedException(); + public bool IsInitError(TypeHandle typeHandle) => throw new NotImplementedException(); bool IsGenericTypeDefinition(TypeHandle typeHandle) => throw new NotImplementedException(); bool HasTypeParam(TypeHandle typeHandle) => 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 c14ee7b76e8462..9bb21f02365ce8 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 @@ -90,6 +90,12 @@ internal enum MethodDescChunkFlags : ushort LoaderModuleAttachedToChunk = 0x8000, } + internal enum MethodTableAuxiliaryFlags : uint + { + Initialized = 0x0001, + IsInitError = 0x0100, + } + internal struct MethodDesc { private readonly Data.MethodDesc _desc; @@ -480,6 +486,24 @@ public ReadOnlySpan GetInstantiation(TypeHandle typeHandle) return _target.ProcessedData.GetOrAdd(typeHandle.Address).TypeHandles; } + public bool IsClassInited(TypeHandle typeHandle) + { + if (!typeHandle.IsMethodTable()) + return false; + MethodTable methodTable = _methodTables[typeHandle.Address]; + MethodTableAuxiliaryData auxiliaryData = _target.ProcessedData.GetOrAdd(methodTable.AuxiliaryData); + return (auxiliaryData.Flags & (uint)MethodTableAuxiliaryFlags.Initialized) != 0; + } + + public bool IsInitError(TypeHandle typeHandle) + { + if (!typeHandle.IsMethodTable()) + return false; + MethodTable methodTable = _methodTables[typeHandle.Address]; + MethodTableAuxiliaryData auxiliaryData = _target.ProcessedData.GetOrAdd(methodTable.AuxiliaryData); + return (auxiliaryData.Flags & (uint)MethodTableAuxiliaryFlags.IsInitError) != 0; + } + private sealed class TypeInstantiation : IData { public static TypeInstantiation Create(Target target, TargetPointer address) => new TypeInstantiation(target, address); diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/MethodTableAuxiliaryData.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/MethodTableAuxiliaryData.cs index c181335f410c63..75b3109b4331e1 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/MethodTableAuxiliaryData.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/MethodTableAuxiliaryData.cs @@ -13,9 +13,11 @@ private MethodTableAuxiliaryData(Target target, TargetPointer address) LoaderModule = target.ReadPointer(address + (ulong)type.Fields[nameof(LoaderModule)].Offset); OffsetToNonVirtualSlots = target.Read(address + (ulong)type.Fields[nameof(OffsetToNonVirtualSlots)].Offset); + Flags = target.Read(address + (ulong)type.Fields[nameof(Flags)].Offset); } public TargetPointer LoaderModule { get; init; } public short OffsetToNonVirtualSlots { get; init; } + public uint Flags { get; init; } } diff --git a/src/native/managed/cdac/mscordaccore_universal/Legacy/ISOSDacInterface.cs b/src/native/managed/cdac/mscordaccore_universal/Legacy/ISOSDacInterface.cs index 3d3e2774d22db5..f2663d4363cce4 100644 --- a/src/native/managed/cdac/mscordaccore_universal/Legacy/ISOSDacInterface.cs +++ b/src/native/managed/cdac/mscordaccore_universal/Legacy/ISOSDacInterface.cs @@ -188,6 +188,12 @@ internal struct DacpMethodTableFieldData public ushort wContextStaticsSize; }; +internal enum MethodTableInitializationFlags +{ + MethodTableInitialized = 1, + MethodTableInitializationFailed = 2 +}; + internal struct DacpReJitData { // FIXME[cdac]: the C++ definition enum doesn't have an explicit underlying type or constant values for the flags @@ -647,7 +653,7 @@ internal unsafe partial interface ISOSDacInterface14 [PreserveSig] int GetThreadStaticBaseAddress(ClrDataAddress methodTable, ClrDataAddress thread, ClrDataAddress* nonGCStaticsAddress, ClrDataAddress* GCStaticsAddress); [PreserveSig] - int GetMethodTableInitializationFlags(ClrDataAddress methodTable, /*MethodTableInitializationFlags*/ int* initializationStatus); + int GetMethodTableInitializationFlags(ClrDataAddress methodTable, MethodTableInitializationFlags* initializationStatus); } [GeneratedComInterface] diff --git a/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs b/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs index 50be4e4c28581e..6d3d6b730262fd 100644 --- a/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs @@ -2503,8 +2503,47 @@ int ISOSDacInterface14.GetThreadStaticBaseAddress(ClrDataAddress methodTable, Cl #endif return hr; } - int ISOSDacInterface14.GetMethodTableInitializationFlags(ClrDataAddress methodTable, /*MethodTableInitializationFlags*/ int* initializationStatus) - => _legacyImpl14 is not null ? _legacyImpl14.GetMethodTableInitializationFlags(methodTable, initializationStatus) : HResults.E_NOTIMPL; + + int ISOSDacInterface14.GetMethodTableInitializationFlags(ClrDataAddress methodTable, MethodTableInitializationFlags* initializationStatus) + { + int hr = HResults.S_OK; + if (methodTable == 0) + hr = HResults.E_INVALIDARG; + else if (initializationStatus == null) + hr = HResults.E_POINTER; + + else + { + try + { + Contracts.IRuntimeTypeSystem rtsContract = _target.Contracts.RuntimeTypeSystem; + Contracts.TypeHandle methodTableHandle = rtsContract.GetTypeHandle(methodTable.ToTargetPointer(_target)); + *initializationStatus = (MethodTableInitializationFlags)0; + if (rtsContract.IsClassInited(methodTableHandle)) + *initializationStatus = MethodTableInitializationFlags.MethodTableInitialized; + if (rtsContract.IsInitError(methodTableHandle)) + *initializationStatus |= MethodTableInitializationFlags.MethodTableInitializationFailed; + } + catch (System.Exception ex) + { + hr = ex.HResult; + } + } +#if DEBUG + if (_legacyImpl14 is not null) + { + MethodTableInitializationFlags initializationStatusLocal; + int hrLocal = _legacyImpl14.GetMethodTableInitializationFlags(methodTable, &initializationStatusLocal); + Debug.Assert(hrLocal == hr, $"cDAC: {hr:x}, DAC: {hrLocal:x}"); + if (hr == HResults.S_OK) + { + Debug.Assert(*initializationStatus == initializationStatusLocal); + } + } +#endif + return hr; + } + #endregion ISOSDacInterface14 #region ISOSDacInterface15 diff --git a/src/native/managed/cdac/tests/MockDescriptors/MockDescriptors.cs b/src/native/managed/cdac/tests/MockDescriptors/MockDescriptors.cs index 5160346c8cb497..2b4a634b46fe4c 100644 --- a/src/native/managed/cdac/tests/MockDescriptors/MockDescriptors.cs +++ b/src/native/managed/cdac/tests/MockDescriptors/MockDescriptors.cs @@ -57,6 +57,7 @@ internal record TypeFields [ new(nameof(Data.MethodTableAuxiliaryData.LoaderModule), DataType.pointer), new(nameof(Data.MethodTableAuxiliaryData.OffsetToNonVirtualSlots), DataType.int16), + new(nameof(Data.MethodTableAuxiliaryData.Flags), DataType.uint32), ] };