* 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.
*/
#ifndef ECMASCRIPT_STRING_TABLE_H
#define ECMASCRIPT_STRING_TABLE_H
#include <array>
#include "common_components/taskpool/task.h"
#include "ecmascript/daemon/daemon_thread.h"
#include "ecmascript/ecma_string.h"
#include "ecmascript/js_tagged_value.h"
#include "ecmascript/mem/c_containers.h"
#include "ecmascript/mem/space.h"
#include "ecmascript/mem/visitor.h"
#include "ecmascript/platform/mutex.h"
#include "ecmascript/tagged_array.h"
#include "ecmascript/string/base_string_table.h"
#include "ecmascript/string/base_string.h"
#include "ecmascript/string/hashtriemap.h"
#include "ecmascript/string/string_table_internal.h"
namespace panda::ecmascript {
class EcmaString;
class EcmaVM;
class JSPandaFile;
class JSThread;
class EcmaStringTable;
class EcmaStringTableCleaner {
public:
using IteratorPtr = std::shared_ptr<std::atomic<uint32_t>>;
EcmaStringTableCleaner(EcmaStringTable *stringTable) : stringTable_(stringTable) {}
~EcmaStringTableCleaner()
{
stringTable_ = nullptr;
}
void PostSweepWeakRefTask(const WeakRootVisitor &visitor);
void JoinAndWaitSweepWeakRefTask(const WeakRootVisitor &visitor);
void WaitSweepWeakRefTaskFinished();
void PostConcurrentSweepWeakRefTask(const WeakRootVisitor &visitor);
void WaitConcurrentSweepWeakRefTaskAndSuspend(JSThread *thread);
void WaitConcurrentSweepWeakRefTaskAndSuspendByDaemonThread(DaemonThread *dThread);
bool IsEnableConcurrentSweep() const
{
return enableConcurrentSweep_;
}
void SetEnableConcurrentSweep(bool enableConcurrentSweep)
{
enableConcurrentSweep_ = enableConcurrentSweep;
}
private:
NO_COPY_SEMANTIC(EcmaStringTableCleaner);
NO_MOVE_SEMANTIC(EcmaStringTableCleaner);
static void ProcessSweepWeakRef(IteratorPtr &iter, EcmaStringTableCleaner *cleaner, const WeakRootVisitor &visitor);
static void ProcessConcurrentSweepWeakRef(IteratorPtr &iter, EcmaStringTableCleaner *cleaner,
const WeakRootVisitor &visitor);
void StartSweepWeakRefTask();
void WaitSweepWeakRefTask();
void WaitConcurrentSweepWeakRefTask();
void SignalSweepWeakRefTaskFinish();
void SignalSweepWeakRefTaskPending();
void SuspendAllAndFinishSweeping(JSThread *thread);
void SuspendAllAndFinishSweepingByDaemonThread(DaemonThread *dThread);
void ProcessCheckAndFreeHeadEntries();
static inline uint32_t GetNextIndexId(IteratorPtr &iter)
{
return iter->fetch_add(1U, std::memory_order_relaxed);
}
static inline bool ReduceCountAndCheckFinish(EcmaStringTableCleaner *cleaner)
{
return (cleaner->PendingTaskCount_.fetch_sub(1U, std::memory_order_relaxed) == 1U);
}
class SweepWeakRefTask : public common::Task {
public:
SweepWeakRefTask(IteratorPtr iter, EcmaStringTableCleaner *cleaner, const WeakRootVisitor &visitor)
: common::Task(0), iter_(iter), cleaner_(cleaner), visitor_(visitor)
{
}
~SweepWeakRefTask() = default;
bool Run(uint32_t threadIndex) override;
NO_COPY_SEMANTIC(SweepWeakRefTask);
NO_MOVE_SEMANTIC(SweepWeakRefTask);
private:
IteratorPtr iter_;
EcmaStringTableCleaner *cleaner_;
const WeakRootVisitor &visitor_;
};
class ConcurrentSweepWeakRefTask : public common::Task {
public:
ConcurrentSweepWeakRefTask(IteratorPtr iter, EcmaStringTableCleaner *cleaner, const WeakRootVisitor &visitor)
: common::Task(0), iter_(iter), cleaner_(cleaner), visitor_(visitor)
{
}
~ConcurrentSweepWeakRefTask() = default;
bool Run(uint32_t threadIndex) override;
NO_COPY_SEMANTIC(ConcurrentSweepWeakRefTask);
NO_MOVE_SEMANTIC(ConcurrentSweepWeakRefTask);
private:
IteratorPtr iter_;
EcmaStringTableCleaner *cleaner_;
WeakRootVisitor visitor_;
};
enum class SweepState {
SWEEPING,
PENDING,
FINISHED
};
IteratorPtr iter_;
EcmaStringTable *stringTable_;
std::atomic<uint32_t> PendingTaskCount_ {0U};
bool enableConcurrentSweep_ {false};
std::array<std::vector<HashTrieMapEntry *>, TrieMapConfig::ROOT_SIZE> waitFreeEntries_ {};
std::array<std::vector<HashTrieMapSlotCheckInfo>, TrieMapConfig::ROOT_SIZE> waitCheckAndFreeHeadEntries_ {};
Mutex sweepWeakRefMutex_;
SweepState sweepWeakRefFinished_ { SweepState::FINISHED };
ConditionVariable sweepWeakRefCV_;
};
class EcmaStringTableMutex {
public:
explicit EcmaStringTableMutex(bool is_init = true) : mtx_(is_init)
{
}
void LockWithThreadState(JSThread* thread);
void Lock()
{
return mtx_.Lock();
}
void Unlock()
{
return mtx_.Unlock();
}
private:
Mutex mtx_;
};
struct EnableCMCGCTrait {
using StringTableInterface = BaseStringTableInterface<BaseStringTableImpl>;
using HashTrieMapType = HashTrieMap<BaseStringTableMutex>;
using HashTrieMapInUseScopeType = HashTrieMapInUseScope<BaseStringTableMutex>;
#ifndef GC_STW_STRINGTABLE
using HashTrieMapOperationType = HashTrieMapOperation<BaseStringTableMutex, common::ThreadHolder,
TrieMapConfig::NeedSlotBarrierCMC>;
static constexpr bool ConcurrentSweep = true;
#else
using HashTrieMapOperationType = HashTrieMapOperation<BaseStringTableMutex, common::ThreadHolder,
TrieMapConfig::NoSlotBarrier>;
static constexpr bool ConcurrentSweep = false;
#endif
using ThreadType = common::ThreadHolder;
static constexpr bool EnableCMCGC = true;
static common::ReadOnlyHandle<BaseString> CreateHandle(ThreadType* holder, BaseString* string)
{
return JSHandle<EcmaString>(holder->GetJSThread(), EcmaString::FromBaseString(string));
}
};
struct DisableCMCGCNormalTrait {
struct DummyStringTableInterface {};
using StringTableInterface = DummyStringTableInterface;
using HashTrieMapType = HashTrieMap<EcmaStringTableMutex>;
using HashTrieMapInUseScopeType = HashTrieMapInUseScope<EcmaStringTableMutex>;
using HashTrieMapOperationType = HashTrieMapOperation<EcmaStringTableMutex, JSThread,
TrieMapConfig::NoSlotBarrierDynamic>;
using ThreadType = JSThread;
static constexpr bool EnableCMCGC = false;
static constexpr bool ConcurrentSweep = false;
static common::ReadOnlyHandle<BaseString> CreateHandle(ThreadType* holder, BaseString* string)
{
return JSHandle<EcmaString>(holder, EcmaString::FromBaseString(string));
}
};
struct DisableCMCGCConcurrentSweepTrait {
struct DummyStringTableInterface {};
using StringTableInterface = DummyStringTableInterface;
using HashTrieMapType = HashTrieMap<EcmaStringTableMutex>;
using HashTrieMapInUseScopeType = HashTrieMapInUseScope<EcmaStringTableMutex>;
using HashTrieMapOperationType = HashTrieMapOperation<EcmaStringTableMutex, JSThread,
TrieMapConfig::NeedSlotBarrier>;
using ThreadType = JSThread;
static constexpr bool EnableCMCGC = false;
static constexpr bool ConcurrentSweep = true;
static common::ReadOnlyHandle<BaseString> CreateHandle(ThreadType* holder, BaseString* string)
{
return JSHandle<EcmaString>(holder, EcmaString::FromBaseString(string));
}
};
static_assert(std::is_same_v<DisableCMCGCNormalTrait::HashTrieMapType,
DisableCMCGCConcurrentSweepTrait::HashTrieMapType>);
class EcmaStringTableImpl final {
public:
EcmaStringTableImpl() {}
#ifdef USE_CMC_GC
void Initialize(void* hashTrieMap, void* itf)
{
hashTrieMap_ = hashTrieMap;
stringTableItf_ = itf;
}
#endif
template <typename Traits>
EcmaString *GetOrInternFlattenString(EcmaVM *vm, EcmaString *string);
template <typename Traits>
EcmaString *GetOrInternFlattenStringNoGC(EcmaVM *vm, EcmaString *string);
template <typename Traits>
EcmaString *GetOrInternStringFromCompressedSubString(EcmaVM *vm, const JSHandle<EcmaString> &string,
uint32_t offset, uint32_t utf8Len);
template <typename Traits>
EcmaString *GetOrInternString(EcmaVM *vm, EcmaString *string);
template <typename Traits, typename LoaderCallback, typename EqualsCallback>
EcmaString *GetOrInternString(EcmaVM *vm, uint32_t hashcode, LoaderCallback loaderCallback,
EqualsCallback equalsCallback);
template <typename Traits>
EcmaString *GetOrInternString(EcmaVM *vm, const JSHandle<EcmaString> &firstString,
const JSHandle<EcmaString> &secondString);
template <typename Traits>
EcmaString *GetOrInternString(EcmaVM *vm, const uint8_t *utf8Data, uint32_t utf8Len, bool canBeCompress,
MemSpaceType type = MemSpaceType::SHARED_OLD_SPACE);
template <typename Traits>
EcmaString *GetOrInternString(EcmaVM *vm, const uint8_t *utf8Data, uint32_t utf16Len, MemSpaceType type);
template <typename Traits>
EcmaString *GetOrInternString(EcmaVM *vm, const uint16_t *utf16Data, uint32_t utf16Len, bool canBeCompress);
template <typename Traits>
EcmaString *GetOrInternStringWithoutJSHandleForJit(EcmaVM *vm, const uint8_t *utf8Data, uint32_t utf16Len,
MemSpaceType type);
template <typename Traits>
EcmaString *GetOrInternStringWithoutJSHandleForJit(EcmaVM *vm, const uint8_t *utf8Data, uint32_t utf8Len,
bool canBeCompress, MemSpaceType type);
template <typename Traits>
EcmaString *TryGetInternString(JSThread *thread, const JSHandle<EcmaString> &string);
template <typename Traits, std::enable_if_t<!Traits::ConcurrentSweep, int> = 0>
void SweepWeakRef(const WeakRootVisitor &visitor, uint32_t rootID);
template <typename Traits, std::enable_if_t<Traits::ConcurrentSweep, int> = 0>
void ConcurrentSweepWeakRef(const WeakRootVisitor &visitor, uint32_t rootID,
std::vector<HashTrieMapEntry*>& waitDeleteEntries,
std::vector<HashTrieMapSlotCheckInfo>& waitCheckAndFreeHeadEntries);
template <typename Traits>
bool CheckStringTableValidity(JSThread *thread);
NO_COPY_SEMANTIC(EcmaStringTableImpl);
NO_MOVE_SEMANTIC(EcmaStringTableImpl);
*
* These are some "incorrect" functions, which need to fix the call chain to be removed.
*
*/
template <typename Traits>
EcmaString *GetOrInternStringThreadUnsafe(EcmaVM *vm, const JSHandle<EcmaString> firstString,
const JSHandle<EcmaString> secondString);
template <typename Traits>
EcmaString* GetOrInternStringThreadUnsafe(EcmaVM* vm, const uint8_t* utf8Data, uint32_t utf8Len,
bool canBeCompress);
#ifdef USE_CMC_GC
void *GetHashTrieMap() const
{
return hashTrieMap_;
}
#else
DisableCMCGCNormalTrait::HashTrieMapType *GetHashTrieMap() const
{
return const_cast<DisableCMCGCNormalTrait::HashTrieMapType *>(&hashTrieMap_);
}
#endif
private:
template <typename Traits>
typename Traits::ThreadType* GetThreadHolder(JSThread* thread);
#ifdef USE_CMC_GC
void* hashTrieMap_ = nullptr;
[[maybe_unused]] void* stringTableItf_ = nullptr;
#else
DisableCMCGCNormalTrait::HashTrieMapType hashTrieMap_;
#endif
};
class EcmaStringTable final {
public:
EcmaStringTable(bool enableCMC, [[maybe_unused]] void* itf = nullptr, [[maybe_unused]] void* map = nullptr)
: enableCMCGC_(enableCMC)
{
#ifdef USE_CMC_GC
if (enableCMC) {
ASSERT(map != nullptr);
impl_.Initialize(map, itf);
} else {
if (map == nullptr) {
map = new DisableCMCGCNormalTrait::HashTrieMapType();
needDeleteHashTrieMap_ = true;
}
impl_.Initialize(map, nullptr);
cleaner_ = new EcmaStringTableCleaner(this);
}
#else
cleaner_ = new EcmaStringTableCleaner(this);
#endif
}
~EcmaStringTable()
{
if (cleaner_ != nullptr) {
delete cleaner_;
cleaner_ = nullptr;
}
#ifdef USE_CMC_GC
if (needDeleteHashTrieMap_) {
ASSERT(!enableCMCGC_);
delete reinterpret_cast<DisableCMCGCNormalTrait::HashTrieMapType *>(impl_.GetHashTrieMap());
}
#endif
}
EcmaString *GetOrInternFlattenString(EcmaVM *vm, EcmaString *string);
EcmaString *GetOrInternFlattenStringNoGC(EcmaVM *vm, EcmaString *string);
EcmaString *GetOrInternStringFromCompressedSubString(EcmaVM *vm, const JSHandle<EcmaString> &string,
uint32_t offset, uint32_t utf8Len);
EcmaString *GetOrInternString(EcmaVM *vm, EcmaString *string);
template <typename LoaderCallback, typename EqualsCallback>
EcmaString *GetOrInternString(EcmaVM *vm, uint32_t hashcode, LoaderCallback loaderCallback,
EqualsCallback equalsCallback);
EcmaString *GetOrInternString(EcmaVM *vm, const JSHandle<EcmaString> &firstString,
const JSHandle<EcmaString> &secondString);
EcmaString *GetOrInternString(EcmaVM *vm, const uint8_t *utf8Data, uint32_t utf8Len, bool canBeCompress,
MemSpaceType type = MemSpaceType::SHARED_OLD_SPACE);
EcmaString *GetOrInternString(EcmaVM *vm, const uint8_t *utf8Data, uint32_t utf16Len, MemSpaceType type);
EcmaString *GetOrInternString(EcmaVM *vm, const uint16_t *utf16Data, uint32_t utf16Len, bool canBeCompress);
EcmaString *GetOrInternStringWithoutJSHandleForJit(EcmaVM *vm, const uint8_t *utf8Data, uint32_t utf16Len,
MemSpaceType type);
EcmaString *GetOrInternStringWithoutJSHandleForJit(EcmaVM *vm, const uint8_t *utf8Data, uint32_t utf8Len,
bool canBeCompress, MemSpaceType type);
EcmaString *TryGetInternString(JSThread *thread, const JSHandle<EcmaString> &string);
void SweepWeakRef(const WeakRootVisitor &visitor, uint32_t index);
void ConcurrentSweepWeakRef(const WeakRootVisitor &visitor, uint32_t index,
std::vector<HashTrieMapEntry*>& waitDeleteEntries,
std::vector<HashTrieMapSlotCheckInfo>& waitCheckAndFreeHeadEntries);
bool CheckStringTableValidity(JSThread *thread);
EcmaStringTableCleaner *GetCleaner()
{
ASSERT(!enableCMCGC_ && "EcmaStringTableCleaner should not be used when cmcgc enabled");
return cleaner_;
}
void WaitSweepWeakRefTaskFinished()
{
#ifdef USE_CMC_GC
if (enableCMCGC_) {
return;
}
#endif
cleaner_->WaitSweepWeakRefTaskFinished();
}
void TransferToNativeAndWaitSweepWeakRefTaskFinished(JSThread *thread);
bool IsSweeping() const
{
#ifdef USE_CMC_GC
if (enableCMCGC_) {
return reinterpret_cast<EnableCMCGCTrait::HashTrieMapType *>(impl_.GetHashTrieMap())->IsSweeping();
}
#endif
return reinterpret_cast<DisableCMCGCNormalTrait::HashTrieMapType *>(impl_.GetHashTrieMap())->IsSweeping();
}
bool IsInUse() const
{
#ifdef USE_CMC_GC
if (enableCMCGC_) {
return reinterpret_cast<EnableCMCGCTrait::HashTrieMapType *>(impl_.GetHashTrieMap())->GetInuseCount() > 0;
}
#endif
return reinterpret_cast<DisableCMCGCNormalTrait::HashTrieMapType *>(
impl_.GetHashTrieMap())->GetInuseCount() > 0;
}
void *GetHashTrieMap() const
{
return impl_.GetHashTrieMap();
}
private:
NO_COPY_SEMANTIC(EcmaStringTable);
NO_MOVE_SEMANTIC(EcmaStringTable);
*
* These are some "incorrect" functions, which need to fix the call chain to be removed.
*
*/
EcmaString *GetOrInternStringThreadUnsafe(EcmaVM *vm, const JSHandle<EcmaString> firstString,
const JSHandle<EcmaString> secondString);
EcmaString* GetOrInternStringThreadUnsafe(EcmaVM* vm, const uint8_t* utf8Data, uint32_t utf8Len,
bool canBeCompress);
EcmaStringTableImpl impl_;
EcmaStringTableCleaner *cleaner_ = nullptr;
bool enableCMCGC_ = false;
#ifdef USE_CMC_GC
bool needDeleteHashTrieMap_ = false;
#endif
friend class SnapshotProcessor;
friend class BaseDeserializer;
};
class SingleCharTable : public TaggedArray {
public:
static SingleCharTable *Cast(TaggedObject *object)
{
return reinterpret_cast<SingleCharTable*>(object);
}
static JSTaggedValue CreateSingleCharTable(JSThread *thread);
JSTaggedValue GetStringFromSingleCharTable(JSThread *thread, int32_t ch)
{
return Get(thread, ch);
}
private:
SingleCharTable() = default;
~SingleCharTable() = default;
NO_COPY_SEMANTIC(SingleCharTable);
NO_MOVE_SEMANTIC(SingleCharTable);
static constexpr uint32_t MAX_ONEBYTE_CHARCODE = 128;
};
}
#endif