Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
192 changes: 186 additions & 6 deletions docs/design/datacontracts/RuntimeTypeSystem.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,14 @@ partial interface IRuntimeTypeSystem : IContract
public virtual CorElementType GetSignatureCorElementType(TypeHandle typeHandle);

// 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.
public virtual bool IsArray(TypeHandle typeHandle, out uint rank);
public virtual TypeHandle GetTypeParam(TypeHandle typeHandle);
public virtual bool IsGenericVariable(TypeHandle typeHandle, out TargetPointer module, out uint token);
public virtual bool IsFunctionPointer(TypeHandle typeHandle, out ReadOnlySpan<TypeHandle> retAndArgTypes, out byte callConv);
public virtual TargetPointer GetLoaderModule(TypeHandle typeHandle);
bool IsArray(TypeHandle typeHandle, out uint rank);
TypeHandle GetTypeParam(TypeHandle typeHandle);
TypeHandle GetConstructedType(TypeHandle typeHandle, CorElementType corElementType, int rank, ImmutableArray<TypeHandle> typeArguments);
TypeHandle GetPrimitiveType(CorElementType typeCode);
bool IsGenericVariable(TypeHandle typeHandle, out TargetPointer module, out uint token);
bool IsFunctionPointer(TypeHandle typeHandle, out ReadOnlySpan<TypeHandle> retAndArgTypes, out byte callConv);
bool IsPointer(TypeHandle typeHandle);
TargetPointer GetLoaderModule(TypeHandle typeHandle);

#endregion TypeHandle inspection APIs
}
Expand Down Expand Up @@ -189,6 +192,16 @@ partial interface IRuntimeTypeSystem : IContract
}
```

### FieldDesc
```csharp
TargetPointer GetMTOfEnclosingClass(TargetPointer fieldDescPointer);
uint GetFieldDescMemberDef(TargetPointer fieldDescPointer);
bool IsFieldDescThreadStatic(TargetPointer fieldDescPointer);
bool IsFieldDescStatic(TargetPointer fieldDescPointer);
uint GetFieldDescType(TargetPointer fieldDescPointer);
uint GetFieldDescOffset(TargetPointer fieldDescPointer, FieldDefinition fieldDef);
```

## Version 1

### TypeHandle
Expand Down Expand Up @@ -403,6 +416,11 @@ Contracts used:
| --- |
| `Thread` |

### Contract Constants:
| Name | Type | Purpose | Value |
| --- | --- | --- | --- |
| `TYPE_MASK_OFFSET` | int | The number of bits the type is shifted left in the field desc flags2 | `27` |


```csharp
private readonly Dictionary<TargetPointer, MethodTable_1> _methodTables;
Expand Down Expand Up @@ -717,6 +735,89 @@ Contracts used:
throw new ArgumentException(nameof(typeHandle));
}

// helper functions

private bool GenericInstantiationMatch(TypeHandle genericType, TypeHandle potentialMatch, ImmutableArray<TypeHandle> typeArguments)
{
ReadOnlySpan<TypeHandle> instantiation = GetInstantiation(potentialMatch);
if (instantiation.Length != typeArguments.Length)
return false;

if (GetTypeDefToken(genericType) != GetTypeDefToken(potentialMatch))
return false;

if (GetModule(genericType) != GetModule(potentialMatch))
return false;

for (int i = 0; i < instantiation.Length; i++)
{
if (!(instantiation[i].Address == typeArguments[i].Address))
return false;
}
return true;
}

private bool ArrayPtrMatch(TypeHandle elementType, CorElementType corElementType, int rank, TypeHandle potentialMatch)
{
IsArray(potentialMatch, out uint typeHandleRank);
return GetSignatureCorElementType(potentialMatch) == corElementType &&
GetTypeParam(potentialMatch).Address == elementType.Address &&
(corElementType == CorElementType.SzArray || corElementType == CorElementType.Byref ||
corElementType == CorElementType.Ptr || (rank == typeHandleRank));

}

