Skip to content

Commit

Permalink
Remove more HelperMethodFrames (HMF) from Reflection (#109882)
Browse files Browse the repository at this point in the history
Convert RuntimeTypeHandle.GetTypeFromHandle() to fast/slow paths.
Convert RuntimeTypeHandle.GetBaseType() to RuntimeType.GetParentType() QCall.
Consolidate Object.GetType() with RuntimeTypeHandle APIs.
Remove HMF from RuntimeFieldHandle.GetAttributes() and RuntimeFieldHandle.GetApproxDeclaringType().
  • Loading branch information
AaronRobinsonMSFT authored Nov 18, 2024
1 parent 2c7c6bf commit 2d9f80b
Show file tree
Hide file tree
Showing 17 changed files with 157 additions and 143 deletions.
13 changes: 1 addition & 12 deletions src/coreclr/System.Private.CoreLib/src/System/Object.CoreCLR.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,14 @@ namespace System
{
public partial class Object
{
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ObjectNative_GetTypeSlow")]
private static unsafe partial void GetTypeSlow(MethodTable* methodTable, ObjectHandleOnStack ret);

// Returns a Type object which represent this object instance.
[Intrinsic]
public unsafe Type GetType()
{
MethodTable* pMT = RuntimeHelpers.GetMethodTable(this);
Type type = pMT->AuxiliaryData->ExposedClassObject ?? GetTypeWorker(pMT);
RuntimeType type = RuntimeTypeHandle.GetRuntimeType(pMT);
GC.KeepAlive(this);
return type;

[MethodImpl(MethodImplOptions.NoInlining)]
static Type GetTypeWorker(MethodTable* pMT)
{
Type? type = null;
GetTypeSlow(pMT, ObjectHandleOnStack.Create(ref type));
return type!;
}
}

// Returns a new object instance that is a memberwise copy of this
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ private static unsafe ref byte GetSpanDataFrom(
if (!RuntimeFieldHandle.GetRVAFieldInfo(fldInfo.Value, out void* data, out uint totalSize))
throw new ArgumentException(SR.Argument_BadFieldForInitializeArray);

TypeHandle th = targetTypeHandle.GetNativeTypeHandle();
TypeHandle th = targetTypeHandle.GetRuntimeType().GetNativeTypeHandle();
Debug.Assert(!th.IsTypeDesc); // TypeDesc can't be used as generic parameter
MethodTable* targetMT = th.AsMethodTable();

Expand Down
64 changes: 51 additions & 13 deletions src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,39 @@ internal RuntimeType GetTypeChecked() =>
/// </summary>
/// <param name="value">An IntPtr handle to a RuntimeType to create a <see cref="RuntimeTypeHandle"/> object from.</param>
/// <returns>A new <see cref="RuntimeTypeHandle"/> object that corresponds to the value parameter.</returns>
public static RuntimeTypeHandle FromIntPtr(IntPtr value) => new RuntimeTypeHandle(Type.GetTypeFromHandleUnsafe(value));
public static RuntimeTypeHandle FromIntPtr(IntPtr value) => new RuntimeTypeHandle(GetTypeFromHandle(value));

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RuntimeTypeHandle_GetTypeFromHandleSlow")]
private static partial void GetTypeFromHandleSlow(
IntPtr handle,
ObjectHandleOnStack typeObject);

[MethodImpl(MethodImplOptions.NoInlining)]
private static RuntimeType GetTypeFromHandleSlow(IntPtr handle)
{
RuntimeType? typeObject = null;
GetTypeFromHandleSlow(handle, ObjectHandleOnStack.Create(ref typeObject));
return typeObject!;
}

[MethodImpl(MethodImplOptions.InternalCall)]
private static extern RuntimeType? GetTypeFromHandleIfExists(IntPtr handle);

private static RuntimeType? GetTypeFromHandle(IntPtr handle)
{
if (handle == IntPtr.Zero)
{
return null;
}

return GetTypeFromHandleIfExists(handle) ?? GetTypeFromHandleSlow(handle);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static unsafe RuntimeType GetRuntimeType(MethodTable* pMT)
{
return pMT->AuxiliaryData->ExposedClassObject ?? GetTypeFromHandleSlow((IntPtr)pMT);
}

/// <summary>
/// Returns the internal pointer representation of a <see cref="RuntimeTypeHandle"/> object.
Expand All @@ -49,7 +81,7 @@ internal RuntimeType GetTypeChecked() =>
public static bool operator !=(object? left, RuntimeTypeHandle right) => !right.Equals(left);

// This is the RuntimeType for the type
internal RuntimeType m_type;
internal RuntimeType? m_type;

public override int GetHashCode()
=> m_type?.GetHashCode() ?? 0;
Expand All @@ -62,7 +94,7 @@ public bool Equals(RuntimeTypeHandle handle)

public IntPtr Value => m_type?.m_handle ?? 0;

internal RuntimeTypeHandle(RuntimeType type)
internal RuntimeTypeHandle(RuntimeType? type)
{
m_type = type;
}
Expand All @@ -72,11 +104,6 @@ internal bool IsNullHandle()
return m_type == null;
}

internal TypeHandle GetNativeTypeHandle()
{
return m_type.GetNativeTypeHandle();
}

internal static bool IsTypeDefinition(RuntimeType type)
{
CorElementType corElemType = type.GetCorElementType();
Expand Down Expand Up @@ -304,7 +331,7 @@ private static object AllocateComObject(void* pClassFactory)

internal RuntimeType GetRuntimeType()
{
return m_type;
return m_type!;
}

internal static RuntimeAssembly GetAssembly(RuntimeType type)
Expand Down Expand Up @@ -347,12 +374,14 @@ static RuntimeModule GetModuleWorker(RuntimeType type)

public ModuleHandle GetModuleHandle()
{
if (m_type is null)
{
throw new ArgumentNullException(SR.Arg_InvalidHandle);
}

return new ModuleHandle(GetModule(m_type));
}

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern RuntimeType GetBaseType(RuntimeType type);

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern TypeAttributes GetAttributes(RuntimeType type);

Expand Down Expand Up @@ -1220,7 +1249,16 @@ internal static MdUtf8String GetUtf8Name(RuntimeFieldHandleInternal field)
internal static extern FieldAttributes GetAttributes(RuntimeFieldHandleInternal field);

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern RuntimeType GetApproxDeclaringType(RuntimeFieldHandleInternal field);
private static extern MethodTable* GetApproxDeclaringMethodTable(RuntimeFieldHandleInternal field);

internal static RuntimeType GetApproxDeclaringType(RuntimeFieldHandleInternal field)
{
Debug.Assert(!field.IsNullHandle());
MethodTable* pMT = GetApproxDeclaringMethodTable(field);
Debug.Assert(pMT != null);

return RuntimeTypeHandle.GetRuntimeType(pMT);
}

internal static RuntimeType GetApproxDeclaringType(IRuntimeFieldInfo field)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ private BoxCache(RuntimeType rt)
_originalRuntimeType = rt;
#endif

TypeHandle handle = rt.TypeHandle.GetNativeTypeHandle();
TypeHandle handle = rt.GetNativeTypeHandle();

if (handle.IsTypeDesc)
throw new ArgumentException(SR.Arg_TypeNotSupported);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -743,7 +743,7 @@ private unsafe RuntimeMethodInfo[] PopulateMethods(Filter filter)
#endregion
}

declaringType = RuntimeTypeHandle.GetBaseType(declaringType);
declaringType = declaringType.GetParentType()!;
} while (declaringType != null);
#endregion
}
Expand Down Expand Up @@ -814,13 +814,14 @@ private RuntimeFieldInfo[] PopulateFields(Filter filter)
while (RuntimeTypeHandle.IsGenericVariable(declaringType))
declaringType = declaringType.GetBaseType()!;

while (declaringType != null)
RuntimeType? populatingType = declaringType;
while (populatingType != null)
{
PopulateRtFields(filter, declaringType, ref list);
PopulateRtFields(filter, populatingType, ref list);

PopulateLiteralFields(filter, declaringType, ref list);
PopulateLiteralFields(filter, populatingType, ref list);

declaringType = RuntimeTypeHandle.GetBaseType(declaringType);
populatingType = populatingType.GetParentType();
}
#endregion

Expand Down Expand Up @@ -1152,10 +1153,11 @@ private RuntimeEventInfo[] PopulateEvents(Filter filter)
declaringType = declaringType.GetBaseType()!;

// Populate associates off of the class hierarchy
while (declaringType != null)
RuntimeType? populatingType = declaringType;
while (populatingType != null)
{
PopulateEvents(filter, declaringType, csEventInfos, ref list);
declaringType = RuntimeTypeHandle.GetBaseType(declaringType);
PopulateEvents(filter, populatingType, csEventInfos, ref list);
populatingType = populatingType.GetParentType();
}
}
else
Expand Down Expand Up @@ -1261,11 +1263,12 @@ private RuntimePropertyInfo[] PopulateProperties(Filter filter)
}

