diff --git a/common_components/objects/string_table/hashtriemap.h b/common_components/objects/string_table/hashtriemap.h index 5f158e2f50c891da0f5e2f7bdcafd83ba8072d82..ce984b1c19d6f2e593dc14b7b2595a336124081d 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 c9524bb1d9f3553f095a7c950eb2d22d0f8fb685..41b4087b94fa85b982632e990415b5a8cc335379 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 eb1c8ea05ad802e34a9101a7e31087bfc5305e29..96063ab48bb5ee92833ee7de64cd737968b230fd 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 f6d4ca96a3d0417865ecfd2dfa694b5fb216c102..c668ba7399320af1bef0958f7370a8acf3837988 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");