1 /* 2 * Copyright (c) 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_RUNTIME_H 17 #define ECMASCRIPT_RUNTIME_H 18 19 #include "ecmascript/ecma_string_table.h" 20 #include "ecmascript/global_env_constants.h" 21 #include "ecmascript/js_runtime_options.h" 22 #include "ecmascript/js_thread.h" 23 #include "ecmascript/mem/heap.h" 24 #include "ecmascript/module/js_shared_module_manager.h" 25 #include "ecmascript/mutator_lock.h" 26 #include "ecmascript/platform/mutex.h" 27 #include "ecmascript/serializer/serialize_chunk.h" 28 29 #include "libpandabase/macros.h" 30 31 #include <list> 32 #include <memory> 33 34 namespace panda::ecmascript { 35 class Runtime { 36 public: 37 PUBLIC_API static Runtime *GetInstance(); 38 39 static void CreateIfFirstVm(const JSRuntimeOptions &options); 40 static void DestroyIfLastVm(); 41 void InitializeIfFirstVm(EcmaVM *vm); 42 43 void RegisterThread(JSThread* newThread); 44 void UnregisterThread(JSThread* thread); 45 46 void SuspendAll(JSThread *current); 47 void ResumeAll(JSThread *current); 48 void IterateSerializeRoot(const RootVisitor &v); 49 GetMainThread() const50 JSThread *GetMainThread() const 51 { 52 return mainThread_; 53 } 54 GetMutatorLock()55 MutatorLock *GetMutatorLock() 56 { 57 return &mutatorLock_; 58 } 59 GetMutatorLock() const60 const MutatorLock *GetMutatorLock() const 61 { 62 return &mutatorLock_; 63 } 64 65 template<class Callback> GCIterateThreadList(const Callback &cb)66 void GCIterateThreadList(const Callback &cb) 67 { 68 LockHolder lock(threadsLock_); 69 GCIterateThreadListWithoutLock(cb); 70 } 71 72 template<class Callback> GCIterateThreadListWithoutLock(const Callback &cb)73 void GCIterateThreadListWithoutLock(const Callback &cb) 74 { 75 for (auto thread : threads_) { 76 if (thread->ReadyForGCIterating()) { 77 cb(thread); 78 } 79 } 80 } 81 82 // Result may be inaccurate, just an approximate value. ApproximateThreadListSize()83 size_t ApproximateThreadListSize() 84 { 85 return threads_.size(); 86 } 87 GetGlobalEnvConstants()88 inline const GlobalEnvConstants *GetGlobalEnvConstants() 89 { 90 return &globalConst_; 91 } 92 SharedConstInited()93 inline bool SharedConstInited() 94 { 95 return sharedConstInited_; 96 } 97 GetGlobalEnv() const98 JSTaggedValue GetGlobalEnv() const 99 { 100 return globalEnv_; 101 } 102 GetEcmaStringTable() const103 inline EcmaStringTable *GetEcmaStringTable() const 104 { 105 return stringTable_.get(); 106 } 107 GetSerializeRootMapValue([[maybe_unused]] JSThread *thread, uint32_t dataIndex)108 inline SerializationChunk *GetSerializeRootMapValue([[maybe_unused]] JSThread *thread, 109 uint32_t dataIndex) 110 { 111 ASSERT(thread->IsInManagedState()); 112 LockHolder lock(serializeLock_); 113 auto iter = serializeRootMap_.find(dataIndex); 114 if (iter == serializeRootMap_.end()) { 115 return nullptr; 116 } 117 return iter->second.get(); 118 } 119 PushSerializationRoot([[maybe_unused]] JSThread *thread, std::unique_ptr<SerializationChunk> chunk)120 uint32_t PushSerializationRoot([[maybe_unused]] JSThread *thread, std::unique_ptr<SerializationChunk> chunk) 121 { 122 ASSERT(thread->IsInManagedState()); 123 LockHolder lock(serializeLock_); 124 uint32_t index = GetSerializeDataIndex(); 125 ASSERT(serializeRootMap_.find(index) == serializeRootMap_.end()); 126 serializeRootMap_.emplace(index, std::move(chunk)); 127 return index; 128 } 129 RemoveSerializationRoot([[maybe_unused]] JSThread *thread, uint32_t index)130 void RemoveSerializationRoot([[maybe_unused]] JSThread *thread, uint32_t index) 131 { 132 ASSERT(thread->IsInManagedState()); 133 LockHolder lock(serializeLock_); 134 ASSERT(serializeRootMap_.find(index) != serializeRootMap_.end()); 135 serializeRootMap_.erase(index); 136 serializeDataIndexVector_.emplace_back(index); 137 } 138 SharedGCRequest()139 static bool SharedGCRequest() 140 { 141 LockHolder lock(*vmCreationLock_); 142 destroyCount_++; 143 if (destroyCount_ == WORKER_DESTRUCTION_COUNT || vmCount_ < MIN_GC_TRIGGER_VM_COUNT) { 144 destroyCount_ = 0; 145 return true; 146 } else { 147 return false; 148 } 149 } 150 151 bool HasCachedConstpool(const JSPandaFile *jsPandaFile); 152 PUBLIC_API JSTaggedValue FindConstpool(const JSPandaFile *jsPandaFile, int32_t index); 153 JSTaggedValue FindConstpoolUnlocked(const JSPandaFile *jsPandaFile, int32_t index); 154 JSHandle<ConstantPool> AddOrUpdateConstpool(const JSPandaFile *jsPandaFile, 155 JSHandle<ConstantPool> constpool, 156 int32_t index = 0); 157 std::optional<std::reference_wrapper<CMap<int32_t, JSTaggedValue>>> FindConstpools( 158 const JSPandaFile *jsPandaFile); 159 void EraseUnusedConstpool(const JSPandaFile *jsPandaFile, int32_t index, int32_t constpoolIndex); 160 161 void ProcessNativeDeleteInSharedGC(const WeakRootVisitor &visitor); 162 163 void ProcessSharedNativeDelete(const WeakRootVisitor &visitor); 164 void InvokeSharedNativePointerCallbacks(); 165 void PushToSharedNativePointerList(JSNativePointer *pointer); 166 CreateStringCacheTable(uint32_t size)167 inline bool CreateStringCacheTable(uint32_t size) 168 { 169 constexpr int32_t MAX_SIZE = 150; 170 if ((size == 0) || (size > MAX_SIZE) || (externalRegisteredStringTable_ != nullptr)) { 171 LOG_ECMA(WARN) << "invalid size of the string cache table or the table has been registered."; 172 LOG_ECMA(WARN) << "Currently, maximum size of the table is " << MAX_SIZE; 173 return false; 174 } 175 176 externalRegisteredStringTable_ = new JSTaggedValue[size]; 177 if (externalRegisteredStringTable_ == nullptr) { 178 LOG_ECMA(ERROR) << "create string cache table failed"; 179 return false; 180 } 181 registeredStringTableSize_ = size; 182 return true; 183 } 184 SetCachedString(JSHandle<EcmaString> str, uint32_t propertyIndex)185 inline bool SetCachedString(JSHandle<EcmaString> str, uint32_t propertyIndex) 186 { 187 if (propertyIndex >= registeredStringTableSize_ || (externalRegisteredStringTable_ == nullptr)) { 188 LOG_ECMA(ERROR) << "invalid size of the string cache table or the table has never been registered."; 189 return false; 190 } 191 externalRegisteredStringTable_[propertyIndex] = str.GetTaggedValue(); 192 return true; 193 } 194 GetCachedString(JSThread *thread, uint32_t propertyIndex)195 inline JSHandle<EcmaString> GetCachedString(JSThread *thread, uint32_t propertyIndex) 196 { 197 if ((externalRegisteredStringTable_ == nullptr) || (propertyIndex >= registeredStringTableSize_)) { 198 LOG_ECMA(ERROR) << "invalid size of the string cache table or the table has never been registered."; 199 return JSHandle<EcmaString>(thread->GlobalConstants()->GetHandledEmptyString()); 200 } 201 return JSHandle<EcmaString>(reinterpret_cast<uintptr_t>(&externalRegisteredStringTable_[propertyIndex])); 202 } 203 HasCachedString(uint32_t propertyIndex)204 inline bool HasCachedString(uint32_t propertyIndex) 205 { 206 if ((externalRegisteredStringTable_ == nullptr) || propertyIndex >= registeredStringTableSize_) { 207 LOG_ECMA(ERROR) << "invalid size of the string cache table or the table has never been registered."; 208 return false; 209 } 210 211 if (externalRegisteredStringTable_[propertyIndex].GetRawData() != JSTaggedValue::NULL_POINTER) { 212 return true; 213 } 214 return false; 215 } 216 IterateCachedStringRoot(const RootRangeVisitor &v)217 void IterateCachedStringRoot(const RootRangeVisitor &v) 218 { 219 if ((externalRegisteredStringTable_ == nullptr) || (registeredStringTableSize_ <= 0)) { 220 return; 221 } 222 auto begin = ObjectSlot(reinterpret_cast<uintptr_t>(externalRegisteredStringTable_)); 223 auto end = ObjectSlot(reinterpret_cast<uintptr_t>(externalRegisteredStringTable_ + 224 registeredStringTableSize_)); 225 v(Root::ROOT_VM, begin, end); 226 } 227 228 private: 229 static constexpr int32_t WORKER_DESTRUCTION_COUNT = 3; 230 static constexpr int32_t MIN_GC_TRIGGER_VM_COUNT = 4; 231 static constexpr uint32_t MAX_SUSPEND_RETRIES = 5000; 232 Runtime() = default; 233 ~Runtime(); 234 void SuspendAllThreadsImpl(JSThread *current); 235 void ResumeAllThreadsImpl(JSThread *current); 236 237 void PreInitialization(const EcmaVM *vm); 238 void PostInitialization(const EcmaVM *vm); 239 GetSerializeDataIndex()240 uint32_t GetSerializeDataIndex() 241 { 242 if (!serializeDataIndexVector_.empty()) { 243 uint32_t index = serializeDataIndexVector_.back(); 244 serializeDataIndexVector_.pop_back(); 245 return index; 246 } 247 return ++serializeDataIndex_; 248 } 249 GetAndIncreaseSharedConstpoolCount()250 int32_t GetAndIncreaseSharedConstpoolCount() 251 { 252 if (freeSharedConstpoolIndex_.size() > 0) { 253 auto iter = freeSharedConstpoolIndex_.begin(); 254 int32_t freeCount = *iter; 255 freeSharedConstpoolIndex_.erase(iter); 256 return freeCount; 257 } 258 return sharedConstpoolCount_++; 259 } 260 GetSharedNativePointerCallbacks()261 std::vector<std::pair<NativePointerCallback, std::pair<void *, void *>>> &GetSharedNativePointerCallbacks() 262 { 263 return sharedNativePointerCallbacks_; 264 } 265 266 Mutex threadsLock_; 267 ConditionVariable threadSuspendCondVar_; 268 Mutex serializeLock_; 269 std::list<JSThread*> threads_; 270 uint32_t suspendNewCount_ {0}; 271 uint32_t serializeDataIndex_ {0}; 272 MutatorLock mutatorLock_; 273 274 bool sharedConstInited_ {false}; 275 GlobalEnvConstants globalConst_; 276 JSTaggedValue globalEnv_ {JSTaggedValue::Hole()}; 277 JSThread *mainThread_ {nullptr}; 278 // for shared heap. 279 std::unique_ptr<NativeAreaAllocator> nativeAreaAllocator_; 280 std::unique_ptr<HeapRegionAllocator> heapRegionAllocator_; 281 // for stringTable. 282 std::unique_ptr<EcmaStringTable> stringTable_; 283 std::unordered_map<uint32_t, std::unique_ptr<SerializationChunk>> serializeRootMap_; 284 std::vector<uint32_t> serializeDataIndexVector_; 285 286 // Shared constantpool cache 287 Mutex constpoolLock_; 288 CMap<const JSPandaFile *, CMap<int32_t, JSTaggedValue>> globalSharedConstpools_ {}; 289 int32_t sharedConstpoolCount_ = 0; // shared constpool count. 290 std::set<int32_t> freeSharedConstpoolIndex_ {}; // reuse shared constpool index. 291 292 // Runtime instance and VMs creation. 293 static int32_t vmCount_; 294 static int32_t destroyCount_; 295 static bool firstVmCreated_; 296 static Mutex *vmCreationLock_; 297 static Runtime *instance_; 298 299 // for string cache 300 JSTaggedValue *externalRegisteredStringTable_ {nullptr}; 301 uint32_t registeredStringTableSize_ = 0; 302 303 // for shared native pointer 304 std::vector<std::pair<NativePointerCallback, std::pair<void *, void *>>> sharedNativePointerCallbacks_ {}; 305 306 friend class EcmaVM; 307 friend class JSThread; 308 friend class SharedHeap; 309 310 NO_COPY_SEMANTIC(Runtime); 311 NO_MOVE_SEMANTIC(Runtime); 312 }; 313 } // namespace panda::ecmascript 314 #endif // ECMASCRIPT_RUNTIME_H 315