Skip to content

Commit c0a9bac

Browse files
rcj1Copilotmax-charlamb
authored
Signature parsing and GetFieldDescData cDAC API (#119417)
* signature parsing and GetFieldDescData cDAC API --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Max Charlamb <44248479+max-charlamb@users.noreply.github.com>
1 parent d34eb61 commit c0a9bac

File tree

20 files changed

+894
-21
lines changed

20 files changed

+894
-21
lines changed

docs/design/datacontracts/RuntimeTypeSystem.md

Lines changed: 186 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,14 @@ partial interface IRuntimeTypeSystem : IContract
8686
public virtual CorElementType GetSignatureCorElementType(TypeHandle typeHandle);
8787

8888
// 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.
89-
public virtual bool IsArray(TypeHandle typeHandle, out uint rank);
90-
public virtual TypeHandle GetTypeParam(TypeHandle typeHandle);
91-
public virtual bool IsGenericVariable(TypeHandle typeHandle, out TargetPointer module, out uint token);
92-
public virtual bool IsFunctionPointer(TypeHandle typeHandle, out ReadOnlySpan<TypeHandle> retAndArgTypes, out byte callConv);
93-
public virtual TargetPointer GetLoaderModule(TypeHandle typeHandle);
89+
bool IsArray(TypeHandle typeHandle, out uint rank);
90+
TypeHandle GetTypeParam(TypeHandle typeHandle);
91+
TypeHandle GetConstructedType(TypeHandle typeHandle, CorElementType corElementType, int rank, ImmutableArray<TypeHandle> typeArguments);
92+
TypeHandle GetPrimitiveType(CorElementType typeCode);
93+
bool IsGenericVariable(TypeHandle typeHandle, out TargetPointer module, out uint token);
94+
bool IsFunctionPointer(TypeHandle typeHandle, out ReadOnlySpan<TypeHandle> retAndArgTypes, out byte callConv);
95+
bool IsPointer(TypeHandle typeHandle);
96+
TargetPointer GetLoaderModule(TypeHandle typeHandle);
9497

9598
#endregion TypeHandle inspection APIs
9699
}
@@ -189,6 +192,16 @@ partial interface IRuntimeTypeSystem : IContract
189192
}
190193
```
191194

195+
### FieldDesc
196+
```csharp
197+
TargetPointer GetMTOfEnclosingClass(TargetPointer fieldDescPointer);
198+
uint GetFieldDescMemberDef(TargetPointer fieldDescPointer);
199+
bool IsFieldDescThreadStatic(TargetPointer fieldDescPointer);
200+
bool IsFieldDescStatic(TargetPointer fieldDescPointer);
201+
uint GetFieldDescType(TargetPointer fieldDescPointer);
202+
uint GetFieldDescOffset(TargetPointer fieldDescPointer, FieldDefinition fieldDef);
203+
```
204+
192205
## Version 1
193206

194207
### TypeHandle
@@ -403,6 +416,11 @@ Contracts used:
403416
| --- |
404417
| `Thread` |
405418

419+
### Contract Constants:
420+
| Name | Type | Purpose | Value |
421+
| --- | --- | --- | --- |
422+
| `TYPE_MASK_OFFSET` | int | The number of bits the type is shifted left in the field desc flags2 | `27` |
423+
406424

407425
```csharp
408426
private readonly Dictionary<TargetPointer, MethodTable_1> _methodTables;
@@ -717,6 +735,89 @@ Contracts used:
717735
throw new ArgumentException(nameof(typeHandle));
718736
}
719737

