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