• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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