From 5407353fbdeefbb12e39672e216641d5855694a9 Mon Sep 17 00:00:00 2001 From: chp Date: Sat, 19 Jul 2025 19:25:39 +0800 Subject: [PATCH] Cases to detect high bits of the Entry pointer Issue:https://gitee.com/openharmony/arkcompiler_ets_runtime/issues/ICNFG5 Signed-off-by: chp Change-Id: Ida81a434399feda96df5cae02806680986c98790 --- .../objects/string_table/hashtriemap.h | 6 ++ ecmascript/ecma_string_table.h | 6 ++ ecmascript/ecma_string_table_optimization.cpp | 40 +++++++++++ ecmascript/tests/ecma_string_table_test.cpp | 71 +++++++++++++++++-- 4 files changed, 119 insertions(+), 4 deletions(-) diff --git a/common_components/objects/string_table/hashtriemap.h b/common_components/objects/string_table/hashtriemap.h index 5f158e2f50..ce984b1c19 100644 --- a/common_components/objects/string_table/hashtriemap.h +++ b/common_components/objects/string_table/hashtriemap.h @@ -41,6 +41,7 @@ public: static constexpr uint32_t INDIRECT_SIZE = 8U; // 8: 2^3 static constexpr uint32_t INDIRECT_MASK = INDIRECT_SIZE - 1U; + static constexpr uint64_t HIGH_BIT_MASK = 1ULL << 63; enum SlotBarrier { NeedSlotBarrier, NoSlotBarrier, @@ -117,6 +118,11 @@ public: return overflow_; } + // to check the bitfield + uint64_t GetBitField() + { + return bitField_; + } private: uint64_t bitField_; std::atomic overflow_; diff --git a/ecmascript/ecma_string_table.h b/ecmascript/ecma_string_table.h index c9524bb1d9..41b4087b94 100644 --- a/ecmascript/ecma_string_table.h +++ b/ecmascript/ecma_string_table.h @@ -150,6 +150,11 @@ public: using StringTableInterface = typename Traits::StringTableInterface; using HashTrieMapImpl = typename Traits::HashTrieMapImpl; using ThreadType = typename Traits::ThreadType; + // only used for GetBitFieldForTest to check whether HashtrieMapEntry's 63 bit is 0 + HashTrieMapImpl& GetStringTable() + { + return stringTable_; + } // CMC constructor template = 0> EcmaStringTableImpl(StringTableInterface* itf, HashTrieMapImpl& map) @@ -230,6 +235,7 @@ public: } } + uint64_t GetBitFieldFromBaseStr(BaseString* baseStr); EcmaString *GetOrInternFlattenString(EcmaVM *vm, EcmaString *string); EcmaString *GetOrInternFlattenStringNoGC(EcmaVM *vm, EcmaString *string); EcmaString *GetOrInternStringFromCompressedSubString(EcmaVM *vm, const JSHandle &string, diff --git a/ecmascript/ecma_string_table_optimization.cpp b/ecmascript/ecma_string_table_optimization.cpp index eb1c8ea05a..96063ab48b 100644 --- a/ecmascript/ecma_string_table_optimization.cpp +++ b/ecmascript/ecma_string_table_optimization.cpp @@ -21,6 +21,10 @@ #include "ecmascript/ecma_string_table_optimization-inl.h" #include "ecmascript/jspandafile/js_pandafile.h" +using common::HashTrieMapIndirect; +using common::HashTrieMapNode; +using common::HashTrieMapEntry; + namespace panda::ecmascript { #if ENABLE_NEXT_OPTIMIZATION void EcmaStringTableCleaner::PostSweepWeakRefTask(const WeakRootVisitor &visitor) @@ -550,6 +554,42 @@ EcmaString* EcmaStringTableImpl::GetOrInternStringThreadUnsafe(EcmaVM* v return EcmaString::FromBaseString(result); } +// used for EcmaStringTableTest: GetOrInternString_utf8Data, GetOrInternString_utf16Data, GetOrInternString_EcmaString +uint64_t EcmaStringTable::GetBitFieldFromBaseStr(BaseString* baseStr) +{ + return visitImpl([baseStr](auto& impl) -> uint64_t { + uint32_t hash = baseStr->GetRawHashcode(); + uint32_t hashShift = 0; + HashTrieMapIndirect* current = impl.GetStringTable().GetRootAndProcessHash(hash); + + for (; hashShift < common::TrieMapConfig::TOTAL_HASH_BITS; + hashShift += common::TrieMapConfig::N_CHILDREN_LOG2) { + size_t index = (hash >> hashShift) & common::TrieMapConfig::N_CHILDREN_MASK; + std::atomic* slot = ¤t->GetChild(index); + HashTrieMapNode* node = slot->load(std::memory_order_acquire); + + if (node == nullptr) { + return 0; + } + + if (!node->IsEntry()) { + current = node->AsIndirect(); + continue; + } + + for (HashTrieMapEntry* entry = node->AsEntry(); entry != nullptr; + entry = entry->Overflow().load(std::memory_order_acquire)) { + BaseString* oldValue = entry->Value(); + if (oldValue == baseStr) { + return entry->GetBitField(); + } + } + } + + return 0; + }); +} + EcmaString* EcmaStringTable::GetOrInternFlattenString(EcmaVM* vm, EcmaString* string) { return visitImpl([&](auto& impl) { return impl.GetOrInternFlattenString(vm, string); }); diff --git a/ecmascript/tests/ecma_string_table_test.cpp b/ecmascript/tests/ecma_string_table_test.cpp index f6d4ca96a3..c668ba7399 100644 --- a/ecmascript/tests/ecma_string_table_test.cpp +++ b/ecmascript/tests/ecma_string_table_test.cpp @@ -55,9 +55,47 @@ HWTEST_F_L0(EcmaStringTableTest, GetOrInternFlattenString_EmptyString) #endif } +/** + * @tc.name: GetBitFieldFromBaseStr + * @tc.desc: Test the correctness of the return value of the GetBitFieldForTest function when string not found, + hash conflict, and normal insertion. + * @tc.type: FUNC + * @tc.require: + */ +HWTEST_F_L0(EcmaStringTableTest, GetBitFieldFromBaseStr_Coverage) +{ + EcmaVM *vm = thread->GetEcmaVM(); + EcmaStringTable *table = vm->GetEcmaStringTable(); + + // Path A: query for uninserted strings + uint8_t utf8DataNotInTable[] = {0x6e, 0x6f, 0x74, 0x69, 0x6e}; // "notin" + EcmaString *strNotInTable = EcmaStringAccessor::CreateFromUtf8(vm, utf8DataNotInTable, + sizeof(utf8DataNotInTable), true); + BaseString* baseStrNotInTable = strNotInTable->ToBaseString(); + uint64_t bitField = table->GetBitFieldFromBaseStr(baseStrNotInTable); + EXPECT_EQ(bitField, 0U) << "Should return 0 when string is not in TrieMap"; + + // Path B: insert a string with hash conflict but different content + uint8_t utf8DataConflict[] = {0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x66}; // "hellof" + EcmaString *strConflict = EcmaStringAccessor::CreateFromUtf8(vm, utf8DataConflict, + sizeof(utf8DataConflict), true); + strConflict->SetRawHashcode(baseStrNotInTable->GetRawHashcode()); + BaseString* baseStrConflict = strConflict->ToBaseString(); + bitField = table->GetBitFieldFromBaseStr(baseStrConflict); + EXPECT_EQ(bitField, 0U) << "Should return 0 when string not found in overflow chain"; + + // Path C: insert normal string + uint8_t utf8Data[] = {0x68, 0x65, 0x6c, 0x6c, 0x6f}; // "hello" + EcmaString *str = table->GetOrInternString(vm, utf8Data, sizeof(utf8Data), true); + BaseString* baseStr = str->ToBaseString(); + bitField = table->GetBitFieldFromBaseStr(baseStr); + EXPECT_NE(bitField, 0U) << "Should return non-zero bitField for valid entry"; +} + /** * @tc.name: GetOrInternString - * @tc.desc: Obtain EcmaString string from utf8 encoded data. If the string already exists in the detention pool, + * @tc.desc: Obtain EcmaString string from utf8 encoded data, and check if the high bits of the pointer stored + in the entry in the hashtriemap are being used. If the string already exists in the detention pool, it will be returned directly. If not, it will be added to the detention pool and then returned. * @tc.type: FUNC * @tc.require: @@ -74,11 +112,20 @@ HWTEST_F_L0(EcmaStringTableTest, GetOrInternString_utf8Data) EcmaString *ecmaStrGetPtr = table->GetOrInternString(vm, utf8Data, sizeof(utf8Data), true); EXPECT_STREQ(EcmaStringAccessor(ecmaStrGetPtr).ToCString(thread).c_str(), "hello"); EXPECT_TRUE(EcmaStringAccessor(ecmaStrGetPtr).IsInternString()); +#if !defined(ARK_ASAN_ON) + // Check if bits 63 are 0 + BaseString* baseStrFromTable = ecmaStrGetPtr->ToBaseString(); + uint64_t bitField = table->GetBitFieldFromBaseStr(baseStrFromTable); + EXPECT_NE(bitField, 0U) << "GetBitFieldForTest returned 0, but string should exist in TrieMap"; + EXPECT_EQ(bitField & common::TrieMapConfig::HIGH_BIT_MASK, 0) + << "Bit63 should be 0, but it is 1"; +#endif } /** * @tc.name: GetOrInternString - * @tc.desc: Obtain EcmaString string from utf16 encoded data. If the string already exists in the detention pool, + * @tc.desc: Obtain EcmaString string from utf16 encoded data, and check if the high bits of the pointer stored + in the entry in the hashtriemap are being used. If the string already exists in the detention pool, it will be returned directly. If not, it will be added to the detention pool and then returned. * @tc.type: FUNC * @tc.require: @@ -96,11 +143,20 @@ HWTEST_F_L0(EcmaStringTableTest, GetOrInternString_utf16Data) EcmaString *ecmaStrGetPtr = table->GetOrInternString(vm, utf16Data, sizeof(utf16Data) / sizeof(uint16_t), false); EXPECT_STREQ(EcmaStringAccessor(ecmaStrGetPtr).ToCString(thread).c_str(), "编码解码"); EXPECT_TRUE(EcmaStringAccessor(ecmaStrGetPtr).IsInternString()); +#if !defined(ARK_ASAN_ON) + // Check if bits 63 are 0 + BaseString* baseStrFromTable = ecmaStrGetPtr->ToBaseString(); + uint64_t bitField = table->GetBitFieldFromBaseStr(baseStrFromTable); + EXPECT_NE(bitField, 0U) << "GetBitFieldForTest returned 0, but string should exist in TrieMap"; + EXPECT_EQ(bitField & common::TrieMapConfig::HIGH_BIT_MASK, 0) + << "Bit63 should be 0, but it is 1"; +#endif } /** * @tc.name: GetOrInternString - * @tc.desc: Obtain EcmaString string from another EcmaString. If the string already exists in the detention pool, + * @tc.desc: Obtain EcmaString string from another EcmaString, and check if the high bits of the pointer stored + in the entry in the hashtriemap are being used. If the string already exists in the detention pool, it will be returned directly. If not, it will be added to the detention pool and then returned. * @tc.type: FUNC * @tc.require: @@ -117,7 +173,14 @@ HWTEST_F_L0(EcmaStringTableTest, GetOrInternString_EcmaString) EcmaString *ecmaStrGetPtr = table->GetOrInternString(vm, *ecmaStrCreateHandle); EXPECT_STREQ(EcmaStringAccessor(ecmaStrGetPtr).ToCString(thread).c_str(), "hello world"); EXPECT_TRUE(EcmaStringAccessor(ecmaStrGetPtr).IsInternString()); - +#if !defined(ARK_ASAN_ON) + // Check if bits 63 are 0 + BaseString* baseStrFromTable = ecmaStrGetPtr->ToBaseString(); + uint64_t bitField = table->GetBitFieldFromBaseStr(baseStrFromTable); + EXPECT_NE(bitField, 0U) << "GetBitFieldForTest returned 0, but string should exist in TrieMap"; + EXPECT_EQ(bitField & common::TrieMapConfig::HIGH_BIT_MASK, 0) + << "Bit63 should be 0, but it is 1"; +#endif #if ENABLE_NEXT_OPTIMIZATION EcmaString *ecmaStr = table->TryGetInternString(thread, ecmaStrCreateHandle); EXPECT_STREQ(EcmaStringAccessor(ecmaStr).ToCString(thread).c_str(), "hello world"); -- Gitee