// Populate associates off of the class hierarchy
do
RuntimeType? populatingType = declaringType;
while (populatingType != null)
{
PopulateProperties(filter, declaringType, csPropertyInfos, usedSlots, isInterface: false, ref list);
declaringType = RuntimeTypeHandle.GetBaseType(declaringType);
} while (declaringType != null);
PopulateProperties(filter, populatingType, csPropertyInfos, usedSlots, isInterface: false, ref list);
populatingType = populatingType.GetParentType();
}
}
else
{
Expand Down Expand Up @@ -1778,6 +1781,28 @@ internal FieldInfo GetField(RuntimeFieldHandleInternal field)
#region Static Members

#region Internal

// Returns the type from which the current type directly inherits from (without reflection quirks).
// The parent type is null for interfaces, pointers, byrefs and generic parameters.
internal unsafe RuntimeType? GetParentType()
{
TypeHandle typeHandle = GetNativeTypeHandle();
if (typeHandle.IsTypeDesc)
{
return null;
}

MethodTable* pParentMT = typeHandle.AsMethodTable()->ParentMethodTable;
if (pParentMT == null)
{
return null;
}

RuntimeType result = RuntimeTypeHandle.GetRuntimeType(pParentMT);
GC.KeepAlive(this);
return result;
}

[RequiresUnreferencedCode("Trimming changes metadata tokens")]
internal static MethodBase? GetMethodBase(RuntimeModule scope, int typeMetadataToken)
{
Expand Down
4 changes: 0 additions & 4 deletions src/coreclr/System.Private.CoreLib/src/System/Type.CoreCLR.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,6 @@ public abstract partial class Type : MemberInfo, IReflect
throwOnError: throwOnError, ignoreCase: ignoreCase);
}

