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 EcmaString *GetOrInternFlattenString(EcmaVM *vm, EcmaString *string); 111 EcmaString *GetOrInternStringFromCompressedSubString(EcmaVM *vm, const JSHandle<EcmaString> &string, 112 uint32_t offset, uint32_t utf8Len); 113 EcmaString *GetOrInternString(EcmaVM *vm, EcmaString *string); 114 EcmaString *GetOrInternString(EcmaVM *vm, 115 const JSHandle<EcmaString> &firstString, const JSHandle<EcmaString> &secondString); 116 EcmaString *GetOrInternString(EcmaVM *vm, const uint8_t *utf8Data, uint32_t utf8Len, bool canBeCompress, 117 MemSpaceType type = MemSpaceType::SHARED_OLD_SPACE); 118 EcmaString *GetOrInternString(EcmaVM *vm, const uint8_t *utf8Data, uint32_t utf8Len, bool canBeCompress, 119 MemSpaceType type, bool isConstantString, uint32_t idOffset); 120 EcmaString *GetOrInternString(EcmaVM *vm, const uint8_t *utf8Data, uint32_t utf16Len, MemSpaceType type); 121 EcmaString *GetOrInternString(EcmaVM *vm, const uint16_t *utf16Data, uint32_t utf16Len, bool canBeCompress); 122 // This is ONLY for JIT Thread, since JIT could not create JSHandle so need to allocate String with holding 123 // lock_ --- need to support JSHandle 124 EcmaString *GetOrInternStringWithoutJSHandleForJit(EcmaVM *vm, const uint8_t *utf8Data, uint32_t utf16Len, 125 MemSpaceType type); 126 EcmaString *GetOrInternStringWithoutJSHandleForJit(EcmaVM *vm, const uint8_t *utf8Data, uint32_t utf8Len, 127 bool canBeCompress, MemSpaceType type, bool isConstantString, uint32_t idOffset); 128 EcmaString *TryGetInternString(JSThread *thread, const JSHandle<EcmaString> &string); 129 EcmaString *InsertStringToTable(EcmaVM *vm, const JSHandle<EcmaString> &strHandle); 130 131 void SweepWeakRef(const WeakRootVisitor &visitor); 132 void SweepWeakRef(const WeakRootVisitor &visitor, uint32_t tableId); 133 134 bool CheckStringTableValidity(JSThread *thread); 135 void RelocateConstantData(EcmaVM *vm, const JSPandaFile *jsPandaFile); 136 GetCleaner()137 EcmaStringTableCleaner* GetCleaner() 138 { 139 return cleaner_; 140 } 141 static constexpr uint32_t SEGMENT_COUNT = 16U; // 16: 2^4 142 static constexpr uint32_t SEGMENT_MASK = SEGMENT_COUNT - 1U; 143 private: 144 NO_COPY_SEMANTIC(EcmaStringTable); 145 NO_MOVE_SEMANTIC(EcmaStringTable); 146 147 EcmaString *GetStringThreadUnsafe(EcmaString *string, uint32_t hashcode) const; 148 void InternStringThreadUnsafe(EcmaString *string, uint32_t hashcode); 149 EcmaString *AtomicGetOrInternStringImpl(JSThread *thread, const JSHandle<EcmaString> string, uint32_t hashcode); 150 151 EcmaString *GetStringFromCompressedSubString(JSThread *thread, const JSHandle<EcmaString> string, uint32_t offset, 152 uint32_t utf8Len, uint32_t hashcode); 153 EcmaString *GetString(JSThread *thread, const JSHandle<EcmaString> string, uint32_t hashcode); 154 EcmaString *GetString(JSThread *thread, const JSHandle<EcmaString> firstString, 155 const JSHandle<EcmaString> secondString, uint32_t hashcode); 156 // utf8Data MUST NOT on JSHeap 157 EcmaString *GetString(JSThread *thread, const uint8_t *utf8Data, uint32_t utf8Len, bool canBeCompress, 158 uint32_t hashcode); 159 // utf16Data MUST NOT on JSHeap 160 EcmaString *GetString(JSThread *thread, const uint16_t *utf16Data, uint32_t utf16Len, uint32_t hashcode); 161 162 // This used only for SnapShot. 163 void InsertStringToTableWithHashThreadUnsafe(EcmaString* string, uint32_t hashcode); 164 /** 165 * 166 * These are some "incorrect" functions, whcih need to fix the call chain to be removed. 167 * 168 */ 169 // This should only call in Debugger Signal, and need to fix and remove 170 EcmaString *GetOrInternStringThreadUnsafe(EcmaVM *vm, 171 const JSHandle<EcmaString> firstString, 172 const JSHandle<EcmaString> secondString); 173 // This should only call in Debugger Signal, and need to fix and remove 174 EcmaString *GetOrInternStringThreadUnsafe(EcmaVM *vm, const uint8_t *utf8Data, uint32_t utf8Len, 175 bool canBeCompress); 176 // This should only call in Debugger Signal, and need to fix and remove 177 EcmaString *GetStringThreadUnsafe(const JSHandle<EcmaString> firstString, const JSHandle<EcmaString> secondString, 178 uint32_t hashcode) const; 179 // This should only call in Debugger Signal or from JIT, and need to fix and remove 180 EcmaString *GetStringThreadUnsafe(const uint8_t *utf8Data, uint32_t utf8Len, bool canBeCompress, 181 uint32_t hashcode) const; 182 // This should only call in JIT Thread, and need to fix and remove 183 EcmaString *GetStringThreadUnsafe(const uint16_t *utf16Data, uint32_t utf16Len, uint32_t hashcode) const; 184 185 struct Segment { 186 CUnorderedMultiMap<uint32_t, EcmaString *> table_; 187 Mutex mutex_; 188 }; 189 190 std::array<Segment, SEGMENT_COUNT> stringTable_; 191 EcmaStringTableCleaner* cleaner_; 192 193 friend class SnapshotProcessor; 194 friend class BaseDeserializer; 195 }; 196 197 class SingleCharTable : public TaggedArray { 198 public: Cast(TaggedObject * object)199 static SingleCharTable *Cast(TaggedObject *object) 200 { 201 return reinterpret_cast<SingleCharTable*>(object); 202 } 203 static JSTaggedValue CreateSingleCharTable(JSThread *thread); GetStringFromSingleCharTable(int32_t ch)204 JSTaggedValue GetStringFromSingleCharTable(int32_t ch) 205 { 206 return Get(ch); 207 } 208 private: 209 SingleCharTable() = default; 210 ~SingleCharTable() = default; 211 NO_COPY_SEMANTIC(SingleCharTable); 212 NO_MOVE_SEMANTIC(SingleCharTable); 213 static constexpr uint32_t MAX_ONEBYTE_CHARCODE = 128; // 0X00-0X7F 214 }; 215 } // namespace panda::ecmascript 216 217 #endif // ECMASCRIPT_STRING_TABLE_H 218