/* * Copyright (c) 2021-2024 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ecmascript/ecma_string_table.h" #include "ecmascript/ecma_string-inl.h" #include "ecmascript/runtime_lock.h" namespace panda::ecmascript { void EcmaStringTableCleaner::PostSweepWeakRefTask(const WeakRootVisitor &visitor) { StartSweepWeakRefTask(); iter_ = std::make_shared>(0U); const uint32_t postTaskCount = Taskpool::GetCurrentTaskpool()->GetTotalThreadNum(); for (uint32_t i = 0U; i < postTaskCount; ++i) { Taskpool::GetCurrentTaskpool()->PostTask(std::make_unique(iter_, this, visitor)); } } void EcmaStringTableCleaner::JoinAndWaitSweepWeakRefTask(const WeakRootVisitor &visitor) { ProcessSweepWeakRef(iter_, this, visitor); WaitSweepWeakRefTask(); iter_.reset(); } void EcmaStringTableCleaner::ProcessSweepWeakRef(IteratorPtr& iter, EcmaStringTableCleaner *cleaner, const WeakRootVisitor &visitor) { uint32_t tableId = 0U; while ((tableId = GetNextTableId(iter)) < EcmaStringTable::SEGMENT_COUNT) { cleaner->stringTable_->SweepWeakRef(visitor, tableId); if (ReduceCountAndCheckFinish(cleaner)) { cleaner->SignalSweepWeakRefTask(); } } } void EcmaStringTableCleaner::StartSweepWeakRefTask() { // No need lock here, only the daemon thread will reset the state. sweepWeakRefFinished_ = false; PendingTaskCount_.store(EcmaStringTable::SEGMENT_COUNT, std::memory_order_relaxed); } void EcmaStringTableCleaner::WaitSweepWeakRefTask() { LockHolder holder(sweepWeakRefMutex_); while (!sweepWeakRefFinished_) { sweepWeakRefCV_.Wait(&sweepWeakRefMutex_); } } void EcmaStringTableCleaner::SignalSweepWeakRefTask() { LockHolder holder(sweepWeakRefMutex_); sweepWeakRefFinished_ = true; sweepWeakRefCV_.SignalAll(); } bool EcmaStringTableCleaner::SweepWeakRefTask::Run([[maybe_unused]] uint32_t threadIndex) { ProcessSweepWeakRef(iter_, cleaner_, visitor_); return true; } std::pair EcmaStringTable::GetStringThreadUnsafe(const JSHandle &firstString, const JSHandle &secondString, uint32_t hashcode) const { ASSERT(EcmaStringAccessor(firstString).NotTreeString()); ASSERT(EcmaStringAccessor(secondString).NotTreeString()); auto range = stringTable_[GetTableId(hashcode)].table_.equal_range(hashcode); for (auto item = range.first; item != range.second;) { auto foundString = (item++)->second; if (EcmaStringAccessor(foundString).EqualToSplicedString(*firstString, *secondString)) { return std::make_pair(foundString, hashcode); } } return std::make_pair(nullptr, hashcode); } std::pair EcmaStringTable::GetStringThreadUnsafe(const uint8_t *utf8Data, uint32_t utf8Len, bool canBeCompress, uint32_t hashcode) const { auto range = stringTable_[GetTableId(hashcode)].table_.equal_range(hashcode); for (auto item = range.first; item != range.second;) { auto foundString = (item++)->second; if (EcmaStringAccessor::StringIsEqualUint8Data(foundString, utf8Data, utf8Len, canBeCompress)) { return std::make_pair(foundString, hashcode); } } return std::make_pair(nullptr, hashcode); } std::pair EcmaStringTable::GetStringThreadUnsafe(const uint16_t *utf16Data, uint32_t utf16Len, uint32_t hashcode) const { auto range = stringTable_[GetTableId(hashcode)].table_.equal_range(hashcode); for (auto item = range.first; item != range.second;) { auto foundString = (item++)->second; if (EcmaStringAccessor::StringsAreEqualUtf16(foundString, utf16Data, utf16Len)) { return std::make_pair(foundString, hashcode); } } return std::make_pair(nullptr, hashcode); } EcmaString *EcmaStringTable::GetStringWithHashThreadUnsafe(EcmaString *string, uint32_t hashcode) const { auto range = stringTable_[GetTableId(hashcode)].table_.equal_range(hashcode); for (auto item = range.first; item != range.second;) { auto foundString = (item++)->second; if (EcmaStringAccessor::StringsAreEqual(foundString, string)) { return foundString; } } return nullptr; } EcmaString *EcmaStringTable::GetStringThreadUnsafe(EcmaString *string, uint32_t hashcode) const { auto range = stringTable_[GetTableId(hashcode)].table_.equal_range(hashcode); for (auto item = range.first; item != range.second;) { auto foundString = (item++)->second; if (EcmaStringAccessor::StringsAreEqual(foundString, string)) { return foundString; } } return nullptr; } void EcmaStringTable::InternStringThreadUnsafe(EcmaString *string, uint32_t hashcode) { if (EcmaStringAccessor(string).IsInternString()) { return; } // Strings in string table should not be in the young space. ASSERT(Region::ObjectAddressToRange(reinterpret_cast(string))->InSharedHeap()); ASSERT(EcmaStringAccessor(string).NotTreeString()); stringTable_[GetTableId(hashcode)].table_.emplace(hashcode, string); EcmaStringAccessor(string).SetInternString(); } void EcmaStringTable::InternEmptyString(JSThread *thread, EcmaString *emptyStr) { auto hashcode = EcmaStringAccessor(emptyStr).GetHashcode(); RuntimeLockHolder locker(thread, stringTable_[GetTableId(hashcode)].mutex_); #if ECMASCRIPT_ENABLE_SCOPE_LOCK_STAT auto vm = thread->GetEcmaVM(); if (vm->IsCollectingScopeLockStats()) { vm->IncreaseStringTableLockCount(); } #endif InternStringThreadUnsafe(emptyStr, hashcode); } void EcmaStringTable::InsertStringIfNotExistThreadUnsafe(EcmaString *string) { auto hashcode = EcmaStringAccessor(string).GetHashcode(); EcmaString *str = GetStringThreadUnsafe(string, hashcode); if (str == nullptr) { InternStringThreadUnsafe(string, hashcode); } } EcmaString *EcmaStringTable::GetOrInternString(EcmaVM *vm, const JSHandle &firstString, const JSHandle &secondString) { JSThread *thread = nullptr; bool signalState = vm->GetJsDebuggerManager()->GetSignalState(); thread = signalState ? vm->GetJSThreadNoCheck() : vm->GetJSThread(); auto firstFlat = JSHandle(thread, EcmaStringAccessor::Flatten(vm, firstString)); auto secondFlat = JSHandle(thread, EcmaStringAccessor::Flatten(vm, secondString)); uint32_t hashcode = EcmaStringAccessor::CalculateAllConcatHashCode(firstFlat, secondFlat); if (signalState) { return GetOrInternStringWithoutLock(thread, firstString, secondString, hashcode); } RuntimeLockHolder locker(thread, stringTable_[GetTableId(hashcode)].mutex_); return GetOrInternStringWithoutLock(thread, firstString, secondString, hashcode); } EcmaString *EcmaStringTable::GetOrInternStringWithoutLock(JSThread *thread, const JSHandle &firstString, const JSHandle &secondString, uint32_t hashcode) { EcmaVM *vm = thread->GetEcmaVM(); auto firstFlat = JSHandle(thread, EcmaStringAccessor::Flatten(vm, firstString)); auto secondFlat = JSHandle(thread, EcmaStringAccessor::Flatten(vm, secondString)); #if ECMASCRIPT_ENABLE_SCOPE_LOCK_STAT if (vm->IsCollectingScopeLockStats()) { vm->IncreaseStringTableLockCount(); } #endif std::pair result = GetStringThreadUnsafe(firstFlat, secondFlat, hashcode); if (result.first != nullptr) { return result.first; } JSHandle concatHandle(thread, EcmaStringAccessor::Concat(vm, firstFlat, secondFlat, MemSpaceType::SHARED_OLD_SPACE)); EcmaString *concatString = EcmaStringAccessor::Flatten(vm, concatHandle, MemSpaceType::SHARED_OLD_SPACE); concatString->SetMixHashcode(result.second); InternStringThreadUnsafe(concatString, hashcode); return concatString; } EcmaString *EcmaStringTable::GetOrInternString(EcmaVM *vm, const uint8_t *utf8Data, uint32_t utf8Len, bool canBeCompress) { uint32_t hashcode = EcmaStringAccessor::ComputeHashcodeUtf8(utf8Data, utf8Len, canBeCompress); if (vm->GetJsDebuggerManager()->GetSignalState()) { return GetOrInternStringWithoutLock(vm, utf8Data, utf8Len, canBeCompress, hashcode); } else { RuntimeLockHolder locker(vm->GetJSThread(), stringTable_[GetTableId(hashcode)].mutex_); return GetOrInternStringWithoutLock(vm, utf8Data, utf8Len, canBeCompress, hashcode); } } EcmaString *EcmaStringTable::GetOrInternStringWithoutLock(EcmaVM *vm, const uint8_t *utf8Data, uint32_t utf8Len, bool canBeCompress, uint32_t hashcode) { #if ECMASCRIPT_ENABLE_SCOPE_LOCK_STAT if (vm->IsCollectingScopeLockStats()) { vm->IncreaseStringTableLockCount(); } #endif std::pair result = GetStringThreadUnsafe(utf8Data, utf8Len, canBeCompress, hashcode); if (result.first != nullptr) { return result.first; } EcmaString *str = EcmaStringAccessor::CreateFromUtf8(vm, utf8Data, utf8Len, canBeCompress, MemSpaceType::SHARED_OLD_SPACE); str->SetMixHashcode(result.second); InternStringThreadUnsafe(str, hashcode); return str; } EcmaString *EcmaStringTable::GetOrInternCompressedSubString(EcmaVM *vm, const JSHandle &string, uint32_t offset, uint32_t utf8Len) { auto *utf8Data = EcmaStringAccessor(string).GetDataUtf8() + offset; uint32_t hashcode = EcmaStringAccessor::ComputeHashcodeUtf8(utf8Data, utf8Len, true); RuntimeLockHolder locker(vm->GetJSThread(), stringTable_[GetTableId(hashcode)].mutex_); #if ECMASCRIPT_ENABLE_SCOPE_LOCK_STAT if (vm->IsCollectingScopeLockStats()) { vm->IncreaseStringTableLockCount(); } #endif // utf8data may be moved after shared full gc, so reload utf8Data here. utf8Data = EcmaStringAccessor(string).GetDataUtf8() + offset; std::pair result = GetStringThreadUnsafe(utf8Data, utf8Len, true, hashcode); if (result.first != nullptr) { return result.first; } EcmaString *str = EcmaStringAccessor::CreateFromUtf8CompressedSubString( vm, string, offset, utf8Len, MemSpaceType::SHARED_OLD_SPACE); str->SetMixHashcode(result.second); InternStringThreadUnsafe(str, hashcode); return str; } /* This function is used to create global constant strings from non-movable sapce only. It only inserts string into string-table and provides no string-table validity check. */ EcmaString *EcmaStringTable::CreateAndInternStringNonMovable(EcmaVM *vm, const uint8_t *utf8Data, uint32_t utf8Len) { uint32_t hashcode = EcmaStringAccessor::ComputeHashcodeUtf8(utf8Data, utf8Len, true); RuntimeLockHolder locker(vm->GetJSThread(), stringTable_[GetTableId(hashcode)].mutex_); #if ECMASCRIPT_ENABLE_SCOPE_LOCK_STAT if (vm->IsCollectingScopeLockStats()) { vm->IncreaseStringTableLockCount(); } #endif std::pair result = GetStringThreadUnsafe(utf8Data, utf8Len, true, hashcode); if (result.first != nullptr) { return result.first; } EcmaString *str = EcmaStringAccessor::CreateFromUtf8(vm, utf8Data, utf8Len, true, MemSpaceType::SHARED_NON_MOVABLE); str->SetMixHashcode(result.second); InternStringThreadUnsafe(str, hashcode); return str; } /* This function is used to create global constant strings from read-only sapce only. It only inserts string into string-table and provides no string-table validity check. */ EcmaString *EcmaStringTable::CreateAndInternStringReadOnly(EcmaVM *vm, const uint8_t *utf8Data, uint32_t utf8Len, bool canBeCompress) { uint32_t hashcode = EcmaStringAccessor::ComputeHashcodeUtf8(utf8Data, utf8Len, canBeCompress); RuntimeLockHolder locker(vm->GetJSThread(), stringTable_[GetTableId(hashcode)].mutex_); #if ECMASCRIPT_ENABLE_SCOPE_LOCK_STAT if (vm->IsCollectingScopeLockStats()) { vm->IncreaseStringTableLockCount(); } #endif std::pair result = GetStringThreadUnsafe(utf8Data, utf8Len, canBeCompress, hashcode); if (result.first != nullptr) { return result.first; } EcmaString *str = EcmaStringAccessor::CreateFromUtf8(vm, utf8Data, utf8Len, canBeCompress, MemSpaceType::SHARED_READ_ONLY_SPACE); str->SetMixHashcode(result.second); InternStringThreadUnsafe(str, hashcode); return str; } EcmaString *EcmaStringTable::GetOrInternString(EcmaVM *vm, const uint16_t *utf16Data, uint32_t utf16Len, bool canBeCompress) { uint32_t hashcode = EcmaStringAccessor::ComputeHashcodeUtf16(const_cast(utf16Data), utf16Len); RuntimeLockHolder locker(vm->GetJSThread(), stringTable_[GetTableId(hashcode)].mutex_); #if ECMASCRIPT_ENABLE_SCOPE_LOCK_STAT if (vm->IsCollectingScopeLockStats()) { vm->IncreaseStringTableLockCount(); } #endif std::pair result = GetStringThreadUnsafe(utf16Data, utf16Len, hashcode); if (result.first != nullptr) { return result.first; } EcmaString *str = EcmaStringAccessor::CreateFromUtf16(vm, utf16Data, utf16Len, canBeCompress, MemSpaceType::SHARED_OLD_SPACE); str->SetMixHashcode(result.second); InternStringThreadUnsafe(str, hashcode); return str; } EcmaString *EcmaStringTable::GetOrInternString(EcmaVM *vm, EcmaString *string) { if (EcmaStringAccessor(string).IsInternString()) { return string; } auto thread = vm->GetJSThread(); JSHandle strHandle(thread, string); // may gc auto strFlat = EcmaStringAccessor::Flatten(vm, strHandle, MemSpaceType::SHARED_OLD_SPACE); if (EcmaStringAccessor(strFlat).IsInternString()) { return strFlat; } JSHandle strFlatHandle(thread, strFlat); // may gc auto hashcode = EcmaStringAccessor(strFlat).GetHashcode(); RuntimeLockHolder locker(thread, stringTable_[GetTableId(hashcode)].mutex_); #if ECMASCRIPT_ENABLE_SCOPE_LOCK_STAT if (vm->IsCollectingScopeLockStats()) { vm->IncreaseStringTableLockCount(); } #endif strFlat = *strFlatHandle; EcmaString *result = GetStringThreadUnsafe(strFlat, hashcode); if (result != nullptr) { return result; } InternStringThreadUnsafe(strFlat, hashcode); return strFlat; } EcmaString *EcmaStringTable::GetOrInternStringThreadUnsafe(EcmaVM *vm, EcmaString *string) { if (EcmaStringAccessor(string).IsInternString()) { return string; } JSHandle strHandle(vm->GetJSThread(), string); EcmaString *strFlat = EcmaStringAccessor::Flatten(vm, strHandle, MemSpaceType::SHARED_OLD_SPACE); if (EcmaStringAccessor(strFlat).IsInternString()) { return strFlat; } auto hashcode = EcmaStringAccessor(strFlat).GetHashcode(); EcmaString *result = GetStringThreadUnsafe(strFlat, hashcode); if (result != nullptr) { return result; } InternStringThreadUnsafe(strFlat, hashcode); return strFlat; } void EcmaStringTable::InsertStringToTableWithHashThreadUnsafe(EcmaString* string, uint32_t hashcode) { // Strings in string table should not be in the young space. ASSERT(Region::ObjectAddressToRange(reinterpret_cast(string))->InSharedHeap()); ASSERT(EcmaStringAccessor(string).NotTreeString()); ASSERT(EcmaStringAccessor(string).GetHashcode() == hashcode); stringTable_[GetTableId(hashcode)].table_.emplace(hashcode, string); EcmaStringAccessor(string).SetInternString(); } EcmaString *EcmaStringTable::InsertStringToTable(EcmaVM *vm, const JSHandle &strHandle) { auto strFlat = EcmaStringAccessor::Flatten(vm, strHandle, MemSpaceType::SHARED_OLD_SPACE); JSHandle strFlatHandle(vm->GetJSThread(), strFlat); auto hashcode = EcmaStringAccessor(strFlat).GetHashcode(); RuntimeLockHolder locker(vm->GetJSThread(), stringTable_[GetTableId(hashcode)].mutex_); #if ECMASCRIPT_ENABLE_SCOPE_LOCK_STAT if (vm->IsCollectingScopeLockStats()) { vm->IncreaseStringTableLockCount(); } #endif strFlat = *strFlatHandle; InternStringThreadUnsafe(strFlat, hashcode); return strFlat; } EcmaString *EcmaStringTable::TryGetInternString(JSThread *thread, const JSHandle &string) { auto hashcode = EcmaStringAccessor(*string).GetHashcode(); RuntimeLockHolder locker(thread, stringTable_[GetTableId(hashcode)].mutex_); #if ECMASCRIPT_ENABLE_SCOPE_LOCK_STAT auto vm = thread->GetEcmaVM(); if (vm->IsCollectingScopeLockStats()) { vm->IncreaseStringTableLockCount(); } #endif return GetStringThreadUnsafe(*string, hashcode); } EcmaString *EcmaStringTable::GetOrInternStringWithSpaceType(EcmaVM *vm, const uint8_t *utf8Data, uint32_t utf8Len, bool canBeCompress, MemSpaceType type, bool isConstantString, uint32_t idOffset) { ASSERT(IsSMemSpace(type)); uint32_t hashcode = EcmaStringAccessor::ComputeHashcodeUtf8(utf8Data, utf8Len, canBeCompress); RuntimeLockHolder locker(vm->GetJSThread(), stringTable_[GetTableId(hashcode)].mutex_); #if ECMASCRIPT_ENABLE_SCOPE_LOCK_STAT if (vm->IsCollectingScopeLockStats()) { vm->IncreaseStringTableLockCount(); } #endif std::pair result = GetStringThreadUnsafe(utf8Data, utf8Len, canBeCompress, hashcode); if (result.first != nullptr) { return result.first; } type = (type == MemSpaceType::SHARED_NON_MOVABLE) ? type : MemSpaceType::SHARED_OLD_SPACE; EcmaString *str = nullptr; if (canBeCompress) { // Constant string will be created in this branch. str = EcmaStringAccessor::CreateFromUtf8(vm, utf8Data, utf8Len, canBeCompress, type, isConstantString, idOffset); } else { str = EcmaStringAccessor::CreateFromUtf8(vm, utf8Data, utf8Len, canBeCompress, type); } str->SetMixHashcode(result.second); InternStringThreadUnsafe(str, hashcode); return str; } EcmaString *EcmaStringTable::GetOrInternStringWithSpaceType(EcmaVM *vm, const uint8_t *utf8Data, uint32_t utf16Len, MemSpaceType type) { ASSERT(IsSMemSpace(type)); type = (type == MemSpaceType::SHARED_NON_MOVABLE) ? type : MemSpaceType::SHARED_OLD_SPACE; EcmaString *str = EcmaStringAccessor::CreateUtf16StringFromUtf8(vm, utf8Data, utf16Len, type); JSHandle stringHandle(vm->GetJSThread(), str); auto hashcode = EcmaStringAccessor(str).GetHashcode(); RuntimeLockHolder locker(vm->GetJSThread(), stringTable_[GetTableId(hashcode)].mutex_); #if ECMASCRIPT_ENABLE_SCOPE_LOCK_STAT if (vm->IsCollectingScopeLockStats()) { vm->IncreaseStringTableLockCount(); } #endif str = *stringHandle; EcmaString *result = GetStringThreadUnsafe(str, hashcode); if (result != nullptr) { return result; } InternStringThreadUnsafe(str, hashcode); return str; } // used in jit thread, which unsupport create jshandle EcmaString *EcmaStringTable::GetOrInternStringWithSpaceTypeWithoutJSHandle(EcmaVM *vm, const uint8_t *utf8Data, uint32_t utf16Len, MemSpaceType type) { ASSERT(IsSMemSpace(type)); type = (type == MemSpaceType::SHARED_NON_MOVABLE) ? type : MemSpaceType::SHARED_OLD_SPACE; CVector u16Buffer(utf16Len); utf::ConvertRegionMUtf8ToUtf16(utf8Data, u16Buffer.data(), utf::Mutf8Size(utf8Data), utf16Len, 0); auto hashcode = EcmaStringAccessor::ComputeHashcodeUtf16(u16Buffer.data(), utf16Len); RuntimeLockHolder locker(vm->GetJSThread(), stringTable_[GetTableId(hashcode)].mutex_); #if ECMASCRIPT_ENABLE_SCOPE_LOCK_STAT if (vm->IsCollectingScopeLockStats()) { vm->IncreaseStringTableLockCount(); } #endif auto result = GetStringThreadUnsafe(u16Buffer.data(), utf16Len, hashcode); if (result.first != nullptr) { return result.first; } EcmaString *str = EcmaStringAccessor::CreateFromUtf16(vm, u16Buffer.data(), utf16Len, false, type); str->SetMixHashcode(hashcode); InternStringThreadUnsafe(str, hashcode); return str; } void EcmaStringTable::SweepWeakRef(const WeakRootVisitor &visitor) { // No need lock here, only shared gc will sweep string table, meanwhile other threads are suspended. for (uint32_t tableId = 0; tableId < stringTable_.size(); ++tableId) { SweepWeakRef(visitor, tableId); } } void EcmaStringTable::SweepWeakRef(const WeakRootVisitor &visitor, uint32_t tableId) { ASSERT(tableId >= 0 && tableId < stringTable_.size()); auto &table = stringTable_[tableId].table_; for (auto it = table.begin(); it != table.end();) { auto *object = it->second; auto fwd = visitor(object); ASSERT(Region::ObjectAddressToRange(object)->InSharedHeap()); if (fwd == nullptr) { LOG_ECMA(VERBOSE) << "StringTable: delete string " << std::hex << object; it = table.erase(it); } else if (fwd != object) { it->second = static_cast(fwd); ++it; LOG_ECMA(VERBOSE) << "StringTable: forward " << std::hex << object << " -> " << fwd; } else { ++it; } } } void EcmaStringTable::RelocateConstantData(EcmaVM *vm, const JSPandaFile *jsPandaFile) { #if ECMASCRIPT_ENABLE_SCOPE_LOCK_STAT if (vm->IsCollectingScopeLockStats()) { vm->IncreaseStringTableLockCount(); } #endif auto thread = vm->GetJSThread(); for (auto &[table, mutex] : stringTable_) { RuntimeLockHolder locker(thread, mutex); for (auto it = table.begin(); it != table.end();) { auto *object = it->second; if (!EcmaStringAccessor(object).IsConstantString()) { ++it; continue; } auto constantStr = ConstantString::Cast(object); if (constantStr->GetEntityId() < 0 || !jsPandaFile->Contain(constantStr->GetConstantData())) { // EntityId is -1, which means this str has been relocated. Or the data is not in pandafile. ++it; continue; } uint32_t id = constantStr->GetEntityIdU32(); panda_file::File::StringData sd = jsPandaFile->GetStringData(EntityId(id)); if (constantStr->GetConstantData() != sd.data) { LOG_ECMA(ERROR) << "ConstantString data pointer is inconsistent with sd.data"; ++it; continue; } uint32_t strLen = sd.utf16_length; if (UNLIKELY(strLen == 0)) { it->second = *(vm->GetFactory()->GetEmptyString()); } size_t byteLength = sd.is_ascii ? 1 : sizeof(uint16_t); JSMutableHandle newData(vm->GetJSThread(), JSTaggedValue::Undefined()); newData.Update(vm->GetFactory()->NewByteArray( strLen, byteLength, reinterpret_cast(const_cast(sd.data)), MemSpaceType::SHARED_NON_MOVABLE)); constantStr->SetRelocatedData(thread, newData.GetTaggedValue()); constantStr->SetConstantData(static_cast(newData->GetData())); constantStr->SetEntityId(-1); ++it; } } } bool EcmaStringTable::CheckStringTableValidity(JSThread *thread) { #if ECMASCRIPT_ENABLE_SCOPE_LOCK_STAT auto vm = thread->GetEcmaVM(); if (vm->IsCollectingScopeLockStats()) { vm->IncreaseStringTableLockCount(); } #endif for (auto &[table, mutex] : stringTable_) { RuntimeLockHolder locker(thread, mutex); for (auto itemOuter = table.begin(); itemOuter != table.end(); ++itemOuter) { auto outerString = itemOuter->second; if (!EcmaStringAccessor(outerString).NotTreeString()) { return false; } int counter = 0; auto hashcode = EcmaStringAccessor(outerString).GetHashcode(); auto range = table.equal_range(hashcode); for (auto it = range.first; it != range.second; ++it) { auto foundString = it->second; counter += EcmaStringAccessor::StringsAreEqual(foundString, outerString) ? 1 : 0; } if (counter > 1) { return false; } } } return true; } JSTaggedValue SingleCharTable::CreateSingleCharTable(JSThread *thread) { auto table = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(MAX_ONEBYTE_CHARCODE, JSTaggedValue::Undefined(), MemSpaceType::SHARED_NON_MOVABLE); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); for (uint32_t i = 1; i < MAX_ONEBYTE_CHARCODE; ++i) { std::string tmp(1, i + 0X00); // 1: size table->Set(thread, i, factory->NewFromASCIIReadOnly(tmp).GetTaggedValue()); } return table.GetTaggedValue(); } } // namespace panda::ecmascript