// Given a class handle, this will return the class for that handle.
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern RuntimeType GetTypeFromHandleUnsafe(IntPtr handle);

[Intrinsic]
public static Type? GetTypeFromHandle(RuntimeTypeHandle handle)
=> handle.m_type;
Expand Down
14 changes: 0 additions & 14 deletions src/coreclr/classlibnative/bcltype/objectnative.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,20 +89,6 @@ FCIMPL2(FC_BOOL_RET, ObjectNative::ContentEquals, Object *pThisRef, Object *pCom
}
FCIMPLEND

extern "C" void QCALLTYPE ObjectNative_GetTypeSlow(MethodTable* pMT, QCall::ObjectHandleOnStack ret)
{
QCALL_CONTRACT;
_ASSERTE(pMT != NULL);

BEGIN_QCALL;

GCX_COOP();

ret.Set(pMT->GetManagedClassObject());

END_QCALL;
}

extern "C" void QCALLTYPE ObjectNative_AllocateUninitializedClone(QCall::ObjectHandleOnStack objHandle)
{
QCALL_CONTRACT;
Expand Down
1 change: 0 additions & 1 deletion src/coreclr/classlibnative/bcltype/objectnative.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ class ObjectNative
};

extern "C" INT32 QCALLTYPE ObjectNative_GetHashCodeSlow(QCall::ObjectHandleOnStack objHandle);
extern "C" void QCALLTYPE ObjectNative_GetTypeSlow(MethodTable* pMT, QCall::ObjectHandleOnStack ret);
extern "C" void QCALLTYPE ObjectNative_AllocateUninitializedClone(QCall::ObjectHandleOnStack objHandle);
extern "C" BOOL QCALLTYPE Monitor_Wait(QCall::ObjectHandleOnStack pThis, INT32 Timeout);
extern "C" void QCALLTYPE Monitor_Pulse(QCall::ObjectHandleOnStack pThis);
Expand Down
9 changes: 2 additions & 7 deletions src/coreclr/vm/ecalllist.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,18 +81,13 @@ FCFuncStart(gExceptionFuncs)
FCFuncElement("GetExceptionCount", ExceptionNative::GetExceptionCount)
FCFuncEnd()

FCFuncStart(gSystem_Type)
FCFuncElement("GetTypeFromHandleUnsafe", RuntimeTypeHandle::GetRuntimeType)
FCFuncEnd()

