Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
100 changes: 100 additions & 0 deletions docs/design/datacontracts/RuntimeTypeSystem.md
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,17 @@ internal partial struct RuntimeTypeSystem_1
}
```

### 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);
TargetPointer GetFieldDescNextField(TargetPointer fieldDescPointer);
```

Internally the contract has a `MethodTable_1` struct that depends on the `MethodTable` data descriptor

```csharp
Expand Down Expand Up @@ -1469,4 +1480,93 @@ Getting a MethodDesc for a certain slot in a MethodTable

return GetMethodDescForEntrypoint(pCode);
}

TypeHandle IRuntimeTypeSystem.GetMethodTable(TypeHandle typeHandle)
{
if (typeHandle.IsTypeDesc())
{
Data.TypeDesc typeDesc = target.ProcessedData.GetOrAdd<TypeDesc>(typeHandle.TypeDescAddress());
CorElementType elemType = (CorElementType)(typeDesc.TypeAndFlags & 0xFF);
switch (elemType)
{
case CorElementType.Ptr:
case CorElementType.FnPtr:
TargetPointer coreLib = target.ReadGlobalPointer(Constants.Globals.CoreLib);
TargetPointer classes = target.ReadPointer(coreLib + /* CoreLibData::Classes offset */);
TargetPointer typeHandlePtr = target.ReadPointer(classes + (ulong)CorElementType.U * (ulong)target.PointerSize);
return GetTypeHandle(typeHandlePtr);
case CorElementType.ValueType:
return GetTypeHandle(target.ReadPointer(typeHandle.TypeDescAddress() + /* ParamTypeDesc::TypeArg offset */));
default:
return new TypeHandle(TargetPointer.Null);
}
}
return typeHandle;
}
```

### 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;
}

TargetPointer GetFieldDescNextField(TargetPointer fieldDescPointer)
=> fieldDescPointer + _target.GetTypeInfo(DataType.FieldDesc).Size!.Value;
```
217 changes: 217 additions & 0 deletions docs/design/datacontracts/SignatureDecoder.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
# 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 |
| --- | --- | --- |
| CoreLibBinder | Classes | MTs for primitive types |
| ILCodeVersionNode | TypeDefToMethodTableMap | Mapping table |
| ILCodeVersionNode | TypeRefToMethodTableMap | Mapping table |

Global variables used:
| Global Name | Type | Purpose |
| --- | --- | --- |
| CoreLib | TargetPointer | pointer to the `CoreLibBinder` |
| PredefinedArrayTypes | TargetPointer | pointer to the cache of primitive-type array MTs |
| ObjectMethodTable | TargetPointer | pointer to the MT of `object` |
| StringMethodTable | TargetPointer | pointer to the MT of `string` |

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

### SignatureTypeProvider
```csharp
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;

