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 86afe347af9c8..0c85e6a43a80a 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
@@ -114,6 +114,17 @@ public static unsafe void PrepareMethod(RuntimeMethodHandle method, RuntimeTypeH
[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
+ /// returned.
+ ///
+ ///
+ /// The advantage of this over is that it avoids assigning a hash
+ /// code to the object if it does not already have one.
+ ///
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal static extern int TryGetHashCode(object o);
+
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern new bool Equals(object? o1, object? o2);
diff --git a/src/coreclr/classlibnative/bcltype/objectnative.cpp b/src/coreclr/classlibnative/bcltype/objectnative.cpp
index 4b93d41631d2d..01192057756d7 100644
--- a/src/coreclr/classlibnative/bcltype/objectnative.cpp
+++ b/src/coreclr/classlibnative/bcltype/objectnative.cpp
@@ -128,6 +128,47 @@ FCIMPL1(INT32, ObjectNative::GetHashCode, Object* obj) {
}
FCIMPLEND
+FCIMPL1(INT32, ObjectNative::TryGetHashCode, Object* obj) {
+
+ CONTRACTL
+ {
+ FCALL_CHECK;
+ }
+ CONTRACTL_END;
+
+ VALIDATEOBJECT(obj);
+
+ if (obj == 0)
+ return 0;
+
+ OBJECTREF objRef(obj);
+
+ {
+ 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. There may be a hash code stored within the sync block.
+ SyncBlock *psb = objRef->PassiveGetSyncBlock();
+ if (psb != NULL)
+ {
+ return psb->GetHashCode();
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+FCIMPLEND
+
//
// Compare by ref for normal classes, by value for value types.
//
diff --git a/src/coreclr/classlibnative/bcltype/objectnative.h b/src/coreclr/classlibnative/bcltype/objectnative.h
index c700ff2394987..819469a983458 100644
--- a/src/coreclr/classlibnative/bcltype/objectnative.h
+++ b/src/coreclr/classlibnative/bcltype/objectnative.h
@@ -30,6 +30,7 @@ class ObjectNative
// If the Class object doesn't exist then you must call the GetClass() method.
static FCDECL1(Object*, GetObjectValue, Object* vThisRef);
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);
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs
index 5b8a86cee2878..29e92efb817fc 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs
@@ -104,6 +104,19 @@ public static unsafe int GetHashCode(object o)
return ObjectHeader.GetHashCode(o);
}
+ ///
+ /// If a hash code has been assigned to the object, it is returned. Otherwise zero is
+ /// returned.
+ ///
+ ///
+ /// The advantage of this over is that it avoids assigning a hash
+ /// code to the object if it does not already have one.
+ ///
+ internal static int TryGetHashCode(object o)
+ {
+ return ObjectHeader.TryGetHashCode(o);
+ }
+
[Obsolete("OffsetToStringData has been deprecated. Use string.GetPinnableReference() instead.")]
public static int OffsetToStringData
{
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ObjectHeader.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ObjectHeader.cs
index 0de12219eb913..327586771fe07 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ObjectHeader.cs
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ObjectHeader.cs
@@ -85,6 +85,38 @@ public static unsafe int GetHashCode(object o)
}
}
+ ///
+ /// If a hash code has been assigned to the object, it is returned. Otherwise zero is
+ /// returned.
+ ///
+ public static unsafe int TryGetHashCode(object o)
+ {
+ if (o == null)
+ return 0;
+
+ fixed (MethodTable** ppMethodTable = &o.GetMethodTableRef())
+ {
+ int* pHeader = GetHeaderPtr(ppMethodTable);
+ int bits = *pHeader;
+ int hashOrIndex = bits & MASK_HASHCODE_INDEX;
+ if ((bits & BIT_SBLK_IS_HASHCODE) != 0)
+ {
+ // Found the hash code in the header
+ Debug.Assert(hashOrIndex != 0);
+ return hashOrIndex;
+ }
+
+ if ((bits & BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX) != 0)
+ {
+ // Look up the hash code in the SyncTable
+ return SyncTable.GetHashCode(hashOrIndex);
+ }
+
+ // The hash code has not yet been set.
+ return 0;
+ }
+ }
+
///
/// Assigns a hash code to the object in a thread-safe way.
///
diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h
index 9015de9e8a90a..edcb2afccdd9f 100644
--- a/src/coreclr/vm/ecalllist.h
+++ b/src/coreclr/vm/ecalllist.h
@@ -552,6 +552,7 @@ FCFuncStart(gRuntimeHelpers)
FCFuncElement("GetSpanDataFrom", ArrayNative::GetSpanDataFrom)
FCFuncElement("PrepareDelegate", ReflectionInvocation::PrepareDelegate)
FCFuncElement("GetHashCode", ObjectNative::GetHashCode)
+ FCFuncElement("TryGetHashCode", ObjectNative::TryGetHashCode)
FCFuncElement("Equals", ObjectNative::Equals)
FCFuncElement("AllocateUninitializedClone", ObjectNative::AllocateUninitializedClone)
FCFuncElement("EnsureSufficientExecutionStack", ReflectionInvocation::EnsureSufficientExecutionStack)
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConditionalWeakTable.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConditionalWeakTable.cs
index 4233c291010e5..0c9806eadf4cf 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConditionalWeakTable.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConditionalWeakTable.cs
@@ -48,7 +48,7 @@ public ConditionalWeakTable()
///
/// Returns "true" if key was found, "false" otherwise.
///
- /// The key may get garbaged collected during the TryGetValue operation. If so, TryGetValue
+ /// The key may get garbage collected during the TryGetValue operation. If so, TryGetValue
/// may at its discretion, return "false" and set "value" to the default (as if the key was not present.)
///
public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value)
@@ -538,7 +538,17 @@ internal int FindEntry(TKey key, out object? value)
{
Debug.Assert(key != null); // Key already validated as non-null.
- int hashCode = RuntimeHelpers.GetHashCode(key) & int.MaxValue;
+ int hashCode = RuntimeHelpers.TryGetHashCode(key);
+
+ if (hashCode == 0)
+ {
+ // No hash code has been assigned to the key, so therefore it has not been added
+ // to any ConditionalWeakTable.
+ value = null;
+ return -1;
+ }
+
+ hashCode &= int.MaxValue;
int bucket = hashCode & (_buckets.Length - 1);
for (int entriesIndex = Volatile.Read(ref _buckets[bucket]); entriesIndex != -1; entriesIndex = _entries[entriesIndex].Next)
{
diff --git a/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs
index 9d49d158a457f..862bdeb00eee1 100644
--- a/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs
+++ b/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs
@@ -44,6 +44,22 @@ public static int GetHashCode(object? o)
return InternalGetHashCode(o);
}
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private static extern int InternalTryGetHashCode(object? o);
+
+ ///
+ /// If a hash code has been assigned to the object, it is returned. Otherwise zero is
+ /// returned.
+ ///
+ ///
+ /// The advantage of this over is that it avoids assigning a hash
+ /// code to the object if it does not already have one.
+ ///
+ public static int TryGetHashCode(object? o)
+ {
+ return InternalTryGetHashCode(o);
+ }
+
public static new bool Equals(object? o1, object? o2)
{
if (o1 == o2)
diff --git a/src/mono/mono/metadata/icall-def.h b/src/mono/mono/metadata/icall-def.h
index b310ee2730cc2..eaad5ea49b736 100644
--- a/src/mono/mono/metadata/icall-def.h
+++ b/src/mono/mono/metadata/icall-def.h
@@ -427,7 +427,8 @@ HANDLES(RUNH_1, "GetObjectValue", ves_icall_System_Runtime_CompilerServices_Runt
HANDLES(RUNH_6, "GetSpanDataFrom", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_GetSpanDataFrom, gpointer, 3, (MonoClassField_ptr, MonoType_ptr, gpointer))
HANDLES(RUNH_2, "GetUninitializedObjectInternal", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_GetUninitializedObjectInternal, MonoObject, 1, (MonoType_ptr))
HANDLES(RUNH_3, "InitializeArray", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_InitializeArray, void, 2, (MonoArray, MonoClassField_ptr))
-HANDLES(RUNH_7, "InternalGetHashCode", mono_object_hash_icall, int, 1, (MonoObject))
+HANDLES(RUNH_7, "InternalGetHashCode", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_InternalGetHashCode, int, 1, (MonoObject))
+HANDLES(RUNH_8, "InternalTryGetHashCode", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_InternalTryGetHashCode, int, 1, (MonoObject))
HANDLES(RUNH_3a, "PrepareMethod", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_PrepareMethod, void, 3, (MonoMethod_ptr, gpointer, int))
HANDLES(RUNH_4, "RunClassConstructor", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_RunClassConstructor, void, 1, (MonoType_ptr))
HANDLES(RUNH_5, "RunModuleConstructor", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_RunModuleConstructor, void, 1, (MonoImage_ptr))
diff --git a/src/mono/mono/metadata/icall.c b/src/mono/mono/metadata/icall.c
index 34a4f476e7d61..8c00f3792904d 100644
--- a/src/mono/mono/metadata/icall.c
+++ b/src/mono/mono/metadata/icall.c
@@ -1072,6 +1072,18 @@ ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_InitializeArray (MonoAr
#endif
}
+int
+ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_InternalGetHashCode (MonoObjectHandle obj, MonoError* error)
+{
+ return mono_object_hash_internal (MONO_HANDLE_RAW (obj));
+}
+
+int
+ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_InternalTryGetHashCode (MonoObjectHandle obj, MonoError* error)
+{
+ return mono_object_try_get_hash_internal (MONO_HANDLE_RAW (obj));
+}
+
MonoObjectHandle
ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_GetObjectValue (MonoObjectHandle obj, MonoError *error)
{
diff --git a/src/mono/mono/metadata/monitor.c b/src/mono/mono/metadata/monitor.c
index fc3487b38776e..322ff8459cb3b 100644
--- a/src/mono/mono/metadata/monitor.c
+++ b/src/mono/mono/metadata/monitor.c
@@ -515,6 +515,12 @@ mono_monitor_inflate (MonoObject *obj)
#define MONO_OBJECT_ALIGNMENT_SHIFT 3
+/*
+ * Wang's address-based hash function:
+ * http://www.concentric.net/~Ttwang/tech/addrhash.htm
+ */
+#define HASH_OBJECT(obj) (GPOINTER_TO_UINT (obj) >> MONO_OBJECT_ALIGNMENT_SHIFT) * 2654435761u
+
int
mono_object_hash_internal (MonoObject* obj)
{
@@ -542,11 +548,14 @@ mono_object_hash_internal (MonoObject* obj)
* another thread computes the hash at the same time, because it'll end up
* with the same value.
*/
- hash = (GPOINTER_TO_UINT (obj) >> MONO_OBJECT_ALIGNMENT_SHIFT) * 2654435761u;
+ hash = HASH_OBJECT(obj);
#if SIZEOF_VOID_P == 4
/* clear the top bits as they can be discarded */
hash &= ~(LOCK_WORD_STATUS_MASK << (32 - LOCK_WORD_STATUS_BITS));
#endif
+ if (hash == 0) {
+ hash = 1;
+ }
if (lock_word_is_free (lw)) {
LockWord old_lw;
lw = lock_word_new_thin_hash (hash);
@@ -581,19 +590,48 @@ mono_object_hash_internal (MonoObject* obj)
#else
-/*
- * Wang's address-based hash function:
- * http://www.concentric.net/~Ttwang/tech/addrhash.htm
- */
- return (GPOINTER_TO_UINT (obj) >> MONO_OBJECT_ALIGNMENT_SHIFT) * 2654435761u;
+ unsigned int hash = HASH_OBJECT(obj);
+ if (hash == 0) {
+ hash = 1;
+ }
+ return hash;
+
#endif
}
int
-mono_object_hash_icall (MonoObjectHandle obj, MonoError* error)
+mono_object_try_get_hash_internal (MonoObject* obj)
{
- return mono_object_hash_internal (MONO_HANDLE_RAW (obj));
+#ifdef HAVE_MOVING_COLLECTOR
+
+ LockWord lw;
+ if (!obj)
+ return 0;
+ lw.sync = obj->synchronisation;
+
+ LOCK_DEBUG (g_message("%s: (%d) Get hash for object %p; LW = %p", __func__, mono_thread_info_get_small_id (), obj, obj->synchronisation));
+
+ if (lock_word_has_hash (lw)) {
+ if (lock_word_is_inflated (lw)) {
+ return lock_word_get_inflated_lock (lw)->hash_code;
+ } else {
+ return lock_word_get_hash (lw);
+ }
+ }
+
+ return 0;
+
+#else
+
+ unsigned int hash = HASH_OBJECT(obj);
+ if (hash == 0) {
+ hash = 1;
+ }
+ return hash;
+
+#endif
+
}
/*
diff --git a/src/mono/mono/metadata/object-internals.h b/src/mono/mono/metadata/object-internals.h
index 119d54ffd800c..dd904a92269f6 100644
--- a/src/mono/mono/metadata/object-internals.h
+++ b/src/mono/mono/metadata/object-internals.h
@@ -1997,6 +1997,9 @@ mono_string_hash_internal (MonoString *s);
MONO_COMPONENT_API int
mono_object_hash_internal (MonoObject* obj);
+int
+mono_object_try_get_hash_internal (MonoObject* obj);
+
ICALL_EXPORT
void
mono_value_copy_internal (void* dest, const void* src, MonoClass *klass);
diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c
index 65e623a4a0310..5d844c4ed8fda 100644
--- a/src/mono/mono/mini/interp/interp.c
+++ b/src/mono/mono/mini/interp/interp.c
@@ -7510,6 +7510,11 @@ MINT_IN_CASE(MINT_BRTRUE_I8_SP) ZEROP_SP(gint64, !=); MINT_IN_BREAK;
ip += 3;
MINT_IN_BREAK;
}
+ MINT_IN_CASE(MINT_INTRINS_TRY_GET_HASHCODE) {
+ LOCAL_VAR (ip [1], gint32) = mono_object_try_get_hash_internal (LOCAL_VAR (ip [2], MonoObject*));
+ ip += 3;
+ MINT_IN_BREAK;
+ }
MINT_IN_CASE(MINT_INTRINS_GET_TYPE) {
MonoObject *o = LOCAL_VAR (ip [2], MonoObject*);
NULL_CHECK (o);
diff --git a/src/mono/mono/mini/interp/mintops.def b/src/mono/mono/mini/interp/mintops.def
index b550fb0737024..444ceb417b59d 100644
--- a/src/mono/mono/mini/interp/mintops.def
+++ b/src/mono/mono/mini/interp/mintops.def
@@ -794,6 +794,7 @@ OPDEF(MINT_TIER_PATCHPOINT, "tier_patchpoint", 2, 0, 0, MintOpShortInt)
OPDEF(MINT_INTRINS_ENUM_HASFLAG, "intrins_enum_hasflag", 5, 1, 2, MintOpClassToken)
OPDEF(MINT_INTRINS_GET_HASHCODE, "intrins_get_hashcode", 3, 1, 1, MintOpNoArgs)
+OPDEF(MINT_INTRINS_TRY_GET_HASHCODE, "intrins_try_get_hashcode", 3, 1, 1, MintOpNoArgs)
OPDEF(MINT_INTRINS_GET_TYPE, "intrins_get_type", 3, 1, 1, MintOpNoArgs)
OPDEF(MINT_INTRINS_SPAN_CTOR, "intrins_span_ctor", 4, 1, 2, MintOpNoArgs)
OPDEF(MINT_INTRINS_UNSAFE_BYTE_OFFSET, "intrins_unsafe_byte_offset", 4, 1, 2, MintOpNoArgs)
diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c
index 6eab4941ae150..76fac6389eb50 100644
--- a/src/mono/mono/mini/interp/transform.c
+++ b/src/mono/mono/mini/interp/transform.c
@@ -2368,6 +2368,10 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas
interp_ins_set_dreg (td->last_ins, td->sp [-1].local);
td->ip += 5;
return TRUE;
+ } else if (!strcmp (tm, "InternalGetHashCode")) {
+ *op = MINT_INTRINS_GET_HASHCODE;
+ } else if (!strcmp (tm, "InternalTryGetHashCode")) {
+ *op = MINT_INTRINS_TRY_GET_HASHCODE;
} else if (!strcmp (tm, "GetRawData")) {
interp_add_ins (td, MINT_LDFLDA_UNSAFE);
td->last_ins->data [0] = (gint16) MONO_ABI_SIZEOF (MonoObject);
@@ -2426,9 +2430,7 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas
td->sp [-1].klass == mono_defaults.runtimetype_class && td->sp [-2].klass == mono_defaults.runtimetype_class) {
*op = MINT_CNE_P;
} else if (in_corlib && target_method->klass == mono_defaults.object_class) {
- if (!strcmp (tm, "InternalGetHashCode")) {
- *op = MINT_INTRINS_GET_HASHCODE;
- } else if (!strcmp (tm, "GetType")) {
+ if (!strcmp (tm, "GetType")) {
if (constrained_class && m_class_is_valuetype (constrained_class) && !mono_class_is_nullable (constrained_class)) {
// If constrained_class is valuetype we already know its type.
// Resolve GetType to a constant so we can fold type comparisons
diff --git a/src/mono/mono/mini/intrinsics.c b/src/mono/mono/mini/intrinsics.c
index d8d6020ddf30c..df07e02cc39ae 100644
--- a/src/mono/mono/mini/intrinsics.c
+++ b/src/mono/mono/mini/intrinsics.c
@@ -877,15 +877,6 @@ mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign
mini_type_to_eval_stack_type (cfg, fsig->ret, ins);
ins->klass = mono_defaults.runtimetype_class;
*ins_type_initialized = TRUE;
- return ins;
- } else if (!cfg->backend->emulate_mul_div && strcmp (cmethod->name, "InternalGetHashCode") == 0 && fsig->param_count == 1 && !mono_gc_is_moving ()) {
- int dreg = alloc_ireg (cfg);
- int t1 = alloc_ireg (cfg);
-
- MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHR_IMM, t1, args [0]->dreg, 3);
- EMIT_NEW_BIALU_IMM (cfg, ins, OP_MUL_IMM, dreg, t1, 2654435761u);
- ins->type = STACK_I4;
-
return ins;
} else if (strcmp (cmethod->name, ".ctor") == 0 && fsig->param_count == 0) {
MONO_INST_NEW (cfg, ins, OP_NOP);
diff --git a/src/tests/Interop/ObjectiveC/ObjectiveCMarshalAPI/Program.cs b/src/tests/Interop/ObjectiveC/ObjectiveCMarshalAPI/Program.cs
index de5badeb0938f..a06a4327a78a0 100644
--- a/src/tests/Interop/ObjectiveC/ObjectiveCMarshalAPI/Program.cs
+++ b/src/tests/Interop/ObjectiveC/ObjectiveCMarshalAPI/Program.cs
@@ -10,6 +10,7 @@ namespace ObjectiveCMarshalAPI
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ObjectiveC;
+ using System.Threading;
using Xunit;
@@ -132,6 +133,39 @@ class DerivedWithFinalizer : Base
[ObjectiveCTrackedTypeAttribute]
class AttributedNoFinalizer { }
+ class HasNoHashCode : Base
+ {
+ }
+
+ class HasHashCode : Base
+ {
+ public HasHashCode()
+ {
+ // this will write a hash code into the object header.
+ RuntimeHelpers.GetHashCode(this);
+ }
+ }
+
+ class HasThinLockHeld : Base
+ {
+ public HasThinLockHeld()
+ {
+ // This will write lock information into the object header.
+ // An attempt to generate a hash code for this object will cause the lock to be
+ // upgrade to a thick lock.
+ Monitor.Enter(this);
+ }
+ }
+
+ class HasSyncBlock : Base
+ {
+ public HasSyncBlock()
+ {
+ RuntimeHelpers.GetHashCode(this);
+ Monitor.Enter(this);
+ }
+ }
+
static void InitializeObjectiveCMarshal()
{
delegate* unmanaged beginEndCallback;
@@ -171,6 +205,12 @@ static void InitializeObjectiveCMarshal()
h.Free();
}
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ static void AllocUntrackedObject() where T : Base, new()
+ {
+ new T();
+ }
+
static unsafe void Validate_ReferenceTracking_Scenario()
{
Console.WriteLine($"Running {nameof(Validate_ReferenceTracking_Scenario)}...");
@@ -193,6 +233,14 @@ static unsafe void Validate_ReferenceTracking_Scenario()
ObjectiveCMarshal.CreateReferenceTrackingHandle(new AttributedNoFinalizer(), out _);
});
+ // Ensure objects who have no tagged memory allocated are handled when they enter the
+ // finalization queue. The NativeAOT implementation looks up objects in a hash table,
+ // so we exercise the various ways a hash code can be stored.
+ AllocUntrackedObject();
+ AllocUntrackedObject();
+ AllocUntrackedObject();
+ AllocUntrackedObject();
+
// Provide the minimum number of times the reference callback should run.
// See IsRefCb() in NativeObjCMarshalTests.cpp for usage logic.
const uint callbackCount = 3;
@@ -287,6 +335,14 @@ class Scenario
// Do not call this method from Main as it depends on a previous test for set up.
static void _Validate_ExceptionPropagation()
{
+ // Not yet implemented for NativeAOT.
+ // https://github.com/dotnet/runtime/issues/77472
+ if (TestLibrary.Utilities.IsNativeAot)
+ {
+ Console.WriteLine($"Skipping {nameof(_Validate_ExceptionPropagation)}, NYI");
+ return;
+ }
+
Console.WriteLine($"Running {nameof(_Validate_ExceptionPropagation)}");
var delThrowInt = new ThrowExceptionDelegate(DEL_ThrowIntException);