14514f5e3Sopenharmony_ci/*
24514f5e3Sopenharmony_ci * Copyright (c) 2024 Huawei Device Co., Ltd.
34514f5e3Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
44514f5e3Sopenharmony_ci * you may not use this file except in compliance with the License.
54514f5e3Sopenharmony_ci * You may obtain a copy of the License at
64514f5e3Sopenharmony_ci *
74514f5e3Sopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
84514f5e3Sopenharmony_ci *
94514f5e3Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
104514f5e3Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
114514f5e3Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
124514f5e3Sopenharmony_ci * See the License for the specific language governing permissions and
134514f5e3Sopenharmony_ci * limitations under the License.
144514f5e3Sopenharmony_ci */
154514f5e3Sopenharmony_ci
164514f5e3Sopenharmony_ci#ifndef ECMASCRIPT_RUNTIME_H
174514f5e3Sopenharmony_ci#define ECMASCRIPT_RUNTIME_H
184514f5e3Sopenharmony_ci
194514f5e3Sopenharmony_ci#include "ecmascript/ecma_string_table.h"
204514f5e3Sopenharmony_ci#include "ecmascript/global_env_constants.h"
214514f5e3Sopenharmony_ci#include "ecmascript/js_runtime_options.h"
224514f5e3Sopenharmony_ci#include "ecmascript/js_thread.h"
234514f5e3Sopenharmony_ci#include "ecmascript/mem/heap.h"
244514f5e3Sopenharmony_ci#include "ecmascript/module/js_shared_module_manager.h"
254514f5e3Sopenharmony_ci#include "ecmascript/mutator_lock.h"
264514f5e3Sopenharmony_ci#include "ecmascript/platform/mutex.h"
274514f5e3Sopenharmony_ci#include "ecmascript/serializer/serialize_chunk.h"
284514f5e3Sopenharmony_ci
294514f5e3Sopenharmony_ci#include "libpandabase/macros.h"
304514f5e3Sopenharmony_ci
314514f5e3Sopenharmony_ci#include <list>
324514f5e3Sopenharmony_ci#include <memory>
334514f5e3Sopenharmony_ci
344514f5e3Sopenharmony_cinamespace panda::ecmascript {
354514f5e3Sopenharmony_ciclass Runtime {
364514f5e3Sopenharmony_cipublic:
374514f5e3Sopenharmony_ci    PUBLIC_API static Runtime *GetInstance();
384514f5e3Sopenharmony_ci
394514f5e3Sopenharmony_ci    static void CreateIfFirstVm(const JSRuntimeOptions &options);
404514f5e3Sopenharmony_ci    static void DestroyIfLastVm();
414514f5e3Sopenharmony_ci    void InitializeIfFirstVm(EcmaVM *vm);
424514f5e3Sopenharmony_ci
434514f5e3Sopenharmony_ci    void RegisterThread(JSThread* newThread);
444514f5e3Sopenharmony_ci    void UnregisterThread(JSThread* thread);
454514f5e3Sopenharmony_ci
464514f5e3Sopenharmony_ci    void SuspendAll(JSThread *current);
474514f5e3Sopenharmony_ci    void ResumeAll(JSThread *current);
484514f5e3Sopenharmony_ci    void IterateSerializeRoot(const RootVisitor &v);
494514f5e3Sopenharmony_ci
504514f5e3Sopenharmony_ci    JSThread *GetMainThread() const
514514f5e3Sopenharmony_ci    {
524514f5e3Sopenharmony_ci        return mainThread_;
534514f5e3Sopenharmony_ci    }
544514f5e3Sopenharmony_ci
554514f5e3Sopenharmony_ci    MutatorLock *GetMutatorLock()
564514f5e3Sopenharmony_ci    {
574514f5e3Sopenharmony_ci        return &mutatorLock_;
584514f5e3Sopenharmony_ci    }
594514f5e3Sopenharmony_ci
604514f5e3Sopenharmony_ci    const MutatorLock *GetMutatorLock() const
614514f5e3Sopenharmony_ci    {
624514f5e3Sopenharmony_ci        return &mutatorLock_;
634514f5e3Sopenharmony_ci    }
644514f5e3Sopenharmony_ci
654514f5e3Sopenharmony_ci    template<class Callback>
664514f5e3Sopenharmony_ci    void GCIterateThreadList(const Callback &cb)
674514f5e3Sopenharmony_ci    {
684514f5e3Sopenharmony_ci        LockHolder lock(threadsLock_);
694514f5e3Sopenharmony_ci        GCIterateThreadListWithoutLock(cb);
704514f5e3Sopenharmony_ci    }
714514f5e3Sopenharmony_ci
724514f5e3Sopenharmony_ci    template<class Callback>
734514f5e3Sopenharmony_ci    void GCIterateThreadListWithoutLock(const Callback &cb)
744514f5e3Sopenharmony_ci    {
754514f5e3Sopenharmony_ci        for (auto thread : threads_) {
764514f5e3Sopenharmony_ci            if (thread->ReadyForGCIterating()) {
774514f5e3Sopenharmony_ci                cb(thread);
784514f5e3Sopenharmony_ci            }
794514f5e3Sopenharmony_ci        }
804514f5e3Sopenharmony_ci    }
814514f5e3Sopenharmony_ci
824514f5e3Sopenharmony_ci    // Result may be inaccurate, just an approximate value.
834514f5e3Sopenharmony_ci    size_t ApproximateThreadListSize()
844514f5e3Sopenharmony_ci    {
854514f5e3Sopenharmony_ci        return threads_.size();
864514f5e3Sopenharmony_ci    }
874514f5e3Sopenharmony_ci
884514f5e3Sopenharmony_ci    inline const GlobalEnvConstants *GetGlobalEnvConstants()
894514f5e3Sopenharmony_ci    {
904514f5e3Sopenharmony_ci        return &globalConst_;
914514f5e3Sopenharmony_ci    }
924514f5e3Sopenharmony_ci
934514f5e3Sopenharmony_ci    inline bool SharedConstInited()
944514f5e3Sopenharmony_ci    {
954514f5e3Sopenharmony_ci        return sharedConstInited_;
964514f5e3Sopenharmony_ci    }
974514f5e3Sopenharmony_ci
984514f5e3Sopenharmony_ci    JSTaggedValue GetGlobalEnv() const
994514f5e3Sopenharmony_ci    {
1004514f5e3Sopenharmony_ci        return globalEnv_;
1014514f5e3Sopenharmony_ci    }
1024514f5e3Sopenharmony_ci
1034514f5e3Sopenharmony_ci    inline EcmaStringTable *GetEcmaStringTable() const
1044514f5e3Sopenharmony_ci    {
1054514f5e3Sopenharmony_ci        return stringTable_.get();
1064514f5e3Sopenharmony_ci    }
1074514f5e3Sopenharmony_ci
1084514f5e3Sopenharmony_ci    inline SerializationChunk *GetSerializeRootMapValue([[maybe_unused]] JSThread *thread,
1094514f5e3Sopenharmony_ci        uint32_t dataIndex)
1104514f5e3Sopenharmony_ci    {
1114514f5e3Sopenharmony_ci        ASSERT(thread->IsInManagedState());
1124514f5e3Sopenharmony_ci        LockHolder lock(serializeLock_);
1134514f5e3Sopenharmony_ci        auto iter = serializeRootMap_.find(dataIndex);
1144514f5e3Sopenharmony_ci        if (iter == serializeRootMap_.end()) {
1154514f5e3Sopenharmony_ci            return nullptr;
1164514f5e3Sopenharmony_ci        }
1174514f5e3Sopenharmony_ci        return iter->second.get();
1184514f5e3Sopenharmony_ci    }
1194514f5e3Sopenharmony_ci
1204514f5e3Sopenharmony_ci    uint32_t PushSerializationRoot([[maybe_unused]] JSThread *thread, std::unique_ptr<SerializationChunk> chunk)
1214514f5e3Sopenharmony_ci    {
1224514f5e3Sopenharmony_ci        ASSERT(thread->IsInManagedState());
1234514f5e3Sopenharmony_ci        LockHolder lock(serializeLock_);
1244514f5e3Sopenharmony_ci        uint32_t index = GetSerializeDataIndex();
1254514f5e3Sopenharmony_ci        ASSERT(serializeRootMap_.find(index) == serializeRootMap_.end());
1264514f5e3Sopenharmony_ci        serializeRootMap_.emplace(index, std::move(chunk));
1274514f5e3Sopenharmony_ci        return index;
1284514f5e3Sopenharmony_ci    }
1294514f5e3Sopenharmony_ci
1304514f5e3Sopenharmony_ci    void RemoveSerializationRoot([[maybe_unused]] JSThread *thread, uint32_t index)
1314514f5e3Sopenharmony_ci    {
1324514f5e3Sopenharmony_ci        ASSERT(thread->IsInManagedState());
1334514f5e3Sopenharmony_ci        LockHolder lock(serializeLock_);
1344514f5e3Sopenharmony_ci        ASSERT(serializeRootMap_.find(index) != serializeRootMap_.end());
1354514f5e3Sopenharmony_ci        serializeRootMap_.erase(index);
1364514f5e3Sopenharmony_ci        serializeDataIndexVector_.emplace_back(index);
1374514f5e3Sopenharmony_ci    }
1384514f5e3Sopenharmony_ci
1394514f5e3Sopenharmony_ci    static bool SharedGCRequest()
1404514f5e3Sopenharmony_ci    {
1414514f5e3Sopenharmony_ci        LockHolder lock(*vmCreationLock_);
1424514f5e3Sopenharmony_ci        destroyCount_++;
1434514f5e3Sopenharmony_ci        if (destroyCount_ == WORKER_DESTRUCTION_COUNT || vmCount_ < MIN_GC_TRIGGER_VM_COUNT) {
1444514f5e3Sopenharmony_ci            destroyCount_ = 0;
1454514f5e3Sopenharmony_ci            return true;
1464514f5e3Sopenharmony_ci        } else {
1474514f5e3Sopenharmony_ci            return false;
1484514f5e3Sopenharmony_ci        }
1494514f5e3Sopenharmony_ci    }
1504514f5e3Sopenharmony_ci
1514514f5e3Sopenharmony_ci    bool HasCachedConstpool(const JSPandaFile *jsPandaFile);
1524514f5e3Sopenharmony_ci    PUBLIC_API JSTaggedValue FindConstpool(const JSPandaFile *jsPandaFile, int32_t index);
1534514f5e3Sopenharmony_ci    JSTaggedValue FindConstpoolUnlocked(const JSPandaFile *jsPandaFile, int32_t index);
1544514f5e3Sopenharmony_ci    JSHandle<ConstantPool> AddOrUpdateConstpool(const JSPandaFile *jsPandaFile,
1554514f5e3Sopenharmony_ci                                                JSHandle<ConstantPool> constpool,
1564514f5e3Sopenharmony_ci                                                int32_t index = 0);
1574514f5e3Sopenharmony_ci    std::optional<std::reference_wrapper<CMap<int32_t, JSTaggedValue>>> FindConstpools(
1584514f5e3Sopenharmony_ci        const JSPandaFile *jsPandaFile);
1594514f5e3Sopenharmony_ci    void EraseUnusedConstpool(const JSPandaFile *jsPandaFile, int32_t index, int32_t constpoolIndex);
1604514f5e3Sopenharmony_ci
1614514f5e3Sopenharmony_ci    void ProcessNativeDeleteInSharedGC(const WeakRootVisitor &visitor);
1624514f5e3Sopenharmony_ci
1634514f5e3Sopenharmony_ci    void ProcessSharedNativeDelete(const WeakRootVisitor &visitor);
1644514f5e3Sopenharmony_ci    void InvokeSharedNativePointerCallbacks();
1654514f5e3Sopenharmony_ci    void PushToSharedNativePointerList(JSNativePointer *pointer);
1664514f5e3Sopenharmony_ci
1674514f5e3Sopenharmony_ci    inline bool CreateStringCacheTable(uint32_t size)
1684514f5e3Sopenharmony_ci    {
1694514f5e3Sopenharmony_ci        constexpr int32_t MAX_SIZE = 150;
1704514f5e3Sopenharmony_ci        if ((size == 0) || (size > MAX_SIZE) || (externalRegisteredStringTable_ != nullptr)) {
1714514f5e3Sopenharmony_ci            LOG_ECMA(WARN) << "invalid size of the string cache table or the table has been registered.";
1724514f5e3Sopenharmony_ci            LOG_ECMA(WARN) << "Currently, maximum size of the table is " << MAX_SIZE;
1734514f5e3Sopenharmony_ci            return false;
1744514f5e3Sopenharmony_ci        }
1754514f5e3Sopenharmony_ci
1764514f5e3Sopenharmony_ci        externalRegisteredStringTable_ = new JSTaggedValue[size];
1774514f5e3Sopenharmony_ci        if (externalRegisteredStringTable_ == nullptr) {
1784514f5e3Sopenharmony_ci            LOG_ECMA(ERROR) << "create string cache table failed";
1794514f5e3Sopenharmony_ci            return false;
1804514f5e3Sopenharmony_ci        }
1814514f5e3Sopenharmony_ci        registeredStringTableSize_ = size;
1824514f5e3Sopenharmony_ci        return true;
1834514f5e3Sopenharmony_ci    }
1844514f5e3Sopenharmony_ci
1854514f5e3Sopenharmony_ci    inline bool SetCachedString(JSHandle<EcmaString> str, uint32_t propertyIndex)
1864514f5e3Sopenharmony_ci    {
1874514f5e3Sopenharmony_ci        if (propertyIndex >= registeredStringTableSize_ || (externalRegisteredStringTable_ == nullptr)) {
1884514f5e3Sopenharmony_ci            LOG_ECMA(ERROR) << "invalid size of the string cache table or the table has never been registered.";
1894514f5e3Sopenharmony_ci            return false;
1904514f5e3Sopenharmony_ci        }
1914514f5e3Sopenharmony_ci        externalRegisteredStringTable_[propertyIndex] = str.GetTaggedValue();
1924514f5e3Sopenharmony_ci        return true;
1934514f5e3Sopenharmony_ci    }
1944514f5e3Sopenharmony_ci
1954514f5e3Sopenharmony_ci    inline JSHandle<EcmaString> GetCachedString(JSThread *thread, uint32_t propertyIndex)
1964514f5e3Sopenharmony_ci    {
1974514f5e3Sopenharmony_ci        if ((externalRegisteredStringTable_ == nullptr) || (propertyIndex >= registeredStringTableSize_)) {
1984514f5e3Sopenharmony_ci            LOG_ECMA(ERROR) << "invalid size of the string cache table or the table has never been registered.";
1994514f5e3Sopenharmony_ci            return JSHandle<EcmaString>(thread->GlobalConstants()->GetHandledEmptyString());
2004514f5e3Sopenharmony_ci        }
2014514f5e3Sopenharmony_ci        return JSHandle<EcmaString>(reinterpret_cast<uintptr_t>(&externalRegisteredStringTable_[propertyIndex]));
2024514f5e3Sopenharmony_ci    }
2034514f5e3Sopenharmony_ci
2044514f5e3Sopenharmony_ci    inline bool HasCachedString(uint32_t propertyIndex)
2054514f5e3Sopenharmony_ci    {
2064514f5e3Sopenharmony_ci        if ((externalRegisteredStringTable_ == nullptr) || propertyIndex >= registeredStringTableSize_) {
2074514f5e3Sopenharmony_ci            LOG_ECMA(ERROR) << "invalid size of the string cache table or the table has never been registered.";
2084514f5e3Sopenharmony_ci            return false;
2094514f5e3Sopenharmony_ci        }
2104514f5e3Sopenharmony_ci
2114514f5e3Sopenharmony_ci        if (externalRegisteredStringTable_[propertyIndex].GetRawData() != JSTaggedValue::NULL_POINTER) {
2124514f5e3Sopenharmony_ci            return true;
2134514f5e3Sopenharmony_ci        }
2144514f5e3Sopenharmony_ci        return false;
2154514f5e3Sopenharmony_ci    }
2164514f5e3Sopenharmony_ci
2174514f5e3Sopenharmony_ci    void IterateCachedStringRoot(const RootRangeVisitor &v)
2184514f5e3Sopenharmony_ci    {
2194514f5e3Sopenharmony_ci        if ((externalRegisteredStringTable_ == nullptr) || (registeredStringTableSize_ <= 0)) {
2204514f5e3Sopenharmony_ci            return;
2214514f5e3Sopenharmony_ci        }
2224514f5e3Sopenharmony_ci        auto begin = ObjectSlot(reinterpret_cast<uintptr_t>(externalRegisteredStringTable_));
2234514f5e3Sopenharmony_ci        auto end = ObjectSlot(reinterpret_cast<uintptr_t>(externalRegisteredStringTable_ +
2244514f5e3Sopenharmony_ci            registeredStringTableSize_));
2254514f5e3Sopenharmony_ci        v(Root::ROOT_VM, begin, end);
2264514f5e3Sopenharmony_ci    }
2274514f5e3Sopenharmony_ci
2284514f5e3Sopenharmony_ciprivate:
2294514f5e3Sopenharmony_ci    static constexpr int32_t WORKER_DESTRUCTION_COUNT = 3;
2304514f5e3Sopenharmony_ci    static constexpr int32_t MIN_GC_TRIGGER_VM_COUNT = 4;
2314514f5e3Sopenharmony_ci    static constexpr uint32_t MAX_SUSPEND_RETRIES = 5000;
2324514f5e3Sopenharmony_ci    Runtime() = default;
2334514f5e3Sopenharmony_ci    ~Runtime();
2344514f5e3Sopenharmony_ci    void SuspendAllThreadsImpl(JSThread *current);
2354514f5e3Sopenharmony_ci    void ResumeAllThreadsImpl(JSThread *current);
2364514f5e3Sopenharmony_ci
2374514f5e3Sopenharmony_ci    void PreInitialization(const EcmaVM *vm);
2384514f5e3Sopenharmony_ci    void PostInitialization(const EcmaVM *vm);
2394514f5e3Sopenharmony_ci
2404514f5e3Sopenharmony_ci    uint32_t GetSerializeDataIndex()
2414514f5e3Sopenharmony_ci    {
2424514f5e3Sopenharmony_ci        if (!serializeDataIndexVector_.empty()) {
2434514f5e3Sopenharmony_ci            uint32_t index = serializeDataIndexVector_.back();
2444514f5e3Sopenharmony_ci            serializeDataIndexVector_.pop_back();
2454514f5e3Sopenharmony_ci            return index;
2464514f5e3Sopenharmony_ci        }
2474514f5e3Sopenharmony_ci        return ++serializeDataIndex_;
2484514f5e3Sopenharmony_ci    }
2494514f5e3Sopenharmony_ci
2504514f5e3Sopenharmony_ci    int32_t GetAndIncreaseSharedConstpoolCount()
2514514f5e3Sopenharmony_ci    {
2524514f5e3Sopenharmony_ci        if (freeSharedConstpoolIndex_.size() > 0) {
2534514f5e3Sopenharmony_ci            auto iter = freeSharedConstpoolIndex_.begin();
2544514f5e3Sopenharmony_ci            int32_t freeCount = *iter;
2554514f5e3Sopenharmony_ci            freeSharedConstpoolIndex_.erase(iter);
2564514f5e3Sopenharmony_ci            return freeCount;
2574514f5e3Sopenharmony_ci        }
2584514f5e3Sopenharmony_ci        return sharedConstpoolCount_++;
2594514f5e3Sopenharmony_ci    }
2604514f5e3Sopenharmony_ci
2614514f5e3Sopenharmony_ci    std::vector<std::pair<NativePointerCallback, std::pair<void *, void *>>> &GetSharedNativePointerCallbacks()
2624514f5e3Sopenharmony_ci    {
2634514f5e3Sopenharmony_ci        return sharedNativePointerCallbacks_;
2644514f5e3Sopenharmony_ci    }
2654514f5e3Sopenharmony_ci
2664514f5e3Sopenharmony_ci    Mutex threadsLock_;
2674514f5e3Sopenharmony_ci    ConditionVariable threadSuspendCondVar_;
2684514f5e3Sopenharmony_ci    Mutex serializeLock_;
2694514f5e3Sopenharmony_ci    std::list<JSThread*> threads_;
2704514f5e3Sopenharmony_ci    uint32_t suspendNewCount_ {0};
2714514f5e3Sopenharmony_ci    uint32_t serializeDataIndex_ {0};
2724514f5e3Sopenharmony_ci    MutatorLock mutatorLock_;
2734514f5e3Sopenharmony_ci
2744514f5e3Sopenharmony_ci    bool sharedConstInited_ {false};
2754514f5e3Sopenharmony_ci    GlobalEnvConstants globalConst_;
2764514f5e3Sopenharmony_ci    JSTaggedValue globalEnv_ {JSTaggedValue::Hole()};
2774514f5e3Sopenharmony_ci    JSThread *mainThread_ {nullptr};
2784514f5e3Sopenharmony_ci    // for shared heap.
2794514f5e3Sopenharmony_ci    std::unique_ptr<NativeAreaAllocator> nativeAreaAllocator_;
2804514f5e3Sopenharmony_ci    std::unique_ptr<HeapRegionAllocator> heapRegionAllocator_;
2814514f5e3Sopenharmony_ci    // for stringTable.
2824514f5e3Sopenharmony_ci    std::unique_ptr<EcmaStringTable> stringTable_;
2834514f5e3Sopenharmony_ci    std::unordered_map<uint32_t, std::unique_ptr<SerializationChunk>> serializeRootMap_;
2844514f5e3Sopenharmony_ci    std::vector<uint32_t> serializeDataIndexVector_;
2854514f5e3Sopenharmony_ci
2864514f5e3Sopenharmony_ci    // Shared constantpool cache
2874514f5e3Sopenharmony_ci    Mutex constpoolLock_;
2884514f5e3Sopenharmony_ci    CMap<const JSPandaFile *, CMap<int32_t, JSTaggedValue>> globalSharedConstpools_ {};
2894514f5e3Sopenharmony_ci    int32_t sharedConstpoolCount_ = 0; // shared constpool count.
2904514f5e3Sopenharmony_ci    std::set<int32_t> freeSharedConstpoolIndex_ {}; // reuse shared constpool index.
2914514f5e3Sopenharmony_ci
2924514f5e3Sopenharmony_ci    // Runtime instance and VMs creation.
2934514f5e3Sopenharmony_ci    static int32_t vmCount_;
2944514f5e3Sopenharmony_ci    static int32_t destroyCount_;
2954514f5e3Sopenharmony_ci    static bool firstVmCreated_;
2964514f5e3Sopenharmony_ci    static Mutex *vmCreationLock_;
2974514f5e3Sopenharmony_ci    static Runtime *instance_;
2984514f5e3Sopenharmony_ci
2994514f5e3Sopenharmony_ci    // for string cache
3004514f5e3Sopenharmony_ci    JSTaggedValue *externalRegisteredStringTable_ {nullptr};
3014514f5e3Sopenharmony_ci    uint32_t registeredStringTableSize_ = 0;
3024514f5e3Sopenharmony_ci
3034514f5e3Sopenharmony_ci    // for shared native pointer
3044514f5e3Sopenharmony_ci    std::vector<std::pair<NativePointerCallback, std::pair<void *, void *>>> sharedNativePointerCallbacks_ {};
3054514f5e3Sopenharmony_ci
3064514f5e3Sopenharmony_ci    friend class EcmaVM;
3074514f5e3Sopenharmony_ci    friend class JSThread;
3084514f5e3Sopenharmony_ci    friend class SharedHeap;
3094514f5e3Sopenharmony_ci
3104514f5e3Sopenharmony_ci    NO_COPY_SEMANTIC(Runtime);
3114514f5e3Sopenharmony_ci    NO_MOVE_SEMANTIC(Runtime);
3124514f5e3Sopenharmony_ci};
3134514f5e3Sopenharmony_ci}  // namespace panda::ecmascript
3144514f5e3Sopenharmony_ci#endif // ECMASCRIPT_RUNTIME_H
315