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