1/*
2 * Copyright (c) 2021-2024 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 "ecmascript/ecma_string_table.h"
17
18#include "ecmascript/ecma_string-inl.h"
19#include "ecmascript/runtime_lock.h"
20namespace panda::ecmascript {
21void EcmaStringTableCleaner::PostSweepWeakRefTask(const WeakRootVisitor &visitor)
22{
23    StartSweepWeakRefTask();
24    iter_ = std::make_shared<std::atomic<uint32_t>>(0U);
25    const uint32_t postTaskCount = Taskpool::GetCurrentTaskpool()->GetTotalThreadNum();
26    for (uint32_t i = 0U; i < postTaskCount; ++i) {
27        Taskpool::GetCurrentTaskpool()->PostTask(std::make_unique<SweepWeakRefTask>(iter_, this, visitor));
28    }
29}
30
31void EcmaStringTableCleaner::JoinAndWaitSweepWeakRefTask(const WeakRootVisitor &visitor)
32{
33    ProcessSweepWeakRef(iter_, this, visitor);
34    WaitSweepWeakRefTask();
35    iter_.reset();
36}
37
38void EcmaStringTableCleaner::ProcessSweepWeakRef(IteratorPtr& iter, EcmaStringTableCleaner *cleaner,
39                                                 const WeakRootVisitor &visitor)
40{
41    uint32_t tableId = 0U;
42    while ((tableId = GetNextTableId(iter)) < EcmaStringTable::SEGMENT_COUNT) {
43        cleaner->stringTable_->SweepWeakRef(visitor, tableId);
44        if (ReduceCountAndCheckFinish(cleaner)) {
45            cleaner->SignalSweepWeakRefTask();
46        }
47    }
48}
49
50void EcmaStringTableCleaner::StartSweepWeakRefTask()
51{
52    // No need lock here, only the daemon thread will reset the state.
53    sweepWeakRefFinished_ = false;
54    PendingTaskCount_.store(EcmaStringTable::SEGMENT_COUNT, std::memory_order_relaxed);
55}
56
57void EcmaStringTableCleaner::WaitSweepWeakRefTask()
58{
59    LockHolder holder(sweepWeakRefMutex_);
60    while (!sweepWeakRefFinished_) {
61        sweepWeakRefCV_.Wait(&sweepWeakRefMutex_);
62    }
63}
64
65void EcmaStringTableCleaner::SignalSweepWeakRefTask()
66{
67    LockHolder holder(sweepWeakRefMutex_);
68    sweepWeakRefFinished_ = true;
69    sweepWeakRefCV_.SignalAll();
70}
71
72bool EcmaStringTableCleaner::SweepWeakRefTask::Run([[maybe_unused]] uint32_t threadIndex)
73{
74    ProcessSweepWeakRef(iter_, cleaner_, visitor_);
75    return true;
76}
77
78std::pair<EcmaString *, uint32_t> EcmaStringTable::GetStringThreadUnsafe(const JSHandle<EcmaString> &firstString,
79                                                                         const JSHandle<EcmaString> &secondString,
80                                                                         uint32_t hashcode) const
81{
82    ASSERT(EcmaStringAccessor(firstString).NotTreeString());
83    ASSERT(EcmaStringAccessor(secondString).NotTreeString());
84    auto range = stringTable_[GetTableId(hashcode)].table_.equal_range(hashcode);
85    for (auto item = range.first; item != range.second;) {
86        auto foundString = (item++)->second;
87        if (EcmaStringAccessor(foundString).EqualToSplicedString(*firstString, *secondString)) {
88            return std::make_pair(foundString, hashcode);
89        }
90    }
91    return std::make_pair(nullptr, hashcode);
92}
93
94std::pair<EcmaString *, uint32_t> EcmaStringTable::GetStringThreadUnsafe(const uint8_t *utf8Data, uint32_t utf8Len,
95                                                                         bool canBeCompress, uint32_t hashcode) const
96{
97    auto range = stringTable_[GetTableId(hashcode)].table_.equal_range(hashcode);
98    for (auto item = range.first; item != range.second;) {
99        auto foundString = (item++)->second;
100        if (EcmaStringAccessor::StringIsEqualUint8Data(foundString, utf8Data, utf8Len, canBeCompress)) {
101            return std::make_pair(foundString, hashcode);
102        }
103    }
104    return std::make_pair(nullptr, hashcode);
105}
106
107std::pair<EcmaString *, uint32_t> EcmaStringTable::GetStringThreadUnsafe(const uint16_t *utf16Data,
108                                                                         uint32_t utf16Len, uint32_t hashcode) const
109{
110    auto range = stringTable_[GetTableId(hashcode)].table_.equal_range(hashcode);
111    for (auto item = range.first; item != range.second;) {
112        auto foundString = (item++)->second;
113        if (EcmaStringAccessor::StringsAreEqualUtf16(foundString, utf16Data, utf16Len)) {
114            return std::make_pair(foundString, hashcode);
115        }
116    }
117    return std::make_pair(nullptr, hashcode);
118}
119
120EcmaString *EcmaStringTable::GetStringWithHashThreadUnsafe(EcmaString *string, uint32_t hashcode) const
121{
122    auto range = stringTable_[GetTableId(hashcode)].table_.equal_range(hashcode);
123    for (auto item = range.first; item != range.second;) {
124        auto foundString = (item++)->second;
125        if (EcmaStringAccessor::StringsAreEqual(foundString, string)) {
126            return foundString;
127        }
128    }
129    return nullptr;
130}
131
132EcmaString *EcmaStringTable::GetStringThreadUnsafe(EcmaString *string, uint32_t hashcode) const
133{
134    auto range = stringTable_[GetTableId(hashcode)].table_.equal_range(hashcode);
135    for (auto item = range.first; item != range.second;) {
136        auto foundString = (item++)->second;
137        if (EcmaStringAccessor::StringsAreEqual(foundString, string)) {
138            return foundString;
139        }
140    }
141    return nullptr;
142}
143
144void EcmaStringTable::InternStringThreadUnsafe(EcmaString *string, uint32_t hashcode)
145{
146    if (EcmaStringAccessor(string).IsInternString()) {
147        return;
148    }
149    // Strings in string table should not be in the young space.
150    ASSERT(Region::ObjectAddressToRange(reinterpret_cast<TaggedObject *>(string))->InSharedHeap());
151    ASSERT(EcmaStringAccessor(string).NotTreeString());
152    stringTable_[GetTableId(hashcode)].table_.emplace(hashcode, string);
153    EcmaStringAccessor(string).SetInternString();
154}
155
156void EcmaStringTable::InternEmptyString(JSThread *thread, EcmaString *emptyStr)
157{
158    auto hashcode = EcmaStringAccessor(emptyStr).GetHashcode();
159    RuntimeLockHolder locker(thread, stringTable_[GetTableId(hashcode)].mutex_);
160#if ECMASCRIPT_ENABLE_SCOPE_LOCK_STAT
161    auto vm = thread->GetEcmaVM();
162    if (vm->IsCollectingScopeLockStats()) {
163        vm->IncreaseStringTableLockCount();
164    }
165#endif
166    InternStringThreadUnsafe(emptyStr, hashcode);
167}
168
169void EcmaStringTable::InsertStringIfNotExistThreadUnsafe(EcmaString *string)
170{
171    auto hashcode = EcmaStringAccessor(string).GetHashcode();
172    EcmaString *str = GetStringThreadUnsafe(string, hashcode);
173    if (str == nullptr) {
174        InternStringThreadUnsafe(string, hashcode);
175    }
176}
177
178EcmaString *EcmaStringTable::GetOrInternString(EcmaVM *vm, const JSHandle<EcmaString> &firstString,
179                                               const JSHandle<EcmaString> &secondString)
180{
181    JSThread *thread = nullptr;
182    bool signalState = vm->GetJsDebuggerManager()->GetSignalState();
183    thread = signalState ? vm->GetJSThreadNoCheck() : vm->GetJSThread();
184    auto firstFlat = JSHandle<EcmaString>(thread, EcmaStringAccessor::Flatten(vm, firstString));
185    auto secondFlat = JSHandle<EcmaString>(thread, EcmaStringAccessor::Flatten(vm, secondString));
186    uint32_t hashcode = EcmaStringAccessor::CalculateAllConcatHashCode(firstFlat, secondFlat);
187    if (signalState) {
188        return GetOrInternStringWithoutLock(thread, firstString, secondString, hashcode);
189    }
190    RuntimeLockHolder locker(thread, stringTable_[GetTableId(hashcode)].mutex_);
191    return GetOrInternStringWithoutLock(thread, firstString, secondString, hashcode);
192}
193
194EcmaString *EcmaStringTable::GetOrInternStringWithoutLock(JSThread *thread, const JSHandle<EcmaString> &firstString,
195                                                          const JSHandle<EcmaString> &secondString, uint32_t hashcode)
196{
197    EcmaVM *vm = thread->GetEcmaVM();
198    auto firstFlat = JSHandle<EcmaString>(thread, EcmaStringAccessor::Flatten(vm, firstString));
199    auto secondFlat = JSHandle<EcmaString>(thread, EcmaStringAccessor::Flatten(vm, secondString));
200#if ECMASCRIPT_ENABLE_SCOPE_LOCK_STAT
201    if (vm->IsCollectingScopeLockStats()) {
202        vm->IncreaseStringTableLockCount();
203    }
204#endif
205    std::pair<EcmaString *, uint32_t> result = GetStringThreadUnsafe(firstFlat, secondFlat, hashcode);
206    if (result.first != nullptr) {
207        return result.first;
208    }
209    JSHandle<EcmaString> concatHandle(thread,
210        EcmaStringAccessor::Concat(vm, firstFlat, secondFlat, MemSpaceType::SHARED_OLD_SPACE));
211    EcmaString *concatString = EcmaStringAccessor::Flatten(vm, concatHandle, MemSpaceType::SHARED_OLD_SPACE);
212    concatString->SetMixHashcode(result.second);
213    InternStringThreadUnsafe(concatString, hashcode);
214    return concatString;
215}
216
217EcmaString *EcmaStringTable::GetOrInternString(EcmaVM *vm, const uint8_t *utf8Data, uint32_t utf8Len,
218                                               bool canBeCompress)
219{
220    uint32_t hashcode = EcmaStringAccessor::ComputeHashcodeUtf8(utf8Data, utf8Len, canBeCompress);
221    if (vm->GetJsDebuggerManager()->GetSignalState()) {
222        return GetOrInternStringWithoutLock(vm, utf8Data, utf8Len, canBeCompress, hashcode);
223    } else {
224        RuntimeLockHolder locker(vm->GetJSThread(), stringTable_[GetTableId(hashcode)].mutex_);
225        return GetOrInternStringWithoutLock(vm, utf8Data, utf8Len, canBeCompress, hashcode);
226    }
227}
228
229EcmaString *EcmaStringTable::GetOrInternStringWithoutLock(EcmaVM *vm, const uint8_t *utf8Data,
230                                                          uint32_t utf8Len, bool canBeCompress, uint32_t hashcode)
231{
232#if ECMASCRIPT_ENABLE_SCOPE_LOCK_STAT
233    if (vm->IsCollectingScopeLockStats()) {
234        vm->IncreaseStringTableLockCount();
235    }
236#endif
237    std::pair<EcmaString *, uint32_t> result = GetStringThreadUnsafe(utf8Data, utf8Len, canBeCompress, hashcode);
238    if (result.first != nullptr) {
239        return result.first;
240    }
241
242    EcmaString *str =
243        EcmaStringAccessor::CreateFromUtf8(vm, utf8Data, utf8Len, canBeCompress, MemSpaceType::SHARED_OLD_SPACE);
244    str->SetMixHashcode(result.second);
245    InternStringThreadUnsafe(str, hashcode);
246    return str;
247}
248
249EcmaString *EcmaStringTable::GetOrInternCompressedSubString(EcmaVM *vm, const JSHandle<EcmaString> &string,
250    uint32_t offset, uint32_t utf8Len)
251{
252    auto *utf8Data = EcmaStringAccessor(string).GetDataUtf8() + offset;
253    uint32_t hashcode = EcmaStringAccessor::ComputeHashcodeUtf8(utf8Data, utf8Len, true);
254    RuntimeLockHolder locker(vm->GetJSThread(), stringTable_[GetTableId(hashcode)].mutex_);
255#if ECMASCRIPT_ENABLE_SCOPE_LOCK_STAT
256    if (vm->IsCollectingScopeLockStats()) {
257        vm->IncreaseStringTableLockCount();
258    }
259#endif
260    // utf8data may be moved after shared full gc, so reload utf8Data here.
261    utf8Data = EcmaStringAccessor(string).GetDataUtf8() + offset;
262    std::pair<EcmaString *, uint32_t> result = GetStringThreadUnsafe(utf8Data, utf8Len, true, hashcode);
263    if (result.first != nullptr) {
264        return result.first;
265    }
266
267    EcmaString *str = EcmaStringAccessor::CreateFromUtf8CompressedSubString(
268        vm, string, offset, utf8Len, MemSpaceType::SHARED_OLD_SPACE);
269    str->SetMixHashcode(result.second);
270    InternStringThreadUnsafe(str, hashcode);
271    return str;
272}
273
274/*
275    This function is used to create global constant strings from non-movable sapce only.
276    It only inserts string into string-table and provides no string-table validity check.
277*/
278EcmaString *EcmaStringTable::CreateAndInternStringNonMovable(EcmaVM *vm, const uint8_t *utf8Data, uint32_t utf8Len)
279{
280    uint32_t hashcode = EcmaStringAccessor::ComputeHashcodeUtf8(utf8Data, utf8Len, true);
281    RuntimeLockHolder locker(vm->GetJSThread(), stringTable_[GetTableId(hashcode)].mutex_);
282#if ECMASCRIPT_ENABLE_SCOPE_LOCK_STAT
283    if (vm->IsCollectingScopeLockStats()) {
284        vm->IncreaseStringTableLockCount();
285    }
286#endif
287    std::pair<EcmaString *, uint32_t> result = GetStringThreadUnsafe(utf8Data, utf8Len, true, hashcode);
288    if (result.first != nullptr) {
289        return result.first;
290    }
291    EcmaString *str = EcmaStringAccessor::CreateFromUtf8(vm, utf8Data, utf8Len, true, MemSpaceType::SHARED_NON_MOVABLE);
292    str->SetMixHashcode(result.second);
293    InternStringThreadUnsafe(str, hashcode);
294    return str;
295}
296
297/*
298    This function is used to create global constant strings from read-only sapce only.
299    It only inserts string into string-table and provides no string-table validity check.
300*/
301EcmaString *EcmaStringTable::CreateAndInternStringReadOnly(EcmaVM *vm, const uint8_t *utf8Data, uint32_t utf8Len,
302                                                           bool canBeCompress)
303{
304    uint32_t hashcode = EcmaStringAccessor::ComputeHashcodeUtf8(utf8Data, utf8Len, canBeCompress);
305    RuntimeLockHolder locker(vm->GetJSThread(), stringTable_[GetTableId(hashcode)].mutex_);
306#if ECMASCRIPT_ENABLE_SCOPE_LOCK_STAT
307    if (vm->IsCollectingScopeLockStats()) {
308        vm->IncreaseStringTableLockCount();
309    }
310#endif
311    std::pair<EcmaString *, uint32_t> result = GetStringThreadUnsafe(utf8Data, utf8Len, canBeCompress, hashcode);
312    if (result.first != nullptr) {
313        return result.first;
314    }
315    EcmaString *str = EcmaStringAccessor::CreateFromUtf8(vm, utf8Data, utf8Len, canBeCompress,
316                                                         MemSpaceType::SHARED_READ_ONLY_SPACE);
317    str->SetMixHashcode(result.second);
318    InternStringThreadUnsafe(str, hashcode);
319    return str;
320}
321
322EcmaString *EcmaStringTable::GetOrInternString(EcmaVM *vm, const uint16_t *utf16Data, uint32_t utf16Len,
323                                               bool canBeCompress)
324{
325    uint32_t hashcode = EcmaStringAccessor::ComputeHashcodeUtf16(const_cast<uint16_t *>(utf16Data), utf16Len);
326    RuntimeLockHolder locker(vm->GetJSThread(), stringTable_[GetTableId(hashcode)].mutex_);
327#if ECMASCRIPT_ENABLE_SCOPE_LOCK_STAT
328    if (vm->IsCollectingScopeLockStats()) {
329        vm->IncreaseStringTableLockCount();
330    }
331#endif
332    std::pair<EcmaString *, uint32_t> result = GetStringThreadUnsafe(utf16Data, utf16Len, hashcode);
333    if (result.first != nullptr) {
334        return result.first;
335    }
336
337    EcmaString *str =
338        EcmaStringAccessor::CreateFromUtf16(vm, utf16Data, utf16Len, canBeCompress, MemSpaceType::SHARED_OLD_SPACE);
339    str->SetMixHashcode(result.second);
340    InternStringThreadUnsafe(str, hashcode);
341    return str;
342}
343
344EcmaString *EcmaStringTable::GetOrInternString(EcmaVM *vm, EcmaString *string)
345{
346    if (EcmaStringAccessor(string).IsInternString()) {
347        return string;
348    }
349    auto thread = vm->GetJSThread();
350    JSHandle<EcmaString> strHandle(thread, string);
351    // may gc
352    auto strFlat = EcmaStringAccessor::Flatten(vm, strHandle, MemSpaceType::SHARED_OLD_SPACE);
353    if (EcmaStringAccessor(strFlat).IsInternString()) {
354        return strFlat;
355    }
356    JSHandle<EcmaString> strFlatHandle(thread, strFlat);
357    // may gc
358    auto hashcode = EcmaStringAccessor(strFlat).GetHashcode();
359    RuntimeLockHolder locker(thread, stringTable_[GetTableId(hashcode)].mutex_);
360#if ECMASCRIPT_ENABLE_SCOPE_LOCK_STAT
361    if (vm->IsCollectingScopeLockStats()) {
362        vm->IncreaseStringTableLockCount();
363    }
364#endif
365    strFlat = *strFlatHandle;
366    EcmaString *result = GetStringThreadUnsafe(strFlat, hashcode);
367    if (result != nullptr) {
368        return result;
369    }
370
371    InternStringThreadUnsafe(strFlat, hashcode);
372    return strFlat;
373}
374
375EcmaString *EcmaStringTable::GetOrInternStringThreadUnsafe(EcmaVM *vm, EcmaString *string)
376{
377    if (EcmaStringAccessor(string).IsInternString()) {
378        return string;
379    }
380    JSHandle<EcmaString> strHandle(vm->GetJSThread(), string);
381    EcmaString *strFlat = EcmaStringAccessor::Flatten(vm, strHandle, MemSpaceType::SHARED_OLD_SPACE);
382    if (EcmaStringAccessor(strFlat).IsInternString()) {
383        return strFlat;
384    }
385    auto hashcode = EcmaStringAccessor(strFlat).GetHashcode();
386    EcmaString *result = GetStringThreadUnsafe(strFlat, hashcode);
387    if (result != nullptr) {
388        return result;
389    }
390
391    InternStringThreadUnsafe(strFlat, hashcode);
392    return strFlat;
393}
394
395void EcmaStringTable::InsertStringToTableWithHashThreadUnsafe(EcmaString* string, uint32_t hashcode)
396{
397    // Strings in string table should not be in the young space.
398    ASSERT(Region::ObjectAddressToRange(reinterpret_cast<TaggedObject *>(string))->InSharedHeap());
399    ASSERT(EcmaStringAccessor(string).NotTreeString());
400    ASSERT(EcmaStringAccessor(string).GetHashcode() == hashcode);
401    stringTable_[GetTableId(hashcode)].table_.emplace(hashcode, string);
402    EcmaStringAccessor(string).SetInternString();
403}
404
405EcmaString *EcmaStringTable::InsertStringToTable(EcmaVM *vm, const JSHandle<EcmaString> &strHandle)
406{
407    auto strFlat = EcmaStringAccessor::Flatten(vm, strHandle, MemSpaceType::SHARED_OLD_SPACE);
408    JSHandle<EcmaString> strFlatHandle(vm->GetJSThread(), strFlat);
409    auto hashcode = EcmaStringAccessor(strFlat).GetHashcode();
410    RuntimeLockHolder locker(vm->GetJSThread(), stringTable_[GetTableId(hashcode)].mutex_);
411#if ECMASCRIPT_ENABLE_SCOPE_LOCK_STAT
412    if (vm->IsCollectingScopeLockStats()) {
413        vm->IncreaseStringTableLockCount();
414    }
415#endif
416    strFlat = *strFlatHandle;
417    InternStringThreadUnsafe(strFlat, hashcode);
418    return strFlat;
419}
420
421EcmaString *EcmaStringTable::TryGetInternString(JSThread *thread, const JSHandle<EcmaString> &string)
422{
423    auto hashcode = EcmaStringAccessor(*string).GetHashcode();
424    RuntimeLockHolder locker(thread, stringTable_[GetTableId(hashcode)].mutex_);
425#if ECMASCRIPT_ENABLE_SCOPE_LOCK_STAT
426    auto vm = thread->GetEcmaVM();
427    if (vm->IsCollectingScopeLockStats()) {
428        vm->IncreaseStringTableLockCount();
429    }
430#endif
431    return GetStringThreadUnsafe(*string, hashcode);
432}
433
434EcmaString *EcmaStringTable::GetOrInternStringWithSpaceType(EcmaVM *vm, const uint8_t *utf8Data, uint32_t utf8Len,
435                                                            bool canBeCompress, MemSpaceType type,
436                                                            bool isConstantString, uint32_t idOffset)
437{
438    ASSERT(IsSMemSpace(type));
439    uint32_t hashcode = EcmaStringAccessor::ComputeHashcodeUtf8(utf8Data, utf8Len, canBeCompress);
440    RuntimeLockHolder locker(vm->GetJSThread(), stringTable_[GetTableId(hashcode)].mutex_);
441#if ECMASCRIPT_ENABLE_SCOPE_LOCK_STAT
442    if (vm->IsCollectingScopeLockStats()) {
443        vm->IncreaseStringTableLockCount();
444    }
445#endif
446    std::pair<EcmaString *, uint32_t> result = GetStringThreadUnsafe(utf8Data, utf8Len, canBeCompress, hashcode);
447    if (result.first != nullptr) {
448        return result.first;
449    }
450    type = (type == MemSpaceType::SHARED_NON_MOVABLE) ? type : MemSpaceType::SHARED_OLD_SPACE;
451    EcmaString *str = nullptr;
452    if (canBeCompress) {
453        // Constant string will be created in this branch.
454        str = EcmaStringAccessor::CreateFromUtf8(vm, utf8Data, utf8Len, canBeCompress, type, isConstantString,
455            idOffset);
456    } else {
457        str = EcmaStringAccessor::CreateFromUtf8(vm, utf8Data, utf8Len, canBeCompress, type);
458    }
459    str->SetMixHashcode(result.second);
460    InternStringThreadUnsafe(str, hashcode);
461    return str;
462}
463
464EcmaString *EcmaStringTable::GetOrInternStringWithSpaceType(EcmaVM *vm, const uint8_t *utf8Data, uint32_t utf16Len,
465                                                            MemSpaceType type)
466{
467    ASSERT(IsSMemSpace(type));
468    type = (type == MemSpaceType::SHARED_NON_MOVABLE) ? type : MemSpaceType::SHARED_OLD_SPACE;
469    EcmaString *str = EcmaStringAccessor::CreateUtf16StringFromUtf8(vm, utf8Data, utf16Len, type);
470    JSHandle<EcmaString> stringHandle(vm->GetJSThread(), str);
471    auto hashcode = EcmaStringAccessor(str).GetHashcode();
472    RuntimeLockHolder locker(vm->GetJSThread(), stringTable_[GetTableId(hashcode)].mutex_);
473#if ECMASCRIPT_ENABLE_SCOPE_LOCK_STAT
474    if (vm->IsCollectingScopeLockStats()) {
475        vm->IncreaseStringTableLockCount();
476    }
477#endif
478    str = *stringHandle;
479    EcmaString *result = GetStringThreadUnsafe(str, hashcode);
480    if (result != nullptr) {
481        return result;
482    }
483    InternStringThreadUnsafe(str, hashcode);
484    return str;
485}
486
487// used in jit thread, which unsupport create jshandle
488EcmaString *EcmaStringTable::GetOrInternStringWithSpaceTypeWithoutJSHandle(EcmaVM *vm, const uint8_t *utf8Data,
489                                                                           uint32_t utf16Len, MemSpaceType type)
490{
491    ASSERT(IsSMemSpace(type));
492    type = (type == MemSpaceType::SHARED_NON_MOVABLE) ? type : MemSpaceType::SHARED_OLD_SPACE;
493    CVector<uint16_t> u16Buffer(utf16Len);
494    utf::ConvertRegionMUtf8ToUtf16(utf8Data, u16Buffer.data(), utf::Mutf8Size(utf8Data), utf16Len, 0);
495    auto hashcode = EcmaStringAccessor::ComputeHashcodeUtf16(u16Buffer.data(), utf16Len);
496    RuntimeLockHolder locker(vm->GetJSThread(), stringTable_[GetTableId(hashcode)].mutex_);
497#if ECMASCRIPT_ENABLE_SCOPE_LOCK_STAT
498    if (vm->IsCollectingScopeLockStats()) {
499        vm->IncreaseStringTableLockCount();
500    }
501#endif
502    auto result = GetStringThreadUnsafe(u16Buffer.data(), utf16Len, hashcode);
503    if (result.first != nullptr) {
504        return result.first;
505    }
506    EcmaString *str = EcmaStringAccessor::CreateFromUtf16(vm, u16Buffer.data(), utf16Len, false, type);
507    str->SetMixHashcode(hashcode);
508    InternStringThreadUnsafe(str, hashcode);
509    return str;
510}
511
512void EcmaStringTable::SweepWeakRef(const WeakRootVisitor &visitor)
513{
514    // No need lock here, only shared gc will sweep string table, meanwhile other threads are suspended.
515    for (uint32_t tableId = 0; tableId < stringTable_.size(); ++tableId) {
516        SweepWeakRef(visitor, tableId);
517    }
518}
519
520void EcmaStringTable::SweepWeakRef(const WeakRootVisitor &visitor, uint32_t tableId)
521{
522    ASSERT(tableId >= 0 && tableId < stringTable_.size());
523    auto &table = stringTable_[tableId].table_;
524    for (auto it = table.begin(); it != table.end();) {
525        auto *object = it->second;
526        auto fwd = visitor(object);
527        ASSERT(Region::ObjectAddressToRange(object)->InSharedHeap());
528        if (fwd == nullptr) {
529            LOG_ECMA(VERBOSE) << "StringTable: delete string " << std::hex << object;
530            it = table.erase(it);
531        } else if (fwd != object) {
532            it->second = static_cast<EcmaString *>(fwd);
533            ++it;
534            LOG_ECMA(VERBOSE) << "StringTable: forward " << std::hex << object << " -> " << fwd;
535        } else {
536            ++it;
537        }
538    }
539}
540
541void EcmaStringTable::RelocateConstantData(EcmaVM *vm, const JSPandaFile *jsPandaFile)
542{
543#if ECMASCRIPT_ENABLE_SCOPE_LOCK_STAT
544    if (vm->IsCollectingScopeLockStats()) {
545        vm->IncreaseStringTableLockCount();
546    }
547#endif
548    auto thread = vm->GetJSThread();
549    for (auto &[table, mutex] : stringTable_) {
550        RuntimeLockHolder locker(thread, mutex);
551        for (auto it = table.begin(); it != table.end();) {
552            auto *object = it->second;
553            if (!EcmaStringAccessor(object).IsConstantString()) {
554                ++it;
555                continue;
556            }
557            auto constantStr = ConstantString::Cast(object);
558            if (constantStr->GetEntityId() < 0 || !jsPandaFile->Contain(constantStr->GetConstantData())) {
559                // EntityId is -1, which means this str has been relocated. Or the data is not in pandafile.
560                ++it;
561                continue;
562            }
563            uint32_t id = constantStr->GetEntityIdU32();
564            panda_file::File::StringData sd = jsPandaFile->GetStringData(EntityId(id));
565            if (constantStr->GetConstantData() != sd.data) {
566                LOG_ECMA(ERROR) << "ConstantString data pointer is inconsistent with sd.data";
567                ++it;
568                continue;
569            }
570            uint32_t strLen = sd.utf16_length;
571            if (UNLIKELY(strLen == 0)) {
572                it->second = *(vm->GetFactory()->GetEmptyString());
573            }
574            size_t byteLength = sd.is_ascii ? 1 : sizeof(uint16_t);
575            JSMutableHandle<ByteArray> newData(vm->GetJSThread(), JSTaggedValue::Undefined());
576            newData.Update(vm->GetFactory()->NewByteArray(
577                strLen, byteLength, reinterpret_cast<void *>(const_cast<uint8_t *>(sd.data)),
578                MemSpaceType::SHARED_NON_MOVABLE));
579            constantStr->SetRelocatedData(thread, newData.GetTaggedValue());
580            constantStr->SetConstantData(static_cast<uint8_t *>(newData->GetData()));
581            constantStr->SetEntityId(-1);
582            ++it;
583        }
584    }
585}
586
587bool EcmaStringTable::CheckStringTableValidity(JSThread *thread)
588{
589#if ECMASCRIPT_ENABLE_SCOPE_LOCK_STAT
590    auto vm = thread->GetEcmaVM();
591    if (vm->IsCollectingScopeLockStats()) {
592        vm->IncreaseStringTableLockCount();
593    }
594#endif
595    for (auto &[table, mutex] : stringTable_) {
596        RuntimeLockHolder locker(thread, mutex);
597        for (auto itemOuter = table.begin(); itemOuter != table.end(); ++itemOuter) {
598            auto outerString = itemOuter->second;
599            if (!EcmaStringAccessor(outerString).NotTreeString()) {
600                return false;
601            }
602            int counter = 0;
603            auto hashcode = EcmaStringAccessor(outerString).GetHashcode();
604            auto range = table.equal_range(hashcode);
605            for (auto it = range.first; it != range.second; ++it) {
606                auto foundString = it->second;
607                counter += EcmaStringAccessor::StringsAreEqual(foundString, outerString) ? 1 : 0;
608            }
609            if (counter > 1) {
610                return false;
611            }
612        }
613    }
614    return true;
615}
616
617JSTaggedValue SingleCharTable::CreateSingleCharTable(JSThread *thread)
618{
619    auto table = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(MAX_ONEBYTE_CHARCODE,
620        JSTaggedValue::Undefined(), MemSpaceType::SHARED_NON_MOVABLE);
621    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
622    for (uint32_t i = 1; i < MAX_ONEBYTE_CHARCODE; ++i) {
623        std::string tmp(1, i + 0X00); // 1: size
624        table->Set(thread, i, factory->NewFromASCIIReadOnly(tmp).GetTaggedValue());
625    }
626    return table.GetTaggedValue();
627}
628}  // namespace panda::ecmascript
629