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