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