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