private bool IsLoaded(TypeHandle typeHandle)
{
if (typeHandle.Address == TargetPointer.Null)
return false;
if (typeHandle.IsTypeDesc())
{
uint typeAndFlags = _target.Read<uint>(typeHandle.TypeDescAddress() + /* TypeDesc::TypeAndFlags offset */);
return (typeAndFlags & (uint)TypeDescFlags.IsNotFullyLoaded) == 0;
}

MethodTable methodTable = _methodTables[typeHandle.Address];
uint flags = _target.Read<uint>(methodTable.AuxiliaryData + /* AuxiliaryData::Flags offset */);
return (flags & (uint)MethodTableAuxiliaryFlags.IsNotFullyLoaded) == 0;
}

TypeHandle GetConstructedType(TypeHandle typeHandle, CorElementType corElementType, int rank, ImmutableArray<TypeHandle> typeArguments)
{
if (typeHandle.Address == TargetPointer.Null)
return new TypeHandle(TargetPointer.Null);
ILoader loaderContract = _target.Contracts.Loader;
TargetPointer loaderModule = GetLoaderModule(typeHandle);
ModuleHandle moduleHandle = loaderContract.GetModuleHandleFromModulePtr(loaderModule);
TypeHandle potentialMatch = new TypeHandle(TargetPointer.Null);
foreach (TargetPointer ptr in loaderContract.GetAvailableTypeParams(moduleHandle))
{
potentialMatch = GetTypeHandle(ptr);
if (corElementType == CorElementType.GenericInst)
{
if (GenericInstantiationMatch(typeHandle, potentialMatch, typeArguments) && IsLoaded(potentialMatch))
{
_ = _typeHandles.TryAdd(new TypeKey(typeHandle, corElementType, rank, typeArguments), potentialMatch);
return potentialMatch;
}
}
else if (ArrayPtrMatch(typeHandle, corElementType, rank, potentialMatch) && IsLoaded(potentialMatch))
{
_ = _typeHandles.TryAdd(new TypeKey(typeHandle, corElementType, rank, typeArguments), potentialMatch);
return potentialMatch;
}
}
return new TypeHandle(TargetPointer.Null);
}

public TypeHandle GetPrimitiveType(CorElementType typeCode)
{
TargetPointer coreLib = _target.ReadGlobalPointer("CoreLib");
TargetPointer classes = _target.ReadPointer(coreLib + /* CoreLibBinder::Classes offset */);
TargetPointer typeHandlePtr = _target.ReadPointer(classes + (ulong)typeCode * (ulong)_target.PointerSize);
return GetTypeHandle(typeHandlePtr);
}

public bool IsGenericVariable(TypeHandle typeHandle, out TargetPointer module, out uint token)
{
module = TargetPointer.Null;
Expand Down Expand Up @@ -748,7 +849,7 @@ Contracts used:
return false;

int TypeAndFlags = // Read TypeAndFlags field from TypeDesc contract using address typeHandle.TypeDescAddress()
CorElementType elemType = (CorElementType)(typeDesc.TypeAndFlags & 0xFF);
CorElementType elemType = (CorElementType)(TypeAndFlags & 0xFF);

if (elemType != CorElementType.FnPtr)
return false;
Expand All @@ -765,6 +866,16 @@ Contracts used:
return true;
}

public bool IsPointer(TypeHandle typeHandle)
{
if (!typeHandle.IsTypeDesc())
return false;

int TypeAndFlags = // Read TypeAndFlags field from TypeDesc contract using address typeHandle.TypeDescAddress()
CorElementType elemType = (CorElementType)(TypeAndFlags & 0xFF);
return elemType == CorElementType.Ptr;
}