public class SignatureTypeProvider<T> : ISignatureTypeProvider<TypeHandle, T>
{
private readonly Target _target;
private readonly DataContractReader.Contracts.ModuleHandle _moduleHandle;

public SignatureTypeProvider(Target target, DataContractReader.Contracts.ModuleHandle moduleHandle)
{
_target = target;
_moduleHandle = moduleHandle;
}
public TypeHandle GetArrayType(TypeHandle elementType, ArrayShape shape)
=> IterateTypeParams(elementType, CorElementType.Array, shape.Rank, default);

public TypeHandle GetByReferenceType(TypeHandle elementType)
=> IterateTypeParams(elementType, CorElementType.Byref, 0, default);

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

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

if (rtsContract.GetModule(genericType) != rtsContract.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)
{
IRuntimeTypeSystem rtsContract = _target.Contracts.RuntimeTypeSystem;
rtsContract.IsArray(potentialMatch, out uint typeHandleRank);
return rtsContract.GetSignatureCorElementType(potentialMatch) == corElementType &&
rtsContract.GetTypeParam(potentialMatch).Address == elementType.Address &&
(corElementType == CorElementType.SzArray || corElementType == CorElementType.Byref ||
corElementType == CorElementType.Ptr || (rank == typeHandleRank));

}

private TypeHandle IterateTypeParams(TypeHandle typeHandle, CorElementType corElementType, int rank, ImmutableArray<TypeHandle> typeArguments)
{
ILoader loaderContract = _target.Contracts.Loader;
IRuntimeTypeSystem rtsContract = _target.Contracts.RuntimeTypeSystem;
TargetPointer loaderModule = rtsContract.GetLoaderModule(typeHandle);
DataContractReader.Contracts.ModuleHandle moduleHandle = loaderContract.GetModuleHandleFromModulePtr(loaderModule);
foreach (TargetPointer ptr in loaderContract.GetAvailableTypeParams(moduleHandle))
{
TypeHandle potentialMatch = rtsContract.GetTypeHandle(ptr);
if (corElementType == CorElementType.GenericInst)
{
if (GenericInstantiationMatch(typeHandle, potentialMatch, typeArguments))
{
return potentialMatch;
}
}
else if (ArrayPtrMatch(typeHandle, corElementType, rank, potentialMatch))
{
return potentialMatch;
}
}
return new TypeHandle(TargetPointer.Null);
}
public TypeHandle GetFunctionPointerType(MethodSignature<TypeHandle> signature)
=> GetPrimitiveType(PrimitiveTypeCode.IntPtr);

public TypeHandle GetGenericInstantiation(TypeHandle genericType, ImmutableArray<TypeHandle> typeArguments)
=> IterateTypeParams(genericType, CorElementType.GenericInst, 0, typeArguments);

public TypeHandle GetGenericMethodParameter(T context, int index)
{
if (typeof(T) == typeof(MethodDescHandle))
{
MethodDescHandle methodContext = (MethodDescHandle)(object)context!;
return _target.Contracts.RuntimeTypeSystem.GetGenericMethodInstantiation(methodContext)[index];
}
throw new NotSupportedException();
}
public TypeHandle GetGenericTypeParameter(T context, int index)
{
TypeHandle typeContext;
if (typeof(T) == typeof(TypeHandle))
{
typeContext = (TypeHandle)(object)context!;
return _target.Contracts.RuntimeTypeSystem.GetInstantiation(typeContext)[index];
}
throw new NotImplementedException();
}
public TypeHandle GetModifiedType(TypeHandle modifier, TypeHandle unmodifiedType, bool isRequired)
=> unmodifiedType;

public TypeHandle GetPinnedType(TypeHandle elementType)
=> elementType;

public TypeHandle GetPointerType(TypeHandle elementType)
=> IterateTypeParams(elementType, CorElementType.Ptr, 0, default);

public TypeHandle GetPrimitiveType(PrimitiveTypeCode typeCode)
{
TargetPointer coreLib = _target.ReadGlobalPointer("CoreLib");
CoreLibBinder coreLibData = _target.ProcessedData.GetOrAdd<CoreLibBinder>(coreLib);
TargetPointer typeHandlePtr = _target.ReadPointer(coreLibData.Classes + (ulong)typeCode * (ulong)_target.PointerSize);
return _target.Contracts.RuntimeTypeSystem.GetTypeHandle(typeHandlePtr);
}

private TypeHandle GetPrimitiveArrayType(CorElementType elementType)
{
TargetPointer arrayPtr = _target.ReadGlobalPointer("PredefinedArrayTypes");
TargetPointer typeHandlePtr = _target.ReadPointer(arrayPtr + (ulong)elementType * (ulong)_target.PointerSize);
return _target.Contracts.RuntimeTypeSystem.GetTypeHandle(typeHandlePtr);
}

public TypeHandle GetSZArrayType(TypeHandle elementType)
{
IRuntimeTypeSystem rtsContract = _target.Contracts.RuntimeTypeSystem;
CorElementType corElementType = rtsContract.GetSignatureCorElementType(elementType);
TypeHandle typeHandle = default;
if (corElementType <= CorElementType.R8)
{
typeHandle = GetPrimitiveArrayType(corElementType);
}
else if (elementType.Address == _target.ReadPointer(_target.ReadGlobalPointer("ObjectMethodTable")))
{
typeHandle = GetPrimitiveArrayType(CorElementType.Object);
}
else if (elementType.Address == _target.ReadPointer(_target.ReadGlobalPointer("StringMethodTable")))
{
typeHandle = GetPrimitiveArrayType(CorElementType.String);
}
if (typeHandle.Address == TargetPointer.Null)
{
return IterateTypeParams(elementType, CorElementType.SzArray, 1, default);
}
return typeHandle;
}

public TypeHandle GetTypeFromDefinition(MetadataReader reader, TypeDefinitionHandle handle, byte rawTypeKind)
{
Module module = _target.ProcessedData.GetOrAdd<Module>(_moduleHandle.Address);
int token = MetadataTokens.GetToken((EntityHandle)handle);
TargetPointer typeHandlePtr = _target.Contracts.Loader.GetModuleLookupMapElement(module.TypeDefToMethodTableMap, (uint)token, out _);
return _target.Contracts.RuntimeTypeSystem.GetTypeHandle(typeHandlePtr);
}

public TypeHandle GetTypeFromReference(MetadataReader reader, TypeReferenceHandle handle, byte rawTypeKind)
{
Module module = _target.ProcessedData.GetOrAdd<Module>(_moduleHandle.Address);
int token = MetadataTokens.GetToken((EntityHandle)handle);
TargetPointer typeHandlePtr = _target.Contracts.Loader.GetModuleLookupMapElement(module.TypeRefToMethodTableMap, (uint)token, out _);
return _target.Contracts.RuntimeTypeSystem.GetTypeHandle(typeHandlePtr);
}

public TypeHandle GetTypeFromSpecification(MetadataReader reader, T context, TypeSpecificationHandle handle, byte rawTypeKind)
=> throw new NotImplementedException();
}

```

### 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 @@ -309,10 +309,17 @@ class CoreLibBinder
};

static const OffsetAndSizeCheck OffsetsAndSizes[];
friend struct ::cdac_data<CoreLibBinder>;

#endif
};

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

//
// Global bound modules:
//
Expand Down
3 changes: 2 additions & 1 deletion src/coreclr/vm/datadescriptor/contracts.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,6 @@
"RuntimeTypeSystem": 1,
"StackWalk": 1,
"StressLog": 2,
"Thread": 1
"Thread": 1,
"SignatureDecoder": 1
}
Loading
Loading