FCFuncStart(gCOMTypeHandleFuncs)
FCFuncElement("GetDeclaringMethod", RuntimeTypeHandle::GetDeclaringMethod)
FCFuncElement("GetDeclaringType", RuntimeTypeHandle::GetDeclaringType)
FCFuncElement("GetFirstIntroducedMethod", RuntimeTypeHandle::GetFirstIntroducedMethod)
FCFuncElement("GetNextIntroducedMethod", RuntimeTypeHandle::GetNextIntroducedMethod)
FCFuncElement("GetAssemblyIfExists", RuntimeTypeHandle::GetAssemblyIfExists)
FCFuncElement("GetModuleIfExists", RuntimeTypeHandle::GetModuleIfExists)
FCFuncElement("GetBaseType", RuntimeTypeHandle::GetBaseType)
FCFuncElement("GetElementType", RuntimeTypeHandle::GetElementType)
FCFuncElement("GetArrayRank", RuntimeTypeHandle::GetArrayRank)
FCFuncElement("GetToken", RuntimeTypeHandle::GetToken)
Expand All @@ -111,6 +106,7 @@ FCFuncStart(gCOMTypeHandleFuncs)
FCFuncElement("IsUnmanagedFunctionPointer", RuntimeTypeHandle::IsUnmanagedFunctionPointer)
FCFuncElement("CompareCanonicalHandles", RuntimeTypeHandle::CompareCanonicalHandles)
FCFuncElement("IsEquivalentTo", RuntimeTypeHandle::IsEquivalentTo)
FCFuncElement("GetTypeFromHandleIfExists", RuntimeTypeHandle::GetTypeFromHandleIfExists)
FCFuncEnd()

FCFuncStart(gMetaDataImport)
Expand Down Expand Up @@ -176,7 +172,7 @@ FCFuncEnd()
FCFuncStart(gCOMFieldHandleNewFuncs)
FCFuncElement("GetUtf8NameInternal", RuntimeFieldHandle::GetUtf8Name)
FCFuncElement("GetAttributes", RuntimeFieldHandle::GetAttributes)
FCFuncElement("GetApproxDeclaringType", RuntimeFieldHandle::GetApproxDeclaringType)
FCFuncElement("GetApproxDeclaringMethodTable", RuntimeFieldHandle::GetApproxDeclaringMethodTable)
FCFuncElement("GetToken", RuntimeFieldHandle::GetToken)
FCFuncElement("GetStaticFieldForGenericType", RuntimeFieldHandle::GetStaticFieldForGenericType)
FCFuncElement("AcquiresContextFromThis", RuntimeFieldHandle::AcquiresContextFromThis)
Expand Down Expand Up @@ -433,7 +429,6 @@ FCClassElement("String", "System", gStringFuncs)
FCClassElement("StubHelpers", "System.StubHelpers", gStubHelperFuncs)
FCClassElement("Thread", "System.Threading", gThreadFuncs)
FCClassElement("ThreadPool", "System.Threading", gThreadPoolFuncs)
FCClassElement("Type", "System", gSystem_Type)

#undef FCFuncElement
#undef FCFuncElementSig
Expand Down
4 changes: 2 additions & 2 deletions src/coreclr/vm/qcallentrypoints.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,6 @@ static const Entry s_QCall[] =
DllImportEntry(ExceptionNative_GetMethodFromStackTrace)
DllImportEntry(ExceptionNative_ThrowAmbiguousResolutionException)
DllImportEntry(ExceptionNative_ThrowEntryPointNotFoundException)
DllImportEntry(RuntimeTypeHandle_CreateInstanceForAnotherGenericParameter)
DllImportEntry(QCall_GetGCHandleForTypeHandle)
DllImportEntry(QCall_FreeGCHandleForTypeHandle)
DllImportEntry(MethodTable_AreTypesEquivalent)
Expand Down Expand Up @@ -137,6 +136,8 @@ static const Entry s_QCall[] =
#ifdef FEATURE_COMINTEROP
DllImportEntry(RuntimeTypeHandle_AllocateComObject)
#endif // FEATURE_COMINTEROP
DllImportEntry(RuntimeTypeHandle_GetTypeFromHandleSlow)
DllImportEntry(RuntimeTypeHandle_CreateInstanceForAnotherGenericParameter)
DllImportEntry(RuntimeTypeHandle_AllocateTypeAssociatedMemory)
DllImportEntry(RuntimeTypeHandle_RegisterCollectibleTypeDependency)
DllImportEntry(MethodBase_GetCurrentMethod)
Expand Down Expand Up @@ -373,7 +374,6 @@ static const Entry s_QCall[] =
DllImportEntry(FileLoadException_GetMessageForHR)
DllImportEntry(Interlocked_MemoryBarrierProcessWide)
DllImportEntry(ObjectNative_GetHashCodeSlow)
DllImportEntry(ObjectNative_GetTypeSlow)
DllImportEntry(ObjectNative_AllocateUninitializedClone)
DllImportEntry(Monitor_Wait)
DllImportEntry(Monitor_Pulse)
Expand Down
Loading

0 comments on commit 2d9f80b

Please sign in to comment.