public TargetPointer GetLoaderModule(TypeHandle typeHandle)
{
if (typeHandle.IsTypeDesc())
Expand Down Expand Up @@ -915,6 +1026,12 @@ And the following enumeration definitions
{
Initialized = 0x0001,
IsInitError = 0x0100,
IsNotFullyLoaded = 0x0040,
}

internal enum TypeDescFlags : uint
{
IsNotFullyLoaded = 0x00001000,
}

```
Expand Down Expand Up @@ -1532,3 +1649,66 @@ Getting a MethodDesc for a certain slot in a MethodTable
return GetMethodDescForEntrypoint(pCode);
}
```

### FieldDesc

The version 1 FieldDesc APIs depend on the following data descriptors:
| Data Descriptor Name | Field | Meaning |
| --- | --- | --- |
| `FieldDesc` | `MTOfEnclosingClass` | Pointer to method table of enclosing class |
| `FieldDesc` | `DWord1` | The FD's flags and token |
| `FieldDesc` | `DWord2` | The FD's kind and offset |

```csharp
internal enum FieldDescFlags1 : uint
{
TokenMask = 0xffffff,
IsStatic = 0x1000000,
IsThreadStatic = 0x2000000,
}

internal enum FieldDescFlags2 : uint
{
TypeMask = 0xf8000000,
OffsetMask = 0x07ffffff,
}

TargetPointer GetMTOfEnclosingClass(TargetPointer fieldDescPointer)
{
return target.ReadPointer(fieldDescPointer + /* FieldDesc::MTOfEnclosingClass offset */);
}

uint GetFieldDescMemberDef(TargetPointer fieldDescPointer)
{
uint DWord1 = target.Read<uint>(fieldDescPointer + /* FieldDesc::DWord1 offset */);
return EcmaMetadataUtils.CreateFieldDef(DWord1 & (uint)FieldDescFlags1.TokenMask);
}

bool IsFieldDescThreadStatic(TargetPointer fieldDescPointer)
{
uint DWord1 = target.Read<uint>(fieldDescPointer + /* FieldDesc::DWord1 offset */);
return (DWord1 & (uint)FieldDescFlags1.IsThreadStatic) != 0;
}

bool IsFieldDescStatic(TargetPointer fieldDescPointer)
{
uint DWord1 = target.Read<uint>(fieldDescPointer + /* FieldDesc::DWord1 offset */);
return (DWord1 & (uint)FieldDescFlags1.IsStatic) != 0;
}

uint GetFieldDescType(TargetPointer fieldDescPointer)
{
uint DWord2 = target.Read<uint>(fieldDescPointer + /* FieldDesc::DWord2 offset */);
return (DWord2 & (uint)FieldDescFlags2.TypeMask) >> 27;
}

uint GetFieldDescOffset(TargetPointer fieldDescPointer)
{
uint DWord2 = target.Read<uint>(fieldDescPointer + /* FieldDesc::DWord2 offset */);
if (DWord2 == _target.ReadGlobal<uint>("FieldOffsetBigRVA"))
{
return (uint)fieldDef.GetRelativeVirtualAddress();
}
return DWord2 & (uint)FieldDescFlags2.OffsetMask;
}
```
70 changes: 70 additions & 0 deletions docs/design/datacontracts/SignatureDecoder.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Contract SignatureDecoder

This contract encapsulates signature decoding in the cDAC.

## APIs of contract

```csharp
TypeHandle DecodeFieldSignature(BlobHandle blobHandle, ModuleHandle moduleHandle, TypeHandle ctx);
```

## Version 1

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.

Data descriptors used:
| Data Descriptor Name | Field | Meaning |
| --- | --- | --- |


Global variables used:
| Global Name | Type | Purpose |
| --- | --- | --- |


Contracts used:
| Contract Name |
| --- |
| RuntimeTypeSystem |
| Loader |
| EcmaMetadata |

### SignatureTypeProvider
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.

A cDAC SignatureTypeProvider is instantiated over a Module which is used to lookup types.

The following ISignatureTypeProvider APIs are trivially implemented using RuntimeTypeSystem.GetPrimitiveType and RuntimeTypeSystem.GetConstructedType:

* GetArrayType - GetConstructedType
* GetByReferenceType - GetConstructedType
* GetFunctionPointerType - Implemented as primitive IntPtr type
* GetGenericInstantiation - GetConstructedType
* GetModifiedType - Returns unmodified type
* GetPinnedType - Returns unpinned type
* GetPointerType - GetConstructedType
* GetPrimitiveType - GetConstructedType
* GetSZArrayType - GetConstructedType

GetGenericMethodParameter is only supported when TGenericContext=MethodDescHandle and looks up the method parameters from the context using RuntimeTypeSystem.GetGenericMethodInstantiation.

GetGenericTypeParameter is only supported when TGenericContext=TypeHandle and looks up the type parameters from the context using RuntimeTypeSystem.GetInstantiation.

GetTypeFromDefinition uses the SignatureTypeProvider's ModuleHandle to lookup the given Token in the Module's TypeDefToMethodTableMap. If a value is not found return null.

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.

GetTypeFromSpecification is not currently implemented.


### APIs
```csharp
TypeHandle ISignatureDecoder.DecodeFieldSignature(BlobHandle blobHandle, ModuleHandle moduleHandle, TypeHandle ctx)
{
SignatureTypeProvider<TypeHandle> provider = new(_target, moduleHandle);
MetadataReader mdReader = _target.Contracts.EcmaMetadata.GetMetadata(moduleHandle)!;
BlobReader blobReader = mdReader.GetBlobReader(blobHandle);
SignatureDecoder<TypeHandle, TypeHandle> decoder = new(provider, mdReader, ctx);
return decoder.DecodeFieldSignature(ref blobReader);
}
```
9 changes: 8 additions & 1 deletion src/coreclr/vm/binder.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ struct HardCodedMetaSig
#define DEFINE_METASIG_T(body) extern body
#define METASIG_BODY(varname, types) HardCodedMetaSig gsig_ ## varname;
#include "metasig.h"

#include "cdacdata.h"
//
// Use the Binder objects to avoid doing unnecessary name lookup
// (esp. in the prejit case)
Expand Down Expand Up @@ -294,6 +294,7 @@ class CoreLibBinder
USHORT m_cFields;

static CrstStatic s_SigConvertCrst;
friend struct ::cdac_data<CoreLibBinder>;

#ifdef _DEBUG

Expand All @@ -313,6 +314,12 @@ class CoreLibBinder
#endif
};