738+
// helper functions
739+
740+
private bool GenericInstantiationMatch(TypeHandle genericType, TypeHandle potentialMatch, ImmutableArray<TypeHandle> typeArguments)
741+
{
742+
ReadOnlySpan<TypeHandle> instantiation = GetInstantiation(potentialMatch);
743+
if (instantiation.Length != typeArguments.Length)
744+
return false;
745+
746+
if (GetTypeDefToken(genericType) != GetTypeDefToken(potentialMatch))
747+
return false;
748+
749+
if (GetModule(genericType) != GetModule(potentialMatch))
750+
return false;
751+
752+
for (int i = 0; i < instantiation.Length; i++)
753+
{
754+
if (!(instantiation[i].Address == typeArguments[i].Address))
755+
return false;
756+
}
757+
return true;
758+
}
759+
760+
private bool ArrayPtrMatch(TypeHandle elementType, CorElementType corElementType, int rank, TypeHandle potentialMatch)
761+
{
762+
IsArray(potentialMatch, out uint typeHandleRank);
763+
return GetSignatureCorElementType(potentialMatch) == corElementType &&
764+
GetTypeParam(potentialMatch).Address == elementType.Address &&
765+
(corElementType == CorElementType.SzArray || corElementType == CorElementType.Byref ||
766+
corElementType == CorElementType.Ptr || (rank == typeHandleRank));
767+
768+
}
769+
770+
private bool IsLoaded(TypeHandle typeHandle)
771+
{
772+
if (typeHandle.Address == TargetPointer.Null)
773+
return false;
774+
if (typeHandle.IsTypeDesc())
775+
{
776+
uint typeAndFlags = _target.Read<uint>(typeHandle.TypeDescAddress() + /* TypeDesc::TypeAndFlags offset */);
777+
return (typeAndFlags & (uint)TypeDescFlags.IsNotFullyLoaded) == 0;
778+
}
779+
780+
MethodTable methodTable = _methodTables[typeHandle.Address];
781+
uint flags = _target.Read<uint>(methodTable.AuxiliaryData + /* AuxiliaryData::Flags offset */);
782+
return (flags & (uint)MethodTableAuxiliaryFlags.IsNotFullyLoaded) == 0;
783+
}
784+
785+
TypeHandle GetConstructedType(TypeHandle typeHandle, CorElementType corElementType, int rank, ImmutableArray<TypeHandle> typeArguments)
786+
{
787+
if (typeHandle.Address == TargetPointer.Null)
788+
return new TypeHandle(TargetPointer.Null);
789+
ILoader loaderContract = _target.Contracts.Loader;
790+
TargetPointer loaderModule = GetLoaderModule(typeHandle);
791+
ModuleHandle moduleHandle = loaderContract.GetModuleHandleFromModulePtr(loaderModule);
792+
TypeHandle potentialMatch = new TypeHandle(TargetPointer.Null);
793+
foreach (TargetPointer ptr in loaderContract.GetAvailableTypeParams(moduleHandle))
794+
{
795+
potentialMatch = GetTypeHandle(ptr);
796+
if (corElementType == CorElementType.GenericInst)
797+
{
798+
if (GenericInstantiationMatch(typeHandle, potentialMatch, typeArguments) && IsLoaded(potentialMatch))
799+
{
800+
_ = _typeHandles.TryAdd(new TypeKey(typeHandle, corElementType, rank, typeArguments), potentialMatch);
801+
return potentialMatch;
802+
}
803+
}
804+
else if (ArrayPtrMatch(typeHandle, corElementType, rank, potentialMatch) && IsLoaded(potentialMatch))
805+
{
806+
_ = _typeHandles.TryAdd(new TypeKey(typeHandle, corElementType, rank, typeArguments), potentialMatch);
807+
return potentialMatch;
808+
}
809+
}
810+
return new TypeHandle(TargetPointer.Null);
811+
}
812+
813+
public TypeHandle GetPrimitiveType(CorElementType typeCode)
814+
{
815+
TargetPointer coreLib = _target.ReadGlobalPointer("CoreLib");
816+
TargetPointer classes = _target.ReadPointer(coreLib + /* CoreLibBinder::Classes offset */);
817+
TargetPointer typeHandlePtr = _target.ReadPointer(classes + (ulong)typeCode * (ulong)_target.PointerSize);
818+
return GetTypeHandle(typeHandlePtr);
819+
}
820+
720821
public bool IsGenericVariable(TypeHandle typeHandle, out TargetPointer module, out uint token)
721822
{
722823
module = TargetPointer.Null;
@@ -748,7 +849,7 @@ Contracts used:
748849
return false;
749850

750851
int TypeAndFlags = // Read TypeAndFlags field from TypeDesc contract using address typeHandle.TypeDescAddress()
751-
CorElementType elemType = (CorElementType)(typeDesc.TypeAndFlags & 0xFF);
852+
CorElementType elemType = (CorElementType)(TypeAndFlags & 0xFF);
752853

753854
if (elemType != CorElementType.FnPtr)
754855
return false;
@@ -765,6 +866,16 @@ Contracts used:
765866
return true;
766867
}
767868

