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