From 359ea509c111e650c879e15bd4aa07ac9d73417f Mon Sep 17 00:00:00 2001 From: emrullahsalik Date: Tue, 9 Sep 2025 16:32:34 +0300 Subject: [PATCH] Fix NaN values in ets.equals reference comparison Issue: https://gitee.com/openharmony/arkcompiler_runtime_core/issues/ICXAUK Testing: ninja all tests & NaN value reference comparison Signed-off-by: emrullahsalik --- .../irtoc_scripts/interpreter_handlers.irt | 26 +++++++++-- .../plugins/ets/runtime/ets_entrypoints.cpp | 8 ++++ .../plugins/ets/runtime/ets_stubs-inl.h | 44 ++++++++++++++++--- .../tests/ref_ops/reference_equals_test.cpp | 42 ++++++++++++++++++ .../tests/ref_ops/reference_equals_test.ets | 13 ++++++ 5 files changed, 122 insertions(+), 11 deletions(-) diff --git a/static_core/plugins/ets/irtoc_scripts/interpreter_handlers.irt b/static_core/plugins/ets/irtoc_scripts/interpreter_handlers.irt index 0d36e01680..33c312684d 100644 --- a/static_core/plugins/ets/irtoc_scripts/interpreter_handlers.irt +++ b/static_core/plugins/ets/irtoc_scripts/interpreter_handlers.irt @@ -292,7 +292,24 @@ end ['', '_strict'].each do |suffix| macro(:"handle_ets_equals#{suffix}") do |v1, v2| If(Compare(v1, v2).b, 0).NE.Unlikely { - result_0 := 1 + v1isnullish_pe = is_nullish(v1) + If(v1isnullish_pe, 0).NE { + pe_true := Compare(v1, v1).EQ.b + Goto(:"PtrEqJoin#{suffix}") + } Else { + v1cls_pe := load_class(v1) + v2cls_pe := load_class(v2) + both_vt_pe := And(is_valuetyped_cls(v1cls_pe), is_valuetyped_cls(v2cls_pe)).b + If(both_vt_pe, 0).NE { + pe_cmp := Compare(call_runtime("IsValueNaNEntrypoint", %tr, v1).b, Compare(v1, v1).NE.b).EQ.b + Goto(:"PtrEqJoin#{suffix}") + } Else { + pe_true2 := Compare(v1, v1).EQ.b + Goto(:"PtrEqJoin#{suffix}") + } + } + Label(:"PtrEqJoin#{suffix}") + result_0 := Phi(pe_true.b, pe_cmp.b, pe_true2.b).b } Else { v1isnullish = is_nullish(v1) v2isnullish = is_nullish(v2) @@ -300,13 +317,14 @@ end if suffix.empty? result_1 := And(v1isnullish, v2isnullish).b else - result_1 := 0 + result_1 := Compare(v1, v1).NE.b end } Else { v1cls := load_class(v1) v2cls := load_class(v2) - If(And(is_valuetyped_cls(v1cls), is_valuetyped_cls(v2cls)).b, 0).EQ.Likely { - result_2 := 0 + both_vt := And(is_valuetyped_cls(v1cls), is_valuetyped_cls(v2cls)).b + If(both_vt, 0).EQ.Likely { + result_2 := Compare(v1, v1).NE.b } Else { result_3 := call_runtime("CompareETSValueTypedEntrypoint", %tr, v1, v2).b } diff --git a/static_core/plugins/ets/runtime/ets_entrypoints.cpp b/static_core/plugins/ets/runtime/ets_entrypoints.cpp index 118750e145..9f80a792fc 100644 --- a/static_core/plugins/ets/runtime/ets_entrypoints.cpp +++ b/static_core/plugins/ets/runtime/ets_entrypoints.cpp @@ -16,6 +16,7 @@ #include "plugins/ets/runtime/ets_entrypoints.h" #include "include/coretypes/string.h" +#include "include/managed_thread.h" #include "include/object_header.h" #include "libarkfile/shorty_iterator.h" #include "plugins/ets/runtime/ets_coroutine.h" @@ -336,6 +337,13 @@ extern "C" bool IsClassValueTypedEntrypoint(Class *cls) return EtsClass::FromRuntimeClass(cls)->IsValueTyped(); } +extern "C" bool IsValueNaNEntrypoint(ManagedThread *thread, ObjectHeader *obj1) +{ + auto coro = EtsCoroutine::CastFromThread(thread); + auto eobj1 = EtsObject::FromCoreType(obj1); + return IsValueNaN(coro, eobj1); +} + extern "C" bool CompareETSValueTypedEntrypoint(ManagedThread *thread, ObjectHeader *obj1, ObjectHeader *obj2) { auto coro = EtsCoroutine::CastFromThread(thread); diff --git a/static_core/plugins/ets/runtime/ets_stubs-inl.h b/static_core/plugins/ets/runtime/ets_stubs-inl.h index 08973e8df5..996f1898e8 100644 --- a/static_core/plugins/ets/runtime/ets_stubs-inl.h +++ b/static_core/plugins/ets/runtime/ets_stubs-inl.h @@ -16,11 +16,15 @@ #ifndef PANDA_PLUGINS_ETS_RUNTIME_STUBS_INL_H #define PANDA_PLUGINS_ETS_RUNTIME_STUBS_INL_H +#include #include "plugins/ets/runtime/ets_coroutine.h" +#include "plugins/ets/runtime/ets_platform_types.h" +#include "plugins/ets/runtime/types/ets_box_primitive.h" #include "plugins/ets/runtime/types/ets_object.h" #include "plugins/ets/runtime/ets_stubs.h" #include "plugins/ets/runtime/ets_exceptions.h" #include "libarkfile/proto_data_accessor-inl.h" +#include "types/ets_primitives.h" namespace ark::ets { @@ -39,27 +43,53 @@ ALWAYS_INLINE inline bool IsReferenceNullish(EtsCoroutine *coro, EtsObject *ref) return ref == nullptr || ref == EtsObject::FromCoreType(coro->GetNullValue()); } +ALWAYS_INLINE inline bool IsValueNaN(EtsCoroutine *coro, EtsObject *ref1) +{ + if (ref1->GetClass() == PlatformTypes(coro)->coreDouble) { + auto *d = EtsBoxPrimitive::FromCoreType(ref1); + return d && std::isnan(static_cast(d->GetValue())); + } + if (ref1->GetClass() == PlatformTypes(coro)->coreFloat) { + auto *f = EtsBoxPrimitive::FromCoreType(ref1); + return f && std::isnan(static_cast(f->GetValue())); + } + return false; +} + template ALWAYS_INLINE inline bool EtsReferenceEquals(EtsCoroutine *coro, EtsObject *ref1, EtsObject *ref2) { if (UNLIKELY(ref1 == ref2)) { + if (IsReferenceNullish(coro, ref1)) { + return true; + } + auto *cls1 = ref1->GetClass(); + auto *cls2 = ref2->GetClass(); + if (LIKELY(!(cls1->IsValueTyped() && cls2->IsValueTyped()))) { + return true; + } + auto ptypes = PlatformTypes(coro); + ASSERT(ptypes != nullptr); + if (cls1 == ptypes->coreDouble || cls1 == ptypes->coreFloat) { + return !IsValueNaN(coro, ref1); + } return true; } if (IsReferenceNullish(coro, ref1) || IsReferenceNullish(coro, ref2)) { - if constexpr (IS_STRICT) { + if constexpr (IS_STRICT) return false; - } else { - return IsReferenceNullish(coro, ref1) && IsReferenceNullish(coro, ref2); - } + return IsReferenceNullish(coro, ref1) && IsReferenceNullish(coro, ref2); } ASSERT(ref1 != nullptr); ASSERT(ref2 != nullptr); - if (LIKELY(!(ref1->GetClass()->IsValueTyped() && ref2->GetClass()->IsValueTyped()))) { - return false; + auto *cls1 = ref1->GetClass(); + auto *cls2 = ref2->GetClass(); + if (LIKELY(cls1->IsValueTyped() && cls2->IsValueTyped())) { + return EtsValueTypedEquals(coro, ref1, ref2); } - return EtsValueTypedEquals(coro, ref1, ref2); + return false; } ALWAYS_INLINE inline EtsString *EtsReferenceTypeof(EtsCoroutine *coro, EtsObject *ref) diff --git a/static_core/plugins/ets/tests/ani/tests/ref_ops/reference_equals_test.cpp b/static_core/plugins/ets/tests/ani/tests/ref_ops/reference_equals_test.cpp index 9ed2a3dcf7..1a1f7e0401 100644 --- a/static_core/plugins/ets/tests/ani/tests/ref_ops/reference_equals_test.cpp +++ b/static_core/plugins/ets/tests/ani/tests/ref_ops/reference_equals_test.cpp @@ -216,6 +216,48 @@ TEST_F(ReferenceEqualsTest, CheckEqualityWithNullishValues) ASSERT_EQ(isCorrect, ANI_TRUE); } +TEST_F(ReferenceEqualsTest, ptr_equal_double_nan_is_false) +{ + auto dnan = CallEtsFunction(MODULE_NAME, "GetDoubleNaN"); + ani_boolean isEquals = ANI_TRUE; + ASSERT_EQ(env_->Reference_Equals(dnan, dnan, &isEquals), ANI_OK); + ASSERT_EQ(isEquals, ANI_FALSE); +} + +TEST_F(ReferenceEqualsTest, ptr_equal_float_nan_is_false) +{ + auto fnan = CallEtsFunction(MODULE_NAME, "GetFloatNaN"); + ani_boolean isEquals = ANI_TRUE; + ASSERT_EQ(env_->Reference_Equals(fnan, fnan, &isEquals), ANI_OK); + ASSERT_EQ(isEquals, ANI_FALSE); +} + +TEST_F(ReferenceEqualsTest, ptr_equal_object_nan_is_false) +{ + auto onan = CallEtsFunction(MODULE_NAME, "GetObjectNaN"); + ani_boolean isEquals = ANI_TRUE; + ASSERT_EQ(env_->Reference_Equals(onan, onan, &isEquals), ANI_OK); + ASSERT_EQ(isEquals, ANI_FALSE); +} + +TEST_F(ReferenceEqualsTest, ptr_equal_double_value_is_true) +{ + auto d42 = CallEtsFunction(MODULE_NAME, "GetDouble42"); + ani_boolean isEquals = ANI_FALSE; + ASSERT_EQ(env_->Reference_Equals(d42, d42, &isEquals), ANI_OK); + ASSERT_EQ(isEquals, ANI_TRUE); +} + +TEST_F(ReferenceEqualsTest, non_ptr_equal_double_same_value_is_true) +{ + auto d1 = CallEtsFunction(MODULE_NAME, "GetDouble42"); + auto d2 = CallEtsFunction(MODULE_NAME, "GetDouble42"); + ani_boolean isEquals = ANI_FALSE; + ASSERT_NE(d1, d2); + ASSERT_EQ(env_->Reference_Equals(d1, d2, &isEquals), ANI_OK); + ASSERT_EQ(isEquals, ANI_TRUE); +} + } // namespace ark::ets::ani::testing // NOLINTEND(cppcoreguidelines-pro-type-vararg) diff --git a/static_core/plugins/ets/tests/ani/tests/ref_ops/reference_equals_test.ets b/static_core/plugins/ets/tests/ani/tests/ref_ops/reference_equals_test.ets index 3dcd5e972a..ea1c426976 100644 --- a/static_core/plugins/ets/tests/ani/tests/ref_ops/reference_equals_test.ets +++ b/static_core/plugins/ets/tests/ani/tests/ref_ops/reference_equals_test.ets @@ -25,6 +25,19 @@ function getObject(): Object { return "Hello World!" } +function GetDoubleNaN(): Object { + let x: double = NaN; return x; +} +function GetFloatNaN(): Object { + let x: float = Double.toFloat(NaN); return x; +} +function GetObjectNaN(): Object { + let x: object = NaN; return x; +} +function GetDouble42(): Object { + let x: double = 42.0; return x; +} + class Pack { boolean_value: boolean = true string_value: string = "" -- Gitee