• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "common_components/objects/string_table/hashtriemap.h"
17 #include "ecmascript/checkpoint/thread_state_transition.h"
18 #include "ecmascript/ecma_string_table_optimization-inl.h"
19 #include "ecmascript/ecma_string_table.h"
20 #include "ecmascript/tests/test_helper.h"
21 
22 using namespace panda::ecmascript;
23 
24 namespace panda::test {
25 class EcmaStringTableTest : public BaseTestWithScope<false> {
26 public:
27     template <common::TrieMapConfig::SlotBarrier barrier>
28     void TestLoadOrStoreConcurrentAccess();
29     template <common::TrieMapConfig::SlotBarrier barrier>
30     void TestLoadOrStoreInsertNewKey();
31     template <common::TrieMapConfig::SlotBarrier barrier>
32     void TestLoadOrStoreStoreExistingKey();
33     template<common::TrieMapConfig::SlotBarrier barrier>
34     void TestExpandHashCollisionHandling();
35 };
36 
37 /**
38  * @tc.name: GetOrInternFlattenString_EmptyString
39  * @tc.desc: Write empty string emptyStr to the Intern pool and takes the hash code as its index.
40  * @tc.type: FUNC
41  * @tc.require:
42  */
HWTEST_F_L0(EcmaStringTableTest,GetOrInternFlattenString_EmptyString)43 HWTEST_F_L0(EcmaStringTableTest, GetOrInternFlattenString_EmptyString)
44 {
45     EcmaStringTable *table = thread->GetEcmaVM()->GetEcmaStringTable();
46 
47     JSHandle<EcmaString> emptyEcmaStrHandle(thread, EcmaStringAccessor::CreateEmptyString(thread->GetEcmaVM()));
48     EXPECT_TRUE(!EcmaStringAccessor(emptyEcmaStrHandle).IsInternString());
49 
50     table->GetOrInternFlattenString(thread->GetEcmaVM(), *emptyEcmaStrHandle);
51     EXPECT_TRUE(!EcmaStringAccessor(emptyEcmaStrHandle).IsInternString());
52 #if ENABLE_NEXT_OPTIMIZATION
53     EcmaString *emptyEcmaStr = table->TryGetInternString(thread, emptyEcmaStrHandle);
54     EXPECT_STREQ(EcmaStringAccessor(emptyEcmaStr).ToCString(thread).c_str(), "");
55     EXPECT_TRUE(EcmaStringAccessor(emptyEcmaStr).IsInternString());
56 #endif
57 }
58 
59 /**
60  * @tc.name: GetOrInternString
61  * @tc.desc: Obtain EcmaString string from utf8 encoded data. If the string already exists in the detention pool,
62              it will be returned directly. If not, it will be added to the detention pool and then returned.
63  * @tc.type: FUNC
64  * @tc.require:
65  */
HWTEST_F_L0(EcmaStringTableTest,GetOrInternString_utf8Data)66 HWTEST_F_L0(EcmaStringTableTest, GetOrInternString_utf8Data)
67 {
68     EcmaVM *vm = thread->GetEcmaVM();
69     EcmaStringTable *table = thread->GetEcmaVM()->GetEcmaStringTable();
70 
71     uint8_t utf8Data[] = {0x68, 0x65, 0x6c, 0x6c, 0x6f}; // " hello "
72     EcmaString *ecmaStrCreatePtr = EcmaStringAccessor::CreateFromUtf8(vm, utf8Data, sizeof(utf8Data), true);
73     EXPECT_TRUE(!EcmaStringAccessor(ecmaStrCreatePtr).IsInternString());
74 
75     EcmaString *ecmaStrGetPtr = table->GetOrInternString(vm, utf8Data, sizeof(utf8Data), true);
76     EXPECT_STREQ(EcmaStringAccessor(ecmaStrGetPtr).ToCString(thread).c_str(), "hello");
77     EXPECT_TRUE(EcmaStringAccessor(ecmaStrGetPtr).IsInternString());
78 }
79 
80 /**
81  * @tc.name: GetOrInternString
82  * @tc.desc: Obtain EcmaString string from utf16 encoded data. If the string already exists in the detention pool,
83              it will be returned directly. If not, it will be added to the detention pool and then returned.
84  * @tc.type: FUNC
85  * @tc.require:
86  */
HWTEST_F_L0(EcmaStringTableTest,GetOrInternString_utf16Data)87 HWTEST_F_L0(EcmaStringTableTest, GetOrInternString_utf16Data)
88 {
89     EcmaVM *vm = thread->GetEcmaVM();
90     EcmaStringTable *table = thread->GetEcmaVM()->GetEcmaStringTable();
91 
92     uint16_t utf16Data[] = {0x7F16, 0x7801, 0x89E3, 0x7801}; // “ 编码解码 ”
93     EcmaString *ecmaStrCreatePtr =
94         EcmaStringAccessor::CreateFromUtf16(vm, utf16Data, sizeof(utf16Data) / sizeof(uint16_t), false);
95     EXPECT_TRUE(!EcmaStringAccessor(ecmaStrCreatePtr).IsInternString());
96 
97     EcmaString *ecmaStrGetPtr = table->GetOrInternString(vm, utf16Data, sizeof(utf16Data) / sizeof(uint16_t), false);
98     EXPECT_STREQ(EcmaStringAccessor(ecmaStrGetPtr).ToCString(thread).c_str(), "编码解码");
99     EXPECT_TRUE(EcmaStringAccessor(ecmaStrGetPtr).IsInternString());
100 }
101 
102 /**
103  * @tc.name: GetOrInternString
104  * @tc.desc: Obtain EcmaString string from another EcmaString. If the string already exists in the detention pool,
105              it will be returned directly. If not, it will be added to the detention pool and then returned.
106  * @tc.type: FUNC
107  * @tc.require:
108  */
HWTEST_F_L0(EcmaStringTableTest,GetOrInternString_EcmaString)109 HWTEST_F_L0(EcmaStringTableTest, GetOrInternString_EcmaString)
110 {
111     EcmaVM *vm = thread->GetEcmaVM();
112     ObjectFactory *factory = vm->GetFactory();
113     EcmaStringTable *table = thread->GetEcmaVM()->GetEcmaStringTable();
114 
115     JSHandle<EcmaString> ecmaStrCreateHandle = factory->NewFromASCII("hello world");
116     EXPECT_TRUE(EcmaStringAccessor(ecmaStrCreateHandle).IsInternString());
117 
118     EcmaString *ecmaStrGetPtr = table->GetOrInternString(vm, *ecmaStrCreateHandle);
119     EXPECT_STREQ(EcmaStringAccessor(ecmaStrGetPtr).ToCString(thread).c_str(), "hello world");
120     EXPECT_TRUE(EcmaStringAccessor(ecmaStrGetPtr).IsInternString());
121 
122 #if ENABLE_NEXT_OPTIMIZATION
123     EcmaString *ecmaStr = table->TryGetInternString(thread, ecmaStrCreateHandle);
124     EXPECT_STREQ(EcmaStringAccessor(ecmaStr).ToCString(thread).c_str(), "hello world");
125     EXPECT_TRUE(EcmaStringAccessor(ecmaStr).IsInternString());
126 #endif
127 }
128 
129 /**
130  * @tc.name: GetOrInternString
131  * @tc.desc: Check the uniqueness of string and its hashcode in stringtable to ensure that no two strings have
132              same contents and same hashcode
133  * @tc.type: FUNC
134  * @tc.require:
135  */
HWTEST_F_L0(EcmaStringTableTest,GetOrInternString_CheckStringTable)136 HWTEST_F_L0(EcmaStringTableTest, GetOrInternString_CheckStringTable)
137 {
138 #if ENABLE_NEXT_OPTIMIZATION
139     EXPECT_TRUE(thread->GetEcmaVM()->GetEcmaStringTable()->CheckStringTableValidity(thread));
140 #else
141     EXPECT_TRUE(thread->GetEcmaVM()->GetEcmaStringTable()->CheckStringTableValidity(thread));
142 #endif
143 }
144 
145 #if ENABLE_NEXT_OPTIMIZATION
146 
147 // Used for thread concurrency loadOrStore verification
148 /**
149  * @tc.name: LoadOrStore_ConcurrentAccess
150  * @tc.desc: Test thread-safe insertion under concurrent access
151  * @tc.type: FUNC
152  * @tc.require: AR005
153  */
HWTEST_F_L0(EcmaStringTableTest,LoadOrStore_ConcurrentAccess)154 HWTEST_F_L0(EcmaStringTableTest, LoadOrStore_ConcurrentAccess)
155 {
156     TestLoadOrStoreConcurrentAccess<common::TrieMapConfig::NeedSlotBarrier>();
157     TestLoadOrStoreConcurrentAccess<common::TrieMapConfig::NoSlotBarrier>();
158 }
159 
160 // Check BitFiled of Entry
161 template<typename Mutex, typename ThreadHolder, common::TrieMapConfig::SlotBarrier SlotBarrier>
CheckBitFields(common::HashTrieMap<Mutex,ThreadHolder,SlotBarrier> * map)162 bool CheckBitFields(common::HashTrieMap<Mutex, ThreadHolder, SlotBarrier>* map)
163 {
164     bool highBitsNotSet = true;
165     std::function<bool(common::HashTrieMapNode *)> checkbit = [&highBitsNotSet](common::HashTrieMapNode *node) {
166         if (node->IsEntry()) {
167             uint64_t bitfield = node->AsEntry()->GetBitField();
168             if ((bitfield & common::TrieMapConfig::HIGH_8_BIT_MASK) != 0) {
169                 highBitsNotSet = false;
170                 return false;
171             }
172         }
173         return true;
174     };
175     map->Range(checkbit);
176     return highBitsNotSet;
177 }
178 
179 template<common::TrieMapConfig::SlotBarrier barrier>
TestLoadOrStoreConcurrentAccess()180 void EcmaStringTableTest::TestLoadOrStoreConcurrentAccess()
181 {
182     for (uint32_t i = 0; i < 20; i++) {
183         auto *map = new common::HashTrieMap<EcmaStringTableMutex, JSThread, barrier>();
184 
185         EcmaVM *vm = thread->GetEcmaVM();
186         [[maybe_unused]] JSHandle<EcmaString> values[] = {
187             vm->GetFactory()->NewFromASCII("581610154"),
188             vm->GetFactory()->NewFromASCII("648719018"),
189             vm->GetFactory()->NewFromASCII("715827882"),
190             vm->GetFactory()->NewFromASCII("782936746"),
191             vm->GetFactory()->NewFromASCII("850045610"),
192             vm->GetFactory()->NewFromASCII("917154474"),
193             vm->GetFactory()->NewFromASCII("984263338"),
194         };
195         constexpr int string_count = 7;
196         constexpr int thread_count = 8;
197         JSRuntimeOptions options;
198         auto thread_proc = [&]() {
199             EcmaVM *ecmaVm1{nullptr};
200             EcmaHandleScope *scope{nullptr};
201             JSThread *thread{nullptr};
202             TestHelper::CreateEcmaVMWithScope(ecmaVm1, thread, scope);
203             JSHandle<EcmaString> value[] = {
204                 ecmaVm1->GetFactory()->NewFromASCII("581610154"),
205                 ecmaVm1->GetFactory()->NewFromASCII("648719018"),
206                 ecmaVm1->GetFactory()->NewFromASCII("715827882"),
207                 ecmaVm1->GetFactory()->NewFromASCII("782936746"),
208                 ecmaVm1->GetFactory()->NewFromASCII("850045610"),
209                 ecmaVm1->GetFactory()->NewFromASCII("917154474"),
210                 ecmaVm1->GetFactory()->NewFromASCII("984263338"),
211             };
212             for (int i = 0; i < 1000; ++i) {
213                 auto readBarrier = [ecmaVm1](const void *obj, size_t offset) -> TaggedObject * {
214                     return Barriers::GetTaggedObject(ecmaVm1->GetAssociatedJSThread(), obj, offset);
215                 };
216                 map->template LoadOrStore<true>(
217                     ecmaVm1->GetJSThread(), value[i % string_count]->ToBaseString()->GetHashcode(readBarrier),
218                     [value, i]() {
219                         return value[i % string_count];
220                     },
221                     [ecmaVm1, &value, i](BaseString *v) {
222                         return EcmaStringAccessor::StringsAreEqual(ecmaVm1->GetJSThread(),
223                                                                    EcmaString::FromBaseString(v),
224                                                                    *value[i % string_count]);
225                     });
226             }
227             TestHelper::DestroyEcmaVMWithScope(ecmaVm1, scope);
228             return nullptr;
229         };
230         std::vector<std::thread> threads;
231         for (int i = 0; i < thread_count; ++i) {
232             threads.emplace_back(thread_proc);
233         }
234         for (auto &t: threads) {
235             ecmascript::ThreadSuspensionScope suspensionScope(thread);
236             t.join();
237         }
238         uint32_t count = 0;
239         std::function<bool(common::HashTrieMapNode *)> iter = [&count](common::HashTrieMapNode *node) {
240             if (node->IsEntry()) {
241                 count++;
242             }
243             return true;
244         };
245         map->Range(iter);
246         ASSERT_PRINT(count == string_count, count);
247         ASSERT_TRUE(CheckBitFields(map)) << "Bits 56-63 of Bitfield are incorrectly set!";
248         delete map;
249     }
250 }
251 /**
252  * @tc.name: LoadOrStore_InsertNewKey
253  * @tc.desc: Test inserting a new key-value pair via LoadOrStore
254  * @tc.type: FUNC
255  * @tc.require: AR001
256  */
HWTEST_F_L0(EcmaStringTableTest,LoadOrStore_InsertNewKey)257 HWTEST_F_L0(EcmaStringTableTest, LoadOrStore_InsertNewKey)
258 {
259     TestLoadOrStoreInsertNewKey<common::TrieMapConfig::NeedSlotBarrier>();
260     TestLoadOrStoreInsertNewKey<common::TrieMapConfig::NoSlotBarrier>();
261 }
262 
263 template<common::TrieMapConfig::SlotBarrier barrier>
TestLoadOrStoreInsertNewKey()264 void EcmaStringTableTest::TestLoadOrStoreInsertNewKey()
265 {
266     EcmaVM* vm = thread->GetEcmaVM();
267     auto* map = new common::HashTrieMap<EcmaStringTableMutex, JSThread, barrier>();
268     JSHandle<EcmaString> value(thread, *vm->GetFactory()->NewFromASCII("test_value"));
269     uint32_t key = value->ToBaseString()->GetMixHashcode();
270     auto readBarrier = [vm](const void *obj, size_t offset) -> TaggedObject * {
271         return Barriers::GetTaggedObject(vm->GetAssociatedJSThread(), obj, offset);
272     };
273     BaseString* loadResult1 = map->template Load<false>(std::move(readBarrier), key, value->ToBaseString());
274     EXPECT_EQ(loadResult1, nullptr);
275     // Test insertion
276     BaseString* result = map->template LoadOrStore<true>(
277         vm->GetJSThread(), key, [value]() { return value; },
278         [vm, value](BaseString *foudString) {
279             return EcmaStringAccessor::StringsAreEqual(vm->GetAssociatedJSThread(), *value,
280                                                        EcmaString::FromBaseString(foudString));
281         });
282     EXPECT_EQ(result, value->ToBaseString());
283     BaseString* loadResult2 = map->template Load<false>(std::move(readBarrier), key, value->ToBaseString());
284     EXPECT_STREQ(EcmaStringAccessor(EcmaString::FromBaseString(loadResult2)).ToCString(thread).c_str(), "test_value");
285     EXPECT_EQ(loadResult2, value->ToBaseString());
286     ASSERT_TRUE(CheckBitFields(map)) << "Bits 56-63 of Bitfield are incorrectly set!";
287     delete map;
288 }
289 
290 /**
291  * @tc.name: LoadOrStore_StoreExistingKey
292  * @tc.desc: Test updating existing key with value comparison
293  * @tc.type: FUNC
294  * @tc.require: AR002
295  */
HWTEST_F_L0(EcmaStringTableTest,LoadOrStore_StoreExistingKey)296 HWTEST_F_L0(EcmaStringTableTest, LoadOrStore_StoreExistingKey)
297 {
298     TestLoadOrStoreStoreExistingKey<common::TrieMapConfig::NeedSlotBarrier>();
299     TestLoadOrStoreStoreExistingKey<common::TrieMapConfig::NoSlotBarrier>();
300 }
301 
302 template<common::TrieMapConfig::SlotBarrier barrier>
TestLoadOrStoreStoreExistingKey()303 void EcmaStringTableTest::TestLoadOrStoreStoreExistingKey()
304 {
305     EcmaVM *vm = thread->GetEcmaVM();
306     auto *map = new common::HashTrieMap<EcmaStringTableMutex, JSThread, barrier>();
307     JSHandle<EcmaString> original(thread, *vm->GetFactory()->NewFromASCII("Aa1"));
308     JSHandle<EcmaString> origina2(thread, *vm->GetFactory()->NewFromASCII("BB1"));
309     // key1 = key2 = 0x0000FFF1
310     uint32_t key1 = original->ToBaseString()->GetMixHashcode();
311     uint32_t key2 = origina2->ToBaseString()->GetMixHashcode();
312 
313     // Initial insertion
314     map->template LoadOrStore<true>(
315         vm->GetJSThread(), key1, [original]() { return original; },
316         [this, original](BaseString *foudString) {
317             return EcmaStringAccessor::StringsAreEqual(thread, *original, EcmaString::FromBaseString(foudString));
318         });
319 
320     // store overflow
321     map->template LoadOrStore<true>(
322         vm->GetJSThread(), key2, [origina2]() { return origina2; },
323         [vm, origina2](BaseString *foudString) {
324             return EcmaStringAccessor::StringsAreEqual(vm->GetAssociatedJSThread(), *origina2,
325                                                        EcmaString::FromBaseString(foudString));
326         });
327     auto readBarrier = [vm](const void *obj, size_t offset) -> TaggedObject * {
328         return Barriers::GetTaggedObject(vm->GetAssociatedJSThread(), obj, offset);
329     };
330     EXPECT_EQ(map->template Load<false>(std::move(readBarrier), key1, original->ToBaseString()),
331               original->ToBaseString());
332     EXPECT_EQ(map->template Load<false>(std::move(readBarrier), key2, origina2->ToBaseString()),
333               origina2->ToBaseString());
334     ASSERT_TRUE(CheckBitFields(map)) << "Bits 56-63 of Bitfield are incorrectly set!";
335     delete map;
336 }
337 
338 /**
339  * @tc.name: Expand_HashCollisionHandling
340  * @tc.desc: Test node expansion during hash collision
341  * @tc.type: FUNC
342  * @tc.require: AR004
343  */
HWTEST_F_L0(EcmaStringTableTest,Expand_HashCollisionHandling)344 HWTEST_F_L0(EcmaStringTableTest, Expand_HashCollisionHandling)
345 {
346     TestExpandHashCollisionHandling<common::TrieMapConfig::NeedSlotBarrier>();
347     TestExpandHashCollisionHandling<common::TrieMapConfig::NoSlotBarrier>();
348 }
349 
350 template <common::TrieMapConfig::SlotBarrier barrier>
TestExpandHashCollisionHandling()351 void EcmaStringTableTest::TestExpandHashCollisionHandling()
352 {
353     EcmaVM* vm = thread->GetEcmaVM();
354     auto* map = new common::HashTrieMap<EcmaStringTableMutex, JSThread, barrier>();
355 
356     JSHandle<EcmaString> value1(thread, *vm->GetFactory()->NewFromASCII("ADF3"));
357     JSHandle<EcmaString> value2(thread, *vm->GetFactory()->NewFromASCII("A ?0"));
358     JSHandle<EcmaString> value3(thread, *vm->GetFactory()->NewFromASCII("AAa1"));
359     JSHandle<EcmaString> value4(thread, *vm->GetFactory()->NewFromASCII("ABB1"));
360     uint32_t key1 = value1->ToBaseString()->GetMixHashcode();
361     uint32_t key2 = value2->ToBaseString()->GetMixHashcode();
362     uint32_t key3 = value3->ToBaseString()->GetMixHashcode();
363     uint32_t key4 = value4->ToBaseString()->GetMixHashcode();
364     uint32_t ROOT_ID = key1 & common::TrieMapConfig::ROOT_BIT_MASK;
365     // Insert first key
366     map->template LoadOrStore<true>(
367         vm->GetJSThread(), key1, [value1]() { return value1; },
368         [this, value1](BaseString *foudString) {
369             return EcmaStringAccessor::StringsAreEqual(thread, *value1, EcmaString::FromBaseString(foudString));
370         });
371 
372     // Insert second key causing collision
373     map->template LoadOrStore<true>(
374         vm->GetJSThread(), key1, [value1]() { return value1; },
375         [this, value1](BaseString *foudString) {
376             return EcmaStringAccessor::StringsAreEqual(thread, *value1, EcmaString::FromBaseString(foudString));
377         });
378 
379     // Insert second key causing collision
380     map->template LoadOrStore<true>(
381         vm->GetJSThread(), key2, [value2]() { return value2; },
382         [this, value2](BaseString *foudString) {
383             return EcmaStringAccessor::StringsAreEqual(thread, *value2, EcmaString::FromBaseString(foudString));
384         });
385     // Insert overflow key3:[value3 value4]
386     map->template LoadOrStore<true>(
387         vm->GetJSThread(), key3, [value3]() { return value3; },
388         [this, value3](BaseString *foudString) {
389             return EcmaStringAccessor::StringsAreEqual(thread, *value3, EcmaString::FromBaseString(foudString));
390         });
391 
392     map->template LoadOrStore<true>(
393         vm->GetJSThread(), key4, [value4]() { return value4; },
394         [this, value4](BaseString *foudString) {
395             return EcmaStringAccessor::StringsAreEqual(thread, *value4, EcmaString::FromBaseString(foudString));
396         });
397 
398     /*map:
399     └── Indirect (----)
400       Children: [1, 2]
401       └── Child[1]:
402       └── Indirect (----)
403             Children: [0, 1]
404             └── Child[0]:
405             └── Entry [key2, value=0x001E0C10]
406             └── Child[2]:
407             └── Entry [key3, value=0x001E8C10]
408                   └── Overflow ->  └── Entry [key4, value=0x001E8C10]
409       └── Child[2]:
410             └── Entry [key1, value=0x001E9410]
411     key1 = 000_000_000_001_111_010_010_10000010000
412     key2 = 000_000_000_001_111_000_001_10000010000
413     key3 = 000_000_000_001_111_010_001_10000010000
414     key4 = 000_000_000_001_111_010_001_10000010000
415 
416     */
417     // Verify structure after expansion
418     common::HashTrieMapIndirect* root = map->GetRoot(ROOT_ID).load();
419     ASSERT_TRUE(root->GetChild(0x1).load() != nullptr); // Check first collision level
420 
421     common::HashTrieMapIndirect* level1 = root->GetChild(0x1).
422         load()->AsIndirect();
423     ASSERT_TRUE(level1->GetChild(0x0).load() != nullptr);
424     ASSERT_TRUE(level1->GetChild(0x2).load() != nullptr);
425     common::HashTrieMapEntry* entry = level1->GetChild(0x2).load()->AsEntry();
426     // Verify overflow
427     ASSERT_TRUE(entry->Overflow().load() != nullptr);
428     ASSERT_TRUE(root->GetChild(0x2).load() != nullptr);
429     ASSERT_TRUE(CheckBitFields(map)) << "Bits 56-63 of Bitfield are incorrectly set!";
430     delete map;
431 }
432 
433 /**
434  * @tc.name: GetOrInternStringFromCompressedSubString_SubString
435  * @tc.desc: Test creating interned string from a compressed substring of another string
436  * @tc.type: FUNC
437  * @tc.require: AR001
438  */
HWTEST_F_L0(EcmaStringTableTest,GetOrInternStringFromCompressedSubString_SubString)439 HWTEST_F_L0(EcmaStringTableTest, GetOrInternStringFromCompressedSubString_SubString)
440 {
441     EcmaVM *vm = thread->GetEcmaVM();
442     ObjectFactory *factory = vm->GetFactory();
443     EcmaStringTable *table = vm->GetEcmaStringTable();
444 
445     JSHandle<EcmaString> originalStr =
446         factory->NewFromASCII("00000x680x650x6c0x6c0x6f0x200x770x6f0x720x6c0x64");  // "hello world"
447     uint32_t offset = 4;
448     uint32_t utf8Len = EcmaStringAccessor(*originalStr).GetLength() - offset;
449 
450     EcmaString *internStr = table->GetOrInternStringFromCompressedSubString(vm, originalStr, offset, utf8Len);
451     EXPECT_STREQ(EcmaStringAccessor(internStr).ToCString(thread).c_str(),
452                  "0x680x650x6c0x6c0x6f0x200x770x6f0x720x6c0x64");
453     EXPECT_TRUE(EcmaStringAccessor(internStr).IsInternString());
454 }
455 
456 /**
457  * @tc.name: GetOrInternString_ConcatenatedStrings
458  * @tc.desc: Test interning concatenated strings from two JSHandle<EcmaString>
459  * @tc.type: FUNC
460  * @tc.require: AR001
461  */
HWTEST_F_L0(EcmaStringTableTest,GetOrInternString_ConcatenatedStrings)462 HWTEST_F_L0(EcmaStringTableTest, GetOrInternString_ConcatenatedStrings)
463 {
464     EcmaVM *vm = thread->GetEcmaVM();
465     ObjectFactory *factory = vm->GetFactory();
466     EcmaStringTable *table = vm->GetEcmaStringTable();
467 
468     JSHandle<EcmaString> str1 = factory->NewFromASCII("hello");
469     JSHandle<EcmaString> str2 = factory->NewFromASCII("world");
470 
471     EcmaString *concatenated = table->GetOrInternString(vm, str1, str2);
472 
473     EXPECT_STREQ(EcmaStringAccessor(concatenated).ToCString(thread).c_str(), "helloworld");
474     EXPECT_TRUE(EcmaStringAccessor(concatenated).IsInternString());
475 }
476 
477 /**
478  * @tc.name: TryGetInternString_ExistingString
479  * @tc.desc: Test retrieving existing interned string using TryGetInternString
480  * @tc.type: FUNC
481  * @tc.require: AR001
482  */
HWTEST_F_L0(EcmaStringTableTest,TryGetInternString_ExistingString)483 HWTEST_F_L0(EcmaStringTableTest, TryGetInternString_ExistingString)
484 {
485     EcmaVM *vm = thread->GetEcmaVM();
486     ObjectFactory *factory = vm->GetFactory();
487     EcmaStringTable *table = vm->GetEcmaStringTable();
488 
489     JSHandle<EcmaString> original = factory->NewFromASCII("test");
490     table->GetOrInternString(vm, *original);
491 
492     EcmaString *retrieved = table->TryGetInternString(thread, original);
493 
494     EXPECT_STREQ(EcmaStringAccessor(retrieved).ToCString(thread).c_str(), "test");
495     EXPECT_TRUE(EcmaStringAccessor(retrieved).IsInternString());
496 }
497 #endif
498 
HWTEST_F_L0(EcmaStringTableTest,GetOrInternFlattenStringNoGC)499 HWTEST_F_L0(EcmaStringTableTest, GetOrInternFlattenStringNoGC)
500 {
501     auto vm = thread->GetEcmaVM();
502     EcmaStringTable *stringTable = vm->GetEcmaStringTable();
503     uint8_t utf8Data[] = {0x74, 0x65, 0x73, 0x74}; // "test"
504 
505     EcmaString *internString = EcmaStringAccessor::CreateFromUtf8(vm, utf8Data, sizeof(utf8Data), true);
506     EcmaStringAccessor(internString).SetInternString();
507     auto result = stringTable->GetOrInternFlattenStringNoGC(vm, internString);
508     EXPECT_EQ(result, internString);
509 
510     EcmaString *nonInternString = EcmaStringAccessor::CreateFromUtf8(vm, utf8Data, sizeof(utf8Data), true);
511     EXPECT_TRUE(!EcmaStringAccessor(nonInternString).IsInternString());
512     internString = stringTable->GetOrInternFlattenStringNoGC(vm, nonInternString);
513     EXPECT_TRUE(EcmaStringAccessor(internString).IsInternString());
514     EXPECT_STREQ(EcmaStringAccessor(internString).ToCString(thread).c_str(), "test");
515 
516     EcmaString *repeatedCallString = stringTable->GetOrInternFlattenStringNoGC(vm, internString);
517     EXPECT_EQ(internString, repeatedCallString);
518 }
519 }  // namespace panda::test
520