From e524b99f194a6bae88741726d71c2a2dfc8f8d96 Mon Sep 17 00:00:00 2001 From: ding-bihan Date: Mon, 8 Sep 2025 19:47:01 +0800 Subject: [PATCH] Solve int as reference crash Issue: https://gitee.com/openharmony/arkcompiler_runtime_core/issues/ICWZPB Signed-off-by: ding-bihan Change-Id: I1dfc7591c88a4adea6843ba23a92d38a68a5d30c --- static_core/plugins/ets/runtime/ets_stubs.cpp | 114 ++++++++++++++++-- .../function_ref_primitive_this.ets | 60 ++++++++- 2 files changed, 161 insertions(+), 13 deletions(-) diff --git a/static_core/plugins/ets/runtime/ets_stubs.cpp b/static_core/plugins/ets/runtime/ets_stubs.cpp index b2f1f6f6ef..a2012729bb 100644 --- a/static_core/plugins/ets/runtime/ets_stubs.cpp +++ b/static_core/plugins/ets/runtime/ets_stubs.cpp @@ -1,4 +1,3 @@ - /** * Copyright (c) 2021-2025 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); @@ -157,6 +156,64 @@ static bool EqualityResursionAllowed(EtsObject *obj) return !(obj->GetClass()->IsEtsEnum() || obj->GetClass()->IsFunctionReference()); } +Method *ResolveMethodIfVirtual(Class *recvRc, Method *decl) +{ + ASSERT(!decl->IsStatic()); + if (decl->IsPrivate() || decl->IsConstructor()) { + return decl; + } + + if (auto *m = recvRc->ResolveVirtualMethod(decl)) { + return m; + } + + ASSERT(false && "ResolveVirtualMethod failed unexpectedly"); + // fallback: if no override/interface impl found on receiver class, use declared method + return decl; +} + +template +static inline bool EqPrim(EtsObject *o1, EtsField *f1, EtsObject *o2, EtsField *f2) +{ + return o1->GetFieldPrimitive(f1) == o2->GetFieldPrimitive(f2); +} + +static bool PrimitiveFieldsExactEquality(EtsObject *o1, EtsField *f1, EtsObject *o2, EtsField *f2, + panda_file::Type::TypeId id) +{ + ASSERT(id != panda_file::Type::TypeId::REFERENCE); + ASSERT(id != panda_file::Type::TypeId::TAGGED); + + switch (id) { + case panda_file::Type::TypeId::U1: + return EqPrim(o1, f1, o2, f2); + case panda_file::Type::TypeId::I8: + return EqPrim(o1, f1, o2, f2); + case panda_file::Type::TypeId::U16: + return EqPrim(o1, f1, o2, f2); + case panda_file::Type::TypeId::I16: + return EqPrim(o1, f1, o2, f2); + case panda_file::Type::TypeId::I32: + return EqPrim(o1, f1, o2, f2); + case panda_file::Type::TypeId::I64: + return EqPrim(o1, f1, o2, f2); + case panda_file::Type::TypeId::F32: + return EqPrim(o1, f1, o2, f2); + case panda_file::Type::TypeId::F64: + return EqPrim(o1, f1, o2, f2); + case panda_file::Type::TypeId::U8: + return EqPrim(o1, f1, o2, f2); + case panda_file::Type::TypeId::U32: + return EqPrim(o1, f1, o2, f2); + case panda_file::Type::TypeId::U64: + return EqPrim(o1, f1, o2, f2); + + default: + UNREACHABLE(); + return false; + } +} + // CC-OFFNXT(huge_cca_cyclomatic_complexity[C++], huge_cyclomatic_complexity[C++], huge_method[C++]) big case bool EtsValueTypedEquals(EtsCoroutine *coro, EtsObject *obj1, EtsObject *obj2) { @@ -202,26 +259,59 @@ bool EtsValueTypedEquals(EtsCoroutine *coro, EtsObject *obj1, EtsObject *obj2) } return EtsReferenceEquals(coro, value1, value2); } - if (cls1->IsFunctionReference()) { if (UNLIKELY(!cls2->IsFunctionReference())) { return false; } - if (cls1->GetTypeMetaData() != cls2->GetTypeMetaData()) { + + const auto fnum1 = cls1->GetFieldsNumber(); + const auto fnum2 = cls2->GetFieldsNumber(); + if (fnum1 == 0 && fnum2 == 0) { + return cls1->GetTypeMetaData() == cls2->GetTypeMetaData(); + } + if ((fnum1 == 0) != (fnum2 == 0)) { return false; } - // function or static method - if (obj1->GetClass()->GetFieldsNumber() == 0) { - return true; + ASSERT(fnum1 == 1 && fnum2 == 1); + + auto *decl1 = reinterpret_cast(cls1->GetTypeMetaData()); + auto *decl2 = reinterpret_cast(cls2->GetTypeMetaData()); + ASSERT(decl1 != nullptr && "Function-reference class missing Method* in TypeMetaData"); + ASSERT(decl2 != nullptr && "Function-reference class missing Method* in TypeMetaData"); + + EtsField *f1 = cls1->GetFieldByIndex(0); + EtsField *f2 = cls2->GetFieldByIndex(0); + auto t1 = f1->GetRuntimeField()->GetType(); + auto t2 = f2->GetRuntimeField()->GetType(); + + const bool isRef1 = t1.IsReference(); + const bool isRef2 = t2.IsReference(); + + if (!isRef1 || !isRef2) { + if (isRef1 != isRef2) { + return false; + } + const panda_file::Type::TypeId id1 = t1.GetId(); + const panda_file::Type::TypeId id2 = t2.GetId(); + if (id1 != id2 || id1 == panda_file::Type::TypeId::TAGGED) { + return false; + } + return PrimitiveFieldsExactEquality(obj1, f1, obj2, f2, id1); + } + + auto *recv1 = obj1->GetFieldObject(f1); + auto *recv2 = obj2->GetFieldObject(f2); + if (!EqualityResursionAllowed(recv1) || !EqualityResursionAllowed(recv2)) { + return false; } - // For instance method, always only have one field as guaranteed by class initialization - ASSERT((obj1->GetClass()->GetFieldsNumber() == 1) && (obj2->GetClass()->GetFieldsNumber() == 1)); - auto instance1 = obj1->GetFieldObject(obj1->GetClass()->GetFieldByIndex(0)); - auto instance2 = obj2->GetFieldObject(obj2->GetClass()->GetFieldByIndex(0)); - if (!EqualityResursionAllowed(instance1) || !EqualityResursionAllowed(instance2)) { + auto *rc1 = recv1->GetClass()->GetRuntimeClass(); + auto *rc2 = recv2->GetClass()->GetRuntimeClass(); + Method *impl1 = ResolveMethodIfVirtual(rc1, decl1); + Method *impl2 = ResolveMethodIfVirtual(rc2, decl2); + if (impl1 != impl2) { return false; } - return EtsReferenceEquals(coro, instance1, instance2); + return EtsReferenceEquals(coro, recv1, recv2); } if (cls1->GetRuntimeClass()->IsXRefClass()) { diff --git a/static_core/plugins/ets/tests/ets-common-tests/function_ref_primitive/function_ref_primitive_this.ets b/static_core/plugins/ets/tests/ets-common-tests/function_ref_primitive/function_ref_primitive_this.ets index 0306588dfa..3bf36a3539 100644 --- a/static_core/plugins/ets/tests/ets-common-tests/function_ref_primitive/function_ref_primitive_this.ets +++ b/static_core/plugins/ets/tests/ets-common-tests/function_ref_primitive/function_ref_primitive_this.ets @@ -117,4 +117,62 @@ for (tmpBool of arrBool) { if (r != arrBool[idxBool]) okBool = false; idxBool++; } -arktest.assertEQ(okBool, true); \ No newline at end of file +arktest.assertEQ(okBool, true); + +// equal tests +class A { + foo() {} +} + +class B extends A { + foo() {} +} + +function cmp(a: Any, b: Any) { + return a == b; +} + +function test(a: A, b: B) { + arktest.assertEQ(cmp(a.foo, b.foo), true); +} + +let v = new B(); +test(v, v); + +let x: Int = 1; +arktest.assertEQ(x.unboxed == x.unboxed, true); + +// Virtual override resolution +class VBase { foo(): Int { return 1; } } +class VSub extends VBase { override foo(): Int { return 1; } } +let vb = new VSub(); +let va: VBase = vb; +arktest.assertEQ(va.foo == vb.foo, true); +let vb2 = new VSub(); +arktest.assertEQ(vb.foo == vb2.foo, false); +let vbaseOnly = new VBase(); +arktest.assertEQ(va.foo == vbaseOnly.foo, false); + +// Interface method comparison +interface IFace { foo(): Int } +class IFImpl1 implements IFace { foo(): Int { return 7; } } +class IFImpl2 implements IFace { foo(): Int { return 8; } } +let iobj1 = new IFImpl1(); +let iface1: IFace = iobj1; +arktest.assertEQ(iface1.foo == iobj1.foo, true); +let iobj1b = new IFImpl1(); +arktest.assertEQ(iobj1.foo == iobj1b.foo, false); +let iobj2 = new IFImpl2(); +arktest.assertEQ(iobj1.foo == iobj2.foo, false); + + +// Non-virtual (final) method comparison +class WithFinal { + final baz(): Int { return 42; } + getBaz() { return this.baz; } +} +let f1 = new WithFinal(); +let f2 = f1; +arktest.assertEQ(f1.getBaz() == f2.getBaz(), true); +let f3 = new WithFinal(); +arktest.assertEQ(f1.getBaz() == f3.getBaz(), false); \ No newline at end of file -- Gitee