Saw it in BasicMinimalApi profiles. The ultimate fix is to make `RuntimeType` a frozen object, but this will do for now.
Also made a bit of progress in eradicating `EETypePtr` while I'm touching this.
/// The purpose of the segment is controlled by the class library. The runtime doesn't
/// use this memory for any purpose.
/// </summary>
- internal IntPtr WritableData
+ internal void* WritableData
{
get
{
uint offset = GetFieldOffset(EETypeField.ETF_WritableData);
if (!IsDynamicType)
- return GetField<RelativePointer>(offset).Value;
+ return (void*)GetField<RelativePointer>(offset).Value;
else
- return GetField<Pointer>(offset).Value;
+ return (void*)GetField<Pointer>(offset).Value;
}
#if TYPE_LOADER_IMPLEMENTATION
set
Debug.Assert(IsDynamicType && SupportsWritableData);
uint cbOffset = GetFieldOffset(EETypeField.ETF_WritableData);
- *(IntPtr*)((byte*)Unsafe.AsPointer(ref this) + cbOffset) = value;
+ *(void**)((byte*)Unsafe.AsPointer(ref this) + cbOffset) = value;
}
#endif
}
}
}
+ // This method is always called with a known constant and there's a lot of benefit in inlining it.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint GetFieldOffset(EETypeField eField)
{
// First part of MethodTable consists of the fixed portion followed by the vtable.
uint cbOffset = (uint)(sizeof(MethodTable) + (IntPtr.Size * _usNumVtableSlots));
- // Then we have the interface map.
- if (eField == EETypeField.ETF_InterfaceMap)
- {
- Debug.Assert(NumInterfaces > 0);
- return cbOffset;
- }
+ // Followed by list of implemented interfaces
cbOffset += (uint)(sizeof(MethodTable*) * NumInterfaces);
uint relativeOrFullPointerOffset = (IsDynamicType || !SupportsRelativePointers ? (uint)IntPtr.Size : 4);
// GetFieldOffset() APIs.
enum EETypeField
{
- ETF_InterfaceMap,
ETF_TypeManagerIndirection,
ETF_WritableData,
ETF_Finalizer,
// First part of MethodTable consists of the fixed portion followed by the vtable.
uint32_t cbOffset = offsetof(MethodTable, m_VTable) + (sizeof(UIntTarget) * m_usNumVtableSlots);
- // Then we have the interface map.
- if (eField == ETF_InterfaceMap)
- {
- ASSERT(GetNumInterfaces() > 0);
- return cbOffset;
- }
+ // Followed by interface list
cbOffset += sizeof(MethodTable*) * GetNumInterfaces();
const uint32_t relativeOrFullPointerOffset =
return returnValue;
}
- private static Type GetRuntimeType(IntPtr pEEType)
+ private static unsafe Type GetRuntimeType(MethodTable* pMT)
{
- return Type.GetTypeFromEETypePtr(new EETypePtr(pEEType));
+ return Type.GetTypeFromMethodTable(pMT);
}
}
}
lockTaken = false;
}
- private static void MonitorEnterStatic(IntPtr pEEType, ref bool lockTaken)
+ private static unsafe void MonitorEnterStatic(MethodTable* pMT, ref bool lockTaken)
{
// Inlined Monitor.Enter with a few tweaks
- object obj = GetStaticLockObject(pEEType);
+ object obj = GetStaticLockObject(pMT);
int resultOrIndex = ObjectHeader.Acquire(obj);
if (resultOrIndex < 0)
{
Monitor.TryAcquireContended(lck, obj, Timeout.Infinite);
lockTaken = true;
}
- private static void MonitorExitStatic(IntPtr pEEType, ref bool lockTaken)
+ private static unsafe void MonitorExitStatic(MethodTable* pMT, ref bool lockTaken)
{
// Inlined Monitor.Exit with a few tweaks
if (!lockTaken)
return;
- object obj = GetStaticLockObject(pEEType);
+ object obj = GetStaticLockObject(pMT);
ObjectHeader.Release(obj);
lockTaken = false;
}
- private static Type GetStaticLockObject(IntPtr pEEType)
+ private static unsafe Type GetStaticLockObject(MethodTable* pMT)
{
- return Type.GetTypeFromEETypePtr(new EETypePtr(pEEType));
+ return Type.GetTypeFromMethodTable(pMT);
}
}
}
internal static IntPtr IDynamicCastableGetInterfaceImplementation(IDynamicInterfaceCastable instance, MethodTable* interfaceType, ushort slot)
{
RuntimeTypeHandle handle = instance.GetInterfaceImplementation(new RuntimeTypeHandle(new EETypePtr(interfaceType)));
- EETypePtr implType = handle.ToEETypePtr();
- if (implType.IsNull)
+ MethodTable* implType = handle.ToMethodTable();
+ if (implType == null)
{
ThrowInvalidCastException(instance, interfaceType);
}
- if (!implType.IsInterface)
+ if (!implType->IsInterface)
{
ThrowInvalidOperationException(implType);
}
- IntPtr result = RuntimeImports.RhResolveDispatchOnType(implType, new EETypePtr(interfaceType), slot);
+ IntPtr result = RuntimeImports.RhResolveDispatchOnType(new EETypePtr(implType), new EETypePtr(interfaceType), slot);
if (result == IntPtr.Zero)
{
IDynamicCastableGetInterfaceImplementationFailure(instance, interfaceType, implType);
private static void ThrowInvalidCastException(object instance, MethodTable* interfaceType)
{
- throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, instance.GetType(), Type.GetTypeFromEETypePtr(new EETypePtr(interfaceType))));
+ throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, instance.GetType(), Type.GetTypeFromMethodTable(interfaceType)));
}
- private static void ThrowInvalidOperationException(EETypePtr resolvedImplType)
+ private static void ThrowInvalidOperationException(MethodTable* resolvedImplType)
{
- throw new InvalidOperationException(SR.Format(SR.IDynamicInterfaceCastable_NotInterface, Type.GetTypeFromEETypePtr(resolvedImplType)));
+ throw new InvalidOperationException(SR.Format(SR.IDynamicInterfaceCastable_NotInterface, Type.GetTypeFromMethodTable(resolvedImplType)));
}
- private static void IDynamicCastableGetInterfaceImplementationFailure(object instance, MethodTable* interfaceType, EETypePtr resolvedImplType)
+ private static void IDynamicCastableGetInterfaceImplementationFailure(object instance, MethodTable* interfaceType, MethodTable* resolvedImplType)
{
- if (resolvedImplType.DispatchMap == IntPtr.Zero)
- throw new InvalidOperationException(SR.Format(SR.IDynamicInterfaceCastable_MissingImplementationAttribute, Type.GetTypeFromEETypePtr(resolvedImplType), nameof(DynamicInterfaceCastableImplementationAttribute)));
+ if (resolvedImplType->DispatchMap == null)
+ throw new InvalidOperationException(SR.Format(SR.IDynamicInterfaceCastable_MissingImplementationAttribute, Type.GetTypeFromMethodTable(resolvedImplType), nameof(DynamicInterfaceCastableImplementationAttribute)));
bool implementsInterface = false;
- var interfaces = resolvedImplType.Interfaces;
- for (int i = 0; i < interfaces.Count; i++)
+ var interfaces = resolvedImplType->InterfaceMap;
+ for (int i = 0; i < resolvedImplType->NumInterfaces; i++)
{
- if (interfaces[i] == new EETypePtr(interfaceType))
+ if (interfaces[i] == interfaceType)
{
implementsInterface = true;
break;
}
if (!implementsInterface)
- throw new InvalidOperationException(SR.Format(SR.IDynamicInterfaceCastable_DoesNotImplementRequested, Type.GetTypeFromEETypePtr(resolvedImplType), Type.GetTypeFromEETypePtr(new EETypePtr(interfaceType))));
+ throw new InvalidOperationException(SR.Format(SR.IDynamicInterfaceCastable_DoesNotImplementRequested, Type.GetTypeFromMethodTable(resolvedImplType), Type.GetTypeFromMethodTable(interfaceType)));
throw new EntryPointNotFoundException();
}
[Intrinsic]
public Type GetType()
{
- return Type.GetTypeFromEETypePtr(this.GetEETypePtr());
+ return Type.GetTypeFromMethodTable(m_pEEType);
}
[Intrinsic]
return ref ret;
}
- private object? GetCoercedDefaultValue(int index, in ArgumentInfo argumentInfo)
+ private unsafe object? GetCoercedDefaultValue(int index, in ArgumentInfo argumentInfo)
{
object? defaultValue = Method.GetParametersNoCopy()[index].DefaultValue;
if (defaultValue == DBNull.Value)
EETypePtr nullableType = argumentInfo.Type.NullableType;
if (nullableType.IsEnum)
{
- defaultValue = Enum.ToObject(Type.GetTypeFromEETypePtr(nullableType), defaultValue);
+ defaultValue = Enum.ToObject(Type.GetTypeFromMethodTable(nullableType.ToPointer()), defaultValue);
}
}
{
if ((transform & Transform.Pointer) != 0)
{
- Type type = Type.GetTypeFromEETypePtr(argumentInfo.Type);
+ Type type = Type.GetTypeFromMethodTable(argumentInfo.Type.ToPointer());
Debug.Assert(type.IsPointer);
obj = Pointer.Box((void*)Unsafe.As<byte, IntPtr>(ref obj.GetRawData()), type);
}
object obj;
if ((_returnTransform & Transform.Pointer) != 0)
{
- Type type = Type.GetTypeFromEETypePtr(_returnType);
+ Type type = Type.GetTypeFromMethodTable(_returnType.ToPointer());
Debug.Assert(type.IsPointer);
obj = Pointer.Box((void*)Unsafe.As<byte, IntPtr>(ref byref), type);
}
if (mt->IsNullable)
{
mt = mt->NullableType;
- return GetUninitializedObject(Type.GetTypeFromEETypePtr(new EETypePtr(mt)));
+ return GetUninitializedObject(Type.GetTypeFromMethodTable(mt));
}
// Triggering the .cctor here is slightly different than desktop/CoreCLR, which
using Internal.Reflection.Augments;
using Internal.Reflection.Core.NonPortable;
+using Internal.Runtime;
using Internal.Runtime.Augments;
using Internal.Runtime.CompilerServices;
public bool IsInterface => (GetAttributeFlagsImpl() & TypeAttributes.ClassSemanticsMask) == TypeAttributes.Interface;
[Intrinsic]
- public static Type? GetTypeFromHandle(RuntimeTypeHandle handle) => handle.IsNull ? null : GetTypeFromEETypePtr(handle.ToEETypePtr());
+ public static unsafe Type? GetTypeFromHandle(RuntimeTypeHandle handle) => handle.IsNull ? null : GetTypeFromMethodTable(handle.ToMethodTable());
- internal static Type GetTypeFromEETypePtr(EETypePtr eeType)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static unsafe Type GetTypeFromMethodTable(MethodTable* pMT)
{
- // If we support the writable data section on EETypes, the runtime type associated with the MethodTable
+ // If we support the writable data section on MethodTables, the runtime type associated with the MethodTable
// is cached there. If writable data is not supported, we need to do a lookup in the runtime type
// unifier's hash table.
- if (Internal.Runtime.MethodTable.SupportsWritableData)
+ if (MethodTable.SupportsWritableData)
{
- ref GCHandle handle = ref eeType.GetWritableData<GCHandle>();
+ ref GCHandle handle = ref Unsafe.AsRef<GCHandle>(pMT->WritableData);
if (handle.IsAllocated)
{
return Unsafe.As<Type>(handle.Target);
}
else
{
- return GetTypeFromEETypePtrSlow(eeType, ref handle);
+ return GetTypeFromMethodTableSlow(pMT, ref handle);
}
}
else
{
- return RuntimeTypeUnifier.GetRuntimeTypeForEEType(eeType);
+ return RuntimeTypeUnifier.GetRuntimeTypeForEEType(new EETypePtr(pMT));
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
- private static Type GetTypeFromEETypePtrSlow(EETypePtr eeType, ref GCHandle handle)
+ private static unsafe Type GetTypeFromMethodTableSlow(MethodTable* pMT, ref GCHandle handle)
{
// Note: this is bypassing the "fast" unifier cache (based on a simple IntPtr
// identity of MethodTable pointers). There is another unifier behind that cache
// that ensures this code is race-free.
- Type result = RuntimeTypeUnifier.GetRuntimeTypeBypassCache(eeType);
+ Type result = RuntimeTypeUnifier.GetRuntimeTypeBypassCache(new EETypePtr(pMT));
GCHandle tempHandle = GCHandle.Alloc(result);
// We don't want to leak a handle if there's a race
{
writableDataPtr = MemoryHelpers.AllocateMemory(WritableData.GetSize(IntPtr.Size));
MemoryHelpers.Memset(writableDataPtr, WritableData.GetSize(IntPtr.Size), 0);
- pEEType->WritableData = writableDataPtr;
+ pEEType->WritableData = (void*)writableDataPtr;
}
pEEType->DynamicTemplateType = pTemplateEEType;
internal enum EETypeField
{
- ETF_InterfaceMap,
ETF_TypeManagerIndirection,
ETF_WritableData,
ETF_DispatchMap,
{
ArgumentNullException.ThrowIfNull(nullableType);
-#if NATIVEAOT
- // This is necessary to handle types without reflection metadata
- if (nullableType.TryGetEEType(out EETypePtr nullableEEType))
- {
- if (nullableEEType.IsGeneric)
- {
- if (nullableEEType.IsNullable)
- {
- return Type.GetTypeFromEETypePtr(nullableEEType.NullableType);
- }
- }
- return null;
- }
-#endif
-
if (nullableType.IsGenericType && !nullableType.IsGenericTypeDefinition)
{
// Instantiated generic type only