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 #ifndef ECMASCRIPT_STRING_TABLE_H 17 #define ECMASCRIPT_STRING_TABLE_H 18 19 #include "ecmascript/js_tagged_value.h" 20 #include "ecmascript/mem/c_containers.h" 21 #include "ecmascript/mem/space.h" 22 #include "ecmascript/mem/visitor.h" 23 #include "ecmascript/platform/mutex.h" 24 #include "ecmascript/tagged_array.h" 25 #include "ecmascript/taskpool/task.h" 26 27 namespace panda::ecmascript { 28 class EcmaString; 29 class EcmaVM; 30 class JSPandaFile; 31 class JSThread; 32 33 class EcmaStringTable; 34 35 class EcmaStringTableCleaner { 36 public: 37 using IteratorPtr = std::shared_ptr<std::atomic<uint32_t>>; EcmaStringTableCleaner(EcmaStringTable * stringTable)38 EcmaStringTableCleaner(EcmaStringTable* stringTable) : stringTable_(stringTable) {} ~EcmaStringTableCleaner()39 ~EcmaStringTableCleaner() { stringTable_ = nullptr; } 40 41 void PostSweepWeakRefTask(const WeakRootVisitor &visitor); 42 void JoinAndWaitSweepWeakRefTask(const WeakRootVisitor &visitor); 43 44 private: 45 NO_COPY_SEMANTIC(EcmaStringTableCleaner); 46 NO_MOVE_SEMANTIC(EcmaStringTableCleaner); 47 48 static void ProcessSweepWeakRef(IteratorPtr& iter, EcmaStringTableCleaner *cleaner, const WeakRootVisitor &visitor); 49 void StartSweepWeakRefTask(); 50 void WaitSweepWeakRefTask(); 51 void SignalSweepWeakRefTask(); 52 GetNextTableId(IteratorPtr & iter)53 static inline uint32_t GetNextTableId(IteratorPtr& iter) 54 { 55 return iter->fetch_add(1U, std::memory_order_relaxed); 56 } 57 ReduceCountAndCheckFinish(EcmaStringTableCleaner * cleaner)58 static inline bool ReduceCountAndCheckFinish(EcmaStringTableCleaner* cleaner) 59 { 60 return (cleaner->PendingTaskCount_.fetch_sub(1U, std::memory_order_relaxed) == 1U); 61 } 62 63 class SweepWeakRefTask : public Task { 64 public: SweepWeakRefTask(IteratorPtr iter,EcmaStringTableCleaner * cleaner,const WeakRootVisitor & visitor)65 SweepWeakRefTask(IteratorPtr iter, EcmaStringTableCleaner* cleaner, const WeakRootVisitor& visitor) 66 : Task(0), iter_(iter), cleaner_(cleaner), visitor_(visitor) {} 67 ~SweepWeakRefTask() = default; 68 69 bool Run(uint32_t threadIndex) override; 70 71 NO_COPY_SEMANTIC(SweepWeakRefTask); 72 NO_MOVE_SEMANTIC(SweepWeakRefTask); 73 74 private: 75 IteratorPtr iter_; 76 EcmaStringTableCleaner* cleaner_; 77 const WeakRootVisitor& visitor_; 78 }; 79 80 IteratorPtr iter_; 81 EcmaStringTable* stringTable_; 82 std::atomic<uint32_t> PendingTaskCount_ {0U}; 83 Mutex sweepWeakRefMutex_; 84 bool sweepWeakRefFinished_ {true}; 85 ConditionVariable sweepWeakRefCV_; 86 }; 87 88 class EcmaStringTable { 89 public: EcmaStringTable()90 EcmaStringTable() : cleaner_(new EcmaStringTableCleaner(this)) 91 { 92 stringTable_.fill(Segment()); 93 } ~EcmaStringTable()94 virtual ~EcmaStringTable() 95 { 96 if (cleaner_ != nullptr) { 97 delete cleaner_; 98 cleaner_ = nullptr; 99 } 100 for (auto &seg : stringTable_) { 101 seg.table_.clear(); 102 } 103 } 104 GetTableId(uint32_t hashcode)105 static inline uint32_t GetTableId(uint32_t hashcode) 106 { 107 return hashcode & SEGMENT_MASK; 108 } 109 void InternEmptyString(JSThread *thread, EcmaString *emptyStr); 110 EcmaString *GetOrInternString(EcmaVM *vm, 111 const JSHandle<EcmaString> &firstString, 112 const JSHandle<EcmaString> &secondString); 113 EcmaString *GetOrInternStringWithoutLock(JSThread *thread, 114 const JSHandle<EcmaString> &firstString, 115 const JSHandle<EcmaString> &secondString, uint32_t hashcode); 116 EcmaString *GetOrInternString(EcmaVM *vm, const uint8_t *utf8Data, uint32_t utf8Len, bool canBeCompress); 117 EcmaString *GetOrInternStringWithoutLock(EcmaVM *vm, const uint8_t *utf8Data, uint32_t utf8Len, 118 bool canBeCompress, uint32_t hashcode); 119 EcmaString *CreateAndInternStringNonMovable(EcmaVM *vm, const uint8_t *utf8Data, uint32_t utf8Len); 120 EcmaString *CreateAndInternStringReadOnly(EcmaVM *vm, const uint8_t *utf8Data, uint32_t utf8Len, 121 bool canBeCompress); 122 EcmaString *GetOrInternString(EcmaVM *vm, const uint16_t *utf16Data, uint32_t utf16Len, bool canBeCompress); 123 EcmaString *GetOrInternString(EcmaVM *vm, EcmaString *string); 124 EcmaString *GetOrInternCompressedSubString(EcmaVM *vm, const JSHandle<EcmaString> &string, 125 uint32_t offset, uint32_t utf8Len); 126 EcmaString *GetOrInternStringWithSpaceType(EcmaVM *vm, const uint8_t *utf8Data, uint32_t utf8Len, 127 bool canBeCompress, MemSpaceType type, bool isConstantString, uint32_t idOffset); 128 EcmaString *GetOrInternStringWithSpaceType(EcmaVM *vm, const uint8_t *utf8Data, uint32_t utf16Len, 129 MemSpaceType type); 130 EcmaString *GetOrInternStringWithSpaceTypeWithoutJSHandle(EcmaVM *vm, const uint8_t *utf8Data, uint32_t utf16Len, 131 MemSpaceType type); 132 EcmaString *TryGetInternString(JSThread *thread, const JSHandle<EcmaString> &string); 133 void InsertStringToTableWithHashThreadUnsafe(EcmaString* string, uint32_t hashcode); 134 EcmaString *InsertStringToTable(EcmaVM *vm, const JSHandle<EcmaString> &strHandle); 135 136 void SweepWeakRef(const WeakRootVisitor &visitor); 137 void SweepWeakRef(const WeakRootVisitor &visitor, uint32_t tableId); 138 139 bool CheckStringTableValidity(JSThread *thread); 140 void RelocateConstantData(EcmaVM *vm, const JSPandaFile *jsPandaFile); 141 GetCleaner()142 EcmaStringTableCleaner* GetCleaner() 143 { 144 return cleaner_; 145 } 146 static constexpr uint32_t SEGMENT_COUNT = 16U; // 16: 2^4 147 static constexpr uint32_t SEGMENT_MASK = SEGMENT_COUNT - 1U; 148 private: 149 NO_COPY_SEMANTIC(EcmaStringTable); 150 NO_MOVE_SEMANTIC(EcmaStringTable); 151 152 std::pair<EcmaString *, uint32_t> GetStringThreadUnsafe(const JSHandle<EcmaString> &firstString, 153 const JSHandle<EcmaString> &secondString, 154 uint32_t hashcode) const; 155 std::pair<EcmaString *, uint32_t> GetStringThreadUnsafe(const uint8_t *utf8Data, uint32_t utf8Len, 156 bool canBeCompress, uint32_t hashcode) const; 157 std::pair<EcmaString *, uint32_t> GetStringThreadUnsafe(const uint16_t *utf16Data, 158 uint32_t utf16Len, uint32_t hashcode) const; 159 EcmaString *GetStringWithHashThreadUnsafe(EcmaString *string, uint32_t hashcode) const; 160 EcmaString *GetStringThreadUnsafe(EcmaString *string, uint32_t hashcode) const; 161 162 void InternStringThreadUnsafe(EcmaString *string, uint32_t hashcode); 163 EcmaString *GetOrInternStringThreadUnsafe(EcmaVM *vm, EcmaString *string); 164 165 void InsertStringIfNotExistThreadUnsafe(EcmaString *string); 166 167 struct Segment { 168 CUnorderedMultiMap<uint32_t, EcmaString *> table_; 169 Mutex mutex_; 170 }; 171 172 std::array<Segment, SEGMENT_COUNT> stringTable_; 173 EcmaStringTableCleaner* cleaner_; 174 175 friend class SnapshotProcessor; 176 friend class BaseDeserializer; 177 }; 178 179 class SingleCharTable : public TaggedArray { 180 public: Cast(TaggedObject * object)181 static SingleCharTable *Cast(TaggedObject *object) 182 { 183 return reinterpret_cast<SingleCharTable*>(object); 184 } 185 static JSTaggedValue CreateSingleCharTable(JSThread *thread); GetStringFromSingleCharTable(int32_t ch)186 JSTaggedValue GetStringFromSingleCharTable(int32_t ch) 187 { 188 return Get(ch); 189 } 190 private: 191 SingleCharTable() = default; 192 ~SingleCharTable() = default; 193 NO_COPY_SEMANTIC(SingleCharTable); 194 NO_MOVE_SEMANTIC(SingleCharTable); 195 static constexpr uint32_t MAX_ONEBYTE_CHARCODE = 128; // 0X00-0X7F 196 }; 197 } // namespace panda::ecmascript 198 199 #endif // ECMASCRIPT_STRING_TABLE_H 200