• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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