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