/* * Copyright (c) 2025 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ECMASCRIPT_RUNTIME_H #define ECMASCRIPT_RUNTIME_H #include "common_interfaces/base_runtime.h" #include "ecmascript/ecma_string_table.h" #include "ecmascript/global_env_constants.h" #include "ecmascript/js_runtime_options.h" #include "ecmascript/js_thread.h" #include "ecmascript/mem/heap.h" #include "ecmascript/mem/visitor.h" #include "ecmascript/module/js_shared_module_manager.h" #include "ecmascript/mutator_lock.h" #include "ecmascript/platform/mutex.h" #include "ecmascript/serializer/serialize_chunk.h" #include "libpandabase/macros.h" #include #include namespace panda::ecmascript { class EcmaStringTable; using AppFreezeFilterCallback = std::function; using ReleaseSecureMemCallback = std::function; class Runtime { public: PUBLIC_API static Runtime *GetInstance(); static void CreateIfFirstVm(const JSRuntimeOptions &options); static void DestroyIfLastVm(); void InitializeIfFirstVm(EcmaVM *vm); void RegisterThread(JSThread* newThread); void UnregisterThread(JSThread* thread); void SuspendAll(JSThread *current); void ResumeAll(JSThread *current); void IterateSerializeRoot(RootVisitor &v); JSThread *GetMainThread() const { return mainThread_; } MutatorLock *GetMutatorLock() { return &mutatorLock_; } const MutatorLock *GetMutatorLock() const { return &mutatorLock_; } template void GCIterateThreadList(const Callback &cb) { LockHolder lock(threadsLock_); GCIterateThreadListWithoutLock(cb); } template void GCIterateThreadListWithoutLock(const Callback &cb) { for (auto thread : threads_) { if (thread->ReadyForGCIterating()) { cb(thread); } } } void SetEnableLargeHeap(bool value) { enableLargeHeap_ = value; } bool GetEnableLargeHeap() const { return enableLargeHeap_; } void SetPostForked(bool postForked) { postForked_ = postForked; } bool IsPostForked() const { return postForked_; } // Result may be inaccurate, just an approximate value. size_t ApproximateThreadListSize() { return threads_.size(); } inline const GlobalEnvConstants *GetGlobalEnvConstants() { return &globalConst_; } inline bool SharedConstInited() { return sharedConstInited_; } JSTaggedValue GetGlobalEnv() const { return globalEnv_; } inline EcmaStringTable *GetEcmaStringTable() const { return stringTable_.get(); } void IterateSharedRoot(RootVisitor &visitor); inline SerializationChunk *GetSerializeRootMapValue([[maybe_unused]] JSThread *thread, uint32_t dataIndex) { ASSERT(thread->IsInManagedState()); LockHolder lock(serializeLock_); auto iter = serializeRootMap_.find(dataIndex); if (iter == serializeRootMap_.end()) { return nullptr; } return iter->second.get(); } uint32_t PushSerializationRoot([[maybe_unused]] JSThread *thread, std::unique_ptr chunk) { ASSERT(thread->IsInManagedState()); LockHolder lock(serializeLock_); uint32_t index = GetSerializeDataIndex(); ASSERT(serializeRootMap_.find(index) == serializeRootMap_.end()); serializeRootMap_.emplace(index, std::move(chunk)); return index; } void RemoveSerializationRoot([[maybe_unused]] JSThread *thread, uint32_t index) { ASSERT(thread->IsInManagedState()); LockHolder lock(serializeLock_); ASSERT(serializeRootMap_.find(index) != serializeRootMap_.end()); serializeRootMap_.erase(index); serializeDataIndexVector_.emplace_back(index); } static bool SharedGCRequest() { LockHolder lock(*vmCreationLock_); destroyCount_++; if (destroyCount_ == WORKER_DESTRUCTION_COUNT || vmCount_ < MIN_GC_TRIGGER_VM_COUNT) { destroyCount_ = 0; return true; } else { return false; } } bool HasCachedConstpool(const JSPandaFile *jsPandaFile); PUBLIC_API JSTaggedValue FindConstpool(const JSPandaFile *jsPandaFile, int32_t index); JSTaggedValue FindConstpoolUnlocked(const JSPandaFile *jsPandaFile, int32_t index); JSHandle AddOrUpdateConstpool(const JSPandaFile *jsPandaFile, JSHandle constpool, int32_t index = 0); std::optional>> FindConstpools( const JSPandaFile *jsPandaFile); void EraseUnusedConstpool(const JSPandaFile *jsPandaFile, int32_t index, int32_t constpoolIndex); void ProcessNativeDeleteInSharedGC(const WeakRootVisitor &visitor); void IteratorNativeDeleteInSharedGC(WeakVisitor &visitor); void ProcessSharedNativeDelete(const WeakRootVisitor &visitor); void InvokeSharedNativePointerCallbacks(); void PushToSharedNativePointerList(JSNativePointer *pointer); inline bool CreateStringCacheTable(uint32_t size) { constexpr int32_t MAX_SIZE = 150; if ((size == 0) || (size > MAX_SIZE) || (externalRegisteredStringTable_ != nullptr)) { LOG_ECMA(WARN) << "invalid size of the string cache table or the table has been registered."; LOG_ECMA(WARN) << "Currently, maximum size of the table is " << MAX_SIZE; return false; } externalRegisteredStringTable_ = new JSTaggedValue[size]; if (externalRegisteredStringTable_ == nullptr) { LOG_ECMA(ERROR) << "create string cache table failed"; return false; } registeredStringTableSize_ = size; return true; } inline bool SetCachedString(JSHandle str, uint32_t propertyIndex) { if (propertyIndex >= registeredStringTableSize_ || (externalRegisteredStringTable_ == nullptr)) { LOG_ECMA(ERROR) << "invalid size of the string cache table or the table has never been registered."; return false; } externalRegisteredStringTable_[propertyIndex] = str.GetTaggedValue(); return true; } inline JSHandle GetCachedString(JSThread *thread, uint32_t propertyIndex) { if ((externalRegisteredStringTable_ == nullptr) || (propertyIndex >= registeredStringTableSize_)) { LOG_ECMA(ERROR) << "invalid size of the string cache table or the table has never been registered."; return JSHandle(thread->GlobalConstants()->GetHandledEmptyString()); } return JSHandle(reinterpret_cast(&externalRegisteredStringTable_[propertyIndex])); } inline bool HasCachedString(uint32_t propertyIndex) { if ((externalRegisteredStringTable_ == nullptr) || propertyIndex >= registeredStringTableSize_) { LOG_ECMA(ERROR) << "invalid size of the string cache table or the table has never been registered."; return false; } if (externalRegisteredStringTable_[propertyIndex].GetRawData() != JSTaggedValue::NULL_POINTER) { return true; } return false; } void IterateCachedStringRoot(RootVisitor &v) { if ((externalRegisteredStringTable_ == nullptr) || (registeredStringTableSize_ <= 0)) { return; } auto begin = ObjectSlot(reinterpret_cast(externalRegisteredStringTable_)); auto end = ObjectSlot(reinterpret_cast(externalRegisteredStringTable_ + registeredStringTableSize_)); v.VisitRangeRoot(Root::ROOT_VM, begin, end); } AppFreezeFilterCallback GetAppFreezeFilterCallback() const { return appfreezeFilterCallback_; } void SetAppFreezeFilterCallback(AppFreezeFilterCallback cb) { appfreezeFilterCallback_ = cb; } NativeAreaAllocator *GetNativeAreaAllocator() const { return nativeAreaAllocator_.get(); } void PreFork(JSThread *thread); void PostFork(); RawHeapDumpCropLevel GetRawHeapDumpCropLevel() const { return rawHeapDumpCropLevel_; } void SetRawHeapDumpCropLevel(RawHeapDumpCropLevel level) { rawHeapDumpCropLevel_ = level; } void SetReleaseSecureMemCallback(ReleaseSecureMemCallback callback) { LockHolder lock(releaseSecureMemCallbackLock_); if (releaseSecureMemCallback_ != nullptr) { return; } releaseSecureMemCallback_ = callback; } ReleaseSecureMemCallback GetReleaseSecureMemCallback() { LockHolder lock(releaseSecureMemCallbackLock_); return releaseSecureMemCallback_; } bool IsHybridVm() const { return isHybridVm_; } void SetHybridVm(bool isHybridVm) { isHybridVm_ = isHybridVm; } private: static constexpr int32_t WORKER_DESTRUCTION_COUNT = 3; static constexpr int32_t MIN_GC_TRIGGER_VM_COUNT = 4; static constexpr uint32_t MAX_SUSPEND_RETRIES = 5000; Runtime(); ~Runtime(); void SuspendAllThreadsImpl(JSThread *current); void ResumeAllThreadsImpl(JSThread *current); void PreInitialization(const EcmaVM *vm); void PostInitialization(const EcmaVM *vm); static void InitGCConfig(const JSRuntimeOptions &options); uint32_t GetSerializeDataIndex() { if (!serializeDataIndexVector_.empty()) { uint32_t index = serializeDataIndexVector_.back(); serializeDataIndexVector_.pop_back(); return index; } return ++serializeDataIndex_; } int32_t GetAndIncreaseSharedConstpoolCount() { if (freeSharedConstpoolIndex_.size() > 0) { auto iter = freeSharedConstpoolIndex_.begin(); int32_t freeCount = *iter; freeSharedConstpoolIndex_.erase(iter); return freeCount; } return sharedConstpoolCount_++; } std::vector>> &GetSharedNativePointerCallbacks() { return sharedNativePointerCallbacks_; } Mutex threadsLock_; ConditionVariable threadSuspendCondVar_; Mutex serializeLock_; std::list threads_; uint32_t suspendNewCount_ {0}; uint32_t serializeDataIndex_ {0}; MutatorLock mutatorLock_; std::atomic enableLargeHeap_ {false}; bool sharedConstInited_ {false}; GlobalEnvConstants globalConst_; JSTaggedValue globalEnv_ {JSTaggedValue::Hole()}; JSThread *mainThread_ {nullptr}; // for shared heap. std::unique_ptr nativeAreaAllocator_; std::unique_ptr heapRegionAllocator_; // for stringTable. std::unique_ptr stringTable_; std::unordered_map> serializeRootMap_; std::vector serializeDataIndexVector_; // Shared constantpool cache Mutex constpoolLock_; CMap> globalSharedConstpools_ {}; int32_t sharedConstpoolCount_ = 0; // shared constpool count. std::set freeSharedConstpoolIndex_ {}; // reuse shared constpool index. bool postForked_ {false}; // Runtime instance and VMs creation. static int32_t vmCount_; static int32_t destroyCount_; static bool firstVmCreated_; static Mutex *vmCreationLock_; static Runtime *instance_; static common::BaseRuntime *baseInstance_; // for string cache JSTaggedValue *externalRegisteredStringTable_ {nullptr}; uint32_t registeredStringTableSize_ = 0; // for shared native pointer std::vector>> sharedNativePointerCallbacks_ {}; // for appfreeze filter function AppFreezeFilterCallback appfreezeFilterCallback_ {nullptr}; // for rawheap dump crop level RawHeapDumpCropLevel rawHeapDumpCropLevel_ {RawHeapDumpCropLevel::DEFAULT}; // for 1.2runtime interface type bool isHybridVm_ {false}; // release secure mem after jspandafile released. ReleaseSecureMemCallback releaseSecureMemCallback_ {nullptr}; Mutex releaseSecureMemCallbackLock_; friend class EcmaVM; friend class JSThread; friend class SharedHeap; NO_COPY_SEMANTIC(Runtime); NO_MOVE_SEMANTIC(Runtime); }; } // namespace panda::ecmascript #endif // ECMASCRIPT_RUNTIME_H