869+
public bool IsPointer(TypeHandle typeHandle)
870+
{
871+
if (!typeHandle.IsTypeDesc())
872+
return false;
873+
874+
int TypeAndFlags = // Read TypeAndFlags field from TypeDesc contract using address typeHandle.TypeDescAddress()
875+
CorElementType elemType = (CorElementType)(TypeAndFlags & 0xFF);
876+
return elemType == CorElementType.Ptr;
877+
}
878+
768879
public TargetPointer GetLoaderModule(TypeHandle typeHandle)
769880
{
770881
if (typeHandle.IsTypeDesc())
@@ -915,6 +1026,12 @@ And the following enumeration definitions
9151026
{
9161027
Initialized = 0x0001,
9171028
IsInitError = 0x0100,
1029+
IsNotFullyLoaded = 0x0040,
1030+
}
1031+
1032+
internal enum TypeDescFlags : uint
1033+
{
1034+
IsNotFullyLoaded = 0x00001000,
9181035
}
9191036

9201037
```
@@ -1532,3 +1649,66 @@ Getting a MethodDesc for a certain slot in a MethodTable
15321649
return GetMethodDescForEntrypoint(pCode);
15331650
}
15341651
```
1652+
1653+
### FieldDesc
1654+
1655+
The version 1 FieldDesc APIs depend on the following data descriptors:
1656+
| Data Descriptor Name | Field | Meaning |
1657+
| --- | --- | --- |
1658+
| `FieldDesc` | `MTOfEnclosingClass` | Pointer to method table of enclosing class |
1659+
| `FieldDesc` | `DWord1` | The FD's flags and token |
1660+
| `FieldDesc` | `DWord2` | The FD's kind and offset |
1661+
1662+
```csharp
1663+
internal enum FieldDescFlags1 : uint
1664+
{
1665+
TokenMask = 0xffffff,
1666+
IsStatic = 0x1000000,
1667+
IsThreadStatic = 0x2000000,
1668+
}
1669+
1670+
internal enum FieldDescFlags2 : uint
1671+
{
1672+
TypeMask = 0xf8000000,
1673+
OffsetMask = 0x07ffffff,
1674+
}
1675+
1676+
TargetPointer GetMTOfEnclosingClass(TargetPointer fieldDescPointer)
1677+
{
1678+
return target.ReadPointer(fieldDescPointer + /* FieldDesc::MTOfEnclosingClass offset */);
1679+
}
1680+
1681+
uint GetFieldDescMemberDef(TargetPointer fieldDescPointer)
1682+
{
1683+
uint DWord1 = target.Read<uint>(fieldDescPointer + /* FieldDesc::DWord1 offset */);
1684+
return EcmaMetadataUtils.CreateFieldDef(DWord1 & (uint)FieldDescFlags1.TokenMask);
1685+
}
1686+
1687+
bool IsFieldDescThreadStatic(TargetPointer fieldDescPointer)
1688+
{
1689+
uint DWord1 = target.Read<uint>(fieldDescPointer + /* FieldDesc::DWord1 offset */);
1690+
return (DWord1 & (uint)FieldDescFlags1.IsThreadStatic) != 0;
1691+
}
1692+
1693+
bool IsFieldDescStatic(TargetPointer fieldDescPointer)
1694+
{
1695+
uint DWord1 = target.Read<uint>(fieldDescPointer + /* FieldDesc::DWord1 offset */);
1696+
return (DWord1 & (uint)FieldDescFlags1.IsStatic) != 0;
1697+
}
1698+
1699+
uint GetFieldDescType(TargetPointer fieldDescPointer)
1700+
{
1701+
uint DWord2 = target.Read<uint>(fieldDescPointer + /* FieldDesc::DWord2 offset */);
1702+
return (DWord2 & (uint)FieldDescFlags2.TypeMask) >> 27;
1703+
}
1704+
1705+
uint GetFieldDescOffset(TargetPointer fieldDescPointer)
1706+
{
1707+
uint DWord2 = target.Read<uint>(fieldDescPointer + /* FieldDesc::DWord2 offset */);
1708+
if (DWord2 == _target.ReadGlobal<uint>("FieldOffsetBigRVA"))
1709+
{
1710+
return (uint)fieldDef.GetRelativeVirtualAddress();
1711+
}
1712+
return DWord2 & (uint)FieldDescFlags2.OffsetMask;
1713+
}
1714+
```
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# Contract SignatureDecoder
2+
3+
This contract encapsulates signature decoding in the cDAC.
4+
5+
## APIs of contract
6+
7+
```csharp
8+
TypeHandle DecodeFieldSignature(BlobHandle blobHandle, ModuleHandle moduleHandle, TypeHandle ctx);
9+
```
10+
11+
## Version 1
12+
13+
In version 1 of the SignatureDecoder contract we take advantage of the System.Reflection.Metadata signature decoding. We implement a SignatureTypeProvider that inherits from System.Reflection.Metadata ISignatureTypeProvider.
14+
15+
Data descriptors used:
16+
| Data Descriptor Name | Field | Meaning |
17+
| --- | --- | --- |
18+
19+
20+
Global variables used:
21+
| Global Name | Type | Purpose |
22+
| --- | --- | --- |
23+
24+
25+
Contracts used:
26+
| Contract Name |
27+
| --- |
28+
| RuntimeTypeSystem |
29+
| Loader |
30+
| EcmaMetadata |
31+
32+
### SignatureTypeProvider
33+
The cDAC implements the ISignatureTypeProvider<TType,TGenericContext> with TType=TypeHandle. TGenericContext can either be a MethodDescHandle or TypeHandle; MethodDescHandle context is used to look up generic method parameters, and TypeHandle context is used to look up generic type parameters.
34+
35+
A cDAC SignatureTypeProvider is instantiated over a Module which is used to lookup types.
36+
37+
The following ISignatureTypeProvider APIs are trivially implemented using RuntimeTypeSystem.GetPrimitiveType and RuntimeTypeSystem.GetConstructedType:
38+
39+
* GetArrayType - GetConstructedType
40+
* GetByReferenceType - GetConstructedType
41+
* GetFunctionPointerType - Implemented as primitive IntPtr type
42+
* GetGenericInstantiation - GetConstructedType
43+
* GetModifiedType - Returns unmodified type
44+
* GetPinnedType - Returns unpinned type
45+
* GetPointerType - GetConstructedType
46+
* GetPrimitiveType - GetConstructedType
47+
* GetSZArrayType - GetConstructedType
48+
49+
GetGenericMethodParameter is only supported when TGenericContext=MethodDescHandle and looks up the method parameters from the context using RuntimeTypeSystem.GetGenericMethodInstantiation.
50+
51+
GetGenericTypeParameter is only supported when TGenericContext=TypeHandle and looks up the type parameters from the context using RuntimeTypeSystem.GetInstantiation.
52+
53+
GetTypeFromDefinition uses the SignatureTypeProvider's ModuleHandle to lookup the given Token in the Module's TypeDefToMethodTableMap. If a value is not found return null.
54+
55+
GetTypeFromReference uses the SignatureTypeProvider's ModuleHandle to lookup the given Token in the Module's TypeRefToMethodTableMap. If a value is not found return null.The implementation when the type exists in a different module is incomplete.
56+
57+
GetTypeFromSpecification is not currently implemented.
58+
59+
60+
### APIs
61+
```csharp
62+
TypeHandle ISignatureDecoder.DecodeFieldSignature(BlobHandle blobHandle, ModuleHandle moduleHandle, TypeHandle ctx)
63+
{
64+
SignatureTypeProvider<TypeHandle> provider = new(_target, moduleHandle);
65+
MetadataReader mdReader = _target.Contracts.EcmaMetadata.GetMetadata(moduleHandle)!;
66+
BlobReader blobReader = mdReader.GetBlobReader(blobHandle);
67+
SignatureDecoder<TypeHandle, TypeHandle> decoder = new(provider, mdReader, ctx);
68+
return decoder.DecodeFieldSignature(ref blobReader);
69+
}
70+
```

src/coreclr/vm/binder.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ struct HardCodedMetaSig
2828
#define DEFINE_METASIG_T(body) extern body
2929
#define METASIG_BODY(varname, types) HardCodedMetaSig gsig_ ## varname;
3030
#include "metasig.h"
31-
31+
#include "cdacdata.h"
3232
//
3333
// Use the Binder objects to avoid doing unnecessary name lookup
3434
// (esp. in the prejit case)
@@ -294,6 +294,7 @@ class CoreLibBinder
294294
USHORT m_cFields;
295295

296296
static CrstStatic s_SigConvertCrst;
297+
friend struct ::cdac_data<CoreLibBinder>;
297298

298299
#ifdef _DEBUG
299300

@@ -313,6 +314,12 @@ class CoreLibBinder
313314
#endif
314315
};
315316

