From ebf2b13e793efe7495b447f62d1e71ed3baa4599 Mon Sep 17 00:00:00 2001 From: likholatovevgeny Date: Tue, 9 Sep 2025 11:05:08 +0000 Subject: [PATCH 1/2] Map/Set intrinsics functionality/security fix Issue: https://gitee.com/openharmony/arkcompiler_runtime_core/issues/ICRL0U Signed-off-by: likholatovevgeny --- static_core/plugins/ets/irtoc_scripts/map.irt | 2 +- .../ets/irtoc_scripts/map_set_common.irt | 104 ++++++++---- .../ets/runtime/asm_defines/asm_defines.def | 7 +- .../ets/runtime/ets_platform_types.cpp | 6 +- .../plugins/ets/runtime/ets_platform_types.h | 18 +- .../plugins/ets/runtime/types/ets_map.cpp | 154 +++++------------- .../plugins/ets/runtime/types/ets_map.h | 63 +++---- .../plugins/ets/runtime/types/ets_set.h | 7 + .../checked/ets_escompat_map_intrinsics.ets | 8 + .../checked/ets_escompat_set_intrinsics.ets | 5 + .../ets/tests/runtime/types/CMakeLists.txt | 2 + .../ets/tests/runtime/types/ets_map_test.cpp | 89 ++++++++++ .../ets/tests/runtime/types/ets_set_test.cpp | 74 +++++++++ 13 files changed, 333 insertions(+), 206 deletions(-) create mode 100644 static_core/plugins/ets/tests/runtime/types/ets_map_test.cpp create mode 100644 static_core/plugins/ets/tests/runtime/types/ets_set_test.cpp diff --git a/static_core/plugins/ets/irtoc_scripts/map.irt b/static_core/plugins/ets/irtoc_scripts/map.irt index 525dc1f09c..00684170c5 100644 --- a/static_core/plugins/ets/irtoc_scripts/map.irt +++ b/static_core/plugins/ets/irtoc_scripts/map.irt @@ -96,7 +96,7 @@ def GenerateGetHashCodeByValue(cgmode) flags := LoadI(klass).Imm(EtsConstants::ETS_CLASS_FLAGS_FROM_RUNTIME_CLASS_OFFSET).u32 If(isBoxedType(flags), 0).NE.b { type := LoadI(klass).Imm(Constants::BASE_CLASS_MANAGED_OBJECT_OFFSET).ref - Return(Cast(calcHashCode(obj, type)).u64).i64 + Return(Cast(calcHashCode(obj, flags)).u64).i64 } If(isBigintType(flags), 0).NE.b { Goto(:SlowPathEntrypointBigint) diff --git a/static_core/plugins/ets/irtoc_scripts/map_set_common.irt b/static_core/plugins/ets/irtoc_scripts/map_set_common.irt index c4a46e435c..70ddbd4d68 100644 --- a/static_core/plugins/ets/irtoc_scripts/map_set_common.irt +++ b/static_core/plugins/ets/irtoc_scripts/map_set_common.irt @@ -31,9 +31,15 @@ module MapConstants SIZE_OFFSET = "cross_values::GetEscompatMapSizeOffset(graph->GetArch())" BUCKETS_OFFSET = "cross_values::GetEscompatMapBucketsOffset(graph->GetArch())" - PLATFORM_TYPES_CORE_LONG_TYPE_OFFSET = "cross_values::GetPlatformTypesCoreLongTypeOffset(graph->GetArch())" - PLATFORM_TYPES_CORE_FLOAT_TYPE_OFFSET = "cross_values::GetPlatformTypesCoreFloatTypeOffset(graph->GetArch())" - PLATFORM_TYPES_CORE_DOUBLE_TYPE_OFFSET = "cross_values::GetPlatformTypesCoreDoubleTypeOffset(graph->GetArch())" + ETS_CLASS_BOXED_TYPE_FIELD_START = "cross_values::GetEtsClassBoxedTypeFieldStart(graph->GetArch())" + ETS_CLASS_BOXED_TYPE_FIELD_SIZE = "cross_values::GetEtsClassBoxedTypeFieldSize(graph->GetArch())" + ETS_CLASS_BOXED_TYPE_FIELD_MASK = "(((1LLU << " + ETS_CLASS_BOXED_TYPE_FIELD_SIZE + ") - 1) << " + ETS_CLASS_BOXED_TYPE_FIELD_START + ")" + ETS_CLASS_BOXED_TYPE_CHAR_MASK_VALUE = "(cross_values::GetEtsClassBoxedTypeCharMaskValue(graph->GetArch()) << " + ETS_CLASS_BOXED_TYPE_FIELD_START + ")" + ETS_CLASS_BOXED_TYPE_SHORT_MASK_VALUE = "(cross_values::GetEtsClassBoxedTypeShortMaskValue(graph->GetArch()) << " + ETS_CLASS_BOXED_TYPE_FIELD_START + ")" + ETS_CLASS_BOXED_TYPE_INT_MASK_VALUE = "(cross_values::GetEtsClassBoxedTypeIntMaskValue(graph->GetArch()) << " + ETS_CLASS_BOXED_TYPE_FIELD_START + ")" + ETS_CLASS_BOXED_TYPE_LONG_MASK_VALUE = "(cross_values::GetEtsClassBoxedTypeLongMaskValue(graph->GetArch()) << " + ETS_CLASS_BOXED_TYPE_FIELD_START + ")" + ETS_CLASS_BOXED_TYPE_FLOAT_MASK_VALUE = "(cross_values::GetEtsClassBoxedTypeFloatMaskValue(graph->GetArch()) << " + ETS_CLASS_BOXED_TYPE_FIELD_START + ")" + ETS_CLASS_BOXED_TYPE_DOUBLE_MASK_VALUE = "(cross_values::GetEtsClassBoxedTypeDoubleMaskValue(graph->GetArch()) << " + ETS_CLASS_BOXED_TYPE_FIELD_START + ")" ETS_CLASS_IS_NULLVALUE_FLAG = "cross_values::GetEtsClassIsNullvalueFlag(graph->GetArch())" ETS_CLASS_IS_BOXED_FLAG = "cross_values::GetEtsClassIsBoxedFlag(graph->GetArch())" @@ -102,6 +108,22 @@ scoped_macro(:isBoxedType) do |flags| AndI(flags).Imm(MapConstants::ETS_CLASS_IS_BOXED_FLAG).u32 end +scoped_macro(:getBoxedType) do |flags| + boxed_type = AndI(flags).Imm(MapConstants::ETS_CLASS_BOXED_TYPE_FIELD_MASK).u32 +end + +# isChar, isShort, isInt, isLong, isFloat, isDouble +['Char', 'Short', 'Int', 'Long', 'Float', 'Double'].each do |name| + scoped_macro(:"is#{name}") do |type| + If(type, Cast(eval("MapConstants::ETS_CLASS_BOXED_TYPE_#{name.upcase}_MASK_VALUE")).u32).EQ.Likely { + _res1 := Cast(1).b + } Else { + _res2 := Cast(0).b + } + Phi(_res1, _res2).b + end +end + scoped_macro(:isBigintType) do |flags| AndI(flags).Imm(MapConstants::ETS_CLASS_IS_BIGINT_FLAG).u32 end @@ -126,14 +148,14 @@ scoped_macro(:fetchHashCode) do |obj, is_string| end -scoped_macro(:calcHashCode) do |obj, type| +scoped_macro(:calcHashCode) do |obj, flags| ptypes := LoadI(%tr).Imm(EtsConstants::CORO_LOCAL_STORAGE_OFFSET).ptr + btype := getBoxedType(flags) int_max := Cast(MapConstants::INT_MAX).f64 int_min := Cast(MapConstants::INT_MIN).f64 - double_type := getPlatType(ptypes, MapConstants::PLATFORM_TYPES_CORE_DOUBLE_TYPE_OFFSET) - If(type, double_type).EQ.b { + If(isDouble(btype), 1).EQ.b { value_double := LoadI(obj).Imm(EtsConstants::BOX_PRIMITIVE_VALUE_OFFSET).f64 If(isNan64(value_double), 0).NE.b { @@ -155,8 +177,7 @@ Label(:ValueDoubleDone) Goto(:Done) } - float_type := getPlatType(ptypes, MapConstants::PLATFORM_TYPES_CORE_FLOAT_TYPE_OFFSET) - If(type, float_type).EQ.b { + If(isFloat(btype), 1).EQ.b { value_float := LoadI(obj).Imm(EtsConstants::BOX_PRIMITIVE_VALUE_OFFSET).f32 If(isNan32(value_float), 0).NE.b { @@ -176,22 +197,33 @@ Label(:ValueDoubleDone) Label(:ValueFloatDone) hash_code_float := Phi(hash_code_float_nan, hash_code_float_large_pos, hash_code_float_large_neg, hash_code_float_norm).u32 Goto(:Done) - } Else { + } + + isIntLong := Or(isInt(btype), isLong(btype)).b + If(isIntLong, 1).EQ.b { hash_code_int := LoadI(obj).Imm(EtsConstants::BOX_PRIMITIVE_VALUE_OFFSET).u32 + Goto(:Done) + } + + isCharShort := Or(isChar(btype), isShort(btype)).b + If(isCharShort, 1).EQ.b { + hash_code_short := Cast(LoadI(obj).Imm(EtsConstants::BOX_PRIMITIVE_VALUE_OFFSET).u16).u32 + } Else { + hash_code_byte := Cast(LoadI(obj).Imm(EtsConstants::BOX_PRIMITIVE_VALUE_OFFSET).u8).u32 } Label(:Done) - hash_code := Phi(hash_code_double, hash_code_float, hash_code_int).u32 + hash_code := Phi(hash_code_double, hash_code_float, hash_code_int, hash_code_short, hash_code_byte).u32 end -scoped_macro(:getBucketIdx) do |key, is_string, type, flags, buck_arr_len| +scoped_macro(:getBucketIdx) do |key, is_string, flags, buck_arr_len| If(isNullValue(flags), 0).NE.b { buck_idx_null := 0 Goto(:Done) } If(isBoxedType(flags), 0).NE.b { - key_hash_boxed := calcHashCode(key, type) + key_hash_boxed := calcHashCode(key, flags) Goto(:GotHashCode) } @@ -218,22 +250,34 @@ scoped_macro(:compareByRef) do |obj, key| res := Compare(obj, key).b end -scoped_macro(:getCompareVal) do |obj, type, long_type, float_type, double_type| - is_64 := Or(Compare(type, long_type).b, Compare(type, double_type).b).b - - If(is_64, 0).NE.b { +scoped_macro(:getCompareVal) do |obj, btype| + is_64 := Or(isLong(btype), isDouble(btype)).b + If(is_64, 1).EQ.b { obj_val_64 := LoadI(obj).Imm(EtsConstants::BOX_PRIMITIVE_VALUE_OFFSET).u64 - } Else { + Goto(:Done) + } + + is_32 := Or(isInt(btype), isFloat(btype)).b + If(is_32, 1).EQ.b { obj_val_32 := Cast(LoadI(obj).Imm(EtsConstants::BOX_PRIMITIVE_VALUE_OFFSET).u32).u64 + Goto(:Done) } - val := Phi(obj_val_64, obj_val_32).u64 + + is_16 := Or(isChar(btype), isShort(btype)).b + If(is_16, 1).EQ.b { + obj_val_16 := Cast(LoadI(obj).Imm(EtsConstants::BOX_PRIMITIVE_VALUE_OFFSET).u16).u64 + } Else { + obj_val_8 := Cast(LoadI(obj).Imm(EtsConstants::BOX_PRIMITIVE_VALUE_OFFSET).u8).u64 + } + +Label(:Done) + val := Phi(obj_val_64, obj_val_32, obj_val_16, obj_val_8).u64 end -scoped_macro(:compareByVal) do |obj, type, key_val, long_type, float_type, double_type| - obj_val := getCompareVal(obj, type, long_type, float_type, double_type) +scoped_macro(:compareByVal) do |obj, key_val, btype| + obj_val := getCompareVal(obj, btype) - is_double := Compare(type, double_type).b - If(is_double, 0).NE.b { + If(isDouble(btype), 0).NE.b { is_key_nan := isNan64(key_val) is_obj_nan := isNan64(obj_val) @@ -251,8 +295,7 @@ scoped_macro(:compareByVal) do |obj, type, key_val, long_type, float_type, doubl Goto(:Done) } - is_float := Compare(type, float_type).b - If(is_float, 0).NE.b { + If(isFloat(btype), 0).NE.b { key_f32 := Bitcast(Cast(key_val).u32).f32 obj_f32 := Bitcast(Cast(obj_val).u32).f32 is_key_nan := isNan32(key_f32) @@ -328,13 +371,10 @@ Label(:Done) found := ofs end -macro(:findKeyByVal) do |buck_data, buck_len, key, type| +macro(:findKeyByVal) do |buck_data, buck_len, key, type, flags| ptypes := LoadI(%tr).Imm(EtsConstants::CORO_LOCAL_STORAGE_OFFSET).ptr - long_type := getPlatType(ptypes, MapConstants::PLATFORM_TYPES_CORE_LONG_TYPE_OFFSET) - float_type := getPlatType(ptypes, MapConstants::PLATFORM_TYPES_CORE_FLOAT_TYPE_OFFSET) - double_type := getPlatType(ptypes, MapConstants::PLATFORM_TYPES_CORE_DOUBLE_TYPE_OFFSET) - - key_val := getCompareVal(key, type, long_type, float_type, double_type) + btype := getBoxedType(flags) + key_val := getCompareVal(key, btype) ofs_0 := Mul(SubI(buck_len).Imm(1).i32, EtsConstants::OBJ_PTR_SIZE).i32 Label(:findKeyByValLoop) @@ -353,7 +393,7 @@ Label(:findKeyByValLoop) Goto(:SlowPathEntrypoint) } - If(compareByVal(object, type, key_val, long_type, float_type, double_type), 0).NE.Likely.b { + If(compareByVal(object, key_val, btype), 0).NE.Likely.b { Goto(:findKeyByValDone) } @@ -392,7 +432,7 @@ macro(:findKey) do |buck_data, buck_len, key, type, flags, is_string| If(isRefKey(key, flags), 0).NE.b { find_by_ref := findKeyByRef(buck_data, buck_len, key) } Else { - find_by_val := findKeyByVal(buck_data, buck_len, key, type) + find_by_val := findKeyByVal(buck_data, buck_len, key, type, flags) } Label(:findKeyDone) diff --git a/static_core/plugins/ets/runtime/asm_defines/asm_defines.def b/static_core/plugins/ets/runtime/asm_defines/asm_defines.def index 062ef499f0..0601b170bd 100644 --- a/static_core/plugins/ets/runtime/asm_defines/asm_defines.def +++ b/static_core/plugins/ets/runtime/asm_defines/asm_defines.def @@ -28,7 +28,11 @@ DEFINE_VALUE(ETS_CLASS_IS_BOXED_FLAG, ark::ets::EtsClass::IS_BOXED) DEFINE_VALUE(ETS_CLASS_IS_BIGINT_FLAG, ark::ets::EtsClass::IS_BIGINT) DEFINE_VALUE(ETS_CLASS_BOXED_TYPE_FIELD_START, ark::ets::EtsClass::BOXED_TYPE_FIELD_START) DEFINE_VALUE(ETS_CLASS_BOXED_TYPE_FIELD_SIZE, ark::ets::EtsClass::BOXED_TYPE_FIELD_SIZE) +DEFINE_VALUE(ETS_CLASS_BOXED_TYPE_CHAR_MASK_VALUE, (unsigned) ark::ets::EtsClass::BoxedType::CHAR) +DEFINE_VALUE(ETS_CLASS_BOXED_TYPE_SHORT_MASK_VALUE, (unsigned) ark::ets::EtsClass::BoxedType::SHORT) DEFINE_VALUE(ETS_CLASS_BOXED_TYPE_INT_MASK_VALUE, (unsigned) ark::ets::EtsClass::BoxedType::INT) +DEFINE_VALUE(ETS_CLASS_BOXED_TYPE_LONG_MASK_VALUE, (unsigned) ark::ets::EtsClass::BoxedType::LONG) +DEFINE_VALUE(ETS_CLASS_BOXED_TYPE_FLOAT_MASK_VALUE, (unsigned) ark::ets::EtsClass::BoxedType::FLOAT) DEFINE_VALUE(ETS_CLASS_BOXED_TYPE_DOUBLE_MASK_VALUE, (unsigned) ark::ets::EtsClass::BoxedType::DOUBLE) DEFINE_VALUE(ETS_CLASS_IS_BIG_INT_FLAG, ark::ets::EtsClass::IS_BIGINT) @@ -59,9 +63,6 @@ DEFINE_VALUE(ARRAY_BUFFER_NATIVE_DATA_OFFSET, ark::ets::EtsEscompatArrayBuffer:: DEFINE_VALUE(ARRAY_BUFFER_MANAGED_DATA_OFFSET, ark::ets::EtsEscompatArrayBuffer::GetManagedDataOffset()) DEFINE_VALUE(ARRAY_BUFFER_IS_RESIZABLE_OFFSET, ark::ets::EtsEscompatArrayBuffer::GetIsResizableOffset()) -DEFINE_VALUE(PLATFORM_TYPES_CORE_LONG_TYPE_OFFSET, ark::ets::EtsPlatformTypes::GetCoreLongTypeOffset()) -DEFINE_VALUE(PLATFORM_TYPES_CORE_FLOAT_TYPE_OFFSET, ark::ets::EtsPlatformTypes::GetCoreFloatTypeOffset()) -DEFINE_VALUE(PLATFORM_TYPES_CORE_DOUBLE_TYPE_OFFSET, ark::ets::EtsPlatformTypes::GetCoreDoubleTypeOffset()) DEFINE_VALUE(PLATFORM_TYPES_ESCOMPAT_ARRAY_CLASS_OFFSET, ark::ets::EtsPlatformTypes::GetEscompatArrayClassOffset()) DEFINE_VALUE(ESCOMPAT_MAP_SIZE_OFFSET, ark::ets::EtsEscompatMap::GetSizeOffset()) diff --git a/static_core/plugins/ets/runtime/ets_platform_types.cpp b/static_core/plugins/ets/runtime/ets_platform_types.cpp index a2733854e7..748f1520cf 100644 --- a/static_core/plugins/ets/runtime/ets_platform_types.cpp +++ b/static_core/plugins/ets/runtime/ets_platform_types.cpp @@ -230,6 +230,9 @@ EtsPlatformTypes::EtsPlatformTypes([[maybe_unused]] EtsCoroutine *coro) findType(&EtsPlatformTypes::escompatRecord, RECORD); findMethod(&EtsPlatformTypes::escompatRecordGetter, escompatRecord, GET_INDEX_METHOD, nullptr, false); findMethod(&EtsPlatformTypes::escompatRecordSetter, escompatRecord, SET_INDEX_METHOD, nullptr, false); + findType(&EtsPlatformTypes::escompatMap, MAP); + findType(&EtsPlatformTypes::escompatMapEntry, MAPENTRY); + findType(&EtsPlatformTypes::escompatSet, SET); findType(&EtsPlatformTypes::interopJSValue, JS_VALUE); @@ -253,9 +256,6 @@ EtsPlatformTypes::EtsPlatformTypes([[maybe_unused]] EtsCoroutine *coro) if (LIKELY(Runtime::GetOptions().IsUseStringCaches())) { CreateAndInitializeCaches(); } - - findType(&EtsPlatformTypes::escompatMap, MAP); - findType(&EtsPlatformTypes::escompatMapEntry, MAPENTRY); } void EtsPlatformTypes::InitializeClasses(EtsCoroutine *coro) diff --git a/static_core/plugins/ets/runtime/ets_platform_types.h b/static_core/plugins/ets/runtime/ets_platform_types.h index ff54afd0c9..21d4abd1df 100644 --- a/static_core/plugins/ets/runtime/ets_platform_types.h +++ b/static_core/plugins/ets/runtime/ets_platform_types.h @@ -115,6 +115,9 @@ public: EtsClass *escompatRecord {}; EtsMethod *escompatRecordGetter {}; EtsMethod *escompatRecordSetter {}; + EtsClass *escompatMap {}; + EtsClass *escompatMapEntry {}; + EtsClass *escompatSet {}; /* InteropJS */ EtsClass *interopJSValue {}; @@ -150,22 +153,7 @@ public: } Entry const *GetTypeEntry(const uint8_t *descriptor) const; - EtsClass *escompatMap {}; - EtsClass *escompatMapEntry {}; - public: - static constexpr size_t GetCoreLongTypeOffset() - { - return MEMBER_OFFSET(EtsPlatformTypes, coreLong); - } - static constexpr size_t GetCoreFloatTypeOffset() - { - return MEMBER_OFFSET(EtsPlatformTypes, coreFloat); - } - static constexpr size_t GetCoreDoubleTypeOffset() - { - return MEMBER_OFFSET(EtsPlatformTypes, coreDouble); - } static constexpr size_t GetEscompatArrayClassOffset() { return MEMBER_OFFSET(EtsPlatformTypes, escompatArray); diff --git a/static_core/plugins/ets/runtime/types/ets_map.cpp b/static_core/plugins/ets/runtime/types/ets_map.cpp index de2b1d7683..352cf2f759 100644 --- a/static_core/plugins/ets/runtime/types/ets_map.cpp +++ b/static_core/plugins/ets/runtime/types/ets_map.cpp @@ -16,131 +16,55 @@ #include "plugins/ets/runtime/types/ets_map.h" #include "plugins/ets/runtime/types/ets_base_enum.h" #include "plugins/ets/runtime/types/ets_bigint.h" +#include "plugins/ets/runtime/ets_stubs-inl.h" +#include "intrinsics.h" namespace ark::ets { -static const uint8_t *g_hashCodeMethod = utf::CStringAsMutf8("$_hashCode"); static constexpr int NUMERIC_INT_MAX = std::numeric_limits::max(); static constexpr int NUMERIC_INT_MIN = std::numeric_limits::min(); -template -static inline bool CompareFloats(T a, T b) +static inline bool CompareDouble(EtsObject *a, EtsObject *b) { - if (std::isinf(a) && std::isinf(b)) { - return std::signbit(a) == std::signbit(b); - } - - if (std::isnan(a) && std::isnan(b)) { + EtsDouble aVal = EtsBoxPrimitive::FromCoreType(a)->GetValue(); + EtsDouble bVal = EtsBoxPrimitive::FromCoreType(b)->GetValue(); + if (intrinsics::StdCoreDoubleIsNan(aVal) && intrinsics::StdCoreDoubleIsNan(bVal)) { return true; } - - return a == b; -} - -template -static inline bool Compare(EtsObject *a, EtsObject *b) -{ - T aVal = EtsBoxPrimitive::FromCoreType(a)->GetValue(); - T bVal = EtsBoxPrimitive::FromCoreType(b)->GetValue(); return aVal == bVal; } -static bool CompareBigInt(EtsObject *a, EtsObject *b) -{ - auto *bigA = reinterpret_cast(a); - auto *bigB = reinterpret_cast(b); - - if (bigA->GetSign() != bigB->GetSign()) { - return false; - } - if (bigA->GetBytes()->GetLength() != bigB->GetBytes()->GetLength()) { - return false; - } - for (uint32_t i = 0; i < bigA->GetBytes()->GetLength(); ++i) { - if (bigA->GetBytes()->Get(i) != bigB->GetBytes()->Get(i)) { - return false; - } - } - return true; -} - -static bool CompareBoxed(EtsObject *a, EtsObject *b) +static inline bool CompareFloat(EtsObject *a, EtsObject *b) { - EtsClass *aKlass = a->GetClass(); - EtsClass *bKlass = b->GetClass(); - - auto type = aKlass->GetBoxedType(); - switch (type) { - case EtsClass::BoxedType::INT: { - EtsInt aVal = EtsBoxPrimitive::Unbox(a); - EtsInt bVal = bKlass->IsBoxedDouble() ? static_cast(EtsBoxPrimitive::Unbox(b)) - : EtsBoxPrimitive::Unbox(b); - return aVal == bVal; - } - case EtsClass::BoxedType::DOUBLE: { - EtsDouble aVal = EtsBoxPrimitive::Unbox(a); - EtsDouble bVal = bKlass->IsBoxedInt() ? static_cast(EtsBoxPrimitive::Unbox(b)) - : EtsBoxPrimitive::Unbox(b); - return CompareFloats(aVal, bVal); - } - case EtsClass::BoxedType::FLOAT: - return CompareFloats(EtsBoxPrimitive::Unbox(a), EtsBoxPrimitive::Unbox(b)); - case EtsClass::BoxedType::LONG: - return Compare(a, b); - case EtsClass::BoxedType::SHORT: - return Compare(a, b); - case EtsClass::BoxedType::BYTE: - return Compare(a, b); - case EtsClass::BoxedType::CHAR: - return Compare(a, b); - case EtsClass::BoxedType::BOOLEAN: - return Compare(a, b); - default: - LOG(FATAL, RUNTIME) << "Unknown boxed type"; - UNREACHABLE(); + EtsFloat aVal = EtsBoxPrimitive::FromCoreType(a)->GetValue(); + EtsFloat bVal = EtsBoxPrimitive::FromCoreType(b)->GetValue(); + if (intrinsics::StdCoreFloatIsNan(aVal) && intrinsics::StdCoreFloatIsNan(bVal)) { + return true; } - - return false; + return aVal == bVal; } -static bool Compare(EtsObject *a, EtsObject *b) +static bool Compare(EtsCoroutine *coro, EtsObject *a, EtsObject *b) { - if (a == b) { + if (UNLIKELY(a == b)) { return true; } - if (a == nullptr || b == nullptr) { - return false; + if (IsReferenceNullish(coro, a) || IsReferenceNullish(coro, b)) { + return IsReferenceNullish(coro, a) && IsReferenceNullish(coro, b); } EtsClass *aKlass = a->GetClass(); EtsClass *bKlass = b->GetClass(); - if (aKlass != bKlass) { - bool isNumericPair = - (aKlass->IsBoxedInt() && bKlass->IsBoxedDouble()) || (aKlass->IsBoxedDouble() && bKlass->IsBoxedInt()); - if (!isNumericPair) { - return false; - } - } - - if (aKlass->IsBoxed()) { - return CompareBoxed(a, b); - } - - if (aKlass->IsStringClass() && bKlass->IsStringClass()) { - return EtsString::FromEtsObject(a)->StringsAreEqual(b); - } - - if (aKlass->IsEtsEnum()) { - auto aEnumValue = EtsBaseEnum::FromEtsObject(a)->GetValue(); - auto bEnumValue = EtsBaseEnum::FromEtsObject(b)->GetValue(); - return aEnumValue == bEnumValue; + auto ptypes = PlatformTypes(coro); + if (aKlass == ptypes->coreDouble && bKlass == ptypes->coreDouble) { + return CompareDouble(a, b); } - if (aKlass->IsBigInt()) { - return CompareBigInt(a, b); + if (aKlass == ptypes->coreFloat && bKlass == ptypes->coreFloat) { + return CompareFloat(a, b); } - return false; + return EtsReferenceEquals(coro, a, b); } template @@ -208,18 +132,21 @@ uint32_t EtsEscompatMap::GetHashCode(EtsObject *&key) static inline EtsEscompatArray *GetBucket(EtsCoroutine *&coro, EtsEscompatMap *&map, EtsInt idx) { - return reinterpret_cast(map->GetBuckets(coro)->GetData()->Get(idx)); + return EtsEscompatArray::FromEtsObject(map->GetBuckets(coro)->GetData()->Get(idx)); } EtsBoolean EtsEscompatMap::Has(EtsEscompatMap *map, EtsObject *key, EtsInt idx) { auto *coro = EtsCoroutine::GetCurrent(); - auto *bucket = GetBucket(coro, map, idx); - if (bucket != nullptr) { + ASSERT(coro->HasPendingException() == false); + [[maybe_unused]] EtsHandleScope scope(coro); + EtsHandle keyHandle(coro, key); + EtsHandle bucket(coro, GetBucket(coro, map, idx)); + if (bucket.GetPtr() != nullptr) { uint32_t bucketSize = bucket->GetActualLength(); for (uint32_t i = 0; i < bucketSize; ++i) { auto *entry = reinterpret_cast(bucket->GetData()->Get(i)); - if (Compare(entry->GetKey(coro), key)) { + if (Compare(coro, entry->GetKey(coro), keyHandle.GetPtr())) { return ToEtsBoolean(true); } } @@ -231,12 +158,16 @@ EtsBoolean EtsEscompatMap::Has(EtsEscompatMap *map, EtsObject *key, EtsInt idx) EtsObject *EtsEscompatMap::Get(EtsEscompatMap *map, EtsObject *key, EtsInt idx) { auto *coro = EtsCoroutine::GetCurrent(); - auto *bucket = GetBucket(coro, map, idx); - if (bucket != nullptr) { + ASSERT(coro->HasPendingException() == false); + [[maybe_unused]] EtsHandleScope scope(coro); + EtsHandle keyHandle(coro, key); + EtsHandle bucket(coro, GetBucket(coro, map, idx)); + if (bucket.GetPtr() != nullptr) { uint32_t bucketSize = bucket->GetActualLength(); for (uint32_t i = 0; i < bucketSize; ++i) { - auto *entry = reinterpret_cast(bucket->GetData()->Get(i)); - if (Compare(entry->GetKey(coro), key)) { + EtsHandle entry(coro, + reinterpret_cast(bucket->GetData()->Get(i))); + if (Compare(coro, entry->GetKey(coro), keyHandle.GetPtr())) { return entry->GetVal(coro); } } @@ -248,15 +179,18 @@ EtsObject *EtsEscompatMap::Get(EtsEscompatMap *map, EtsObject *key, EtsInt idx) EtsBoolean EtsEscompatMap::Delete(EtsEscompatMap *map, EtsObject *key, EtsInt idx) { auto *coro = EtsCoroutine::GetCurrent(); - auto *bucket = GetBucket(coro, map, idx); - if (bucket == nullptr) { + ASSERT(coro->HasPendingException() == false); + [[maybe_unused]] EtsHandleScope scope(coro); + EtsHandle keyHandle(coro, key); + EtsHandle bucket(coro, GetBucket(coro, map, idx)); + if (bucket.GetPtr() == nullptr) { return ToEtsBoolean(false); } uint32_t bucketSize = bucket->GetActualLength(); for (uint32_t i = 0; i < bucketSize; ++i) { - auto *entry = reinterpret_cast(bucket->GetData()->Get(i)); - if (!Compare(entry->GetKey(coro), key)) { + EtsHandle entry(coro, reinterpret_cast(bucket->GetData()->Get(i))); + if (!Compare(coro, entry->GetKey(coro), keyHandle.GetPtr())) { continue; } diff --git a/static_core/plugins/ets/runtime/types/ets_map.h b/static_core/plugins/ets/runtime/types/ets_map.h index e71af01b0d..3e58f6b6d9 100644 --- a/static_core/plugins/ets/runtime/types/ets_map.h +++ b/static_core/plugins/ets/runtime/types/ets_map.h @@ -21,6 +21,10 @@ namespace ark::ets { +namespace test { +class EtsMapTest; +} // namespace test + class EtsEscompatMapEntry : public EtsObject { public: EtsEscompatMapEntry() = delete; @@ -29,57 +33,28 @@ public: NO_COPY_SEMANTIC(EtsEscompatMapEntry); NO_MOVE_SEMANTIC(EtsEscompatMapEntry); - static EtsEscompatMapEntry *Create(EtsObject *key, EtsObject *val) + static EtsEscompatMapEntry *Create(EtsCoroutine *coro, EtsHandle &keyHandle, + EtsHandle &valHandle) { - auto *coro = EtsCoroutine::GetCurrent(); - ASSERT(coro->HasPendingException() == false); - - [[maybe_unused]] EtsHandleScope scope(coro); - EtsHandle keyHandle(coro, key); - EtsHandle valHandle(coro, val); + ASSERT(coro != nullptr); + const EtsPlatformTypes *platformTypes = PlatformTypes(coro); + EtsHandle entryHandle( + coro, FromEtsObject(EtsObject::Create(coro, platformTypes->escompatMapEntry))); + if (UNLIKELY(entryHandle.GetPtr() == nullptr)) { + return nullptr; + } - const EtsPlatformTypes *platformTypes = PlatformTypes(coro->GetPandaVM()); - EtsHandle klass(coro, platformTypes->escompatMapEntry); - EtsHandle entryHandle(coro, FromEtsObject(EtsObject::Create(klass.GetPtr()))); entryHandle->SetKey(coro, keyHandle.GetPtr()); entryHandle->SetVal(coro, valHandle.GetPtr()); - if (UNLIKELY(coro->HasPendingException())) { - if (coro->GetPandaVM()->GetOOMErrorObject() == nullptr || - (coro->GetException()->ClassAddr() == - coro->GetPandaVM()->GetOOMErrorObject()->ClassAddr())) { - coro->ClearException(); - return nullptr; - } - // We do not expect any other exception than OOM - UNREACHABLE(); - } - return entryHandle.GetPtr(); } - static EtsEscompatMapEntry *Create() + static EtsEscompatMapEntry *Create(EtsCoroutine *coro) { - auto *coro = EtsCoroutine::GetCurrent(); - ASSERT(coro->HasPendingException() == false); - - EtsHandleScope scope(coro); - const EtsPlatformTypes *platformTypes = PlatformTypes(coro->GetPandaVM()); - EtsHandle klass(coro, platformTypes->escompatMapEntry); - EtsHandle entryHandle(coro, FromEtsObject(EtsObject::Create(klass.GetPtr()))); - - if (UNLIKELY(coro->HasPendingException())) { - if (coro->GetPandaVM()->GetOOMErrorObject() == nullptr || - (coro->GetException()->ClassAddr() == - coro->GetPandaVM()->GetOOMErrorObject()->ClassAddr())) { - coro->ClearException(); - return nullptr; - } - // We do not expect any other exception than OOM - UNREACHABLE(); - } - - return entryHandle.GetPtr(); + ASSERT(coro != nullptr); + const EtsPlatformTypes *platformTypes = PlatformTypes(coro); + return FromEtsObject(EtsObject::Create(coro, platformTypes->escompatMapEntry)); } static EtsEscompatMapEntry *FromCoreType(ObjectHeader *obj) @@ -166,6 +141,8 @@ private: ObjectPointer next_; ObjectPointer key_; ObjectPointer val_; + + friend class test::EtsMapTest; }; class EtsEscompatMap : public EtsObject { @@ -238,6 +215,8 @@ private: ObjectPointer head_; ObjectPointer buckets_; FIELD_UNUSED EtsInt sizeVal_; + + friend class test::EtsMapTest; }; } // namespace ark::ets diff --git a/static_core/plugins/ets/runtime/types/ets_set.h b/static_core/plugins/ets/runtime/types/ets_set.h index 013ad4ae6b..ba050da172 100644 --- a/static_core/plugins/ets/runtime/types/ets_set.h +++ b/static_core/plugins/ets/runtime/types/ets_set.h @@ -19,6 +19,11 @@ #include "plugins/ets/runtime/types/ets_map.h" namespace ark::ets { + +namespace test { +class EtsSetTest; +} // namespace test + class EtsEscompatSet : public EtsObject { public: EtsEscompatSet() = delete; @@ -39,6 +44,8 @@ public: private: ObjectPointer elements_; + + friend class test::EtsSetTest; }; } // namespace ark::ets diff --git a/static_core/plugins/ets/tests/checked/ets_escompat_map_intrinsics.ets b/static_core/plugins/ets/tests/checked/ets_escompat_map_intrinsics.ets index 2b08d8b9ad..dddb067d68 100644 --- a/static_core/plugins/ets/tests/checked/ets_escompat_map_intrinsics.ets +++ b/static_core/plugins/ets/tests/checked/ets_escompat_map_intrinsics.ets @@ -419,6 +419,14 @@ function test_map_char_double() { map.set(c'B', 2.5) arktest.assertTrue(map.delete(c'B')) arktest.assertFalse(map.delete(c'B')) + + map.set(c'\u012C', 1.5) + arktest.assertTrue(map.has(c'\u012C')) + arktest.assertEQ(map.get(c'\u012C'), 1.5) + arktest.assertFalse(map.has(c'\u002C')) + arktest.assertEQ(map.get(c'\u002C'), undefined) + arktest.assertFalse(map.has(c'\u0001')) + arktest.assertEQ(map.get(c'\u0001'), undefined) } function test_map_byte_int() { diff --git a/static_core/plugins/ets/tests/checked/ets_escompat_set_intrinsics.ets b/static_core/plugins/ets/tests/checked/ets_escompat_set_intrinsics.ets index 44cd4a2ec0..c49b66f8cd 100644 --- a/static_core/plugins/ets/tests/checked/ets_escompat_set_intrinsics.ets +++ b/static_core/plugins/ets/tests/checked/ets_escompat_set_intrinsics.ets @@ -310,6 +310,11 @@ function test_set_char() { set.add(c'B') arktest.assertTrue(set.delete(c'B')) arktest.assertFalse(set.delete(c'B')) + + set.add(c'\u012C') + arktest.assertTrue(set.has(c'\u012C')) + arktest.assertFalse(set.has(c'\u002C')) + arktest.assertFalse(set.has(c'\u0001')) } function test_set_byte() { diff --git a/static_core/plugins/ets/tests/runtime/types/CMakeLists.txt b/static_core/plugins/ets/tests/runtime/types/CMakeLists.txt index 541ddb9c20..fda1512cdf 100644 --- a/static_core/plugins/ets/tests/runtime/types/CMakeLists.txt +++ b/static_core/plugins/ets/tests/runtime/types/CMakeLists.txt @@ -63,6 +63,8 @@ panda_ets_add_gtest( ets_stacktrace_element_test.cpp ets_typed_arrays_test.cpp ets_typed_unsigned_arrays_test.cpp + ets_map_test.cpp + ets_set_test.cpp LIBRARIES arkassembler arkruntime diff --git a/static_core/plugins/ets/tests/runtime/types/ets_map_test.cpp b/static_core/plugins/ets/tests/runtime/types/ets_map_test.cpp new file mode 100644 index 0000000000..102904711c --- /dev/null +++ b/static_core/plugins/ets/tests/runtime/types/ets_map_test.cpp @@ -0,0 +1,89 @@ +/** + * Copyright (c) 2021-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "ets_coroutine.h" +#include "types/ets_map.h" + +#include "tests/runtime/types/ets_test_mirror_classes.h" + +namespace ark::ets::test { + +class EtsMapTest : public testing::Test { +public: + EtsMapTest() + { + RuntimeOptions options; + options.SetShouldLoadBootPandaFiles(true); + options.SetShouldInitializeIntrinsics(false); + options.SetCompilerEnableJit(false); + options.SetGcType("epsilon"); + options.SetLoadRuntimes({"ets"}); + + auto stdlib = std::getenv("PANDA_STD_LIB"); + if (stdlib == nullptr) { + std::cerr << "PANDA_STD_LIB env variable should be set and point to mock_stdlib.abc" << std::endl; + std::abort(); + } + options.SetBootPandaFiles({stdlib}); + + Runtime::Create(options); + EtsCoroutine *coroutine = EtsCoroutine::GetCurrent(); + vm_ = coroutine->GetPandaVM(); + } + + ~EtsMapTest() override + { + Runtime::Destroy(); + } + + NO_COPY_SEMANTIC(EtsMapTest); + NO_MOVE_SEMANTIC(EtsMapTest); + + static std::vector GetMapMembers() + { + return std::vector {MIRROR_FIELD_INFO(EtsEscompatMap, head_, "headEntry"), + MIRROR_FIELD_INFO(EtsEscompatMap, buckets_, "buckets"), + MIRROR_FIELD_INFO(EtsEscompatMap, sizeVal_, "sizeVal")}; + } + + static std::vector GetMapEntryMembers() + { + return std::vector {MIRROR_FIELD_INFO(EtsEscompatMapEntry, prev_, "prev"), + MIRROR_FIELD_INFO(EtsEscompatMapEntry, next_, "next"), + MIRROR_FIELD_INFO(EtsEscompatMapEntry, key_, "key"), + MIRROR_FIELD_INFO(EtsEscompatMapEntry, val_, "val")}; + } + +protected: + PandaEtsVM *vm_ = nullptr; // NOLINT(misc-non-private-member-variables-in-classes) +}; + +// Check both EtsEscompatMap and ark::Class has the same number of fields and at the same offsets +TEST_F(EtsMapTest, MapMemoryLayout) +{ + EtsClass *mapClass = PlatformTypes(vm_)->escompatMap; + MirrorFieldInfo::CompareMemberOffsets(mapClass, GetMapMembers()); +} + +// Check both EtsEscompatMapEntry and ark::Class has the same number of fields and at the same offsets +TEST_F(EtsMapTest, MapEntryMemoryLayout) +{ + EtsClass *mapEntryClass = PlatformTypes(vm_)->escompatMapEntry; + MirrorFieldInfo::CompareMemberOffsets(mapEntryClass, GetMapEntryMembers()); +} + +} // namespace ark::ets::test diff --git a/static_core/plugins/ets/tests/runtime/types/ets_set_test.cpp b/static_core/plugins/ets/tests/runtime/types/ets_set_test.cpp new file mode 100644 index 0000000000..844c05fe69 --- /dev/null +++ b/static_core/plugins/ets/tests/runtime/types/ets_set_test.cpp @@ -0,0 +1,74 @@ +/** + * Copyright (c) 2021-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "ets_coroutine.h" +#include "types/ets_set.h" + +#include "tests/runtime/types/ets_test_mirror_classes.h" + +namespace ark::ets::test { + +class EtsSetTest : public testing::Test { +public: + EtsSetTest() + { + RuntimeOptions options; + options.SetShouldLoadBootPandaFiles(true); + options.SetShouldInitializeIntrinsics(false); + options.SetCompilerEnableJit(false); + options.SetGcType("epsilon"); + options.SetLoadRuntimes({"ets"}); + + auto stdlib = std::getenv("PANDA_STD_LIB"); + if (stdlib == nullptr) { + std::cerr << "PANDA_STD_LIB env variable should be set and point to mock_stdlib.abc" << std::endl; + std::abort(); + } + options.SetBootPandaFiles({stdlib}); + + Runtime::Create(options); + EtsCoroutine *coroutine = EtsCoroutine::GetCurrent(); + vm_ = coroutine->GetPandaVM(); + } + + ~EtsSetTest() override + { + Runtime::Destroy(); + } + + NO_COPY_SEMANTIC(EtsSetTest); + NO_MOVE_SEMANTIC(EtsSetTest); + + static std::vector GetSetMembers() + { + return std::vector { + MIRROR_FIELD_INFO(EtsEscompatSet, elements_, "elements"), + }; + } + +protected: + PandaEtsVM *vm_ = nullptr; // NOLINT(misc-non-private-member-variables-in-classes) +}; + +// Check both EtsEscompatSet and ark::Class has the same number of fields and at the same offsets +TEST_F(EtsSetTest, SetMemoryLayout) +{ + EtsClass *setClass = PlatformTypes(vm_)->escompatSet; + MirrorFieldInfo::CompareMemberOffsets(setClass, GetSetMembers()); +} + +} // namespace ark::ets::test -- Gitee From 58d9cfb9aa30776ccceff5d7775935b51e11fee5 Mon Sep 17 00:00:00 2001 From: German Tretiakov Date: Thu, 11 Sep 2025 07:30:48 +0000 Subject: [PATCH 2/2] Set hash code for strings in IrToc Issue: https://gitee.com/openharmony/arkcompiler_runtime_core/issues/ICXJBZ Signed-off-by: German Tretiakov --- static_core/irtoc/scripts/string_helpers.irt | 16 ++++++++++++++++ static_core/plugins/ets/irtoc_scripts/map.irt | 16 ++++------------ .../plugins/ets/irtoc_scripts/map_set_common.irt | 14 ++++++++++++++ 3 files changed, 34 insertions(+), 12 deletions(-) diff --git a/static_core/irtoc/scripts/string_helpers.irt b/static_core/irtoc/scripts/string_helpers.irt index c3663a7663..830490a164 100644 --- a/static_core/irtoc/scripts/string_helpers.irt +++ b/static_core/irtoc/scripts/string_helpers.irt @@ -1104,6 +1104,22 @@ Label(:Loop_hash) hashcode := Phi(hash, hash2).u32 end # u16_string_hashcode +scoped_macro(:setStringHashCode) do |str| + str_data := Add(Cast(str).SrcType(Constants::COMPILER_REFERENCE).ptr, Cast(Constants::STRING_DATA_OFFSET).word).ptr + length_packed := LoadI(str).Imm(Constants::STRING_LENGTH_OFFSET).u32 + length := ShrI(length_packed).Imm(Constants::STRING_LENGTH_SHIFT).u32 + not_compressed := AndI(length_packed).Imm(1).u32 + If(not_compressed, 0).EQ.Likely.b { + # String contains 8-bit chars + h_8 := u8_string_hashcode(str_data, length) + StoreI(str, h_8).Imm(Constants::STRING_HASHCODE_OFFSET).u32 + } Else { + # String contains 16-bit chars + h_16 := u16_string_hashcode(str_data, length) + StoreI(str, h_16).Imm(Constants::STRING_HASHCODE_OFFSET).u32 + } + hash_code := Phi(h_8, h_16).u32 +end def GenerateStringHashCode(string_compression_enabled, cgmode) suffix = (string_compression_enabled ? "Compressed" : "") diff --git a/static_core/plugins/ets/irtoc_scripts/map.irt b/static_core/plugins/ets/irtoc_scripts/map.irt index 00684170c5..0430304f4e 100644 --- a/static_core/plugins/ets/irtoc_scripts/map.irt +++ b/static_core/plugins/ets/irtoc_scripts/map.irt @@ -87,10 +87,11 @@ def GenerateGetHashCodeByValue(cgmode) klass := LoadI(obj).Imm(Constants::OBJECT_CLASS_OFFSET).ref If(isString(klass), 0).NE.b { - If(isHashedString(obj), 0).EQ.b { - Goto(:SlowPathEntrypointString) + hash := fetchHashCodeString(obj) + If(hash, 0).EQ.b { + Return(Cast(setStringHashCode(obj)).u64).i64 } - Return(Cast(fetchHashCodeString(obj)).u64).i64 + Return(Cast(hash).u64).i64 } flags := LoadI(klass).Imm(EtsConstants::ETS_CLASS_FLAGS_FROM_RUNTIME_CLASS_OFFSET).u32 @@ -108,15 +109,6 @@ def GenerateGetHashCodeByValue(cgmode) Return(Cast(fetchHashCodeObject(obj)).u64).i64 -Label(:SlowPathEntrypointString) - if mode == :NativePlus - Return(Call(obj).Method('GetHashCodeStringSlowPath').i64).i64 - else - entrypoint = get_entrypoint_offset("GET_HASH_CODE_STRING_ODD_SAVED") - Intrinsic(:SLOW_PATH_ENTRY, obj).AddImm(entrypoint).MethodAsImm("GetHashCodeStringOddSavedBridge").Terminator.i64 - Intrinsic(:UNREACHABLE).Terminator.void if defines.DEBUG - end - Label(:SlowPathEntrypointBigint) if mode == :NativePlus Return(Call(obj).Method('GetHashCodeBigintSlowPath').i64).i64 diff --git a/static_core/plugins/ets/irtoc_scripts/map_set_common.irt b/static_core/plugins/ets/irtoc_scripts/map_set_common.irt index 70ddbd4d68..23dafb97da 100644 --- a/static_core/plugins/ets/irtoc_scripts/map_set_common.irt +++ b/static_core/plugins/ets/irtoc_scripts/map_set_common.irt @@ -323,6 +323,7 @@ end scoped_macro(:findKeyByStr) do |buck_data, buck_len, key| ofs_0 := Mul(SubI(buck_len).Imm(1).i32, EtsConstants::OBJ_PTR_SIZE).i32 + key_hash := fetchHashCodeString(key) Label(:Loop) ofs := Phi(ofs_0, ofs_1).i32 If(ofs, 0).LT.b { @@ -332,11 +333,20 @@ Label(:Loop) entry := Load(buck_data, ofs).ref object := LoadI(entry).Imm(MapConstants::ENTRY_KEY_OFFSET).ref + If(object, key).EQ.b { + Goto(:Done) + } + obj_klass := LoadI(object).Imm(Constants::OBJECT_CLASS_OFFSET).ref If(isString(obj_klass), 0).EQ.b { Goto(:Continue) } + obj_hash := fetchHashCodeString(object) + If(obj_hash, key_hash).NE.b { + Goto(:Continue) + } + If(macroStringCompareTo(object, key), 0).EQ.Likely.b { Goto(:Done) } @@ -386,6 +396,10 @@ Label(:findKeyByValLoop) entry := Load(buck_data, ofs).ref object := LoadI(entry).Imm(MapConstants::ENTRY_KEY_OFFSET).ref + If(object, key).EQ.b { + Goto(:findKeyByValDone) + } + obj_klass := LoadI(object).Imm(Constants::OBJECT_CLASS_OFFSET).ref obj_type := LoadI(obj_klass).Imm(Constants::BASE_CLASS_MANAGED_OBJECT_OFFSET).ref -- Gitee