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