317+
template<>
318+
struct cdac_data<CoreLibBinder>
319+
{
320+
static constexpr size_t Classes = offsetof(CoreLibBinder, m_pClasses);
321+
};
322+
316323
//
317324
// Global bound modules:
318325
//

src/coreclr/vm/datadescriptor/datadescriptor.inc

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,13 @@ CDAC_TYPE_INDETERMINATE(TypeDesc)
333333
CDAC_TYPE_FIELD(TypeDesc, /*uint32*/, TypeAndFlags, cdac_data<TypeDesc>::TypeAndFlags)
334334
CDAC_TYPE_END(TypeDesc)
335335

336+
CDAC_TYPE_BEGIN(FieldDesc)
337+
CDAC_TYPE_SIZE(sizeof(FieldDesc))
338+
CDAC_TYPE_FIELD(FieldDesc, /*uint32*/, DWord1, cdac_data<FieldDesc>::DWord1)
339+
CDAC_TYPE_FIELD(FieldDesc, /*uint32*/, DWord2, cdac_data<FieldDesc>::DWord2)
340+
CDAC_TYPE_FIELD(FieldDesc, /*pointer*/, MTOfEnclosingClass, cdac_data<FieldDesc>::MTOfEnclosingClass)
341+
CDAC_TYPE_END(FieldDesc)
342+
336343
CDAC_TYPE_BEGIN(ParamTypeDesc)
337344
CDAC_TYPE_INDETERMINATE(ParamTypeDesc)
338345
CDAC_TYPE_FIELD(ParamTypeDesc, /*pointer*/, TypeArg, cdac_data<ParamTypeDesc>::TypeArg)
@@ -912,6 +919,11 @@ CDAC_TYPE_FIELD(InstMethodHashTable, /*pointer*/, VolatileEntryValue, cdac_data<
912919
CDAC_TYPE_FIELD(InstMethodHashTable, /*pointer*/, VolatileEntryNextEntry, cdac_data<InstMethodHashTable>::VolatileEntryNextEntry)
913920
CDAC_TYPE_END(InstMethodHashTable)
914921

922+
CDAC_TYPE_BEGIN(CoreLibBinder)
923+
CDAC_TYPE_INDETERMINATE(CoreLibBinder)
924+
CDAC_TYPE_FIELD(CoreLibBinder, /*pointer*/, Classes, cdac_data<CoreLibBinder>::Classes)
925+
CDAC_TYPE_END(CoreLibBinder)
926+
915927
// this is an SHash type
916928
CDAC_TYPE_BEGIN(DynamicILBlobTable)
917929
CDAC_TYPE_SIZE(cdac_data<DynamicILBlobTable>::EntrySize)
@@ -999,6 +1011,7 @@ CDAC_GLOBAL(StaticsPointerMask, uintptr_t, DynamicStaticsInfo::STATICSPOINTERMAS
9991011
CDAC_GLOBAL(PtrArrayOffsetToDataArray, uintptr_t, offsetof(PtrArray, m_Array))
10001012
CDAC_GLOBAL(NumberOfTlsOffsetsNotUsedInNoncollectibleArray, uint8, NUMBER_OF_TLSOFFSETS_NOT_USED_IN_NONCOLLECTIBLE_ARRAY)
10011013
CDAC_GLOBAL(MaxClrNotificationArgs, uint32, MAX_CLR_NOTIFICATION_ARGS)
1014+
CDAC_GLOBAL(FieldOffsetBigRVA, uint32, FIELD_OFFSET_BIG_RVA)
10021015
CDAC_GLOBAL_POINTER(ClrNotificationArguments, &::g_clrNotificationArguments)
10031016
CDAC_GLOBAL_POINTER(ArrayBoundsZero, cdac_data<ArrayBase>::ArrayBoundsZero)
10041017
CDAC_GLOBAL_POINTER(ExceptionMethodTable, &::g_pExceptionClass)
@@ -1012,6 +1025,7 @@ CDAC_GLOBAL_POINTER(MiniMetaDataBuffMaxSize, &::g_MiniMetaDataBuffMaxSize)
10121025
CDAC_GLOBAL_POINTER(DacNotificationFlags, &::g_dacNotificationFlags)
10131026
CDAC_GLOBAL_POINTER(OffsetOfCurrentThreadInfo, &::g_offsetOfCurrentThreadInfo)
10141027
CDAC_GLOBAL_POINTER(ThinlockThreadIdDispenser, &::g_pThinLockThreadIdDispenser)
1028+
CDAC_GLOBAL_POINTER(CoreLib, &::g_CoreLib)
10151029
#ifdef TARGET_WINDOWS
10161030
CDAC_GLOBAL_POINTER(TlsIndexBase, &::_tls_index)
10171031
#endif // TARGET_WINDOWS
@@ -1053,6 +1067,7 @@ CDAC_GLOBAL_CONTRACT(ReJIT, 1)
10531067
CDAC_GLOBAL_CONTRACT(RuntimeInfo, 1)
10541068
CDAC_GLOBAL_CONTRACT(RuntimeTypeSystem, 1)
10551069
CDAC_GLOBAL_CONTRACT(SHash, 1)
1070+
CDAC_GLOBAL_CONTRACT(SignatureDecoder, 1)
10561071
CDAC_GLOBAL_CONTRACT(StackWalk, 1)
10571072
CDAC_GLOBAL_CONTRACT(StressLog, 2)
10581073
CDAC_GLOBAL_CONTRACT(Thread, 1)

0 commit comments

Comments
 (0)