• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2025 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 "common_components/objects/string_table/hashtriemap-inl.h"
19 #include "common_components/taskpool/taskpool.h"
20 #include "ecmascript/ecma_string-inl.h"
21 #include "ecmascript/ecma_string_table_optimization-inl.h"
22 #include "ecmascript/jspandafile/js_pandafile.h"
23 
24 namespace panda::ecmascript {
25 #if ENABLE_NEXT_OPTIMIZATION
PostSweepWeakRefTask(const WeakRootVisitor & visitor)26 void EcmaStringTableCleaner::PostSweepWeakRefTask(const WeakRootVisitor &visitor)
27 {
28     StartSweepWeakRefTask();
29     iter_ = std::make_shared<std::atomic<uint32_t>>(0U);
30     const uint32_t postTaskCount = common::Taskpool::GetCurrentTaskpool()->GetTotalThreadNum();
31     for (uint32_t i = 0U; i < postTaskCount; ++i) {
32         common::Taskpool::GetCurrentTaskpool()->PostTask(std::make_unique<SweepWeakRefTask>(iter_, this, visitor));
33     }
34 }
35 
JoinAndWaitSweepWeakRefTask(const WeakRootVisitor & visitor)36 void EcmaStringTableCleaner::JoinAndWaitSweepWeakRefTask(const WeakRootVisitor &visitor)
37 {
38     ProcessSweepWeakRef(iter_, this, visitor);
39     WaitSweepWeakRefTask();
40     iter_.reset();
41 }
42 
ProcessSweepWeakRef(IteratorPtr & iter,EcmaStringTableCleaner * cleaner,const WeakRootVisitor & visitor)43 void EcmaStringTableCleaner::ProcessSweepWeakRef(IteratorPtr &iter, EcmaStringTableCleaner *cleaner,
44                                                  const WeakRootVisitor &visitor)
45 {
46     uint32_t index = 0U;
47     while ((index = GetNextIndexId(iter)) < common::TrieMapConfig::ROOT_SIZE) {
48         cleaner->stringTable_->SweepWeakRef(visitor, index);
49         if (ReduceCountAndCheckFinish(cleaner)) {
50             cleaner->SignalSweepWeakRefTask();
51         }
52     }
53 }
54 
StartSweepWeakRefTask()55 void EcmaStringTableCleaner::StartSweepWeakRefTask()
56 {
57     // No need lock here, only the daemon thread will reset the state.
58     sweepWeakRefFinished_ = false;
59     PendingTaskCount_.store(common::TrieMapConfig::ROOT_SIZE, std::memory_order_relaxed);
60 }
61 
WaitSweepWeakRefTask()62 void EcmaStringTableCleaner::WaitSweepWeakRefTask()
63 {
64     LockHolder holder(sweepWeakRefMutex_);
65     while (!sweepWeakRefFinished_) {
66         sweepWeakRefCV_.Wait(&sweepWeakRefMutex_);
67     }
68 }
69 
SignalSweepWeakRefTask()70 void EcmaStringTableCleaner::SignalSweepWeakRefTask()
71 {
72     LockHolder holder(sweepWeakRefMutex_);
73     sweepWeakRefFinished_ = true;
74     sweepWeakRefCV_.SignalAll();
75 }
76 
Run(uint32_t threadIndex)77 bool EcmaStringTableCleaner::SweepWeakRefTask::Run([[maybe_unused]] uint32_t threadIndex)
78 {
79     ProcessSweepWeakRef(iter_, cleaner_, visitor_);
80     return true;
81 }
82 
83 template class EcmaStringTableImpl<DisableCMCGCTrait>;
84 
85 template class EcmaStringTableImpl<EnableCMCGCTrait>;
86 
87 template <typename Traits>
GetOrInternFlattenString(EcmaVM * vm,EcmaString * string)88 EcmaString* EcmaStringTableImpl<Traits>::GetOrInternFlattenString(EcmaVM* vm, EcmaString* string)
89 {
90     ASSERT(EcmaStringAccessor(string).NotTreeString());
91     if (EcmaStringAccessor(string).IsInternString()) {
92         return string;
93     }
94     JSThread *thread = vm->GetAssociatedJSThread();
95     uint32_t hashcode = EcmaStringAccessor(string).GetHashcode(thread);
96     ASSERT(!EcmaStringAccessor(string).IsInternString());
97     ASSERT(EcmaStringAccessor(string).NotTreeString());
98     // Strings in string table should not be in the young space.
99     if constexpr (Traits::EnableCMCGC) {
100         ASSERT(string->IsInSharedHeap());
101     } else {
102         ASSERT(Region::ObjectAddressToRange(reinterpret_cast<TaggedObject *>(string))->InSharedHeap());
103     }
104     auto readBarrier = [thread](const void *obj, size_t offset) -> TaggedObject * {
105         return Barriers::GetTaggedObject(thread, obj, offset);
106     };
107     auto loadResult = stringTable_.template Load(std::move(readBarrier), hashcode, string->ToBaseString());
108     if (loadResult.value != nullptr) {
109         return EcmaString::FromBaseString(loadResult.value);
110     }
111     JSHandle<EcmaString> stringHandle(thread, string);
112     ThreadType* holder = GetThreadHolder(thread);
113     BaseString *result = stringTable_.template StoreOrLoad<
114         true, decltype(readBarrier), common::ReadOnlyHandle<BaseString>>(
115         holder, std::move(readBarrier), hashcode, loadResult, stringHandle);
116     ASSERT(result != nullptr);
117     return EcmaString::FromBaseString(result);
118 }
119 
120 template <typename Traits>
GetOrInternFlattenStringNoGC(EcmaVM * vm,EcmaString * string)121 EcmaString* EcmaStringTableImpl<Traits>::GetOrInternFlattenStringNoGC(EcmaVM* vm, EcmaString* string)
122 {
123     ASSERT(EcmaStringAccessor(string).NotTreeString());
124     if (EcmaStringAccessor(string).IsInternString()) {
125         return string;
126     }
127     JSThread *thread = vm->GetAssociatedJSThread();
128     uint32_t hashcode = EcmaStringAccessor(string).GetHashcode(thread);
129     ASSERT(!EcmaStringAccessor(string).IsInternString());
130     ASSERT(EcmaStringAccessor(string).NotTreeString());
131     // Strings in string table should not be in the young space.
132     if constexpr (Traits::EnableCMCGC) {
133         ASSERT(string->IsInSharedHeap());
134     } else {
135         ASSERT(Region::ObjectAddressToRange(reinterpret_cast<TaggedObject *>(string))->InSharedHeap());
136     }
137     auto readBarrier = [thread](const void *obj, size_t offset) -> TaggedObject * {
138         return Barriers::GetTaggedObject(thread, obj, offset);
139     };
140     auto loadResult = stringTable_.template Load(readBarrier, hashcode, string->ToBaseString());
141     if (loadResult.value != nullptr) {
142         return EcmaString::FromBaseString(loadResult.value);
143     }
144     ThreadType* holder = GetThreadHolder(vm->GetJSThread());
145     JSHandle<EcmaString> stringHandle(thread, string);
146     BaseString* result = stringTable_.template StoreOrLoad<
147         false, decltype(readBarrier), common::ReadOnlyHandle<BaseString>>(
148         holder, std::move(readBarrier), hashcode, loadResult, stringHandle);
149     ASSERT(result != nullptr);
150     return EcmaString::FromBaseString(result);
151 }
152 
153 template <typename Traits>
GetOrInternStringFromCompressedSubString(EcmaVM * vm,const JSHandle<EcmaString> & string,uint32_t offset,uint32_t utf8Len)154 EcmaString* EcmaStringTableImpl<Traits>::GetOrInternStringFromCompressedSubString(
155     EcmaVM* vm, const JSHandle<EcmaString>& string, uint32_t offset, uint32_t utf8Len)
156 {
157     const uint8_t* utf8Data = EcmaStringAccessor(string).GetDataUtf8() + offset;
158     uint32_t hashcode = EcmaStringAccessor::ComputeHashcodeUtf8(utf8Data, utf8Len, true);
159     JSThread *thread = vm->GetAssociatedJSThread();
160     auto readBarrier = [thread](const void *obj, size_t offset) -> TaggedObject * {
161         return Barriers::GetTaggedObject(thread, obj, offset);
162     };
163     auto loadResult = stringTable_.template Load(std::move(readBarrier), hashcode, string, offset, utf8Len);
164     if (loadResult.value != nullptr) {
165         return EcmaString::FromBaseString(loadResult.value);
166     }
167     ThreadType* holder = GetThreadHolder(vm->GetJSThread());
168     BaseString *result = stringTable_.template StoreOrLoad(
169         holder, hashcode, loadResult,
170         [vm, string, offset, utf8Len, hashcode]() -> common::ReadOnlyHandle<BaseString> {
171             EcmaString* str = EcmaStringAccessor::CreateFromUtf8CompressedSubString(vm, string, offset, utf8Len,
172                 MemSpaceType::SHARED_OLD_SPACE);
173             str->SetMixHashcode(hashcode);
174             ASSERT(!EcmaStringAccessor(str).IsInternString());
175             ASSERT(EcmaStringAccessor(str).NotTreeString());
176             // Strings in string table should not be in the young space.
177             if constexpr (Traits::EnableCMCGC) {
178                 ASSERT(str->IsInSharedHeap());
179             } else {
180                 ASSERT(Region::ObjectAddressToRange(reinterpret_cast<TaggedObject *>(str))->InSharedHeap());
181             }
182             JSThread *thread = vm->GetJSThread();
183             JSHandle<EcmaString> strHandle(thread, str);
184             return strHandle;
185         },
186         [vm, utf8Len, string, offset](BaseString *foundString) {
187             JSThread *thread = vm->GetAssociatedJSThread();
188             const uint8_t *utf8Data = EcmaStringAccessor(string).GetDataUtf8() + offset;
189             return EcmaStringAccessor::StringIsEqualUint8Data(thread, EcmaString::FromBaseString(foundString), utf8Data,
190                                                               utf8Len, true);
191         });
192     ASSERT(result != nullptr);
193     return EcmaString::FromBaseString(result);
194 }
195 
196 template <typename Traits>
GetOrInternString(EcmaVM * vm,EcmaString * string)197 EcmaString *EcmaStringTableImpl<Traits>::GetOrInternString(EcmaVM *vm, EcmaString *string)
198 {
199     if (EcmaStringAccessor(string).IsInternString()) {
200         return string;
201     }
202     JSThread *thread = vm->GetJSThread();
203     JSHandle<EcmaString> stringHandle(thread, string);
204     // may gc
205     EcmaString *strFlat = EcmaStringAccessor::Flatten(vm, stringHandle, MemSpaceType::SHARED_OLD_SPACE);
206     if (EcmaStringAccessor(strFlat).IsInternString()) {
207         return strFlat;
208     }
209     uint32_t hashcode = EcmaStringAccessor(strFlat).GetHashcode(thread);
210     auto readBarrier = [thread](const void *obj, size_t offset) -> TaggedObject * {
211         return Barriers::GetTaggedObject(thread, obj, offset);
212     };
213     auto loadResult = stringTable_.template Load(readBarrier, hashcode, strFlat->ToBaseString());
214     if (loadResult.value != nullptr) {
215         return EcmaString::FromBaseString(loadResult.value);
216     }
217     JSHandle<EcmaString> strFlatHandle(thread, strFlat);
218     ThreadType* holder = GetThreadHolder(vm->GetJSThread());
219     BaseString* result = stringTable_.template StoreOrLoad<
220         true, decltype(readBarrier), common::ReadOnlyHandle<BaseString>>(
221         holder, std::move(readBarrier), hashcode, loadResult, strFlatHandle);
222     ASSERT(result != nullptr);
223     return EcmaString::FromBaseString(result);
224 }
225 
226 template <typename Traits>
GetOrInternString(EcmaVM * vm,const JSHandle<EcmaString> & firstString,const JSHandle<EcmaString> & secondString)227 EcmaString* EcmaStringTableImpl<Traits>::GetOrInternString(EcmaVM* vm, const JSHandle<EcmaString>& firstString,
228                                                            const JSHandle<EcmaString>& secondString)
229 {
230     bool signalState = vm->GetJsDebuggerManager()->GetSignalState();
231     if (UNLIKELY(signalState)) {
232         return GetOrInternStringThreadUnsafe(vm, firstString, secondString);
233     }
234     JSThread *thread = vm->GetJSThread();
235     JSHandle<EcmaString> firstFlat(thread, EcmaStringAccessor::Flatten(vm, firstString));
236     JSHandle<EcmaString> secondFlat(thread, EcmaStringAccessor::Flatten(vm, secondString));
237     uint32_t hashcode = EcmaStringAccessor::CalculateAllConcatHashCode(thread, firstFlat, secondFlat);
238     ASSERT(EcmaStringAccessor(firstFlat).NotTreeString());
239     ASSERT(EcmaStringAccessor(secondFlat).NotTreeString());
240     ThreadType* holder = GetThreadHolder(vm->GetJSThread());
241     BaseString *result = stringTable_.template LoadOrStore<true>(
242         holder, hashcode,
243         [vm, hashcode, thread, firstFlat, secondFlat]() {
244             JSHandle<EcmaString> concatHandle(
245                 thread, EcmaStringAccessor::Concat(vm, firstFlat, secondFlat, MemSpaceType::SHARED_OLD_SPACE));
246             EcmaString *value = EcmaStringAccessor::Flatten(vm, concatHandle, MemSpaceType::SHARED_OLD_SPACE);
247             value->SetMixHashcode(hashcode);
248             ASSERT(!EcmaStringAccessor(value).IsInternString());
249             ASSERT(EcmaStringAccessor(value).NotTreeString());
250             // Strings in string table should not be in the young space.
251             if constexpr (Traits::EnableCMCGC) {
252                 ASSERT(value->IsInSharedHeap());
253             } else {
254                 ASSERT(Region::ObjectAddressToRange(reinterpret_cast<TaggedObject *>(value))->InSharedHeap());
255             }
256             JSHandle<EcmaString> stringHandle(thread, value);
257             return stringHandle;
258         },
259         [thread, firstFlat, secondFlat](BaseString *foundString) {
260             EcmaString *firstStr = *firstFlat;
261             EcmaString *secondStr = *secondFlat;
262             return EcmaStringAccessor(EcmaString::FromBaseString(foundString))
263                 .EqualToSplicedString(thread, firstStr, secondStr);
264         });
265     ASSERT(result != nullptr);
266     return EcmaString::FromBaseString(result);
267 }
268 
269 template <typename Traits>
GetOrInternString(EcmaVM * vm,const uint8_t * utf8Data,uint32_t utf8Len,bool canBeCompress,MemSpaceType type)270 EcmaString* EcmaStringTableImpl<Traits>::GetOrInternString(EcmaVM* vm, const uint8_t* utf8Data, uint32_t utf8Len,
271                                                            bool canBeCompress, [[maybe_unused]] MemSpaceType type)
272 {
273     ASSERT(IsSMemSpace(type));
274     bool signalState = vm->GetJsDebuggerManager()->GetSignalState();
275     if (UNLIKELY(signalState)) {
276         return GetOrInternStringThreadUnsafe(vm, utf8Data, utf8Len, canBeCompress);
277     }
278     ThreadType* holder = GetThreadHolder(vm->GetJSThread());
279     uint32_t hashcode = EcmaStringAccessor::ComputeHashcodeUtf8(utf8Data, utf8Len, canBeCompress);
280     BaseString *result = stringTable_.template LoadOrStore<true>(
281         holder, hashcode,
282         [vm, hashcode, utf8Data, utf8Len, canBeCompress, type]() {
283             EcmaString* value = EcmaStringAccessor::CreateFromUtf8(vm, utf8Data, utf8Len, canBeCompress, type);
284             value->SetMixHashcode(hashcode);
285             ASSERT(!EcmaStringAccessor(value).IsInternString());
286             ASSERT(EcmaStringAccessor(value).NotTreeString());
287             // Strings in string table should not be in the young space.
288             if constexpr (Traits::EnableCMCGC) {
289                 ASSERT(value->IsInSharedHeap());
290             } else {
291                 ASSERT(Region::ObjectAddressToRange(reinterpret_cast<TaggedObject *>(value))->InSharedHeap());
292             }
293             JSThread *thread = vm->GetJSThread();
294             JSHandle<EcmaString> stringHandle(thread, value);
295             return stringHandle;
296         },
297         [vm, utf8Data, utf8Len, canBeCompress](BaseString *foundString) {
298             JSThread *thread = vm->GetAssociatedJSThread();
299             return EcmaStringAccessor::StringIsEqualUint8Data(thread, EcmaString::FromBaseString(foundString), utf8Data,
300                                                               utf8Len, canBeCompress);
301         });
302     ASSERT(result != nullptr);
303     return EcmaString::FromBaseString(result);
304 }
305 
306 template <typename Traits>
GetOrInternString(EcmaVM * vm,const uint8_t * utf8Data,uint32_t utf16Len,MemSpaceType type)307 EcmaString* EcmaStringTableImpl<Traits>::GetOrInternString(EcmaVM* vm, const uint8_t* utf8Data, uint32_t utf16Len,
308                                                            MemSpaceType type)
309 {
310     ASSERT(IsSMemSpace(type));
311     ASSERT(type == MemSpaceType::SHARED_NON_MOVABLE || type == MemSpaceType::SHARED_OLD_SPACE);
312     JSThread* thread = vm->GetJSThread();
313     EcmaString* str = EcmaStringAccessor::CreateUtf16StringFromUtf8(vm, utf8Data, utf16Len, type);
314     uint32_t hashcode = EcmaStringAccessor(str).GetHashcode(thread);
315     auto readBarrier = [thread](const void *obj, size_t offset) -> TaggedObject * {
316         return Barriers::GetTaggedObject(thread, obj, offset);
317     };
318     auto loadResult = stringTable_.template Load(std::move(readBarrier), hashcode, str->ToBaseString());
319     if (loadResult.value != nullptr) {
320         return EcmaString::FromBaseString(loadResult.value);
321     }
322     JSHandle<EcmaString> strHandle(thread, str);
323     ThreadType* holder = GetThreadHolder(vm->GetJSThread());
324     BaseString* result = stringTable_.template StoreOrLoad<
325         true, decltype(readBarrier), common::ReadOnlyHandle<BaseString>>(
326         holder, std::move(readBarrier), hashcode, loadResult, strHandle);
327     ASSERT(result != nullptr);
328     return EcmaString::FromBaseString(result);
329 }
330 
331 template <typename Traits>
GetOrInternString(EcmaVM * vm,const uint16_t * utf16Data,uint32_t utf16Len,bool canBeCompress)332 EcmaString* EcmaStringTableImpl<Traits>::GetOrInternString(EcmaVM* vm, const uint16_t* utf16Data, uint32_t utf16Len,
333                                                            bool canBeCompress)
334 {
335     ThreadType* holder = GetThreadHolder(vm->GetJSThread());
336     uint32_t hashcode = EcmaStringAccessor::ComputeHashcodeUtf16(const_cast<uint16_t*>(utf16Data), utf16Len);
337     BaseString *result = stringTable_.template LoadOrStore<true>(
338         holder, hashcode,
339         [vm, utf16Data, utf16Len, canBeCompress, hashcode]() {
340             EcmaString* value = EcmaStringAccessor::CreateFromUtf16(vm, utf16Data, utf16Len, canBeCompress,
341                                                                     MemSpaceType::SHARED_OLD_SPACE);
342             value->SetMixHashcode(hashcode);
343             ASSERT(!EcmaStringAccessor(value).IsInternString());
344             ASSERT(EcmaStringAccessor(value).NotTreeString());
345             // Strings in string table should not be in the young space.
346             if constexpr (Traits::EnableCMCGC) {
347                 ASSERT(value->IsInSharedHeap());
348             } else {
349                 ASSERT(Region::ObjectAddressToRange(reinterpret_cast<TaggedObject *>(value))->InSharedHeap());
350             }
351             JSThread *thread = vm->GetJSThread();
352             JSHandle<EcmaString> stringHandle(thread, value);
353             return stringHandle;
354         },
355         [vm, utf16Data, utf16Len](BaseString *foundString) {
356             JSThread *thread = vm->GetAssociatedJSThread();
357             return EcmaStringAccessor::StringsAreEqualUtf16(thread, EcmaString::FromBaseString(foundString), utf16Data,
358                                                             utf16Len);
359         });
360     ASSERT(result != nullptr);
361     return EcmaString::FromBaseString(result);
362 }
363 
364 template <typename Traits>
TryGetInternString(JSThread * thread,const JSHandle<EcmaString> & string)365 EcmaString *EcmaStringTableImpl<Traits>::TryGetInternString(JSThread *thread, const JSHandle<EcmaString> &string)
366 {
367     uint32_t hashcode = EcmaStringAccessor(*string).GetHashcode(thread);
368     EcmaString *str = *string;
369     auto readBarrier = [thread](const void *obj, size_t offset) -> TaggedObject * {
370         return Barriers::GetTaggedObject(thread, obj, offset);
371     };
372     return EcmaString::FromBaseString(
373         stringTable_.template Load<false>(std::move(readBarrier), hashcode, str->ToBaseString()));
374 }
375 
376 // used in jit thread, which unsupport create jshandle
377 template <typename Traits>
GetOrInternStringWithoutJSHandleForJit(EcmaVM * vm,const uint8_t * utf8Data,uint32_t utf8Len,bool canBeCompress,MemSpaceType type)378 EcmaString* EcmaStringTableImpl<Traits>::GetOrInternStringWithoutJSHandleForJit(EcmaVM* vm, const uint8_t* utf8Data,
379     uint32_t utf8Len, bool canBeCompress, MemSpaceType type)
380 {
381     ASSERT(IsSMemSpace(type));
382     ASSERT(type == MemSpaceType::SHARED_NON_MOVABLE || type == MemSpaceType::SHARED_OLD_SPACE);
383     uint32_t hashcode = EcmaStringAccessor::ComputeHashcodeUtf8(utf8Data, utf8Len, canBeCompress);
384     ThreadType* holder = GetThreadHolder(vm->GetJSThread());
385     BaseString *result = stringTable_.LoadOrStoreForJit(
386         holder, hashcode,
387         [vm, utf8Data, utf8Len, canBeCompress, type, hashcode]() {
388             EcmaString *value = EcmaStringAccessor::CreateFromUtf8(vm, utf8Data, utf8Len, canBeCompress, type);
389 
390             value->SetMixHashcode(hashcode);
391             ASSERT(!EcmaStringAccessor(value).IsInternString());
392             ASSERT(EcmaStringAccessor(value).NotTreeString());
393             // Strings in string table should not be in the young space.
394             if constexpr (Traits::EnableCMCGC) {
395                 ASSERT(value->IsInSharedHeap());
396             } else {
397                 ASSERT(Region::ObjectAddressToRange(reinterpret_cast<TaggedObject *>(value))->InSharedHeap());
398             }
399             return value->ToBaseString();
400         },
401         [vm, utf8Data, utf8Len, canBeCompress](BaseString *foundString) {
402             JSThread *thread = vm->GetAssociatedJSThread();
403             return EcmaStringAccessor::StringIsEqualUint8Data(thread, EcmaString::FromBaseString(foundString), utf8Data,
404                                                               utf8Len, canBeCompress);
405         });
406     ASSERT(result != nullptr);
407     return EcmaString::FromBaseString(result);
408 }
409 
410 // used in jit thread, which unsupport create jshandle
411 template <typename Traits>
GetOrInternStringWithoutJSHandleForJit(EcmaVM * vm,const uint8_t * utf8Data,uint32_t utf16Len,MemSpaceType type)412 EcmaString* EcmaStringTableImpl<Traits>::GetOrInternStringWithoutJSHandleForJit(EcmaVM* vm, const uint8_t* utf8Data,
413     uint32_t utf16Len, MemSpaceType type)
414 {
415     ASSERT(vm->GetJSThread()->IsJitThread());
416     ASSERT(IsSMemSpace(type));
417     type = (type == MemSpaceType::SHARED_NON_MOVABLE) ? type : MemSpaceType::SHARED_OLD_SPACE;
418     CVector<uint16_t> u16Buffer(utf16Len);
419     utf::ConvertRegionMUtf8ToUtf16(utf8Data, u16Buffer.data(), utf::Mutf8Size(utf8Data), utf16Len, 0);
420     uint32_t hashcode = EcmaStringAccessor::ComputeHashcodeUtf16(u16Buffer.data(), utf16Len);
421     const uint16_t *utf16Data = u16Buffer.data();
422     ThreadType* holder = GetThreadHolder(vm->GetJSThread());
423     BaseString *result = stringTable_.LoadOrStoreForJit(
424         holder, hashcode,
425         [vm, u16Buffer, utf16Len, hashcode, type]() {
426             EcmaString *value = EcmaStringAccessor::CreateFromUtf16(vm, u16Buffer.data(), utf16Len, false, type);
427             value->SetMixHashcode(hashcode);
428             ASSERT(!EcmaStringAccessor(value).IsInternString());
429             ASSERT(EcmaStringAccessor(value).NotTreeString());
430             // Strings in string table should not be in the young space.
431             if constexpr (Traits::EnableCMCGC) {
432                 ASSERT(value->IsInSharedHeap());
433             } else {
434                 ASSERT(Region::ObjectAddressToRange(reinterpret_cast<TaggedObject *>(value))->InSharedHeap());
435             }
436             return value->ToBaseString();
437         },
438         [vm, utf16Data, utf16Len](BaseString *foundString) {
439             JSThread *thread = vm->GetAssociatedJSThread();
440             return EcmaStringAccessor::StringsAreEqualUtf16(thread, EcmaString::FromBaseString(foundString), utf16Data,
441                                                             utf16Len);
442         });
443     ASSERT(result != nullptr);
444     return EcmaString::FromBaseString(result);
445 }
446 
447 template <typename Traits>
SweepWeakRef(const WeakRootVisitor & visitor,uint32_t rootID)448 void EcmaStringTableImpl<Traits>::SweepWeakRef(const WeakRootVisitor &visitor, uint32_t rootID)
449 {
450     ASSERT(rootID >= 0 && rootID < common::TrieMapConfig::ROOT_SIZE);
451     auto *root_node = stringTable_.root_[rootID].load(std::memory_order_relaxed);
452     if (root_node == nullptr) {
453         return;
454     }
455     for (uint32_t index = 0; index < common::TrieMapConfig::INDIRECT_SIZE; ++index) {
456         stringTable_.ClearNodeFromGC(root_node, index, visitor);
457     }
458 }
459 
460 template <typename Traits>
CheckStringTableValidity(JSThread * thread)461 bool EcmaStringTableImpl<Traits>::CheckStringTableValidity(JSThread *thread)
462 {
463     bool isValid = true;
464     auto readBarrier = [thread](const void *obj, size_t offset) -> TaggedObject * {
465         return Barriers::GetTaggedObject(thread, obj, offset);
466     };
467     stringTable_.Range(std::move(readBarrier), isValid);
468     return isValid;
469 }
470 
CreateSingleCharTable(JSThread * thread)471 JSTaggedValue SingleCharTable::CreateSingleCharTable(JSThread *thread)
472 {
473     auto table = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(MAX_ONEBYTE_CHARCODE, JSTaggedValue::Undefined(),
474                                                                    MemSpaceType::SHARED_NON_MOVABLE);
475     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
476     for (uint32_t i = 1; i < MAX_ONEBYTE_CHARCODE; ++i) {
477         std::string tmp(1, i + 0X00);  // 1: size
478         table->Set(thread, i, factory->NewFromASCIIReadOnly(tmp).GetTaggedValue());
479     }
480     return table.GetTaggedValue();
481 }
482 
483 // This should only call in Debugger Signal, and need to fix and remove
484 template <typename Traits>
GetOrInternStringThreadUnsafe(EcmaVM * vm,const JSHandle<EcmaString> firstString,const JSHandle<EcmaString> secondString)485 EcmaString* EcmaStringTableImpl<Traits>::GetOrInternStringThreadUnsafe(
486     EcmaVM* vm, const JSHandle<EcmaString> firstString,
487     const JSHandle<EcmaString> secondString)
488 {
489     ASSERT(vm->GetJsDebuggerManager()->GetSignalState());
490     JSThread *thread = vm->GetJSThreadNoCheck();
491     JSHandle<EcmaString> firstFlat(thread, EcmaStringAccessor::Flatten(vm, firstString));
492     JSHandle<EcmaString> secondFlat(thread, EcmaStringAccessor::Flatten(vm, secondString));
493     uint32_t hashcode = EcmaStringAccessor::CalculateAllConcatHashCode(thread, firstFlat, secondFlat);
494     ASSERT(EcmaStringAccessor(firstFlat).NotTreeString());
495     ASSERT(EcmaStringAccessor(secondFlat).NotTreeString());
496     ThreadType* holder = GetThreadHolder(vm->GetJSThread());
497     BaseString *result = stringTable_.template LoadOrStore<false>(
498         holder, hashcode,
499         [hashcode, thread, vm, firstFlat, secondFlat]() {
500             JSHandle<EcmaString> concatHandle(
501                 thread, EcmaStringAccessor::Concat(vm, firstFlat, secondFlat, MemSpaceType::SHARED_OLD_SPACE));
502             EcmaString *concatString = EcmaStringAccessor::Flatten(vm, concatHandle, MemSpaceType::SHARED_OLD_SPACE);
503             concatString->SetMixHashcode(hashcode);
504             ASSERT(!EcmaStringAccessor(concatString).IsInternString());
505             ASSERT(EcmaStringAccessor(concatString).NotTreeString());
506             // Strings in string table should not be in the young space.
507             if constexpr (Traits::EnableCMCGC) {
508                 ASSERT(concatString->IsInSharedHeap());
509             } else {
510                 ASSERT(Region::ObjectAddressToRange(reinterpret_cast<TaggedObject *>(concatString))->InSharedHeap());
511             }
512             JSHandle<EcmaString> stringHandle(thread, concatString);
513             return stringHandle;
514         },
515         [vm, firstFlat, secondFlat](BaseString *foundString) {
516             EcmaString *firstStr = *firstFlat;
517             EcmaString *secondStr = *secondFlat;
518             JSThread *thread = vm->GetAssociatedJSThread();
519             return EcmaStringAccessor(EcmaString::FromBaseString(foundString))
520                 .EqualToSplicedString(thread, firstStr, secondStr);
521         });
522     ASSERT(result != nullptr);
523     return EcmaString::FromBaseString(result);
524 }
525 
526 // This should only call in Debugger Signal, and need to fix and remove
527 template <typename Traits>
GetOrInternStringThreadUnsafe(EcmaVM * vm,const uint8_t * utf8Data,uint32_t utf8Len,bool canBeCompress)528 EcmaString* EcmaStringTableImpl<Traits>::GetOrInternStringThreadUnsafe(EcmaVM* vm, const uint8_t* utf8Data,
529                                                                        uint32_t utf8Len, bool canBeCompress)
530 {
531     ASSERT(vm->GetJsDebuggerManager()->GetSignalState());
532     uint32_t hashcode = EcmaStringAccessor::ComputeHashcodeUtf8(utf8Data, utf8Len, canBeCompress);
533     ThreadType* holder = GetThreadHolder(vm->GetJSThread());
534     BaseString *result = stringTable_.template LoadOrStore<false>(
535         holder, hashcode,
536         [vm, utf8Data, utf8Len, canBeCompress, hashcode]() {
537             EcmaString *value = EcmaStringAccessor::CreateFromUtf8(vm, utf8Data, utf8Len, canBeCompress,
538                                                                    MemSpaceType::SHARED_OLD_SPACE);
539             value->SetMixHashcode(hashcode);
540             JSThread *thread = vm->GetJSThread();
541             JSHandle<EcmaString> stringHandle(thread, value);
542             return stringHandle;
543         },
544         [vm, utf8Data, utf8Len, canBeCompress](BaseString *foundString) {
545             JSThread *thread = vm->GetAssociatedJSThread();
546             return EcmaStringAccessor::StringIsEqualUint8Data(thread, EcmaString::FromBaseString(foundString), utf8Data,
547                                                               utf8Len, canBeCompress);
548         });
549     ASSERT(result != nullptr);
550     return EcmaString::FromBaseString(result);
551 }
552 
GetOrInternFlattenString(EcmaVM * vm,EcmaString * string)553 EcmaString* EcmaStringTable::GetOrInternFlattenString(EcmaVM* vm, EcmaString* string)
554 {
555     return visitImpl([&](auto& impl) { return impl.GetOrInternFlattenString(vm, string); });
556 }
557 
GetOrInternFlattenStringNoGC(EcmaVM * vm,EcmaString * string)558 EcmaString* EcmaStringTable::GetOrInternFlattenStringNoGC(EcmaVM* vm, EcmaString* string)
559 {
560     return visitImpl([&](auto& impl) { return impl.GetOrInternFlattenStringNoGC(vm, string); });
561 }
562 
GetOrInternStringFromCompressedSubString(EcmaVM * vm,const JSHandle<EcmaString> & string,uint32_t offset,uint32_t utf8Len)563 EcmaString* EcmaStringTable::GetOrInternStringFromCompressedSubString(EcmaVM* vm, const JSHandle<EcmaString>& string,
564                                                                       uint32_t offset, uint32_t utf8Len)
565 {
566     return visitImpl([&](auto& impl) {
567         return impl.GetOrInternStringFromCompressedSubString(vm, string, offset, utf8Len);
568     });
569 }
570 
GetOrInternString(EcmaVM * vm,EcmaString * string)571 EcmaString* EcmaStringTable::GetOrInternString(EcmaVM* vm, EcmaString* string)
572 {
573     return visitImpl([&](auto& impl) { return impl.GetOrInternString(vm, string); });
574 }
575 
GetOrInternString(EcmaVM * vm,const JSHandle<EcmaString> & firstString,const JSHandle<EcmaString> & secondString)576 EcmaString* EcmaStringTable::GetOrInternString(EcmaVM* vm, const JSHandle<EcmaString>& firstString,
577                                                const JSHandle<EcmaString>& secondString)
578 {
579     return visitImpl([&](auto& impl) {
580         return impl.GetOrInternString(vm, firstString, secondString);
581     });
582 }
583 
GetOrInternString(EcmaVM * vm,const uint8_t * utf8Data,uint32_t utf8Len,bool canBeCompress,MemSpaceType type)584 EcmaString* EcmaStringTable::GetOrInternString(EcmaVM* vm, const uint8_t* utf8Data, uint32_t utf8Len,
585                                                bool canBeCompress,
586                                                MemSpaceType type)
587 {
588     return visitImpl([&](auto& impl) {
589         return impl.GetOrInternString(vm, utf8Data, utf8Len, canBeCompress, type);
590     });
591 }
592 
GetOrInternString(EcmaVM * vm,const uint8_t * utf8Data,uint32_t utf16Len,MemSpaceType type)593 EcmaString* EcmaStringTable::GetOrInternString(EcmaVM* vm, const uint8_t* utf8Data, uint32_t utf16Len,
594                                                MemSpaceType type)
595 {
596     return visitImpl([&](auto& impl) {
597         return impl.GetOrInternString(vm, utf8Data, utf16Len, type);
598     });
599 }
600 
GetOrInternString(EcmaVM * vm,const uint16_t * utf16Data,uint32_t utf16Len,bool canBeCompress)601 EcmaString* EcmaStringTable::GetOrInternString(EcmaVM* vm, const uint16_t* utf16Data, uint32_t utf16Len,
602                                                bool canBeCompress)
603 {
604     return visitImpl([&](auto& impl) {
605         return impl.GetOrInternString(vm, utf16Data, utf16Len, canBeCompress);
606     });
607 }
608 
609 // This is ONLY for JIT Thread, since JIT could not create JSHandle so need to allocate String with holding
610 // lock_ --- need to support JSHandle
GetOrInternStringWithoutJSHandleForJit(EcmaVM * vm,const uint8_t * utf8Data,uint32_t utf16Len,MemSpaceType type)611 EcmaString* EcmaStringTable::GetOrInternStringWithoutJSHandleForJit(EcmaVM* vm, const uint8_t* utf8Data,
612                                                                     uint32_t utf16Len,
613                                                                     MemSpaceType type)
614 {
615     return visitImpl([&](auto& impl) {
616         return impl.GetOrInternStringWithoutJSHandleForJit(vm, utf8Data, utf16Len, type);
617     });
618 }
619 
GetOrInternStringWithoutJSHandleForJit(EcmaVM * vm,const uint8_t * utf8Data,uint32_t utf8Len,bool canBeCompress,MemSpaceType type)620 EcmaString* EcmaStringTable::GetOrInternStringWithoutJSHandleForJit(EcmaVM* vm, const uint8_t* utf8Data,
621                                                                     uint32_t utf8Len,
622                                                                     bool canBeCompress, MemSpaceType type)
623 {
624     return visitImpl([&](auto& impl) {
625         return impl.GetOrInternStringWithoutJSHandleForJit(vm, utf8Data, utf8Len, canBeCompress, type);
626     });
627 }
628 
TryGetInternString(JSThread * thread,const JSHandle<EcmaString> & string)629 EcmaString *EcmaStringTable::TryGetInternString(JSThread *thread, const JSHandle<EcmaString> &string)
630 {
631     return visitImpl([&](auto &impl) { return impl.TryGetInternString(thread, string); });
632 }
633 
SweepWeakRef(const WeakRootVisitor & visitor,uint32_t rootID)634 void EcmaStringTable::SweepWeakRef(const WeakRootVisitor& visitor, uint32_t rootID)
635 {
636     if (std::holds_alternative<EcmaStringTableImpl<DisableCMCGCTrait>>(impl_)) {
637         return std::get<EcmaStringTableImpl<DisableCMCGCTrait>>(impl_).SweepWeakRef(visitor, rootID);
638     }
639     UNREACHABLE();
640 }
641 
CheckStringTableValidity(JSThread * thread)642 bool EcmaStringTable::CheckStringTableValidity(JSThread *thread)
643 {
644     return visitImpl([&](auto &impl) { return impl.CheckStringTableValidity(thread); });
645 }
646 
647 
GetOrInternStringThreadUnsafe(EcmaVM * vm,const JSHandle<EcmaString> firstString,const JSHandle<EcmaString> secondString)648 EcmaString* EcmaStringTable::GetOrInternStringThreadUnsafe(EcmaVM* vm, const JSHandle<EcmaString> firstString,
649                                                            const JSHandle<EcmaString> secondString)
650 {
651     return visitImpl([&](auto& impl) {
652         return impl.GetOrInternStringThreadUnsafe(vm, firstString, secondString);
653     });
654 }
655 
656 // This should only call in Debugger Signal, and need to fix and remove
GetOrInternStringThreadUnsafe(EcmaVM * vm,const uint8_t * utf8Data,uint32_t utf8Len,bool canBeCompress)657 EcmaString* EcmaStringTable::GetOrInternStringThreadUnsafe(EcmaVM* vm, const uint8_t* utf8Data, uint32_t utf8Len,
658                                                            bool canBeCompress)
659 {
660     return visitImpl([&](auto& impl) {
661         return impl.GetOrInternStringThreadUnsafe(vm, utf8Data, utf8Len, canBeCompress);
662     });
663 }
664 #endif
665 }  // namespace panda::ecmascript
666