• 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 
GetStringFromCompressedSubString(JSThread * thread,const JSHandle<EcmaString> string,uint32_t offset,uint32_t utf8Len,uint32_t hashcode)78 EcmaString *EcmaStringTable::GetStringFromCompressedSubString(JSThread *thread, const JSHandle<EcmaString> string,
79                                                               uint32_t offset, uint32_t utf8Len, uint32_t hashcode)
80 {
81     RuntimeLockHolder locker(thread, stringTable_[GetTableId(hashcode)].mutex_);
82 #if ECMASCRIPT_ENABLE_SCOPE_LOCK_STAT
83     EcmaVM *vm = thread->GetEcmaVM();
84     if (vm->IsCollectingScopeLockStats()) {
85         vm->IncreaseStringTableLockCount();
86     }
87 #endif
88     const uint8_t *utf8Data = EcmaStringAccessor(string).GetDataUtf8() + offset;
89     auto range = stringTable_[GetTableId(hashcode)].table_.equal_range(hashcode);
90     for (auto item = range.first; item != range.second;) {
91         auto foundString = (item++)->second;
92         if (EcmaStringAccessor::StringIsEqualUint8Data(foundString, utf8Data, utf8Len, true)) {
93             return foundString;
94         }
95     }
96     return nullptr;
97 }
98 
GetString(JSThread * thread,JSHandle<EcmaString> string,uint32_t hashcode)99 EcmaString *EcmaStringTable::GetString(JSThread *thread, JSHandle<EcmaString> string, uint32_t hashcode)
100 {
101     RuntimeLockHolder locker(thread, stringTable_[GetTableId(hashcode)].mutex_);
102 #if ECMASCRIPT_ENABLE_SCOPE_LOCK_STAT
103     EcmaVM *vm = thread->GetEcmaVM();
104     if (vm->IsCollectingScopeLockStats()) {
105         vm->IncreaseStringTableLockCount();
106     }
107 #endif
108     EcmaString *str = *string;
109     auto range = stringTable_[GetTableId(hashcode)].table_.equal_range(hashcode);
110     for (auto item = range.first; item != range.second;) {
111         auto foundString = (item++)->second;
112         if (EcmaStringAccessor::StringsAreEqual(foundString, str)) {
113             return foundString;
114         }
115     }
116     return nullptr;
117 }
118 
GetString(JSThread * thread,const JSHandle<EcmaString> firstString,const JSHandle<EcmaString> secondString,uint32_t hashcode)119 EcmaString *EcmaStringTable::GetString(JSThread *thread, const JSHandle<EcmaString> firstString,
120                                        const JSHandle<EcmaString> secondString, uint32_t hashcode)
121 {
122     ASSERT(EcmaStringAccessor(firstString).NotTreeString());
123     ASSERT(EcmaStringAccessor(secondString).NotTreeString());
124     RuntimeLockHolder locker(thread, stringTable_[GetTableId(hashcode)].mutex_);
125 #if ECMASCRIPT_ENABLE_SCOPE_LOCK_STAT
126     EcmaVM *vm = thread->GetEcmaVM();
127     if (vm->IsCollectingScopeLockStats()) {
128         vm->IncreaseStringTableLockCount();
129     }
130 #endif
131     EcmaString *firstStr = *firstString;
132     EcmaString *secondStr = *secondString;
133     auto range = stringTable_[GetTableId(hashcode)].table_.equal_range(hashcode);
134     for (auto item = range.first; item != range.second;) {
135         auto foundString = (item++)->second;
136         if (EcmaStringAccessor(foundString).EqualToSplicedString(firstStr, secondStr)) {
137             return foundString;
138         }
139     }
140     return nullptr;
141 }
142 
GetString(JSThread * thread,const uint8_t * utf8Data,uint32_t utf8Len,bool canBeCompress,uint32_t hashcode)143 EcmaString *EcmaStringTable::GetString(JSThread *thread, const uint8_t *utf8Data, uint32_t utf8Len, bool canBeCompress,
144                                        uint32_t hashcode)
145 {
146     RuntimeLockHolder locker(thread, stringTable_[GetTableId(hashcode)].mutex_);
147 #if ECMASCRIPT_ENABLE_SCOPE_LOCK_STAT
148     EcmaVM *vm = thread->GetEcmaVM();
149     if (vm->IsCollectingScopeLockStats()) {
150         vm->IncreaseStringTableLockCount();
151     }
152 #endif
153     auto range = stringTable_[GetTableId(hashcode)].table_.equal_range(hashcode);
154     for (auto item = range.first; item != range.second;) {
155         auto foundString = (item++)->second;
156         if (EcmaStringAccessor::StringIsEqualUint8Data(foundString, utf8Data, utf8Len, canBeCompress)) {
157             return foundString;
158         }
159     }
160     return nullptr;
161 }
162 
GetString(JSThread * thread,const uint16_t * utf16Data,uint32_t utf16Len,uint32_t hashcode)163 EcmaString *EcmaStringTable::GetString(JSThread *thread, const uint16_t *utf16Data, uint32_t utf16Len,
164                                        uint32_t hashcode)
165 {
166     RuntimeLockHolder locker(thread, stringTable_[GetTableId(hashcode)].mutex_);
167 #if ECMASCRIPT_ENABLE_SCOPE_LOCK_STAT
168     EcmaVM *vm = thread->GetEcmaVM();
169     if (vm->IsCollectingScopeLockStats()) {
170         vm->IncreaseStringTableLockCount();
171     }
172 #endif
173     auto range = stringTable_[GetTableId(hashcode)].table_.equal_range(hashcode);
174     for (auto item = range.first; item != range.second;) {
175         auto foundString = (item++)->second;
176         if (EcmaStringAccessor::StringsAreEqualUtf16(foundString, utf16Data, utf16Len)) {
177             return foundString;
178         }
179     }
180     return nullptr;
181 }
182 
GetStringThreadUnsafe(EcmaString * string,uint32_t hashcode) const183 EcmaString *EcmaStringTable::GetStringThreadUnsafe(EcmaString *string, uint32_t hashcode) const
184 {
185     ASSERT(EcmaStringAccessor(string).NotTreeString());
186     auto range = stringTable_[GetTableId(hashcode)].table_.equal_range(hashcode);
187     for (auto item = range.first; item != range.second;) {
188         auto foundString = (item++)->second;
189         if (EcmaStringAccessor::StringsAreEqual(foundString, string)) {
190             return foundString;
191         }
192     }
193     return nullptr;
194 }
195 
InternStringThreadUnsafe(EcmaString * string,uint32_t hashcode)196 void EcmaStringTable::InternStringThreadUnsafe(EcmaString *string, uint32_t hashcode)
197 {
198     ASSERT(!EcmaStringAccessor(string).IsInternString());
199     ASSERT(EcmaStringAccessor(string).NotTreeString());
200     // Strings in string table should not be in the young space.
201     ASSERT(Region::ObjectAddressToRange(reinterpret_cast<TaggedObject *>(string))->InSharedHeap());
202     stringTable_[GetTableId(hashcode)].table_.emplace(hashcode, string);
203     EcmaStringAccessor(string).SetInternString();
204 }
205 
AtomicGetOrInternStringImpl(JSThread * thread,const JSHandle<EcmaString> string,uint32_t hashcode)206 EcmaString *EcmaStringTable::AtomicGetOrInternStringImpl(JSThread *thread, const JSHandle<EcmaString> string,
207                                                          uint32_t hashcode)
208 {
209     ASSERT(EcmaStringAccessor(string).NotTreeString());
210     RuntimeLockHolder locker(thread, stringTable_[GetTableId(hashcode)].mutex_);
211 #if ECMASCRIPT_ENABLE_SCOPE_LOCK_STAT
212     EcmaVM *vm = thread->GetEcmaVM();
213     if (vm->IsCollectingScopeLockStats()) {
214         vm->IncreaseStringTableLockCount();
215     }
216 #endif
217     EcmaString *str = *string;
218     EcmaString *result = GetStringThreadUnsafe(str, hashcode);
219     if (result == nullptr) {
220         InternStringThreadUnsafe(str, hashcode);
221         return str;
222     }
223     return result;
224 }
225 
GetOrInternFlattenString(EcmaVM * vm,EcmaString * string)226 EcmaString *EcmaStringTable::GetOrInternFlattenString(EcmaVM *vm, EcmaString *string)
227 {
228     ASSERT(EcmaStringAccessor(string).NotTreeString());
229     if (EcmaStringAccessor(string).IsInternString()) {
230         return string;
231     }
232     JSThread *thread = vm->GetJSThread();
233     JSHandle<EcmaString> stringHandle(thread, string);
234     uint32_t hashcode = EcmaStringAccessor(string).GetHashcode();
235     return AtomicGetOrInternStringImpl(thread, stringHandle, hashcode);
236 }
237 
GetOrInternStringFromCompressedSubString(EcmaVM * vm,const JSHandle<EcmaString> & string,uint32_t offset,uint32_t utf8Len)238 EcmaString *EcmaStringTable::GetOrInternStringFromCompressedSubString(EcmaVM *vm, const JSHandle<EcmaString> &string,
239     uint32_t offset, uint32_t utf8Len)
240 {
241     const uint8_t *utf8Data = EcmaStringAccessor(string).GetDataUtf8() + offset;
242     uint32_t hashcode = EcmaStringAccessor::ComputeHashcodeUtf8(utf8Data, utf8Len, true);
243     JSThread *thread = vm->GetJSThread();
244     EcmaString *result = GetStringFromCompressedSubString(thread, string, offset, utf8Len, hashcode);
245     if (result != nullptr) {
246         return result;
247     }
248     utf8Data = EcmaStringAccessor(string).GetDataUtf8() + offset;
249     EcmaString *str = EcmaStringAccessor::CreateFromUtf8CompressedSubString(vm, string, offset, utf8Len,
250         MemSpaceType::SHARED_OLD_SPACE);
251     JSHandle<EcmaString> strHandle(thread, str);
252     strHandle->SetMixHashcode(hashcode);
253     return AtomicGetOrInternStringImpl(thread, strHandle, hashcode);
254 }
255 
GetOrInternString(EcmaVM * vm,EcmaString * string)256 EcmaString *EcmaStringTable::GetOrInternString(EcmaVM *vm, EcmaString *string)
257 {
258     if (EcmaStringAccessor(string).IsInternString()) {
259         return string;
260     }
261     JSThread *thread = vm->GetJSThread();
262     JSHandle<EcmaString> stringHandle(thread, string);
263     // may gc
264     EcmaString *strFlat = EcmaStringAccessor::Flatten(vm, stringHandle, MemSpaceType::SHARED_OLD_SPACE);
265     if (EcmaStringAccessor(strFlat).IsInternString()) {
266         return strFlat;
267     }
268     uint32_t hashcode = EcmaStringAccessor(strFlat).GetHashcode();
269     JSHandle<EcmaString> strFlatHandle(thread, strFlat);
270     EcmaString *result = GetString(thread, strFlatHandle, hashcode);
271     if (result != nullptr) {
272         return result;
273     }
274     return AtomicGetOrInternStringImpl(thread, strFlatHandle, hashcode);
275 }
276 
GetOrInternString(EcmaVM * vm,const JSHandle<EcmaString> & firstString,const JSHandle<EcmaString> & secondString)277 EcmaString *EcmaStringTable::GetOrInternString(EcmaVM *vm, const JSHandle<EcmaString> &firstString,
278                                                const JSHandle<EcmaString> &secondString)
279 {
280     bool signalState = vm->GetJsDebuggerManager()->GetSignalState();
281     if (UNLIKELY(signalState)) {
282         return GetOrInternStringThreadUnsafe(vm, firstString, secondString);
283     }
284     JSThread *thread = vm->GetJSThread();
285     JSHandle<EcmaString> firstFlat(thread, EcmaStringAccessor::Flatten(vm, firstString));
286     JSHandle<EcmaString> secondFlat(thread, EcmaStringAccessor::Flatten(vm, secondString));
287     uint32_t hashcode = EcmaStringAccessor::CalculateAllConcatHashCode(firstFlat, secondFlat);
288     EcmaString *result = GetString(thread, firstFlat, secondFlat, hashcode);
289     if (result != nullptr) {
290         return result;
291     }
292     JSHandle<EcmaString> concatHandle(thread,
293         EcmaStringAccessor::Concat(vm, firstFlat, secondFlat, MemSpaceType::SHARED_OLD_SPACE));
294     JSHandle<EcmaString> flattenConcatHandle(thread,
295         EcmaStringAccessor::Flatten(vm, concatHandle, MemSpaceType::SHARED_OLD_SPACE));
296     flattenConcatHandle->SetMixHashcode(hashcode);
297     return AtomicGetOrInternStringImpl(thread, flattenConcatHandle, hashcode);
298 }
299 
GetOrInternString(EcmaVM * vm,const uint8_t * utf8Data,uint32_t utf8Len,bool canBeCompress,MemSpaceType type)300 EcmaString *EcmaStringTable::GetOrInternString(EcmaVM *vm, const uint8_t *utf8Data, uint32_t utf8Len,
301                                                bool canBeCompress, MemSpaceType type)
302 {
303     ASSERT(IsSMemSpace(type));
304     bool signalState = vm->GetJsDebuggerManager()->GetSignalState();
305     if (UNLIKELY(signalState)) {
306         return GetOrInternStringThreadUnsafe(vm, utf8Data, utf8Len, canBeCompress);
307     }
308     JSThread *thread = vm->GetJSThread();
309     uint32_t hashcode = EcmaStringAccessor::ComputeHashcodeUtf8(utf8Data, utf8Len, canBeCompress);
310     EcmaString *result = GetString(thread, utf8Data, utf8Len, canBeCompress, hashcode);
311     if (result != nullptr) {
312         return result;
313     }
314     JSHandle<EcmaString> strHandle(thread,
315         EcmaStringAccessor::CreateFromUtf8(vm, utf8Data, utf8Len, canBeCompress, type));
316     strHandle->SetMixHashcode(hashcode);
317     return AtomicGetOrInternStringImpl(thread, strHandle, hashcode);
318 }
319 
GetOrInternString(EcmaVM * vm,const uint8_t * utf8Data,uint32_t utf8Len,bool canBeCompress,MemSpaceType type,bool isConstantString,uint32_t idOffset)320 EcmaString *EcmaStringTable::GetOrInternString(EcmaVM *vm, const uint8_t *utf8Data, uint32_t utf8Len,
321                                                bool canBeCompress, MemSpaceType type,
322                                                bool isConstantString, uint32_t idOffset)
323 {
324     ASSERT(IsSMemSpace(type));
325     ASSERT(type == MemSpaceType::SHARED_NON_MOVABLE || type == MemSpaceType::SHARED_OLD_SPACE);
326     uint32_t hashcode = EcmaStringAccessor::ComputeHashcodeUtf8(utf8Data, utf8Len, canBeCompress);
327     JSThread *thread = vm->GetJSThread();
328     EcmaString *result = GetString(thread, utf8Data, utf8Len, canBeCompress, hashcode);
329     if (result != nullptr) {
330         return result;
331     }
332     EcmaString *str = nullptr;
333     if (canBeCompress) {
334         // Constant string will be created in this branch.
335         str = EcmaStringAccessor::CreateFromUtf8(vm, utf8Data, utf8Len, canBeCompress, type, isConstantString,
336             idOffset);
337     } else {
338         str = EcmaStringAccessor::CreateFromUtf8(vm, utf8Data, utf8Len, canBeCompress, type);
339     }
340     JSHandle<EcmaString> strHandle(thread, str);
341     strHandle->SetMixHashcode(hashcode);
342     return AtomicGetOrInternStringImpl(thread, strHandle, hashcode);
343 }
344 
GetOrInternString(EcmaVM * vm,const uint8_t * utf8Data,uint32_t utf16Len,MemSpaceType type)345 EcmaString *EcmaStringTable::GetOrInternString(EcmaVM *vm, const uint8_t *utf8Data, uint32_t utf16Len,
346                                                MemSpaceType type)
347 {
348     ASSERT(IsSMemSpace(type));
349     ASSERT(type == MemSpaceType::SHARED_NON_MOVABLE || type == MemSpaceType::SHARED_OLD_SPACE);
350     JSThread *thread = vm->GetJSThread();
351     EcmaString *str = EcmaStringAccessor::CreateUtf16StringFromUtf8(vm, utf8Data, utf16Len, type);
352     JSHandle<EcmaString> strHandle(thread, str);
353     uint32_t hashcode = EcmaStringAccessor(str).GetHashcode();
354     return AtomicGetOrInternStringImpl(thread, strHandle, hashcode);
355 }
356 
GetOrInternString(EcmaVM * vm,const uint16_t * utf16Data,uint32_t utf16Len,bool canBeCompress)357 EcmaString *EcmaStringTable::GetOrInternString(EcmaVM *vm, const uint16_t *utf16Data, uint32_t utf16Len,
358                                                bool canBeCompress)
359 {
360     uint32_t hashcode = EcmaStringAccessor::ComputeHashcodeUtf16(const_cast<uint16_t *>(utf16Data), utf16Len);
361     JSThread *thread = vm->GetJSThread();
362     EcmaString *result = GetString(thread, utf16Data, utf16Len, hashcode);
363     if (result != nullptr) {
364         return result;
365     }
366     JSHandle<EcmaString> strHandle(thread,
367         EcmaStringAccessor::CreateFromUtf16(vm, utf16Data, utf16Len, canBeCompress, MemSpaceType::SHARED_OLD_SPACE));
368     strHandle->SetMixHashcode(hashcode);
369     return AtomicGetOrInternStringImpl(thread, strHandle, hashcode);
370 }
371 
InsertStringToTableWithHashThreadUnsafe(EcmaString * string,uint32_t hashcode)372 void EcmaStringTable::InsertStringToTableWithHashThreadUnsafe(EcmaString* string, uint32_t hashcode)
373 {
374     // Strings in string table should not be in the young space.
375     ASSERT(Region::ObjectAddressToRange(reinterpret_cast<TaggedObject *>(string))->InSharedHeap());
376     ASSERT(EcmaStringAccessor(string).NotTreeString());
377     ASSERT(EcmaStringAccessor(string).GetHashcode() == hashcode);
378     stringTable_[GetTableId(hashcode)].table_.emplace(hashcode, string);
379     EcmaStringAccessor(string).SetInternString();
380 }
381 
InsertStringToTable(EcmaVM * vm,const JSHandle<EcmaString> & strHandle)382 EcmaString *EcmaStringTable::InsertStringToTable(EcmaVM *vm, const JSHandle<EcmaString> &strHandle)
383 {
384     JSThread *thread = vm->GetJSThread();
385     EcmaString *strFlat = EcmaStringAccessor::Flatten(vm, strHandle, MemSpaceType::SHARED_OLD_SPACE);
386     JSHandle<EcmaString> strFlatHandle(thread, strFlat);
387     uint32_t hashcode = EcmaStringAccessor(strFlat).GetHashcode();
388     RuntimeLockHolder locker(thread, stringTable_[GetTableId(hashcode)].mutex_);
389 #if ECMASCRIPT_ENABLE_SCOPE_LOCK_STAT
390     if (vm->IsCollectingScopeLockStats()) {
391         vm->IncreaseStringTableLockCount();
392     }
393 #endif
394     strFlat = *strFlatHandle;
395     InternStringThreadUnsafe(strFlat, hashcode);
396     return strFlat;
397 }
398 
TryGetInternString(JSThread * thread,const JSHandle<EcmaString> & string)399 EcmaString *EcmaStringTable::TryGetInternString(JSThread *thread, const JSHandle<EcmaString> &string)
400 {
401     uint32_t hashcode = EcmaStringAccessor(*string).GetHashcode();
402     return GetString(thread, string, hashcode);
403 }
404 
405 // used in jit thread, which unsupport create jshandle
GetOrInternStringWithoutJSHandleForJit(EcmaVM * vm,const uint8_t * utf8Data,uint32_t utf8Len,bool canBeCompress,MemSpaceType type,bool isConstantString,uint32_t idOffset)406 EcmaString *EcmaStringTable::GetOrInternStringWithoutJSHandleForJit(EcmaVM *vm, const uint8_t *utf8Data,
407     uint32_t utf8Len, bool canBeCompress, MemSpaceType type, bool isConstantString, uint32_t idOffset)
408 {
409     ASSERT(IsSMemSpace(type));
410     ASSERT(type == MemSpaceType::SHARED_NON_MOVABLE || type == MemSpaceType::SHARED_OLD_SPACE);
411     uint32_t hashcode = EcmaStringAccessor::ComputeHashcodeUtf8(utf8Data, utf8Len, canBeCompress);
412     JSThread *thread = vm->GetJSThread();
413     RuntimeLockHolder locker(thread, stringTable_[GetTableId(hashcode)].mutex_);
414 #if ECMASCRIPT_ENABLE_SCOPE_LOCK_STAT
415     if (vm->IsCollectingScopeLockStats()) {
416         vm->IncreaseStringTableLockCount();
417     }
418 #endif
419     EcmaString *result = GetStringThreadUnsafe(utf8Data, utf8Len, canBeCompress, hashcode);
420     if (result != nullptr) {
421         return result;
422     }
423     EcmaString *str = nullptr;
424     if (canBeCompress) {
425         // Constant string will be created in this branch.
426         str = EcmaStringAccessor::CreateFromUtf8(vm, utf8Data, utf8Len, canBeCompress, type, isConstantString,
427             idOffset);
428     } else {
429         str = EcmaStringAccessor::CreateFromUtf8(vm, utf8Data, utf8Len, canBeCompress, type);
430     }
431     str->SetMixHashcode(hashcode);
432     InternStringThreadUnsafe(str, hashcode);
433     return str;
434 }
435 
436 // used in jit thread, which unsupport create jshandle
GetOrInternStringWithoutJSHandleForJit(EcmaVM * vm,const uint8_t * utf8Data,uint32_t utf16Len,MemSpaceType type)437 EcmaString *EcmaStringTable::GetOrInternStringWithoutJSHandleForJit(EcmaVM *vm, const uint8_t *utf8Data,
438                                                                     uint32_t utf16Len, MemSpaceType type)
439 {
440     ASSERT(vm->GetJSThread()->IsJitThread());
441     ASSERT(IsSMemSpace(type));
442     type = (type == MemSpaceType::SHARED_NON_MOVABLE) ? type : MemSpaceType::SHARED_OLD_SPACE;
443     CVector<uint16_t> u16Buffer(utf16Len);
444     utf::ConvertRegionMUtf8ToUtf16(utf8Data, u16Buffer.data(), utf::Mutf8Size(utf8Data), utf16Len, 0);
445     uint32_t hashcode = EcmaStringAccessor::ComputeHashcodeUtf16(u16Buffer.data(), utf16Len);
446     RuntimeLockHolder locker(vm->GetJSThread(), stringTable_[GetTableId(hashcode)].mutex_);
447 #if ECMASCRIPT_ENABLE_SCOPE_LOCK_STAT
448     if (vm->IsCollectingScopeLockStats()) {
449         vm->IncreaseStringTableLockCount();
450     }
451 #endif
452     EcmaString *result = GetStringThreadUnsafe(u16Buffer.data(), utf16Len, hashcode);
453     if (result != nullptr) {
454         return result;
455     }
456     EcmaString *str = EcmaStringAccessor::CreateFromUtf16(vm, u16Buffer.data(), utf16Len, false, type);
457     str->SetMixHashcode(hashcode);
458     InternStringThreadUnsafe(str, hashcode);
459     return str;
460 }
461 
SweepWeakRef(const WeakRootVisitor & visitor)462 void EcmaStringTable::SweepWeakRef(const WeakRootVisitor &visitor)
463 {
464     // No need lock here, only shared gc will sweep string table, meanwhile other threads are suspended.
465     for (uint32_t tableId = 0; tableId < stringTable_.size(); ++tableId) {
466         SweepWeakRef(visitor, tableId);
467     }
468 }
469 
SweepWeakRef(const WeakRootVisitor & visitor,uint32_t tableId)470 void EcmaStringTable::SweepWeakRef(const WeakRootVisitor &visitor, uint32_t tableId)
471 {
472     ASSERT(tableId >= 0 && tableId < stringTable_.size());
473     auto &table = stringTable_[tableId].table_;
474     for (auto it = table.begin(); it != table.end();) {
475         auto *object = it->second;
476         auto fwd = visitor(object);
477         ASSERT(Region::ObjectAddressToRange(object)->InSharedHeap());
478         if (fwd == nullptr) {
479             LOG_ECMA(VERBOSE) << "StringTable: delete string " << std::hex << object;
480             it = table.erase(it);
481         } else if (fwd != object) {
482             it->second = static_cast<EcmaString *>(fwd);
483             ++it;
484             LOG_ECMA(VERBOSE) << "StringTable: forward " << std::hex << object << " -> " << fwd;
485         } else {
486             ++it;
487         }
488     }
489 }
490 
RelocateConstantData(EcmaVM * vm,const JSPandaFile * jsPandaFile)491 void EcmaStringTable::RelocateConstantData(EcmaVM *vm, const JSPandaFile *jsPandaFile)
492 {
493 #if ECMASCRIPT_ENABLE_SCOPE_LOCK_STAT
494     if (vm->IsCollectingScopeLockStats()) {
495         vm->IncreaseStringTableLockCount();
496     }
497 #endif
498     auto thread = vm->GetJSThread();
499     for (auto &[table, mutex] : stringTable_) {
500         RuntimeLockHolder locker(thread, mutex);
501         for (auto it = table.begin(); it != table.end();) {
502             auto *object = it->second;
503             if (!EcmaStringAccessor(object).IsConstantString()) {
504                 ++it;
505                 continue;
506             }
507             auto constantStr = ConstantString::Cast(object);
508             if (constantStr->GetEntityId() < 0 || !jsPandaFile->Contain(constantStr->GetConstantData())) {
509                 // EntityId is -1, which means this str has been relocated. Or the data is not in pandafile.
510                 ++it;
511                 continue;
512             }
513             uint32_t id = constantStr->GetEntityIdU32();
514             panda_file::File::StringData sd = jsPandaFile->GetStringData(EntityId(id));
515             if (constantStr->GetConstantData() != sd.data) {
516                 LOG_ECMA(ERROR) << "ConstantString data pointer is inconsistent with sd.data";
517                 ++it;
518                 continue;
519             }
520             uint32_t strLen = sd.utf16_length;
521             if (UNLIKELY(strLen == 0)) {
522                 it->second = *(vm->GetFactory()->GetEmptyString());
523             }
524             size_t byteLength = sd.is_ascii ? 1 : sizeof(uint16_t);
525             JSMutableHandle<ByteArray> newData(vm->GetJSThread(), JSTaggedValue::Undefined());
526             newData.Update(vm->GetFactory()->NewByteArray(
527                 strLen, byteLength, reinterpret_cast<void *>(const_cast<uint8_t *>(sd.data)),
528                 MemSpaceType::SHARED_NON_MOVABLE));
529             constantStr->SetRelocatedData(thread, newData.GetTaggedValue());
530             constantStr->SetConstantData(static_cast<uint8_t *>(newData->GetData()));
531             constantStr->SetEntityId(-1);
532             ++it;
533         }
534     }
535 }
536 
CheckStringTableValidity(JSThread * thread)537 bool EcmaStringTable::CheckStringTableValidity(JSThread *thread)
538 {
539 #if ECMASCRIPT_ENABLE_SCOPE_LOCK_STAT
540     auto vm = thread->GetEcmaVM();
541     if (vm->IsCollectingScopeLockStats()) {
542         vm->IncreaseStringTableLockCount();
543     }
544 #endif
545     for (auto &[table, mutex] : stringTable_) {
546         RuntimeLockHolder locker(thread, mutex);
547         for (auto itemOuter = table.begin(); itemOuter != table.end(); ++itemOuter) {
548             auto outerString = itemOuter->second;
549             if (!EcmaStringAccessor(outerString).NotTreeString()) {
550                 return false;
551             }
552             int counter = 0;
553             auto hashcode = EcmaStringAccessor(outerString).GetHashcode();
554             auto range = table.equal_range(hashcode);
555             for (auto it = range.first; it != range.second; ++it) {
556                 auto foundString = it->second;
557                 counter += EcmaStringAccessor::StringsAreEqual(foundString, outerString) ? 1 : 0;
558             }
559             if (counter > 1) {
560                 return false;
561             }
562         }
563     }
564     return true;
565 }
566 
CreateSingleCharTable(JSThread * thread)567 JSTaggedValue SingleCharTable::CreateSingleCharTable(JSThread *thread)
568 {
569     auto table = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(MAX_ONEBYTE_CHARCODE,
570         JSTaggedValue::Undefined(), MemSpaceType::SHARED_NON_MOVABLE);
571     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
572     for (uint32_t i = 1; i < MAX_ONEBYTE_CHARCODE; ++i) {
573         std::string tmp(1, i + 0X00); // 1: size
574         table->Set(thread, i, factory->NewFromASCIIReadOnly(tmp).GetTaggedValue());
575     }
576     return table.GetTaggedValue();
577 }
578 
579 // This should only call in Debugger Signal, and need to fix and remove
GetOrInternStringThreadUnsafe(EcmaVM * vm,const JSHandle<EcmaString> firstString,const JSHandle<EcmaString> secondString)580 EcmaString *EcmaStringTable::GetOrInternStringThreadUnsafe(EcmaVM *vm,
581                                                            const JSHandle<EcmaString> firstString,
582                                                            const JSHandle<EcmaString> secondString)
583 {
584     ASSERT(vm->GetJsDebuggerManager()->GetSignalState());
585     JSThread *thread = vm->GetJSThreadNoCheck();
586     JSHandle<EcmaString> firstFlat(thread, EcmaStringAccessor::Flatten(vm, firstString));
587     JSHandle<EcmaString> secondFlat(thread, EcmaStringAccessor::Flatten(vm, secondString));
588     uint32_t hashcode = EcmaStringAccessor::CalculateAllConcatHashCode(firstFlat, secondFlat);
589     EcmaString *result = GetStringThreadUnsafe(firstFlat, secondFlat, hashcode);
590     if (result != nullptr) {
591         return result;
592     }
593     JSHandle<EcmaString> concatHandle(thread,
594         EcmaStringAccessor::Concat(vm, firstFlat, secondFlat, MemSpaceType::SHARED_OLD_SPACE));
595     EcmaString *concatString = EcmaStringAccessor::Flatten(vm, concatHandle, MemSpaceType::SHARED_OLD_SPACE);
596     concatString->SetMixHashcode(hashcode);
597     InternStringThreadUnsafe(concatString, hashcode);
598     return concatString;
599 }
600 
601 // 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)602 EcmaString *EcmaStringTable::GetOrInternStringThreadUnsafe(EcmaVM *vm, const uint8_t *utf8Data, uint32_t utf8Len,
603                                                            bool canBeCompress)
604 {
605     ASSERT(vm->GetJsDebuggerManager()->GetSignalState());
606     uint32_t hashcode = EcmaStringAccessor::ComputeHashcodeUtf8(utf8Data, utf8Len, canBeCompress);
607     EcmaString *result = GetStringThreadUnsafe(utf8Data, utf8Len, canBeCompress, hashcode);
608     if (result != nullptr) {
609         return result;
610     }
611     EcmaString *str =
612         EcmaStringAccessor::CreateFromUtf8(vm, utf8Data, utf8Len, canBeCompress, MemSpaceType::SHARED_OLD_SPACE);
613     str->SetMixHashcode(hashcode);
614     InternStringThreadUnsafe(str, hashcode);
615     return str;
616 }
617 
618 // This should only call in Debugger Signal, and need to fix and remove
GetStringThreadUnsafe(const JSHandle<EcmaString> firstString,const JSHandle<EcmaString> secondString,uint32_t hashcode) const619 EcmaString *EcmaStringTable::GetStringThreadUnsafe(const JSHandle<EcmaString> firstString,
620                                                    const JSHandle<EcmaString> secondString,
621                                                    uint32_t hashcode) const
622 {
623     ASSERT(EcmaStringAccessor(firstString).NotTreeString());
624     ASSERT(EcmaStringAccessor(secondString).NotTreeString());
625     EcmaString *firstStr = *firstString;
626     EcmaString *secondStr = *secondString;
627     auto range = stringTable_[GetTableId(hashcode)].table_.equal_range(hashcode);
628     for (auto item = range.first; item != range.second;) {
629         auto foundString = (item++)->second;
630         if (EcmaStringAccessor(foundString).EqualToSplicedString(firstStr, secondStr)) {
631             return foundString;
632         }
633     }
634     return nullptr;
635 }
636 
637 // This should only call in Debugger Signal, and need to fix and remove
GetStringThreadUnsafe(const uint8_t * utf8Data,uint32_t utf8Len,bool canBeCompress,uint32_t hashcode) const638 EcmaString *EcmaStringTable::GetStringThreadUnsafe(const uint8_t *utf8Data, uint32_t utf8Len, bool canBeCompress,
639                                                    uint32_t hashcode) const
640 {
641     auto range = stringTable_[GetTableId(hashcode)].table_.equal_range(hashcode);
642     for (auto item = range.first; item != range.second;) {
643         auto foundString = (item++)->second;
644         if (EcmaStringAccessor::StringIsEqualUint8Data(foundString, utf8Data, utf8Len, canBeCompress)) {
645             return foundString;
646         }
647     }
648     return nullptr;
649 }
650 
651 // This should only call in JIT Thread, and need to fix and remove
GetStringThreadUnsafe(const uint16_t * utf16Data,uint32_t utf16Len,uint32_t hashcode) const652 EcmaString *EcmaStringTable::GetStringThreadUnsafe(const uint16_t *utf16Data, uint32_t utf16Len,
653                                                    uint32_t hashcode) const
654 {
655     auto range = stringTable_[GetTableId(hashcode)].table_.equal_range(hashcode);
656     for (auto item = range.first; item != range.second;) {
657         auto foundString = (item++)->second;
658         if (EcmaStringAccessor::StringsAreEqualUtf16(foundString, utf16Data, utf16Len)) {
659             return foundString;
660         }
661     }
662     return nullptr;
663 }
664 }  // namespace panda::ecmascript
665