1 /* 2 * Copyright (c) 2025 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 "common_interfaces/base_runtime.h" 20 #include "ecmascript/ecma_string_table.h" 21 #include "ecmascript/global_env_constants.h" 22 #include "ecmascript/js_runtime_options.h" 23 #include "ecmascript/js_thread.h" 24 #include "ecmascript/mem/heap.h" 25 #include "ecmascript/mem/visitor.h" 26 #include "ecmascript/module/js_shared_module_manager.h" 27 #include "ecmascript/mutator_lock.h" 28 #include "ecmascript/platform/mutex.h" 29 #include "ecmascript/serializer/serialize_chunk.h" 30 31 #include "libpandabase/macros.h" 32 33 #include <list> 34 #include <memory> 35 36 namespace panda::ecmascript { 37 class EcmaStringTable; 38 using AppFreezeFilterCallback = 39 std::function<bool(const int32_t pid, const bool needDecreaseQuota, std::string &eventConfig)>; 40 using ReleaseSecureMemCallback = std::function<void(void* fileMapper)>; 41 42 class Runtime { 43 public: 44 PUBLIC_API static Runtime *GetInstance(); 45 46 static void CreateIfFirstVm(const JSRuntimeOptions &options); 47 static void DestroyIfLastVm(); 48 void InitializeIfFirstVm(EcmaVM *vm); 49 50 void RegisterThread(JSThread* newThread); 51 void UnregisterThread(JSThread* thread); 52 53 void SuspendAll(JSThread *current); 54 void ResumeAll(JSThread *current); 55 void IterateSerializeRoot(RootVisitor &v); 56 GetMainThread()57 JSThread *GetMainThread() const 58 { 59 return mainThread_; 60 } 61 GetMutatorLock()62 MutatorLock *GetMutatorLock() 63 { 64 return &mutatorLock_; 65 } 66 GetMutatorLock()67 const MutatorLock *GetMutatorLock() const 68 { 69 return &mutatorLock_; 70 } 71 72 template<class Callback> GCIterateThreadList(const Callback & cb)73 void GCIterateThreadList(const Callback &cb) 74 { 75 LockHolder lock(threadsLock_); 76 GCIterateThreadListWithoutLock(cb); 77 } 78 79 template<class Callback> GCIterateThreadListWithoutLock(const Callback & cb)80 void GCIterateThreadListWithoutLock(const Callback &cb) 81 { 82 for (auto thread : threads_) { 83 if (thread->ReadyForGCIterating()) { 84 cb(thread); 85 } 86 } 87 } 88 SetEnableLargeHeap(bool value)89 void SetEnableLargeHeap(bool value) 90 { 91 enableLargeHeap_ = value; 92 } 93 GetEnableLargeHeap()94 bool GetEnableLargeHeap() const 95 { 96 return enableLargeHeap_; 97 } 98 SetPostForked(bool postForked)99 void SetPostForked(bool postForked) 100 { 101 postForked_ = postForked; 102 } 103 IsPostForked()104 bool IsPostForked() const 105 { 106 return postForked_; 107 } 108 109 // Result may be inaccurate, just an approximate value. ApproximateThreadListSize()110 size_t ApproximateThreadListSize() 111 { 112 return threads_.size(); 113 } 114 GetGlobalEnvConstants()115 inline const GlobalEnvConstants *GetGlobalEnvConstants() 116 { 117 return &globalConst_; 118 } 119 SharedConstInited()120 inline bool SharedConstInited() 121 { 122 return sharedConstInited_; 123 } 124 GetGlobalEnv()125 JSTaggedValue GetGlobalEnv() const 126 { 127 return globalEnv_; 128 } 129 GetEcmaStringTable()130 inline EcmaStringTable *GetEcmaStringTable() const 131 { 132 return stringTable_.get(); 133 } 134 135 void IterateSharedRoot(RootVisitor &visitor); 136 GetSerializeRootMapValue(JSThread * thread,uint32_t dataIndex)137 inline SerializationChunk *GetSerializeRootMapValue([[maybe_unused]] JSThread *thread, 138 uint32_t dataIndex) 139 { 140 ASSERT(thread->IsInManagedState()); 141 LockHolder lock(serializeLock_); 142 auto iter = serializeRootMap_.find(dataIndex); 143 if (iter == serializeRootMap_.end()) { 144 return nullptr; 145 } 146 return iter->second.get(); 147 } 148 PushSerializationRoot(JSThread * thread,std::unique_ptr<SerializationChunk> chunk)149 uint32_t PushSerializationRoot([[maybe_unused]] JSThread *thread, std::unique_ptr<SerializationChunk> chunk) 150 { 151 ASSERT(thread->IsInManagedState()); 152 LockHolder lock(serializeLock_); 153 uint32_t index = GetSerializeDataIndex(); 154 ASSERT(serializeRootMap_.find(index) == serializeRootMap_.end()); 155 serializeRootMap_.emplace(index, std::move(chunk)); 156 return index; 157 } 158 RemoveSerializationRoot(JSThread * thread,uint32_t index)159 void RemoveSerializationRoot([[maybe_unused]] JSThread *thread, uint32_t index) 160 { 161 ASSERT(thread->IsInManagedState()); 162 LockHolder lock(serializeLock_); 163 ASSERT(serializeRootMap_.find(index) != serializeRootMap_.end()); 164 serializeRootMap_.erase(index); 165 serializeDataIndexVector_.emplace_back(index); 166 } 167 SharedGCRequest()168 static bool SharedGCRequest() 169 { 170 LockHolder lock(*vmCreationLock_); 171 destroyCount_++; 172 if (destroyCount_ == WORKER_DESTRUCTION_COUNT || vmCount_ < MIN_GC_TRIGGER_VM_COUNT) { 173 destroyCount_ = 0; 174 return true; 175 } else { 176 return false; 177 } 178 } 179 180 bool HasCachedConstpool(const JSPandaFile *jsPandaFile); 181 PUBLIC_API JSTaggedValue FindConstpool(const JSPandaFile *jsPandaFile, int32_t index); 182 JSTaggedValue FindConstpoolUnlocked(const JSPandaFile *jsPandaFile, int32_t index); 183 JSHandle<ConstantPool> AddOrUpdateConstpool(const JSPandaFile *jsPandaFile, 184 JSHandle<ConstantPool> constpool, 185 int32_t index = 0); 186 std::optional<std::reference_wrapper<CMap<int32_t, JSTaggedValue>>> FindConstpools( 187 const JSPandaFile *jsPandaFile); 188 void EraseUnusedConstpool(const JSPandaFile *jsPandaFile, int32_t index, int32_t constpoolIndex); 189 190 void ProcessNativeDeleteInSharedGC(const WeakRootVisitor &visitor); 191 void IteratorNativeDeleteInSharedGC(WeakVisitor &visitor); 192 193 void ProcessSharedNativeDelete(const WeakRootVisitor &visitor); 194 void InvokeSharedNativePointerCallbacks(); 195 void PushToSharedNativePointerList(JSNativePointer *pointer); 196 CreateStringCacheTable(uint32_t size)197 inline bool CreateStringCacheTable(uint32_t size) 198 { 199 constexpr int32_t MAX_SIZE = 150; 200 if ((size == 0) || (size > MAX_SIZE) || (externalRegisteredStringTable_ != nullptr)) { 201 LOG_ECMA(WARN) << "invalid size of the string cache table or the table has been registered."; 202 LOG_ECMA(WARN) << "Currently, maximum size of the table is " << MAX_SIZE; 203 return false; 204 } 205 206 externalRegisteredStringTable_ = new JSTaggedValue[size]; 207 if (externalRegisteredStringTable_ == nullptr) { 208 LOG_ECMA(ERROR) << "create string cache table failed"; 209 return false; 210 } 211 registeredStringTableSize_ = size; 212 return true; 213 } 214 SetCachedString(JSHandle<EcmaString> str,uint32_t propertyIndex)215 inline bool SetCachedString(JSHandle<EcmaString> str, uint32_t propertyIndex) 216 { 217 if (propertyIndex >= registeredStringTableSize_ || (externalRegisteredStringTable_ == nullptr)) { 218 LOG_ECMA(ERROR) << "invalid size of the string cache table or the table has never been registered."; 219 return false; 220 } 221 externalRegisteredStringTable_[propertyIndex] = str.GetTaggedValue(); 222 return true; 223 } 224 GetCachedString(JSThread * thread,uint32_t propertyIndex)225 inline JSHandle<EcmaString> GetCachedString(JSThread *thread, uint32_t propertyIndex) 226 { 227 if ((externalRegisteredStringTable_ == nullptr) || (propertyIndex >= registeredStringTableSize_)) { 228 LOG_ECMA(ERROR) << "invalid size of the string cache table or the table has never been registered."; 229 return JSHandle<EcmaString>(thread->GlobalConstants()->GetHandledEmptyString()); 230 } 231 return JSHandle<EcmaString>(reinterpret_cast<uintptr_t>(&externalRegisteredStringTable_[propertyIndex])); 232 } 233 HasCachedString(uint32_t propertyIndex)234 inline bool HasCachedString(uint32_t propertyIndex) 235 { 236 if ((externalRegisteredStringTable_ == nullptr) || propertyIndex >= registeredStringTableSize_) { 237 LOG_ECMA(ERROR) << "invalid size of the string cache table or the table has never been registered."; 238 return false; 239 } 240 241 if (externalRegisteredStringTable_[propertyIndex].GetRawData() != JSTaggedValue::NULL_POINTER) { 242 return true; 243 } 244 return false; 245 } 246 IterateCachedStringRoot(RootVisitor & v)247 void IterateCachedStringRoot(RootVisitor &v) 248 { 249 if ((externalRegisteredStringTable_ == nullptr) || (registeredStringTableSize_ <= 0)) { 250 return; 251 } 252 auto begin = ObjectSlot(reinterpret_cast<uintptr_t>(externalRegisteredStringTable_)); 253 auto end = ObjectSlot(reinterpret_cast<uintptr_t>(externalRegisteredStringTable_ + 254 registeredStringTableSize_)); 255 v.VisitRangeRoot(Root::ROOT_VM, begin, end); 256 } 257 GetAppFreezeFilterCallback()258 AppFreezeFilterCallback GetAppFreezeFilterCallback() const 259 { 260 return appfreezeFilterCallback_; 261 } 262 SetAppFreezeFilterCallback(AppFreezeFilterCallback cb)263 void SetAppFreezeFilterCallback(AppFreezeFilterCallback cb) 264 { 265 appfreezeFilterCallback_ = cb; 266 } 267 GetNativeAreaAllocator()268 NativeAreaAllocator *GetNativeAreaAllocator() const 269 { 270 return nativeAreaAllocator_.get(); 271 } 272 273 void PreFork(JSThread *thread); 274 void PostFork(); 275 GetRawHeapDumpCropLevel()276 RawHeapDumpCropLevel GetRawHeapDumpCropLevel() const 277 { 278 return rawHeapDumpCropLevel_; 279 } 280 SetRawHeapDumpCropLevel(RawHeapDumpCropLevel level)281 void SetRawHeapDumpCropLevel(RawHeapDumpCropLevel level) 282 { 283 rawHeapDumpCropLevel_ = level; 284 } 285 SetReleaseSecureMemCallback(ReleaseSecureMemCallback callback)286 void SetReleaseSecureMemCallback(ReleaseSecureMemCallback callback) 287 { 288 LockHolder lock(releaseSecureMemCallbackLock_); 289 if (releaseSecureMemCallback_ != nullptr) { 290 return; 291 } 292 releaseSecureMemCallback_ = callback; 293 } 294 GetReleaseSecureMemCallback()295 ReleaseSecureMemCallback GetReleaseSecureMemCallback() 296 { 297 LockHolder lock(releaseSecureMemCallbackLock_); 298 return releaseSecureMemCallback_; 299 } 300 IsHybridVm()301 bool IsHybridVm() const 302 { 303 return isHybridVm_; 304 } 305 SetHybridVm(bool isHybridVm)306 void SetHybridVm(bool isHybridVm) 307 { 308 isHybridVm_ = isHybridVm; 309 } 310 311 private: 312 static constexpr int32_t WORKER_DESTRUCTION_COUNT = 3; 313 static constexpr int32_t MIN_GC_TRIGGER_VM_COUNT = 4; 314 static constexpr uint32_t MAX_SUSPEND_RETRIES = 5000; 315 Runtime(); 316 ~Runtime(); 317 void SuspendAllThreadsImpl(JSThread *current); 318 void ResumeAllThreadsImpl(JSThread *current); 319 320 void PreInitialization(const EcmaVM *vm); 321 void PostInitialization(const EcmaVM *vm); 322 323 static void InitGCConfig(const JSRuntimeOptions &options); 324 GetSerializeDataIndex()325 uint32_t GetSerializeDataIndex() 326 { 327 if (!serializeDataIndexVector_.empty()) { 328 uint32_t index = serializeDataIndexVector_.back(); 329 serializeDataIndexVector_.pop_back(); 330 return index; 331 } 332 return ++serializeDataIndex_; 333 } 334 GetAndIncreaseSharedConstpoolCount()335 int32_t GetAndIncreaseSharedConstpoolCount() 336 { 337 if (freeSharedConstpoolIndex_.size() > 0) { 338 auto iter = freeSharedConstpoolIndex_.begin(); 339 int32_t freeCount = *iter; 340 freeSharedConstpoolIndex_.erase(iter); 341 return freeCount; 342 } 343 return sharedConstpoolCount_++; 344 } 345 GetSharedNativePointerCallbacks()346 std::vector<std::pair<NativePointerCallback, std::pair<void *, void *>>> &GetSharedNativePointerCallbacks() 347 { 348 return sharedNativePointerCallbacks_; 349 } 350 351 Mutex threadsLock_; 352 ConditionVariable threadSuspendCondVar_; 353 Mutex serializeLock_; 354 std::list<JSThread*> threads_; 355 uint32_t suspendNewCount_ {0}; 356 uint32_t serializeDataIndex_ {0}; 357 MutatorLock mutatorLock_; 358 std::atomic<bool> enableLargeHeap_ {false}; 359 bool sharedConstInited_ {false}; 360 GlobalEnvConstants globalConst_; 361 JSTaggedValue globalEnv_ {JSTaggedValue::Hole()}; 362 JSThread *mainThread_ {nullptr}; 363 // for shared heap. 364 std::unique_ptr<NativeAreaAllocator> nativeAreaAllocator_; 365 std::unique_ptr<HeapRegionAllocator> heapRegionAllocator_; 366 // for stringTable. 367 std::unique_ptr<EcmaStringTable> stringTable_; 368 std::unordered_map<uint32_t, std::unique_ptr<SerializationChunk>> serializeRootMap_; 369 std::vector<uint32_t> serializeDataIndexVector_; 370 371 // Shared constantpool cache 372 Mutex constpoolLock_; 373 CMap<const JSPandaFile *, CMap<int32_t, JSTaggedValue>> globalSharedConstpools_ {}; 374 int32_t sharedConstpoolCount_ = 0; // shared constpool count. 375 std::set<int32_t> freeSharedConstpoolIndex_ {}; // reuse shared constpool index. 376 bool postForked_ {false}; 377 378 // Runtime instance and VMs creation. 379 static int32_t vmCount_; 380 static int32_t destroyCount_; 381 static bool firstVmCreated_; 382 static Mutex *vmCreationLock_; 383 static Runtime *instance_; 384 static common::BaseRuntime *baseInstance_; 385 386 // for string cache 387 JSTaggedValue *externalRegisteredStringTable_ {nullptr}; 388 uint32_t registeredStringTableSize_ = 0; 389 390 // for shared native pointer 391 std::vector<std::pair<NativePointerCallback, std::pair<void *, void *>>> sharedNativePointerCallbacks_ {}; 392 393 // for appfreeze filter function 394 AppFreezeFilterCallback appfreezeFilterCallback_ {nullptr}; 395 396 // for rawheap dump crop level 397 RawHeapDumpCropLevel rawHeapDumpCropLevel_ {RawHeapDumpCropLevel::DEFAULT}; 398 // for 1.2runtime interface type 399 bool isHybridVm_ {false}; 400 401 // release secure mem after jspandafile released. 402 ReleaseSecureMemCallback releaseSecureMemCallback_ {nullptr}; 403 Mutex releaseSecureMemCallbackLock_; 404 405 friend class EcmaVM; 406 friend class JSThread; 407 friend class SharedHeap; 408 409 NO_COPY_SEMANTIC(Runtime); 410 NO_MOVE_SEMANTIC(Runtime); 411 }; 412 } // namespace panda::ecmascript 413 #endif // ECMASCRIPT_RUNTIME_H 414