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