|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using Internal.Runtime.Augments;
using Internal.TypeSystem;
namespace Internal.Runtime.TypeLoader
{
internal static class RuntimeTypeHandleEETypeExtensions
{
public static unsafe MethodTable* ToEETypePtr(this RuntimeTypeHandle rtth)
{
return (MethodTable*)(*(IntPtr*)&rtth);
}
public static unsafe IntPtr ToIntPtr(this RuntimeTypeHandle rtth)
{
return *(IntPtr*)&rtth;
}
public static unsafe bool IsDynamicType(this RuntimeTypeHandle rtth)
{
return rtth.ToEETypePtr()->IsDynamicType;
}
public static unsafe bool IsDynamicTypeWithCctor(this RuntimeTypeHandle rtth)
{
return rtth.ToEETypePtr()->IsDynamicTypeWithCctor;
}
public static unsafe int GetNumVtableSlots(this RuntimeTypeHandle rtth)
{
return rtth.ToEETypePtr()->NumVtableSlots;
}
public static unsafe TypeManagerHandle GetTypeManager(this RuntimeTypeHandle rtth)
{
return rtth.ToEETypePtr()->TypeManager;
}
public static unsafe IntPtr GetDictionary(this RuntimeTypeHandle rtth)
{
return EETypeCreator.GetDictionary(rtth.ToEETypePtr());
}
public static unsafe void SetDictionary(this RuntimeTypeHandle rtth, int dictionarySlot, IntPtr dictionary)
{
Debug.Assert(rtth.ToEETypePtr()->IsDynamicType && dictionarySlot < rtth.GetNumVtableSlots());
*(IntPtr*)((byte*)rtth.ToEETypePtr() + sizeof(MethodTable) + dictionarySlot * IntPtr.Size) = dictionary;
}
public static unsafe void SetInterface(this RuntimeTypeHandle rtth, int interfaceIndex, RuntimeTypeHandle interfaceType)
{
rtth.ToEETypePtr()->InterfaceMap[interfaceIndex] = interfaceType.ToEETypePtr();
}
public static unsafe void SetGenericDefinition(this RuntimeTypeHandle rtth, RuntimeTypeHandle genericDefinitionHandle)
{
rtth.ToEETypePtr()->GenericDefinition = genericDefinitionHandle.ToEETypePtr();
}
public static unsafe void SetGenericArgument(this RuntimeTypeHandle rtth, int argumentIndex, RuntimeTypeHandle argumentType)
{
MethodTableList argumentList = rtth.ToEETypePtr()->GenericArguments;
argumentList[argumentIndex] = argumentType.ToEETypePtr();
}
public static unsafe void SetRelatedParameterType(this RuntimeTypeHandle rtth, RuntimeTypeHandle relatedTypeHandle)
{
rtth.ToEETypePtr()->RelatedParameterType = relatedTypeHandle.ToEETypePtr();
}
public static unsafe void SetParameterizedTypeShape(this RuntimeTypeHandle rtth, uint value)
{
rtth.ToEETypePtr()->ParameterizedTypeShape = value;
}
public static unsafe void SetBaseType(this RuntimeTypeHandle rtth, RuntimeTypeHandle baseTypeHandle)
{
rtth.ToEETypePtr()->BaseType = baseTypeHandle.ToEETypePtr();
}
public static unsafe void SetComponentSize(this RuntimeTypeHandle rtth, ushort componentSize)
{
Debug.Assert(componentSize > 0);
Debug.Assert(rtth.ToEETypePtr()->IsArray || rtth.ToEETypePtr()->IsString);
rtth.ToEETypePtr()->HasComponentSize = true;
rtth.ToEETypePtr()->ComponentSize = componentSize;
}
}
internal static class MemoryHelpers
{
public static int AlignUp(int val, int alignment)
{
Debug.Assert(val >= 0 && alignment >= 0);
// alignment must be a power of 2 for this implementation to work (need modulo otherwise)
Debug.Assert(0 == (alignment & (alignment - 1)));
int result = (val + (alignment - 1)) & ~(alignment - 1);
Debug.Assert(result >= val); // check for overflow
return result;
}
public static unsafe void* AllocateMemory(int cbBytes)
{
return NativeMemory.Alloc((nuint)cbBytes);
}
public static unsafe void FreeMemory(void* memoryPtrToFree)
{
NativeMemory.Free(memoryPtrToFree);
}
}
internal static unsafe class EETypeCreator
{
private static void CreateEETypeWorker(MethodTable* pTemplateEEType, uint hashCodeOfNewType,
int arity, TypeBuilderState state)
{
bool successful = false;
void* eeTypePlusGCDesc = null;
void* writableData = null;
void* nonGcStaticData = null;
void* genericComposition = null;
void* threadStaticIndex = null;
nint gcStaticData = 0;
try
{
Debug.Assert(pTemplateEEType != null);
// In some situations involving arrays we can find as a template a dynamically generated type.
// In that case, the correct template would be the template used to create the dynamic type in the first
// place.
if (pTemplateEEType->IsDynamicType)
{
pTemplateEEType = pTemplateEEType->DynamicTemplateType;
}
int baseSize = 0;
bool isValueType;
bool hasDispatchMap;
bool hasFinalizer;
bool isNullable;
bool isArray;
bool isGeneric;
bool hasSealedVTable;
uint flags;
ushort runtimeInterfacesLength = 0;
IntPtr typeManager = IntPtr.Zero;
if (state.RuntimeInterfaces != null)
{
runtimeInterfacesLength = checked((ushort)state.RuntimeInterfaces.Length);
}
baseSize = (int)pTemplateEEType->RawBaseSize;
isValueType = pTemplateEEType->IsValueType;
hasFinalizer = pTemplateEEType->IsFinalizable;
hasDispatchMap = pTemplateEEType->HasDispatchMap;
isNullable = pTemplateEEType->IsNullable;
flags = pTemplateEEType->Flags;
isArray = pTemplateEEType->IsArray;
isGeneric = pTemplateEEType->IsGeneric;
hasSealedVTable = pTemplateEEType->HasSealedVTableEntries;
typeManager = pTemplateEEType->PointerToTypeManager;
Debug.Assert(pTemplateEEType->NumInterfaces == runtimeInterfacesLength);
flags |= (uint)EETypeFlags.IsDynamicTypeFlag;
int numFunctionPointerTypeParameters = 0;
if (state.TypeBeingBuilt.IsMdArray)
{
// If we're building an MDArray, the template is object[,] and we
// need to recompute the base size.
baseSize = IntPtr.Size + // sync block
2 * IntPtr.Size + // EETypePtr + Length
state.ArrayRank.Value * sizeof(int) * 2; // 2 ints per rank for bounds
}
else if (state.TypeBeingBuilt.IsFunctionPointer)
{
// Base size encodes number of parameters and calling convention
MethodSignature sig = ((FunctionPointerType)state.TypeBeingBuilt).Signature;
baseSize = (sig.Flags & MethodSignatureFlags.UnmanagedCallingConventionMask) switch
{
0 => sig.Length,
_ => sig.Length | unchecked((int)FunctionPointerFlags.IsUnmanaged),
};
numFunctionPointerTypeParameters = sig.Length;
}
DynamicTypeFlags dynamicTypeFlags = 0;
int allocatedNonGCDataSize = state.NonGcDataSize;
if (state.HasStaticConstructor)
{
allocatedNonGCDataSize += -TypeBuilder.ClassConstructorOffset;
dynamicTypeFlags |= DynamicTypeFlags.HasLazyCctor;
}
if (allocatedNonGCDataSize != 0)
dynamicTypeFlags |= DynamicTypeFlags.HasNonGCStatics;
if (state.GcDataSize != 0)
dynamicTypeFlags |= DynamicTypeFlags.HasGCStatics;
if (state.ThreadDataSize != 0)
dynamicTypeFlags |= DynamicTypeFlags.HasThreadStatics;
ushort numVtableSlots = state.NumVTableSlots;
// Compute the MethodTable size and allocate it
MethodTable* pEEType;
{
// In order to get the size of the MethodTable to allocate we need the following information
// 1) The number of VTable slots (from the TypeBuilderState)
// 2) The number of Interfaces (from the template)
// 3) Whether or not there is a finalizer (from the template)
// 4) Optional fields size
// 5) Whether or not the type has sealed virtuals (from the TypeBuilderState)
int cbEEType = (int)MethodTable.GetSizeofEEType(
numVtableSlots,
runtimeInterfacesLength,
hasDispatchMap,
hasFinalizer,
hasSealedVTable,
isGeneric,
numFunctionPointerTypeParameters,
allocatedNonGCDataSize != 0,
state.GcDataSize != 0,
state.ThreadDataSize != 0);
// Dynamic types have an extra pointer-sized field that contains a pointer to their template type
cbEEType += IntPtr.Size;
int cbGCDesc = GetInstanceGCDescSize(state, pTemplateEEType, isValueType, isArray);
int cbGCDescAligned = MemoryHelpers.AlignUp(cbGCDesc, IntPtr.Size);
// Allocate enough space for the MethodTable + gcDescSize
eeTypePlusGCDesc = MemoryHelpers.AllocateMemory(cbGCDescAligned + cbEEType);
// Get the MethodTable pointer, and the template MethodTable pointer
pEEType = (MethodTable*)((byte*)eeTypePlusGCDesc + cbGCDescAligned);
state.HalfBakedRuntimeTypeHandle = pEEType->ToRuntimeTypeHandle();
// Set basic MethodTable fields
pEEType->Flags = flags;
pEEType->RawBaseSize = (uint)baseSize;
pEEType->NumVtableSlots = numVtableSlots;
pEEType->NumInterfaces = runtimeInterfacesLength;
pEEType->HashCode = hashCodeOfNewType;
pEEType->PointerToTypeManager = typeManager;
// Write the GCDesc
bool isSzArray = isArray ? state.ArrayRank < 1 : false;
int arrayRank = isArray ? state.ArrayRank.Value : 0;
CreateInstanceGCDesc(state, pTemplateEEType, pEEType, baseSize, cbGCDesc, isValueType, isArray, isSzArray, arrayRank);
Debug.Assert(pEEType->ContainsGCPointers == (cbGCDesc != 0));
// Copy VTable entries from template type
IntPtr* pVtable = (IntPtr*)((byte*)pEEType + sizeof(MethodTable));
IntPtr* pTemplateVtable = (IntPtr*)((byte*)pTemplateEEType + sizeof(MethodTable));
for (int i = 0; i < numVtableSlots; i++)
pVtable[i] = pTemplateVtable[i];
// Copy dispatch map from the template type
if (hasDispatchMap)
{
pEEType->DispatchMap = pTemplateEEType->DispatchMap;
}
// Copy Pointer to finalizer method from the template type
if (hasFinalizer)
{
pEEType->FinalizerCode = pTemplateEEType->FinalizerCode;
}
}
// Copy the sealed vtable entries if they exist on the template type
if (hasSealedVTable)
{
uint cbSealedVirtualSlotsTypeOffset = pEEType->GetFieldOffset(EETypeField.ETF_SealedVirtualSlots);
*((void**)((byte*)pEEType + cbSealedVirtualSlotsTypeOffset)) = pTemplateEEType->GetSealedVirtualTable();
}
writableData = MemoryHelpers.AllocateMemory(WritableData.GetSize(IntPtr.Size));
NativeMemory.Clear(writableData, (nuint)WritableData.GetSize(IntPtr.Size));
pEEType->WritableData = writableData;
pEEType->DynamicTemplateType = pTemplateEEType;
pEEType->DynamicTypeFlags = dynamicTypeFlags;
int nonGCStaticDataOffset = 0;
if (!isArray)
{
nonGCStaticDataOffset = state.HasStaticConstructor ? -TypeBuilder.ClassConstructorOffset : 0;
// If we have a class constructor, our NonGcDataSize MUST be non-zero
Debug.Assert(!state.HasStaticConstructor || (allocatedNonGCDataSize != 0));
}
if (isGeneric)
{
if (arity > 1)
{
genericComposition = MemoryHelpers.AllocateMemory(MethodTable.GetGenericCompositionSize(arity));
pEEType->SetGenericComposition((IntPtr)genericComposition);
}
if (allocatedNonGCDataSize > 0)
{
nonGcStaticData = MemoryHelpers.AllocateMemory(allocatedNonGCDataSize);
NativeMemory.Clear(nonGcStaticData, (nuint)allocatedNonGCDataSize);
Debug.Assert(nonGCStaticDataOffset <= allocatedNonGCDataSize);
pEEType->DynamicNonGcStaticsData = (IntPtr)((byte*)nonGcStaticData + nonGCStaticDataOffset);
}
}
if (state.ThreadDataSize != 0)
{
state.ThreadStaticOffset = TypeLoaderEnvironment.Instance.GetNextThreadStaticsOffsetValue(pEEType->TypeManager);
threadStaticIndex = MemoryHelpers.AllocateMemory(IntPtr.Size * 2);
*(IntPtr*)threadStaticIndex = pEEType->PointerToTypeManager;
*(((IntPtr*)threadStaticIndex) + 1) = (IntPtr)state.ThreadStaticOffset;
pEEType->DynamicThreadStaticsIndex = (IntPtr)threadStaticIndex;
}
if (state.GcDataSize != 0)
{
// Statics are allocated on GC heap
object obj = RuntimeAugments.RawNewObject(((MethodTable*)state.GcStaticDesc)->ToRuntimeTypeHandle());
gcStaticData = RuntimeAugments.RhHandleAlloc(obj, GCHandleType.Normal);
pEEType->DynamicGcStaticsData = (IntPtr)gcStaticData;
}
if (state.Dictionary != null)
state.HalfBakedDictionary = state.Dictionary.Allocate();
Debug.Assert(!state.HalfBakedRuntimeTypeHandle.IsNull());
Debug.Assert((state.Dictionary == null && state.HalfBakedDictionary == IntPtr.Zero) || (state.Dictionary != null && state.HalfBakedDictionary != IntPtr.Zero));
successful = true;
}
finally
{
if (!successful)
{
if (gcStaticData != 0)
RuntimeAugments.RhHandleFree(gcStaticData);
MemoryHelpers.FreeMemory((void*)state.HalfBakedDictionary);
MemoryHelpers.FreeMemory(threadStaticIndex);
MemoryHelpers.FreeMemory(nonGcStaticData);
MemoryHelpers.FreeMemory(genericComposition);
MemoryHelpers.FreeMemory(writableData);
MemoryHelpers.FreeMemory(eeTypePlusGCDesc);
}
}
}
private static void CreateInstanceGCDesc(TypeBuilderState state, MethodTable* pTemplateEEType, MethodTable* pEEType, int baseSize, int cbGCDesc, bool isValueType, bool isArray, bool isSzArray, int arrayRank)
{
var gcBitfield = state.InstanceGCLayout;
if (isArray)
{
if (cbGCDesc != 0)
{
pEEType->ContainsGCPointers = true;
if (state.IsArrayOfReferenceTypes || IsAllGCPointers(gcBitfield))
{
IntPtr* gcDescStart = (IntPtr*)((byte*)pEEType - cbGCDesc);
// Series size
gcDescStart[0] = new IntPtr(-baseSize);
// Series offset
gcDescStart[1] = new IntPtr(baseSize - sizeof(IntPtr));
// NumSeries
gcDescStart[2] = new IntPtr(1);
}
else
{
CreateArrayGCDesc(gcBitfield, arrayRank, isSzArray, ((void**)pEEType) - 1);
}
}
else
{
pEEType->ContainsGCPointers = false;
}
}
else
{
Debug.Assert(gcBitfield == null);
if (pTemplateEEType != null)
{
Buffer.MemoryCopy((byte*)pTemplateEEType - cbGCDesc, (byte*)pEEType - cbGCDesc, cbGCDesc, cbGCDesc);
pEEType->ContainsGCPointers = pTemplateEEType->ContainsGCPointers;
}
else
{
pEEType->ContainsGCPointers = false;
}
}
}
private static unsafe int GetInstanceGCDescSize(TypeBuilderState state, MethodTable* pTemplateEEType, bool isValueType, bool isArray)
{
var gcBitfield = state.InstanceGCLayout;
if (isArray)
{
if (state.IsArrayOfReferenceTypes ||
(gcBitfield != null && IsAllGCPointers(gcBitfield)))
{
// For efficiency this is special cased and encoded as one serie
return 3 * sizeof(IntPtr);
}
else
{
int series = 0;
if (gcBitfield != null)
series = CreateArrayGCDesc(gcBitfield, 1, true, null);
return series > 0 ? (series + 2) * IntPtr.Size : 0;
}
}
else
{
Debug.Assert(gcBitfield == null);
if (pTemplateEEType != null)
{
return RuntimeAugments.GetGCDescSize(pTemplateEEType->ToRuntimeTypeHandle());
}
else
{
return 0;
}
}
}
private static bool IsAllGCPointers(bool[] bitfield)
{
int count = bitfield.Length;
Debug.Assert(count > 0);
for (int i = 0; i < count; i++)
{
if (!bitfield[i])
return false;
}
return true;
}
private static unsafe int CreateArrayGCDesc(bool[] bitfield, int rank, bool isSzArray, void* gcdesc)
{
if (bitfield == null)
return 0;
void** baseOffsetPtr = (void**)gcdesc - 1;
#if TARGET_64BIT
int* ptr = (int*)baseOffsetPtr - 1;
#else
short* ptr = (short*)baseOffsetPtr - 1;
#endif
int baseOffset = 2;
if (!isSzArray)
{
baseOffset += 2 * rank / (sizeof(IntPtr) / sizeof(int));
}
int numSeries = 0;
int i = 0;
int first = -1;
int last = 0;
short numPtrs = 0;
while (i < bitfield.Length)
{
if (bitfield[i])
{
if (first == -1)
{
first = i;
baseOffset += first;
}
else if (gcdesc != null)
{
*ptr-- = (short)((i - last) * IntPtr.Size);
*ptr-- = numPtrs;
}
numSeries++;
numPtrs = 0;
while ((i < bitfield.Length) && (bitfield[i]))
{
numPtrs++;
i++;
}
last = i;
}
else
{
i++;
}
}
if (gcdesc != null)
{
if (numSeries > 0)
{
*ptr-- = (short)((first + bitfield.Length - last) * IntPtr.Size);
*ptr-- = numPtrs;
*(void**)gcdesc = (void*)-numSeries;
*baseOffsetPtr = (void*)(baseOffset * IntPtr.Size);
}
}
return numSeries;
}
public static RuntimeTypeHandle CreateFunctionPointerEEType(uint hashCodeOfNewType, RuntimeTypeHandle returnTypeHandle, RuntimeTypeHandle[] parameterHandles, FunctionPointerType functionPointerType)
{
TypeBuilderState state = new TypeBuilderState(functionPointerType);
CreateEETypeWorker(typeof(delegate*<void>).TypeHandle.ToEETypePtr(), hashCodeOfNewType, 0, state);
Debug.Assert(!state.HalfBakedRuntimeTypeHandle.IsNull());
TypeLoaderLogger.WriteLine("Allocated new FUNCTION POINTER type " + functionPointerType.ToString() + " with hashcode value = 0x" + hashCodeOfNewType.LowLevelToString() + " with MethodTable = " + state.HalfBakedRuntimeTypeHandle.ToIntPtr().LowLevelToString());
state.HalfBakedRuntimeTypeHandle.ToEETypePtr()->FunctionPointerReturnType = returnTypeHandle.ToEETypePtr();
Debug.Assert(state.HalfBakedRuntimeTypeHandle.ToEETypePtr()->NumFunctionPointerParameters == parameterHandles.Length);
MethodTableList paramList = state.HalfBakedRuntimeTypeHandle.ToEETypePtr()->FunctionPointerParameters;
for (int i = 0; i < parameterHandles.Length; i++)
paramList[i] = parameterHandles[i].ToEETypePtr();
return state.HalfBakedRuntimeTypeHandle;
}
public static RuntimeTypeHandle CreatePointerEEType(uint hashCodeOfNewType, RuntimeTypeHandle pointeeTypeHandle, TypeDesc pointerType)
{
TypeBuilderState state = new TypeBuilderState(pointerType);
CreateEETypeWorker(typeof(void*).TypeHandle.ToEETypePtr(), hashCodeOfNewType, 0, state);
Debug.Assert(!state.HalfBakedRuntimeTypeHandle.IsNull());
TypeLoaderLogger.WriteLine("Allocated new POINTER type " + pointerType.ToString() + " with hashcode value = 0x" + hashCodeOfNewType.LowLevelToString() + " with MethodTable = " + state.HalfBakedRuntimeTypeHandle.ToIntPtr().LowLevelToString());
state.HalfBakedRuntimeTypeHandle.ToEETypePtr()->RelatedParameterType = pointeeTypeHandle.ToEETypePtr();
return state.HalfBakedRuntimeTypeHandle;
}
public static RuntimeTypeHandle CreateByRefEEType(uint hashCodeOfNewType, RuntimeTypeHandle pointeeTypeHandle, TypeDesc byRefType)
{
TypeBuilderState state = new TypeBuilderState(byRefType);
// ByRef and pointer types look similar enough that we can use void* as a template.
// Ideally this should be typeof(void&) but C# doesn't support that syntax. We adjust for this below.
CreateEETypeWorker(typeof(void*).TypeHandle.ToEETypePtr(), hashCodeOfNewType, 0, state);
Debug.Assert(!state.HalfBakedRuntimeTypeHandle.IsNull());
TypeLoaderLogger.WriteLine("Allocated new BYREF type " + byRefType.ToString() + " with hashcode value = 0x" + hashCodeOfNewType.LowLevelToString() + " with MethodTable = " + state.HalfBakedRuntimeTypeHandle.ToIntPtr().LowLevelToString());
state.HalfBakedRuntimeTypeHandle.ToEETypePtr()->RelatedParameterType = pointeeTypeHandle.ToEETypePtr();
// We used a pointer as a template. We need to make this a byref.
Debug.Assert(state.HalfBakedRuntimeTypeHandle.ToEETypePtr()->ElementType == EETypeElementType.Pointer);
state.HalfBakedRuntimeTypeHandle.ToEETypePtr()->ElementType = EETypeElementType.ByRef;
Debug.Assert(state.HalfBakedRuntimeTypeHandle.ToEETypePtr()->ParameterizedTypeShape == ParameterizedTypeShapeConstants.Pointer);
state.HalfBakedRuntimeTypeHandle.ToEETypePtr()->ParameterizedTypeShape = ParameterizedTypeShapeConstants.ByRef;
return state.HalfBakedRuntimeTypeHandle;
}
public static RuntimeTypeHandle CreateEEType(TypeDesc type, TypeBuilderState state)
{
Debug.Assert(type != null && state != null);
MethodTable* pTemplateEEType;
if (type is PointerType || type is ByRefType || type is FunctionPointerType)
{
Debug.Assert(0 == state.NonGcDataSize);
Debug.Assert(!state.HasStaticConstructor);
Debug.Assert(0 == state.GcDataSize);
Debug.Assert(0 == state.ThreadStaticOffset);
Debug.Assert(IntPtr.Zero == state.GcStaticDesc);
Debug.Assert(IntPtr.Zero == state.ThreadStaticDesc);
RuntimeTypeHandle templateTypeHandle;
if (type is FunctionPointerType)
{
// There's still differences to paper over, but `delegate*<void>` is close enough.
templateTypeHandle = typeof(delegate*<void>).TypeHandle;
}
else
{
// Pointers and ByRefs only differ by the ParameterizedTypeShape and ElementType value.
templateTypeHandle = typeof(void*).TypeHandle;
}
pTemplateEEType = templateTypeHandle.ToEETypePtr();
}
else
{
Debug.Assert(state.TemplateType != null && !state.TemplateType.RuntimeTypeHandle.IsNull());
RuntimeTypeHandle templateTypeHandle = state.TemplateType.RuntimeTypeHandle;
pTemplateEEType = templateTypeHandle.ToEETypePtr();
}
DefType typeAsDefType = type as DefType;
// Use a checked typecast to 'ushort' for the arity to ensure its value never exceeds 65535 and cause integer
// overflows later when computing size of memory blocks to allocate for the type and its GenericInstanceDescriptor structures
int arity = checked((ushort)((typeAsDefType != null && typeAsDefType.HasInstantiation ? typeAsDefType.Instantiation.Length : 0)));
CreateEETypeWorker(pTemplateEEType, (uint)type.GetHashCode(), arity, state);
return state.HalfBakedRuntimeTypeHandle;
}
public static int GetDictionaryOffsetInEEtype(MethodTable* pEEType)
{
// Dictionary slot is the first vtable slot
MethodTable* pBaseType = pEEType->BaseType;
int dictionarySlot = (pBaseType == null ? 0 : pBaseType->NumVtableSlots);
return sizeof(MethodTable) + dictionarySlot * IntPtr.Size;
}
public static IntPtr GetDictionaryAtOffset(MethodTable* pEEType, int offset)
{
return *(IntPtr*)((byte*)pEEType + offset);
}
public static IntPtr GetDictionary(MethodTable* pEEType)
{
return GetDictionaryAtOffset(pEEType, GetDictionaryOffsetInEEtype(pEEType));
}
public static int GetDictionarySlotInVTable(TypeDesc type)
{
if (!type.CanShareNormalGenericCode())
return -1;
// Dictionary slot is the first slot in the vtable after the base type's vtable entries
return type.BaseType != null ? type.BaseType.GetOrCreateTypeBuilderState().NumVTableSlots : 0;
}
}
}
|