From d10863806942d413aeba9ff663dd27f4cb96ae1d Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Mon, 29 Jan 2024 19:00:51 +0800 Subject: [PATCH 01/16] Equals --- .../RuntimeHelpers.CoreCLR.cs | 29 ++++++++++- .../classlibnative/bcltype/objectnative.cpp | 49 ------------------- .../classlibnative/bcltype/objectnative.h | 1 - src/coreclr/vm/ecalllist.h | 1 - 4 files changed, 27 insertions(+), 53 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index 534a251630cdac..3ad57d6df1cd8e 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -139,8 +139,33 @@ public static unsafe void PrepareMethod(RuntimeMethodHandle method, RuntimeTypeH [MethodImpl(MethodImplOptions.InternalCall)] internal static extern int TryGetHashCode(object o); - [MethodImpl(MethodImplOptions.InternalCall)] - public static extern new bool Equals(object? o1, object? o2); + public static new unsafe bool Equals(object? o1, object? o2) + { + // Compare by ref for normal classes, by value for value types. + + if (ReferenceEquals(o1, o2)) + return true; + + if (o1 is null || o2 is null) + return false; + + MethodTable* pMT = GetMethodTable(o1); + + // If it's not a value class, don't compare by value + if (!pMT->IsValueType) + return false; + + // Make sure they are the same type. + if (pMT != GetMethodTable(o2)) + return false; + + // Compare the contents + uint size = pMT->GetNumInstanceFieldBytes(); + return SpanHelpers.SequenceEqual( + ref GetRawData(o1), + ref GetRawData(o2), + size); + } [Obsolete("OffsetToStringData has been deprecated. Use string.GetPinnableReference() instead.")] public static int OffsetToStringData diff --git a/src/coreclr/classlibnative/bcltype/objectnative.cpp b/src/coreclr/classlibnative/bcltype/objectnative.cpp index 4622955b44adf6..303ffd603c22bb 100644 --- a/src/coreclr/classlibnative/bcltype/objectnative.cpp +++ b/src/coreclr/classlibnative/bcltype/objectnative.cpp @@ -123,55 +123,6 @@ FCIMPL1(INT32, ObjectNative::TryGetHashCode, Object* obj) { } FCIMPLEND -// -// Compare by ref for normal classes, by value for value types. -// -// @todo: it would be nice to customize this method based on the -// defining class rather than doing a runtime check whether it is -// a value type. -// - -FCIMPL2(FC_BOOL_RET, ObjectNative::Equals, Object *pThisRef, Object *pCompareRef) -{ - CONTRACTL - { - FCALL_CHECK; - INJECT_FAULT(FCThrow(kOutOfMemoryException);); - } - CONTRACTL_END; - - if (pThisRef == pCompareRef) - FC_RETURN_BOOL(TRUE); - - // Since we are in FCALL, we must handle NULL specially. - if (pThisRef == NULL || pCompareRef == NULL) - FC_RETURN_BOOL(FALSE); - - MethodTable *pThisMT = pThisRef->GetMethodTable(); - - // If it's not a value class, don't compare by value - if (!pThisMT->IsValueType()) - FC_RETURN_BOOL(FALSE); - - // Make sure they are the same type. - if (pThisMT != pCompareRef->GetMethodTable()) - FC_RETURN_BOOL(FALSE); - - // Compare the contents (size - vtable - sync block index). - DWORD dwBaseSize = pThisMT->GetBaseSize(); - if(pThisMT == g_pStringClass) - dwBaseSize -= sizeof(WCHAR); - BOOL ret = memcmp( - (void *) (pThisRef+1), - (void *) (pCompareRef+1), - dwBaseSize - sizeof(Object) - sizeof(int)) == 0; - - FC_GC_POLL_RET(); - - FC_RETURN_BOOL(ret); -} -FCIMPLEND - NOINLINE static Object* GetClassHelper(OBJECTREF objRef) { FC_INNER_PROLOG(ObjectNative::GetClass); diff --git a/src/coreclr/classlibnative/bcltype/objectnative.h b/src/coreclr/classlibnative/bcltype/objectnative.h index d8948922dd0b74..2f8eddd78843fd 100644 --- a/src/coreclr/classlibnative/bcltype/objectnative.h +++ b/src/coreclr/classlibnative/bcltype/objectnative.h @@ -27,7 +27,6 @@ class ObjectNative static FCDECL1(INT32, GetHashCode, Object* vThisRef); static FCDECL1(INT32, TryGetHashCode, Object* vThisRef); - static FCDECL2(FC_BOOL_RET, Equals, Object *pThisRef, Object *pCompareRef); static FCDECL1(Object*, AllocateUninitializedClone, Object* pObjUNSAFE); static FCDECL1(Object*, GetClass, Object* pThis); static FCDECL1(FC_BOOL_RET, IsLockHeld, Object* pThisUNSAFE); diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 64142b1fb69d13..ee7edab1f1c5a0 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -473,7 +473,6 @@ FCFuncStart(gRuntimeHelpers) FCFuncElement("PrepareDelegate", ReflectionInvocation::PrepareDelegate) FCFuncElement("GetHashCode", ObjectNative::GetHashCode) FCFuncElement("TryGetHashCode", ObjectNative::TryGetHashCode) - FCFuncElement("Equals", ObjectNative::Equals) FCFuncElement("AllocateUninitializedClone", ObjectNative::AllocateUninitializedClone) FCFuncElement("EnsureSufficientExecutionStack", ReflectionInvocation::EnsureSufficientExecutionStack) FCFuncElement("TryEnsureSufficientExecutionStack", ReflectionInvocation::TryEnsureSufficientExecutionStack) From 5c827a5104ddbb2ccfd8696eefb78025152a1b35 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Mon, 29 Jan 2024 19:14:43 +0800 Subject: [PATCH 02/16] GetHashCode --- .../RuntimeHelpers.CoreCLR.cs | 17 ++++- .../classlibnative/bcltype/objectnative.cpp | 62 +++---------------- .../classlibnative/bcltype/objectnative.h | 2 +- src/coreclr/vm/ecalllist.h | 1 - src/coreclr/vm/qcallentrypoints.cpp | 1 + 5 files changed, 24 insertions(+), 59 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index 3ad57d6df1cd8e..e969a87d6364e6 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -125,8 +125,21 @@ public static unsafe void PrepareMethod(RuntimeMethodHandle method, RuntimeTypeH [MethodImpl(MethodImplOptions.InternalCall)] public static extern void PrepareDelegate(Delegate d); - [MethodImpl(MethodImplOptions.InternalCall)] - public static extern int GetHashCode(object? o); + public static int GetHashCode(object? o) + { + if (o is null) + return 0; + + int hashCode = TryGetHashCode(o); + if (hashCode != 0) + return hashCode; + + object objRef = o; + return GetHashCodeHelper(ObjectHandleOnStack.Create(ref objRef)); + } + + [LibraryImport(QCall, EntryPoint = "ObjectNative_GetHashCodeHelper")] + private static partial int GetHashCodeHelper(ObjectHandleOnStack objHandle); /// /// If a hash code has been assigned to the object, it is returned. Otherwise zero is diff --git a/src/coreclr/classlibnative/bcltype/objectnative.cpp b/src/coreclr/classlibnative/bcltype/objectnative.cpp index 303ffd603c22bb..711b50f3fa799d 100644 --- a/src/coreclr/classlibnative/bcltype/objectnative.cpp +++ b/src/coreclr/classlibnative/bcltype/objectnative.cpp @@ -20,67 +20,19 @@ #include "eeconfig.h" -NOINLINE static INT32 GetHashCodeHelper(OBJECTREF objRef) +extern "C" INT32 QCALLTYPE ObjectNative_GetHashCodeHelper(QCall::ObjectHandleOnStack objHandle) { - DWORD idx = 0; - - FC_INNER_PROLOG(ObjectNative::GetHashCode); - - HELPER_METHOD_FRAME_BEGIN_RET_ATTRIB_1(Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2, objRef); - - idx = objRef->GetHashCodeEx(); - - HELPER_METHOD_FRAME_END(); - FC_INNER_EPILOG(); - return idx; -} - -// Note that we obtain a sync block index without actually building a sync block. -// That's because a lot of objects are hashed, without requiring support for -FCIMPL1(INT32, ObjectNative::GetHashCode, Object* obj) { - - CONTRACTL - { - FCALL_CHECK; - INJECT_FAULT(FCThrow(kOutOfMemoryException);); - } - CONTRACTL_END; - - VALIDATEOBJECT(obj); - - if (obj == 0) - return 0; + QCALL_CONTRACT; - OBJECTREF objRef(obj); + INT32 ret = 0; - { - DWORD bits = objRef->GetHeader()->GetBits(); + BEGIN_QCALL; - if (bits & BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX) - { - if (bits & BIT_SBLK_IS_HASHCODE) - { - // Common case: the object already has a hash code - return bits & MASK_HASHCODE; - } - else - { - // We have a sync block index. This means if we already have a hash code, - // it is in the sync block, otherwise we generate a new one and store it there - SyncBlock *psb = objRef->PassiveGetSyncBlock(); - if (psb != NULL) - { - DWORD hashCode = psb->GetHashCode(); - if (hashCode != 0) - return hashCode; - } - } - } - } + GCX_COOP(); + ret = objHandle.Get()->GetHashCodeEx(); - FC_INNER_RETURN(INT32, GetHashCodeHelper(objRef)); + END_QCALL; } -FCIMPLEND FCIMPL1(INT32, ObjectNative::TryGetHashCode, Object* obj) { diff --git a/src/coreclr/classlibnative/bcltype/objectnative.h b/src/coreclr/classlibnative/bcltype/objectnative.h index 2f8eddd78843fd..a84a20ca6ae015 100644 --- a/src/coreclr/classlibnative/bcltype/objectnative.h +++ b/src/coreclr/classlibnative/bcltype/objectnative.h @@ -25,13 +25,13 @@ class ObjectNative { public: - static FCDECL1(INT32, GetHashCode, Object* vThisRef); static FCDECL1(INT32, TryGetHashCode, Object* vThisRef); static FCDECL1(Object*, AllocateUninitializedClone, Object* pObjUNSAFE); static FCDECL1(Object*, GetClass, Object* pThis); static FCDECL1(FC_BOOL_RET, IsLockHeld, Object* pThisUNSAFE); }; +extern "C" INT32 QCALLTYPE ObjectNative_GetHashCodeHelper(QCall::ObjectHandleOnStack objHandle); extern "C" BOOL QCALLTYPE Monitor_Wait(QCall::ObjectHandleOnStack pThis, INT32 Timeout); extern "C" void QCALLTYPE Monitor_Pulse(QCall::ObjectHandleOnStack pThis); extern "C" void QCALLTYPE Monitor_PulseAll(QCall::ObjectHandleOnStack pThis); diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index ee7edab1f1c5a0..3498e77adf76aa 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -471,7 +471,6 @@ FCFuncStart(gRuntimeHelpers) FCFuncElement("InitializeArray", ArrayNative::InitializeArray) FCFuncElement("GetSpanDataFrom", ArrayNative::GetSpanDataFrom) FCFuncElement("PrepareDelegate", ReflectionInvocation::PrepareDelegate) - FCFuncElement("GetHashCode", ObjectNative::GetHashCode) FCFuncElement("TryGetHashCode", ObjectNative::TryGetHashCode) FCFuncElement("AllocateUninitializedClone", ObjectNative::AllocateUninitializedClone) FCFuncElement("EnsureSufficientExecutionStack", ReflectionInvocation::EnsureSufficientExecutionStack) diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index 3e149e1a763a28..fc53ee92db0dd1 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -306,6 +306,7 @@ static const Entry s_QCall[] = DllImportEntry(GetFileLoadExceptionMessage) DllImportEntry(FileLoadException_GetMessageForHR) DllImportEntry(Interlocked_MemoryBarrierProcessWide) + DllImportEntry(ObjectNative_GetHashCodeHelper) DllImportEntry(Monitor_Wait) DllImportEntry(Monitor_Pulse) DllImportEntry(Monitor_PulseAll) From 581bb9c5740d63bbc8a21a5aaa259b91c4b8d099 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Mon, 29 Jan 2024 20:27:25 +0800 Subject: [PATCH 03/16] AllocateUninitializedClone --- .../src/System/Object.CoreCLR.cs | 5 ++- .../RuntimeHelpers.CoreCLR.cs | 4 +-- .../classlibnative/bcltype/objectnative.cpp | 34 +++++++++---------- .../classlibnative/bcltype/objectnative.h | 2 +- src/coreclr/vm/ecalllist.h | 1 - src/coreclr/vm/qcallentrypoints.cpp | 1 + 6 files changed, 24 insertions(+), 23 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Object.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Object.CoreCLR.cs index 70cff629fc28e6..15aa39892aeb30 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Object.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Object.CoreCLR.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics; using System.Runtime.CompilerServices; namespace System @@ -19,7 +20,9 @@ public partial class Object [Intrinsic] protected internal unsafe object MemberwiseClone() { - object clone = RuntimeHelpers.AllocateUninitializedClone(this); + object clone = this; + RuntimeHelpers.AllocateUninitializedClone(ObjectHandleOnStack.Create(ref clone)); + Debug.Assert(clone != this); // copy contents of "this" to the clone diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index e969a87d6364e6..2a939cc0270c69 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -237,8 +237,8 @@ public static object GetUninitializedObject( [LibraryImport(QCall, EntryPoint = "ReflectionSerialization_GetUninitializedObject")] private static partial void GetUninitializedObject(QCallTypeHandle type, ObjectHandleOnStack retObject); - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern object AllocateUninitializedClone(object obj); + [LibraryImport(QCall, EntryPoint = "ObjectNative_AllocateUninitializedClone")] + internal static partial void AllocateUninitializedClone(ObjectHandleOnStack objHandle); /// true if given type is reference type or value type that contains references [Intrinsic] diff --git a/src/coreclr/classlibnative/bcltype/objectnative.cpp b/src/coreclr/classlibnative/bcltype/objectnative.cpp index 711b50f3fa799d..a6ecce4d3e422d 100644 --- a/src/coreclr/classlibnative/bcltype/objectnative.cpp +++ b/src/coreclr/classlibnative/bcltype/objectnative.cpp @@ -118,36 +118,34 @@ FCIMPL1(Object*, ObjectNative::GetClass, Object* pThis) } FCIMPLEND -FCIMPL1(Object*, ObjectNative::AllocateUninitializedClone, Object* pObjUNSAFE) +extern "C" void QCALLTYPE ObjectNative_AllocateUninitializedClone(QCall::ObjectHandleOnStack objHandle) { - FCALL_CONTRACT; - - // Delegate error handling to managed side (it will throw NullReferenceException) - if (pObjUNSAFE == NULL) - return NULL; + QCALL_CONTRACT; - OBJECTREF refClone = ObjectToOBJECTREF(pObjUNSAFE); + _ASSERTE(objHandle.Get() != NULL); // Should be handled at managed side - HELPER_METHOD_FRAME_BEGIN_RET_1(refClone); + BEGIN_QCALL; + GCX_COOP(); + OBJECTREF refClone = objHandle.Get(); MethodTable* pMT = refClone->GetMethodTable(); - + // assert that String has overloaded the Clone() method _ASSERTE(pMT != g_pStringClass); - - if (pMT->IsArray()) { - refClone = DupArrayForCloning((BASEARRAYREF)refClone); - } else { + + if (pMT->IsArray()) + { + objHandle.Set(DupArrayForCloning((BASEARRAYREF)refClone)); + } + else + { // We don't need to call the because we know // that it has been called....(It was called before this was created) - refClone = AllocateObject(pMT); + objHandle.Set(AllocateObject(pMT)); } - HELPER_METHOD_FRAME_END(); - - return OBJECTREFToObject(refClone); + END_QCALL; } -FCIMPLEND extern "C" BOOL QCALLTYPE Monitor_Wait(QCall::ObjectHandleOnStack pThis, INT32 Timeout) { diff --git a/src/coreclr/classlibnative/bcltype/objectnative.h b/src/coreclr/classlibnative/bcltype/objectnative.h index a84a20ca6ae015..d28929f02bd36d 100644 --- a/src/coreclr/classlibnative/bcltype/objectnative.h +++ b/src/coreclr/classlibnative/bcltype/objectnative.h @@ -26,12 +26,12 @@ class ObjectNative public: static FCDECL1(INT32, TryGetHashCode, Object* vThisRef); - static FCDECL1(Object*, AllocateUninitializedClone, Object* pObjUNSAFE); static FCDECL1(Object*, GetClass, Object* pThis); static FCDECL1(FC_BOOL_RET, IsLockHeld, Object* pThisUNSAFE); }; extern "C" INT32 QCALLTYPE ObjectNative_GetHashCodeHelper(QCall::ObjectHandleOnStack objHandle); +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); extern "C" void QCALLTYPE Monitor_PulseAll(QCall::ObjectHandleOnStack pThis); diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 3498e77adf76aa..f77ef24f558998 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -472,7 +472,6 @@ FCFuncStart(gRuntimeHelpers) FCFuncElement("GetSpanDataFrom", ArrayNative::GetSpanDataFrom) FCFuncElement("PrepareDelegate", ReflectionInvocation::PrepareDelegate) FCFuncElement("TryGetHashCode", ObjectNative::TryGetHashCode) - FCFuncElement("AllocateUninitializedClone", ObjectNative::AllocateUninitializedClone) FCFuncElement("EnsureSufficientExecutionStack", ReflectionInvocation::EnsureSufficientExecutionStack) FCFuncElement("TryEnsureSufficientExecutionStack", ReflectionInvocation::TryEnsureSufficientExecutionStack) FCFuncElement("AllocTailCallArgBuffer", TailCallHelp::AllocTailCallArgBuffer) diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index fc53ee92db0dd1..ef4a94606ee8fa 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -307,6 +307,7 @@ static const Entry s_QCall[] = DllImportEntry(FileLoadException_GetMessageForHR) DllImportEntry(Interlocked_MemoryBarrierProcessWide) DllImportEntry(ObjectNative_GetHashCodeHelper) + DllImportEntry(ObjectNative_AllocateUninitializedClone) DllImportEntry(Monitor_Wait) DllImportEntry(Monitor_Pulse) DllImportEntry(Monitor_PulseAll) From d3b9dccf5aa596a104f258515d71760798627c12 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Mon, 29 Jan 2024 21:52:13 +0800 Subject: [PATCH 04/16] GetClass --- .../src/System/Object.CoreCLR.cs | 24 ++++++++++- .../classlibnative/bcltype/objectnative.cpp | 41 +++++-------------- .../classlibnative/bcltype/objectnative.h | 3 +- src/coreclr/vm/ecalllist.h | 2 +- src/coreclr/vm/qcallentrypoints.cpp | 1 + 5 files changed, 37 insertions(+), 34 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Object.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Object.CoreCLR.cs index 15aa39892aeb30..f7bf2eccc81bd9 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Object.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Object.CoreCLR.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace System { @@ -10,8 +11,29 @@ public partial class Object { // Returns a Type object which represent this object instance. [Intrinsic] + public unsafe Type GetType() + { + // Throws NullReferenceException as expected + MethodTable* pMT = RuntimeHelpers.GetMethodTable(this); + + Type? type = GetTypeIfExists(pMT); + + if (type is not null) + return type; + + GetTypeHelper(pMT, ObjectHandleOnStack.Create(ref type)); + + Debug.Assert(type is not null); + GC.KeepAlive(this); + + return type; + } + [MethodImpl(MethodImplOptions.InternalCall)] - public extern Type GetType(); + private static extern unsafe Type? GetTypeIfExists(MethodTable* pMT); + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ObjectNative_GetClassHelper")] + private static unsafe partial void GetTypeHelper(MethodTable* pMT, ObjectHandleOnStack ret); // Returns a new object instance that is a memberwise copy of this // object. This is always a shallow copy of the instance. The method is protected diff --git a/src/coreclr/classlibnative/bcltype/objectnative.cpp b/src/coreclr/classlibnative/bcltype/objectnative.cpp index a6ecce4d3e422d..c94f3129ed64d9 100644 --- a/src/coreclr/classlibnative/bcltype/objectnative.cpp +++ b/src/coreclr/classlibnative/bcltype/objectnative.cpp @@ -75,46 +75,25 @@ FCIMPL1(INT32, ObjectNative::TryGetHashCode, Object* obj) { } FCIMPLEND -NOINLINE static Object* GetClassHelper(OBJECTREF objRef) +extern "C" void QCALLTYPE ObjectNative_GetClassHelper(MethodTable* pMT, QCall::ObjectHandleOnStack ret) { - FC_INNER_PROLOG(ObjectNative::GetClass); - _ASSERTE(objRef != NULL); - TypeHandle typeHandle = objRef->GetTypeHandle(); - OBJECTREF refType = NULL; + QCALL_CONTRACT; - HELPER_METHOD_FRAME_BEGIN_RET_ATTRIB_1(Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2, refType); + BEGIN_QCALL; - refType = typeHandle.GetManagedClassObject(); + GCX_COOP(); + ret.Set(pMT->GetManagedClassObject()); - HELPER_METHOD_FRAME_END(); - FC_INNER_EPILOG(); - return OBJECTREFToObject(refType); + END_QCALL; } // This routine is called by the Object.GetType() routine. It is a major way to get the System.Type -FCIMPL1(Object*, ObjectNative::GetClass, Object* pThis) +FCIMPL1(Object*, ObjectNative::GetClassIfExists, MethodTable* pMT) { - CONTRACTL - { - FCALL_CHECK; - INJECT_FAULT(FCThrow(kOutOfMemoryException);); - } - CONTRACTL_END; - - OBJECTREF objRef = ObjectToOBJECTREF(pThis); - if (objRef != NULL) - { - MethodTable* pMT = objRef->GetMethodTable(); - OBJECTREF typePtr = pMT->GetManagedClassObjectIfExists(); - if (typePtr != NULL) - { - return OBJECTREFToObject(typePtr); - } - } - else - FCThrow(kNullReferenceException); + FCALL_CONTRACT; - FC_INNER_RETURN(Object*, GetClassHelper(objRef)); + OBJECTREF typePtr = pMT->GetManagedClassObjectIfExists(); + return OBJECTREFToObject(typePtr); } FCIMPLEND diff --git a/src/coreclr/classlibnative/bcltype/objectnative.h b/src/coreclr/classlibnative/bcltype/objectnative.h index d28929f02bd36d..e971a5b8f216ef 100644 --- a/src/coreclr/classlibnative/bcltype/objectnative.h +++ b/src/coreclr/classlibnative/bcltype/objectnative.h @@ -26,11 +26,12 @@ class ObjectNative public: static FCDECL1(INT32, TryGetHashCode, Object* vThisRef); - static FCDECL1(Object*, GetClass, Object* pThis); + static FCDECL1(Object*, GetClassIfExists, MethodTable* pMT); static FCDECL1(FC_BOOL_RET, IsLockHeld, Object* pThisUNSAFE); }; extern "C" INT32 QCALLTYPE ObjectNative_GetHashCodeHelper(QCall::ObjectHandleOnStack objHandle); +extern "C" void QCALLTYPE ObjectNative_GetClassHelper(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); diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index f77ef24f558998..2eeb1c3c1fb5db 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -60,7 +60,7 @@ FCFuncStart(gEnumFuncs) FCFuncEnd() FCFuncStart(gObjectFuncs) - FCFuncElement("GetType", ObjectNative::GetClass) + FCFuncElement("GetTypeIfExists", ObjectNative::GetClassIfExists) FCFuncEnd() FCFuncStart(gStringFuncs) diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index ef4a94606ee8fa..fe83064af8bec4 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -307,6 +307,7 @@ static const Entry s_QCall[] = DllImportEntry(FileLoadException_GetMessageForHR) DllImportEntry(Interlocked_MemoryBarrierProcessWide) DllImportEntry(ObjectNative_GetHashCodeHelper) + DllImportEntry(ObjectNative_GetClassHelper) DllImportEntry(ObjectNative_AllocateUninitializedClone) DllImportEntry(Monitor_Wait) DllImportEntry(Monitor_Pulse) From 62fa6732826b70d91c17e86e8649326a320cac5a Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Mon, 29 Jan 2024 22:21:35 +0800 Subject: [PATCH 05/16] Use GetTypeFromHandleUnsafe instead to reduce code duplication --- .../src/System/Object.CoreCLR.cs | 18 +----------------- .../classlibnative/bcltype/objectnative.h | 2 -- src/coreclr/vm/ecalllist.h | 5 ----- src/coreclr/vm/qcallentrypoints.cpp | 1 - 4 files changed, 1 insertion(+), 25 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Object.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Object.CoreCLR.cs index f7bf2eccc81bd9..6dffb3cd2f614d 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Object.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Object.CoreCLR.cs @@ -3,7 +3,6 @@ using System.Diagnostics; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; namespace System { @@ -15,26 +14,11 @@ public unsafe Type GetType() { // Throws NullReferenceException as expected MethodTable* pMT = RuntimeHelpers.GetMethodTable(this); - - Type? type = GetTypeIfExists(pMT); - - if (type is not null) - return type; - - GetTypeHelper(pMT, ObjectHandleOnStack.Create(ref type)); - - Debug.Assert(type is not null); + Type type = Type.GetTypeFromHandleUnsafe((IntPtr)pMT); GC.KeepAlive(this); - return type; } - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern unsafe Type? GetTypeIfExists(MethodTable* pMT); - - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ObjectNative_GetClassHelper")] - private static unsafe partial void GetTypeHelper(MethodTable* pMT, ObjectHandleOnStack ret); - // Returns a new object instance that is a memberwise copy of this // object. This is always a shallow copy of the instance. The method is protected // so that other object may only call this method on themselves. It is intended to diff --git a/src/coreclr/classlibnative/bcltype/objectnative.h b/src/coreclr/classlibnative/bcltype/objectnative.h index e971a5b8f216ef..2e1ca19e007859 100644 --- a/src/coreclr/classlibnative/bcltype/objectnative.h +++ b/src/coreclr/classlibnative/bcltype/objectnative.h @@ -26,12 +26,10 @@ class ObjectNative public: static FCDECL1(INT32, TryGetHashCode, Object* vThisRef); - static FCDECL1(Object*, GetClassIfExists, MethodTable* pMT); static FCDECL1(FC_BOOL_RET, IsLockHeld, Object* pThisUNSAFE); }; extern "C" INT32 QCALLTYPE ObjectNative_GetHashCodeHelper(QCall::ObjectHandleOnStack objHandle); -extern "C" void QCALLTYPE ObjectNative_GetClassHelper(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); diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 2eeb1c3c1fb5db..e163ef79644f1a 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -59,10 +59,6 @@ FCFuncStart(gEnumFuncs) FCFuncElement("InternalBoxEnum", ReflectionEnum::InternalBoxEnum) FCFuncEnd() -FCFuncStart(gObjectFuncs) - FCFuncElement("GetTypeIfExists", ObjectNative::GetClassIfExists) -FCFuncEnd() - FCFuncStart(gStringFuncs) FCDynamic("FastAllocateString", ECall::FastAllocateString) FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_ArrChar_RetVoid, ECall::CtorCharArrayManaged) @@ -639,7 +635,6 @@ FCClassElement("MngdSafeArrayMarshaler", "System.StubHelpers", gMngdSafeArrayMar #endif // FEATURE_COMINTEROP FCClassElement("ModuleHandle", "System", gCOMModuleHandleFuncs) FCClassElement("Monitor", "System.Threading", gMonitorFuncs) -FCClassElement("Object", "System", gObjectFuncs) FCClassElement("RuntimeAssembly", "System.Reflection", gRuntimeAssemblyFuncs) FCClassElement("RuntimeFieldHandle", "System", gCOMFieldHandleNewFuncs) diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index fe83064af8bec4..ef4a94606ee8fa 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -307,7 +307,6 @@ static const Entry s_QCall[] = DllImportEntry(FileLoadException_GetMessageForHR) DllImportEntry(Interlocked_MemoryBarrierProcessWide) DllImportEntry(ObjectNative_GetHashCodeHelper) - DllImportEntry(ObjectNative_GetClassHelper) DllImportEntry(ObjectNative_AllocateUninitializedClone) DllImportEntry(Monitor_Wait) DllImportEntry(Monitor_Pulse) From 6a56bd697a30871c0c9374245f0d44ede1d9d910 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Fri, 23 Feb 2024 00:01:41 +0800 Subject: [PATCH 06/16] Revert change of GetType --- .../src/System/Object.CoreCLR.cs | 10 +---- .../classlibnative/bcltype/objectnative.cpp | 41 +++++++++++++++++-- .../classlibnative/bcltype/objectnative.h | 1 + src/coreclr/vm/ecalllist.h | 5 +++ 4 files changed, 45 insertions(+), 12 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Object.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Object.CoreCLR.cs index 6dffb3cd2f614d..15aa39892aeb30 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Object.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Object.CoreCLR.cs @@ -10,14 +10,8 @@ public partial class Object { // Returns a Type object which represent this object instance. [Intrinsic] - public unsafe Type GetType() - { - // Throws NullReferenceException as expected - MethodTable* pMT = RuntimeHelpers.GetMethodTable(this); - Type type = Type.GetTypeFromHandleUnsafe((IntPtr)pMT); - GC.KeepAlive(this); - return type; - } + [MethodImpl(MethodImplOptions.InternalCall)] + public extern Type GetType(); // Returns a new object instance that is a memberwise copy of this // object. This is always a shallow copy of the instance. The method is protected diff --git a/src/coreclr/classlibnative/bcltype/objectnative.cpp b/src/coreclr/classlibnative/bcltype/objectnative.cpp index c94f3129ed64d9..5279b0b447022d 100644 --- a/src/coreclr/classlibnative/bcltype/objectnative.cpp +++ b/src/coreclr/classlibnative/bcltype/objectnative.cpp @@ -87,13 +87,46 @@ extern "C" void QCALLTYPE ObjectNative_GetClassHelper(MethodTable* pMT, QCall::O END_QCALL; } +NOINLINE static Object* GetClassHelper(OBJECTREF objRef) +{ + FC_INNER_PROLOG(ObjectNative::GetClass); + _ASSERTE(objRef != NULL); + TypeHandle typeHandle = objRef->GetTypeHandle(); + OBJECTREF refType = NULL; + + HELPER_METHOD_FRAME_BEGIN_RET_ATTRIB_1(Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2, refType); + + refType = typeHandle.GetManagedClassObject(); + + HELPER_METHOD_FRAME_END(); + FC_INNER_EPILOG(); + return OBJECTREFToObject(refType); +} + // This routine is called by the Object.GetType() routine. It is a major way to get the System.Type -FCIMPL1(Object*, ObjectNative::GetClassIfExists, MethodTable* pMT) +FCIMPL1(Object*, ObjectNative::GetClass, Object* pThis) { - FCALL_CONTRACT; + CONTRACTL + { + FCALL_CHECK; + INJECT_FAULT(FCThrow(kOutOfMemoryException);); + } + CONTRACTL_END; + + OBJECTREF objRef = ObjectToOBJECTREF(pThis); + if (objRef != NULL) + { + MethodTable* pMT = objRef->GetMethodTable(); + OBJECTREF typePtr = pMT->GetManagedClassObjectIfExists(); + if (typePtr != NULL) + { + return OBJECTREFToObject(typePtr); + } + } + else + FCThrow(kNullReferenceException); - OBJECTREF typePtr = pMT->GetManagedClassObjectIfExists(); - return OBJECTREFToObject(typePtr); + FC_INNER_RETURN(Object*, GetClassHelper(objRef)); } FCIMPLEND diff --git a/src/coreclr/classlibnative/bcltype/objectnative.h b/src/coreclr/classlibnative/bcltype/objectnative.h index 2e1ca19e007859..d28929f02bd36d 100644 --- a/src/coreclr/classlibnative/bcltype/objectnative.h +++ b/src/coreclr/classlibnative/bcltype/objectnative.h @@ -26,6 +26,7 @@ class ObjectNative public: static FCDECL1(INT32, TryGetHashCode, Object* vThisRef); + static FCDECL1(Object*, GetClass, Object* pThis); static FCDECL1(FC_BOOL_RET, IsLockHeld, Object* pThisUNSAFE); }; diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 3bf691e3fb9c08..cd1949c0a9633d 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -59,6 +59,10 @@ FCFuncStart(gEnumFuncs) FCFuncElement("InternalBoxEnum", ReflectionEnum::InternalBoxEnum) FCFuncEnd() +FCFuncStart(gObjectFuncs) + FCFuncElement("GetType", ObjectNative::GetClass) +FCFuncEnd() + FCFuncStart(gStringFuncs) FCDynamic("FastAllocateString", ECall::FastAllocateString) FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_ArrChar_RetVoid, ECall::CtorCharArrayManaged) @@ -589,6 +593,7 @@ FCClassElement("MetadataImport", "System.Reflection", gMetaDataImport) FCClassElement("MethodTable", "System.Runtime.CompilerServices", gMethodTableFuncs) FCClassElement("ModuleHandle", "System", gCOMModuleHandleFuncs) FCClassElement("Monitor", "System.Threading", gMonitorFuncs) +FCClassElement("Object", "System", gObjectFuncs) FCClassElement("RuntimeAssembly", "System.Reflection", gRuntimeAssemblyFuncs) FCClassElement("RuntimeFieldHandle", "System", gCOMFieldHandleNewFuncs) From fb27520a40f90086a0331f6953a95b97f9701d37 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Fri, 23 Feb 2024 00:03:56 +0800 Subject: [PATCH 07/16] Merge diff for AllocateUninitializedClone --- .../System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index df36f992a111f1..15bda211e3e014 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -232,9 +232,6 @@ public static object GetUninitializedObject( return rt.GetUninitializedObject(); } - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern object AllocateUninitializedClone(object obj); - [LibraryImport(QCall, EntryPoint = "ReflectionSerialization_GetUninitializedObject")] private static partial void GetUninitializedObject(QCallTypeHandle type, ObjectHandleOnStack retObject); From 8c45bd34f80a679d3944c5b67b44dce27e07fbe2 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Fri, 23 Feb 2024 21:52:48 +0800 Subject: [PATCH 08/16] Compare object content in native code --- .../RuntimeHelpers.CoreCLR.cs | 15 +++++------- .../classlibnative/bcltype/objectnative.cpp | 24 +++++++++++++++++++ .../classlibnative/bcltype/objectnative.h | 1 + src/coreclr/vm/ecalllist.h | 1 + 4 files changed, 32 insertions(+), 9 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index 15bda211e3e014..e85d0fabb72a1b 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -162,24 +162,21 @@ public static int GetHashCode(object? o) if (o1 is null || o2 is null) return false; - MethodTable* pMT = GetMethodTable(o1); - // If it's not a value class, don't compare by value - if (!pMT->IsValueType) + if (!o1.GetType().IsValueType) return false; // Make sure they are the same type. - if (pMT != GetMethodTable(o2)) + if (o1.GetType() != o2.GetType()) return false; // Compare the contents - uint size = pMT->GetNumInstanceFieldBytes(); - return SpanHelpers.SequenceEqual( - ref GetRawData(o1), - ref GetRawData(o2), - size); + return SequenceEqualWithReferences(o1, o2); } + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern unsafe bool SequenceEqualWithReferences(object o1, object o2); + [Obsolete("OffsetToStringData has been deprecated. Use string.GetPinnableReference() instead.")] public static int OffsetToStringData { diff --git a/src/coreclr/classlibnative/bcltype/objectnative.cpp b/src/coreclr/classlibnative/bcltype/objectnative.cpp index 5279b0b447022d..c4a04e003946ce 100644 --- a/src/coreclr/classlibnative/bcltype/objectnative.cpp +++ b/src/coreclr/classlibnative/bcltype/objectnative.cpp @@ -75,6 +75,30 @@ FCIMPL1(INT32, ObjectNative::TryGetHashCode, Object* obj) { } FCIMPLEND +FCIMPL2(FC_BOOL_RET, ObjectNative::SequenceEqualWithReferences, Object *pThisRef, Object *pCompareRef) +{ + FCALL_CONTRACT; + + // Should be ensured by caller + _ASSERTE(pThisRef != NULL); + _ASSERTE(pCompareRef != NULL); + _ASSERTE(pThisRef->GetMethodTable() == pCompareRef->GetMethodTable()); + + MethodTable *pThisMT = pThisRef->GetMethodTable(); + + // Compare the contents (size - vtable - sync block index). + DWORD dwBaseSize = pThisMT->GetBaseSize(); + BOOL ret = memcmp( + (void *) (pThisRef+1), + (void *) (pCompareRef+1), + dwBaseSize - sizeof(Object) - sizeof(int)) == 0; + + FC_GC_POLL_RET(); + + FC_RETURN_BOOL(ret); +} +FCIMPLEND + extern "C" void QCALLTYPE ObjectNative_GetClassHelper(MethodTable* pMT, QCall::ObjectHandleOnStack ret) { QCALL_CONTRACT; diff --git a/src/coreclr/classlibnative/bcltype/objectnative.h b/src/coreclr/classlibnative/bcltype/objectnative.h index d28929f02bd36d..2b5e3c7889636b 100644 --- a/src/coreclr/classlibnative/bcltype/objectnative.h +++ b/src/coreclr/classlibnative/bcltype/objectnative.h @@ -26,6 +26,7 @@ class ObjectNative public: static FCDECL1(INT32, TryGetHashCode, Object* vThisRef); + static FCDECL2(FC_BOOL_RET, SequenceEqualWithReferences, Object *pThisRef, Object *pCompareRef); static FCDECL1(Object*, GetClass, Object* pThis); static FCDECL1(FC_BOOL_RET, IsLockHeld, Object* pThisUNSAFE); }; diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index cd1949c0a9633d..b4b9523aa61823 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -470,6 +470,7 @@ FCFuncStart(gRuntimeHelpers) FCFuncElement("GetSpanDataFrom", ArrayNative::GetSpanDataFrom) FCFuncElement("PrepareDelegate", ReflectionInvocation::PrepareDelegate) FCFuncElement("TryGetHashCode", ObjectNative::TryGetHashCode) + FCFuncElement("Equals", ObjectNative::SequenceEqualWithReferences) FCFuncElement("EnsureSufficientExecutionStack", ReflectionInvocation::EnsureSufficientExecutionStack) FCFuncElement("TryEnsureSufficientExecutionStack", ReflectionInvocation::TryEnsureSufficientExecutionStack) FCFuncElement("AllocTailCallArgBuffer", TailCallHelp::AllocTailCallArgBuffer) From 84ff6d18c6fd33b0b50d6462b70196de0288a4ca Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Fri, 23 Feb 2024 23:17:56 +0800 Subject: [PATCH 09/16] Fix return --- src/coreclr/classlibnative/bcltype/objectnative.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/coreclr/classlibnative/bcltype/objectnative.cpp b/src/coreclr/classlibnative/bcltype/objectnative.cpp index c4a04e003946ce..a0501c083d520e 100644 --- a/src/coreclr/classlibnative/bcltype/objectnative.cpp +++ b/src/coreclr/classlibnative/bcltype/objectnative.cpp @@ -32,6 +32,8 @@ extern "C" INT32 QCALLTYPE ObjectNative_GetHashCodeHelper(QCall::ObjectHandleOnS ret = objHandle.Get()->GetHashCodeEx(); END_QCALL; + + return ret; } FCIMPL1(INT32, ObjectNative::TryGetHashCode, Object* obj) { From 46e9b7cdc12eae8a729c57dd641e870811270a30 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Sat, 24 Feb 2024 02:02:50 +0800 Subject: [PATCH 10/16] Revert changes around GetHashCode --- .../RuntimeHelpers.CoreCLR.cs | 17 +---- .../classlibnative/bcltype/objectnative.cpp | 62 ++++++++++++++++--- .../classlibnative/bcltype/objectnative.h | 2 +- src/coreclr/vm/ecalllist.h | 1 + src/coreclr/vm/qcallentrypoints.cpp | 1 - 5 files changed, 58 insertions(+), 25 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index e85d0fabb72a1b..259070bf4f8437 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -125,21 +125,8 @@ public static unsafe void PrepareMethod(RuntimeMethodHandle method, RuntimeTypeH [MethodImpl(MethodImplOptions.InternalCall)] public static extern void PrepareDelegate(Delegate d); - public static int GetHashCode(object? o) - { - if (o is null) - return 0; - - int hashCode = TryGetHashCode(o); - if (hashCode != 0) - return hashCode; - - object objRef = o; - return GetHashCodeHelper(ObjectHandleOnStack.Create(ref objRef)); - } - - [LibraryImport(QCall, EntryPoint = "ObjectNative_GetHashCodeHelper")] - private static partial int GetHashCodeHelper(ObjectHandleOnStack objHandle); + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern int GetHashCode(object? o); /// /// If a hash code has been assigned to the object, it is returned. Otherwise zero is diff --git a/src/coreclr/classlibnative/bcltype/objectnative.cpp b/src/coreclr/classlibnative/bcltype/objectnative.cpp index a0501c083d520e..fcbed75d8e9c31 100644 --- a/src/coreclr/classlibnative/bcltype/objectnative.cpp +++ b/src/coreclr/classlibnative/bcltype/objectnative.cpp @@ -20,21 +20,67 @@ #include "eeconfig.h" -extern "C" INT32 QCALLTYPE ObjectNative_GetHashCodeHelper(QCall::ObjectHandleOnStack objHandle) +NOINLINE static INT32 GetHashCodeHelper(OBJECTREF objRef) { - QCALL_CONTRACT; + DWORD idx = 0; - INT32 ret = 0; + FC_INNER_PROLOG(ObjectNative::GetHashCode); - BEGIN_QCALL; + HELPER_METHOD_FRAME_BEGIN_RET_ATTRIB_1(Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2, objRef); - GCX_COOP(); - ret = objHandle.Get()->GetHashCodeEx(); + idx = objRef->GetHashCodeEx(); - END_QCALL; + HELPER_METHOD_FRAME_END(); + FC_INNER_EPILOG(); + return idx; +} + +// Note that we obtain a sync block index without actually building a sync block. +// That's because a lot of objects are hashed, without requiring support for +FCIMPL1(INT32, ObjectNative::GetHashCode, Object* obj) { + + CONTRACTL + { + FCALL_CHECK; + INJECT_FAULT(FCThrow(kOutOfMemoryException);); + } + CONTRACTL_END; + + VALIDATEOBJECT(obj); + + if (obj == 0) + return 0; + + OBJECTREF objRef(obj); - return ret; + { + DWORD bits = objRef->GetHeader()->GetBits(); + + if (bits & BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX) + { + if (bits & BIT_SBLK_IS_HASHCODE) + { + // Common case: the object already has a hash code + return bits & MASK_HASHCODE; + } + else + { + // We have a sync block index. This means if we already have a hash code, + // it is in the sync block, otherwise we generate a new one and store it there + SyncBlock *psb = objRef->PassiveGetSyncBlock(); + if (psb != NULL) + { + DWORD hashCode = psb->GetHashCode(); + if (hashCode != 0) + return hashCode; + } + } + } + } + + FC_INNER_RETURN(INT32, GetHashCodeHelper(objRef)); } +FCIMPLEND FCIMPL1(INT32, ObjectNative::TryGetHashCode, Object* obj) { diff --git a/src/coreclr/classlibnative/bcltype/objectnative.h b/src/coreclr/classlibnative/bcltype/objectnative.h index 2b5e3c7889636b..5db574a59ee354 100644 --- a/src/coreclr/classlibnative/bcltype/objectnative.h +++ b/src/coreclr/classlibnative/bcltype/objectnative.h @@ -25,13 +25,13 @@ class ObjectNative { public: + static FCDECL1(INT32, GetHashCode, Object* vThisRef); static FCDECL1(INT32, TryGetHashCode, Object* vThisRef); static FCDECL2(FC_BOOL_RET, SequenceEqualWithReferences, Object *pThisRef, Object *pCompareRef); static FCDECL1(Object*, GetClass, Object* pThis); static FCDECL1(FC_BOOL_RET, IsLockHeld, Object* pThisUNSAFE); }; -extern "C" INT32 QCALLTYPE ObjectNative_GetHashCodeHelper(QCall::ObjectHandleOnStack objHandle); 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); diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index b4b9523aa61823..8802d753788812 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -469,6 +469,7 @@ FCFuncStart(gRuntimeHelpers) FCFuncElement("InitializeArray", ArrayNative::InitializeArray) FCFuncElement("GetSpanDataFrom", ArrayNative::GetSpanDataFrom) FCFuncElement("PrepareDelegate", ReflectionInvocation::PrepareDelegate) + FCFuncElement("GetHashCode", ObjectNative::GetHashCode) FCFuncElement("TryGetHashCode", ObjectNative::TryGetHashCode) FCFuncElement("Equals", ObjectNative::SequenceEqualWithReferences) FCFuncElement("EnsureSufficientExecutionStack", ReflectionInvocation::EnsureSufficientExecutionStack) diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index 82ca94ed256485..2d6466fc7d7c75 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -324,7 +324,6 @@ static const Entry s_QCall[] = DllImportEntry(GetFileLoadExceptionMessage) DllImportEntry(FileLoadException_GetMessageForHR) DllImportEntry(Interlocked_MemoryBarrierProcessWide) - DllImportEntry(ObjectNative_GetHashCodeHelper) DllImportEntry(ObjectNative_AllocateUninitializedClone) DllImportEntry(Monitor_Wait) DllImportEntry(Monitor_Pulse) From 1bac399dd7d72cdbe36a4571fabc2c49825bf3ae Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Sat, 24 Feb 2024 02:05:26 +0800 Subject: [PATCH 11/16] Remove unused functions --- .../CompilerServices/RuntimeHelpers.CoreCLR.cs | 3 --- src/coreclr/classlibnative/bcltype/objectnative.cpp | 12 ------------ 2 files changed, 15 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index 259070bf4f8437..b61e113bdc06b8 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -216,9 +216,6 @@ public static object GetUninitializedObject( return rt.GetUninitializedObject(); } - [LibraryImport(QCall, EntryPoint = "ReflectionSerialization_GetUninitializedObject")] - private static partial void GetUninitializedObject(QCallTypeHandle type, ObjectHandleOnStack retObject); - [LibraryImport(QCall, EntryPoint = "ObjectNative_AllocateUninitializedClone")] internal static partial void AllocateUninitializedClone(ObjectHandleOnStack objHandle); diff --git a/src/coreclr/classlibnative/bcltype/objectnative.cpp b/src/coreclr/classlibnative/bcltype/objectnative.cpp index fcbed75d8e9c31..34bd6648cacfd6 100644 --- a/src/coreclr/classlibnative/bcltype/objectnative.cpp +++ b/src/coreclr/classlibnative/bcltype/objectnative.cpp @@ -147,18 +147,6 @@ FCIMPL2(FC_BOOL_RET, ObjectNative::SequenceEqualWithReferences, Object *pThisRef } FCIMPLEND -extern "C" void QCALLTYPE ObjectNative_GetClassHelper(MethodTable* pMT, QCall::ObjectHandleOnStack ret) -{ - QCALL_CONTRACT; - - BEGIN_QCALL; - - GCX_COOP(); - ret.Set(pMT->GetManagedClassObject()); - - END_QCALL; -} - NOINLINE static Object* GetClassHelper(OBJECTREF objRef) { FC_INNER_PROLOG(ObjectNative::GetClass); From e041418050943c34927ec1a93a4e9f9a91e17c8b Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Sat, 24 Feb 2024 21:39:07 +0800 Subject: [PATCH 12/16] Do not compare padding in Equals --- .../Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs | 6 ++++-- src/coreclr/classlibnative/bcltype/objectnative.cpp | 9 ++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index b61e113bdc06b8..783e73f5c7818b 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -149,12 +149,14 @@ public static unsafe void PrepareMethod(RuntimeMethodHandle method, RuntimeTypeH if (o1 is null || o2 is null) return false; + MethodTable* pMT = GetMethodTable(o1); + // If it's not a value class, don't compare by value - if (!o1.GetType().IsValueType) + if (!pMT->IsValueType) return false; // Make sure they are the same type. - if (o1.GetType() != o2.GetType()) + if (pMT != GetMethodTable(o2)) return false; // Compare the contents diff --git a/src/coreclr/classlibnative/bcltype/objectnative.cpp b/src/coreclr/classlibnative/bcltype/objectnative.cpp index 34bd6648cacfd6..558c0b2802d11b 100644 --- a/src/coreclr/classlibnative/bcltype/objectnative.cpp +++ b/src/coreclr/classlibnative/bcltype/objectnative.cpp @@ -134,12 +134,11 @@ FCIMPL2(FC_BOOL_RET, ObjectNative::SequenceEqualWithReferences, Object *pThisRef MethodTable *pThisMT = pThisRef->GetMethodTable(); - // Compare the contents (size - vtable - sync block index). - DWORD dwBaseSize = pThisMT->GetBaseSize(); + // Compare the contents BOOL ret = memcmp( - (void *) (pThisRef+1), - (void *) (pCompareRef+1), - dwBaseSize - sizeof(Object) - sizeof(int)) == 0; + pThisRef->GetData(), + pCompareRef->GetData(), + pThisMT->GetNumInstanceFieldBytes()) == 0; FC_GC_POLL_RET(); From e7bbdd41716c9c97b9f7cb35754a27436635c7d8 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Sun, 25 Feb 2024 16:37:10 +0800 Subject: [PATCH 13/16] Fix assert touching OBJECTREF in preemptive mode --- src/coreclr/classlibnative/bcltype/objectnative.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/classlibnative/bcltype/objectnative.cpp b/src/coreclr/classlibnative/bcltype/objectnative.cpp index 558c0b2802d11b..95df7187834cac 100644 --- a/src/coreclr/classlibnative/bcltype/objectnative.cpp +++ b/src/coreclr/classlibnative/bcltype/objectnative.cpp @@ -193,12 +193,12 @@ extern "C" void QCALLTYPE ObjectNative_AllocateUninitializedClone(QCall::ObjectH { QCALL_CONTRACT; - _ASSERTE(objHandle.Get() != NULL); // Should be handled at managed side - BEGIN_QCALL; GCX_COOP(); + OBJECTREF refClone = objHandle.Get(); + _ASSERTE(refClone != NULL); // Should be handled at managed side MethodTable* pMT = refClone->GetMethodTable(); // assert that String has overloaded the Clone() method From 2d46bc01b33dfb787ce6ef8c1801da88d629e00e Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Sun, 25 Feb 2024 17:12:53 +0800 Subject: [PATCH 14/16] Add test coverage for Equals --- .../Runtime/CompilerServices/RuntimeHelpersTests.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs index 7359e11283f740..caad7e1dfd3ab7 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs @@ -68,6 +68,15 @@ public static unsafe void GetObjectValue() Assert.Equal(i, (int)iOV); } + [Fact] + public static void EqualsTest() + { + // Boolean RuntimeHelpers.Equals(Object, Object) + + Assert.True(RuntimeHelpers.Equals(Guid.Empty, Guid.Empty)); + Assert.False(RuntimeHelpers.Equals(Guid.Empty, Guid.NewGuid())); + } + [Fact] public static void InitializeArray() { From 633f52500e57b65316ff4941f429fff4e5ad78a4 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Mon, 26 Feb 2024 22:27:25 +0800 Subject: [PATCH 15/16] Fix FCall method binding --- .../System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs | 4 ++-- src/coreclr/classlibnative/bcltype/objectnative.cpp | 2 +- src/coreclr/classlibnative/bcltype/objectnative.h | 2 +- src/coreclr/vm/ecalllist.h | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index 783e73f5c7818b..733e3a664bcc52 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -160,11 +160,11 @@ public static unsafe void PrepareMethod(RuntimeMethodHandle method, RuntimeTypeH return false; // Compare the contents - return SequenceEqualWithReferences(o1, o2); + return ContentEquals(o1, o2); } [MethodImpl(MethodImplOptions.InternalCall)] - private static extern unsafe bool SequenceEqualWithReferences(object o1, object o2); + private static extern unsafe bool ContentEquals(object o1, object o2); [Obsolete("OffsetToStringData has been deprecated. Use string.GetPinnableReference() instead.")] public static int OffsetToStringData diff --git a/src/coreclr/classlibnative/bcltype/objectnative.cpp b/src/coreclr/classlibnative/bcltype/objectnative.cpp index 95df7187834cac..afbda5fad99167 100644 --- a/src/coreclr/classlibnative/bcltype/objectnative.cpp +++ b/src/coreclr/classlibnative/bcltype/objectnative.cpp @@ -123,7 +123,7 @@ FCIMPL1(INT32, ObjectNative::TryGetHashCode, Object* obj) { } FCIMPLEND -FCIMPL2(FC_BOOL_RET, ObjectNative::SequenceEqualWithReferences, Object *pThisRef, Object *pCompareRef) +FCIMPL2(FC_BOOL_RET, ObjectNative::ContentEquals, Object *pThisRef, Object *pCompareRef) { FCALL_CONTRACT; diff --git a/src/coreclr/classlibnative/bcltype/objectnative.h b/src/coreclr/classlibnative/bcltype/objectnative.h index 5db574a59ee354..418fd2561d7cc8 100644 --- a/src/coreclr/classlibnative/bcltype/objectnative.h +++ b/src/coreclr/classlibnative/bcltype/objectnative.h @@ -27,7 +27,7 @@ class ObjectNative static FCDECL1(INT32, GetHashCode, Object* vThisRef); static FCDECL1(INT32, TryGetHashCode, Object* vThisRef); - static FCDECL2(FC_BOOL_RET, SequenceEqualWithReferences, Object *pThisRef, Object *pCompareRef); + static FCDECL2(FC_BOOL_RET, ContentEquals, Object *pThisRef, Object *pCompareRef); static FCDECL1(Object*, GetClass, Object* pThis); static FCDECL1(FC_BOOL_RET, IsLockHeld, Object* pThisUNSAFE); }; diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 8802d753788812..9da9ebdb102a6f 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -471,7 +471,7 @@ FCFuncStart(gRuntimeHelpers) FCFuncElement("PrepareDelegate", ReflectionInvocation::PrepareDelegate) FCFuncElement("GetHashCode", ObjectNative::GetHashCode) FCFuncElement("TryGetHashCode", ObjectNative::TryGetHashCode) - FCFuncElement("Equals", ObjectNative::SequenceEqualWithReferences) + FCFuncElement("ContentEquals", ObjectNative::ContentEquals) FCFuncElement("EnsureSufficientExecutionStack", ReflectionInvocation::EnsureSufficientExecutionStack) FCFuncElement("TryEnsureSufficientExecutionStack", ReflectionInvocation::TryEnsureSufficientExecutionStack) FCFuncElement("AllocTailCallArgBuffer", TailCallHelp::AllocTailCallArgBuffer) From f2a0fc72eb780d393e5b2267c0aec730722d09f6 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Mon, 26 Feb 2024 23:42:29 +0800 Subject: [PATCH 16/16] Add more test cases --- .../Runtime/CompilerServices/RuntimeHelpersTests.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs index caad7e1dfd3ab7..0aea60fd1921e0 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs @@ -75,6 +75,17 @@ public static void EqualsTest() Assert.True(RuntimeHelpers.Equals(Guid.Empty, Guid.Empty)); Assert.False(RuntimeHelpers.Equals(Guid.Empty, Guid.NewGuid())); + + // Reference equal + object o = new object(); + Assert.True(RuntimeHelpers.Equals(o, o)); + + // Type mismatch + Assert.False(RuntimeHelpers.Equals(Guid.Empty, string.Empty)); + + // Non value types + Assert.False(RuntimeHelpers.Equals(new object(), new object())); + Assert.False(RuntimeHelpers.Equals(new int[] { 1, 2, 3 }, new int[] { 1, 2, 3 })); } [Fact]