template<>
struct cdac_data<CoreLibBinder>
{
static constexpr size_t Classes = offsetof(CoreLibBinder, m_pClasses);
};

//
// Global bound modules:
//
Expand Down
15 changes: 15 additions & 0 deletions src/coreclr/vm/datadescriptor/datadescriptor.inc
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,13 @@ CDAC_TYPE_INDETERMINATE(TypeDesc)
CDAC_TYPE_FIELD(TypeDesc, /*uint32*/, TypeAndFlags, cdac_data<TypeDesc>::TypeAndFlags)
CDAC_TYPE_END(TypeDesc)

CDAC_TYPE_BEGIN(FieldDesc)
CDAC_TYPE_SIZE(sizeof(FieldDesc))
CDAC_TYPE_FIELD(FieldDesc, /*uint32*/, DWord1, cdac_data<FieldDesc>::DWord1)
CDAC_TYPE_FIELD(FieldDesc, /*uint32*/, DWord2, cdac_data<FieldDesc>::DWord2)
CDAC_TYPE_FIELD(FieldDesc, /*pointer*/, MTOfEnclosingClass, cdac_data<FieldDesc>::MTOfEnclosingClass)
CDAC_TYPE_END(FieldDesc)

CDAC_TYPE_BEGIN(ParamTypeDesc)
CDAC_TYPE_INDETERMINATE(ParamTypeDesc)
CDAC_TYPE_FIELD(ParamTypeDesc, /*pointer*/, TypeArg, cdac_data<ParamTypeDesc>::TypeArg)
Expand Down Expand Up @@ -912,6 +919,11 @@ CDAC_TYPE_FIELD(InstMethodHashTable, /*pointer*/, VolatileEntryValue, cdac_data<
CDAC_TYPE_FIELD(InstMethodHashTable, /*pointer*/, VolatileEntryNextEntry, cdac_data<InstMethodHashTable>::VolatileEntryNextEntry)
CDAC_TYPE_END(InstMethodHashTable)

CDAC_TYPE_BEGIN(CoreLibBinder)
CDAC_TYPE_INDETERMINATE(CoreLibBinder)
CDAC_TYPE_FIELD(CoreLibBinder, /*pointer*/, Classes, cdac_data<CoreLibBinder>::Classes)
CDAC_TYPE_END(CoreLibBinder)

// this is an SHash type
CDAC_TYPE_BEGIN(DynamicILBlobTable)
CDAC_TYPE_SIZE(cdac_data<DynamicILBlobTable>::EntrySize)
Expand Down Expand Up @@ -999,6 +1011,7 @@ CDAC_GLOBAL(StaticsPointerMask, uintptr_t, DynamicStaticsInfo::STATICSPOINTERMAS
CDAC_GLOBAL(PtrArrayOffsetToDataArray, uintptr_t, offsetof(PtrArray, m_Array))
CDAC_GLOBAL(NumberOfTlsOffsetsNotUsedInNoncollectibleArray, uint8, NUMBER_OF_TLSOFFSETS_NOT_USED_IN_NONCOLLECTIBLE_ARRAY)
CDAC_GLOBAL(MaxClrNotificationArgs, uint32, MAX_CLR_NOTIFICATION_ARGS)
CDAC_GLOBAL(FieldOffsetBigRVA, uint32, FIELD_OFFSET_BIG_RVA)
CDAC_GLOBAL_POINTER(ClrNotificationArguments, &::g_clrNotificationArguments)
CDAC_GLOBAL_POINTER(ArrayBoundsZero, cdac_data<ArrayBase>::ArrayBoundsZero)
CDAC_GLOBAL_POINTER(ExceptionMethodTable, &::g_pExceptionClass)
Expand All @@ -1012,6 +1025,7 @@ CDAC_GLOBAL_POINTER(MiniMetaDataBuffMaxSize, &::g_MiniMetaDataBuffMaxSize)
CDAC_GLOBAL_POINTER(DacNotificationFlags, &::g_dacNotificationFlags)
CDAC_GLOBAL_POINTER(OffsetOfCurrentThreadInfo, &::g_offsetOfCurrentThreadInfo)
CDAC_GLOBAL_POINTER(ThinlockThreadIdDispenser, &::g_pThinLockThreadIdDispenser)
CDAC_GLOBAL_POINTER(CoreLib, &::g_CoreLib)
#ifdef TARGET_WINDOWS
CDAC_GLOBAL_POINTER(TlsIndexBase, &::_tls_index)
#endif // TARGET_WINDOWS
Expand Down Expand Up @@ -1053,6 +1067,7 @@ CDAC_GLOBAL_CONTRACT(ReJIT, 1)
CDAC_GLOBAL_CONTRACT(RuntimeInfo, 1)
CDAC_GLOBAL_CONTRACT(RuntimeTypeSystem, 1)
CDAC_GLOBAL_CONTRACT(SHash, 1)
CDAC_GLOBAL_CONTRACT(SignatureDecoder, 1)
CDAC_GLOBAL_CONTRACT(StackWalk, 1)
CDAC_GLOBAL_CONTRACT(StressLog, 2)
CDAC_GLOBAL_CONTRACT(Thread, 1)
Expand Down
Loading
Loading