• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 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_ECMA_GLOBAL_STORAGE_H
17 #define ECMASCRIPT_ECMA_GLOBAL_STORAGE_H
18 
19 #include "ecmascript/cross_vm/ecma_global_storage_hybrid.h"
20 
21 #include "ecmascript/js_thread.h"
22 #include "ecmascript/mem/native_area_allocator.h"
23 #include "ecmascript/mem/c_containers.h"
24 #include "ecmascript/platform/backtrace.h"
25 #ifdef HOOK_ENABLE
26 #include "memory_trace.h"
27 #endif
28 
29 namespace panda::ecmascript {
30 class Node {
31 public:
GetObject()32     JSTaggedType GetObject() const
33     {
34         return obj_;
35     }
36 
SetObject(JSTaggedType obj)37     void SetObject(JSTaggedType obj)
38     {
39         obj_ = obj;
40     }
41 
GetNext()42     Node *GetNext() const
43     {
44         return next_;
45     }
46 
SetNext(Node * node)47     void SetNext(Node *node)
48     {
49         next_ = node;
50     }
51 
GetPrev()52     Node *GetPrev() const
53     {
54         return prev_;
55     }
56 
SetPrev(Node * node)57     void SetPrev(Node *node)
58     {
59         prev_ = node;
60     }
61 
GetIndex()62     uint32_t GetIndex() const
63     {
64         return index_;
65     }
66 
SetIndex(uint32_t index)67     void SetIndex(uint32_t index)
68     {
69         index_ = index;
70     }
71 
SetUsing(bool isUsing)72     void SetUsing(bool isUsing)
73     {
74         isUsing_ = isUsing;
75     }
76 
SetWeak(bool isWeak)77     void SetWeak(bool isWeak)
78     {
79         isWeak_ = isWeak;
80     }
81 
IsUsing()82     bool IsUsing() const
83     {
84         return isUsing_;
85     }
86 
IsWeak()87     bool IsWeak() const
88     {
89         return isWeak_;
90     }
91 
GetObjectAddress()92     uintptr_t GetObjectAddress() const
93     {
94         return reinterpret_cast<uintptr_t>(&obj_);
95     }
96 
97     // If isUsing is true, it means that the node is being used, otherwise it means that node is be freed
Reset(JSThread * thread,Node * next,JSTaggedType value,bool isUsing)98     void Reset([[maybe_unused]] JSThread *thread, Node *next, JSTaggedType value, bool isUsing)
99     {
100         SetPrev(nullptr);
101         SetNext(next);
102         SetObject(value);
103         SetUsing(isUsing);
104 #ifdef HOOK_ENABLE
105         memtrace((void *)next, sizeof(Node), "ArkJsGlobalHandle", isUsing);
106 #endif
107     }
108 
109 private:
110     JSTaggedType obj_;
111     Node *next_ {nullptr};
112     Node *prev_ {nullptr};
113     uint32_t index_ {0};
114     bool isUsing_ {false};
115     bool isWeak_ {false};
116 };
117 
118 class DebugNode : public Node {
119 public:
Reset(JSThread * thread,Node * next,JSTaggedType value,bool isUsing)120     void Reset(JSThread *thread, Node *next, JSTaggedType value, bool isUsing)
121     {
122         Node::Reset(thread, next, value, isUsing);
123         ResetDebugInfo();
124         if (isUsing && thread->IsStartGlobalLeakCheck()) {
125             if (JSTaggedValue(value).IsHeapObject()) {
126                 if (thread->EnableGlobalObjectLeakCheck()) {
127                     SaveBacktrace(thread, value);
128                 }
129             } else {
130                 if (thread->EnableGlobalPrimitiveLeakCheck()) {
131                     SaveBacktrace(thread, value);
132                 }
133             }
134         }
135     }
136 
GetMarkCount()137     int32_t GetMarkCount() const
138     {
139         return markCount_;
140     }
141 
MarkCount()142     void MarkCount()
143     {
144         markCount_++;
145     }
146 
ResetDebugInfo()147     void ResetDebugInfo()
148     {
149         markCount_ = 0;
150         globalNumber_ = -1;
151     }
152 
GetGlobalNumber()153     int32_t GetGlobalNumber()
154     {
155         return globalNumber_;
156     }
157 
158 private:
SaveBacktrace(JSThread * thread,JSTaggedType value)159     void SaveBacktrace(JSThread *thread, JSTaggedType value)
160     {
161         globalNumber_ = static_cast<int32_t>(thread->IncreaseGlobalNumberCount());
162         std::ostringstream stack;
163         stack << "Global Handle Number:[" << globalNumber_ << "], value:0x" <<
164             std::hex << value << std::endl;
165         Backtrace(stack, true);
166         thread->WriteToStackTraceFd(stack);
167     }
168 
169     int32_t markCount_ {0};
170     // A number generated in the order of distribution.It Used to help locate global memory leaks.
171     int32_t globalNumber_ {-1};
172 };
173 
174 class WeakNode : public Node {
175 public:
SetReference(void * ref)176     void SetReference(void *ref)
177     {
178         reference_ = ref;
179     }
180 
GetReference()181     void* GetReference() const
182     {
183         return reference_;
184     }
185 
SetFreeGlobalCallback(WeakClearCallback callback)186     void SetFreeGlobalCallback(WeakClearCallback callback)
187     {
188         freeGlobalCallback_ = callback;
189     }
190 
SetNativeFinalizeCallback(WeakClearCallback callback)191     void SetNativeFinalizeCallback(WeakClearCallback callback)
192     {
193         nativeFinalizeCallback_ = callback;
194     }
195 
GetNativeFinalizeCallback()196     WeakClearCallback GetNativeFinalizeCallback() const
197     {
198         return nativeFinalizeCallback_;
199     }
200 
GetFreeGlobalCallback()201     WeakClearCallback GetFreeGlobalCallback() const
202     {
203         return freeGlobalCallback_;
204     }
205 
CallFreeGlobalCallback()206     void CallFreeGlobalCallback()
207     {
208         if (freeGlobalCallback_ != nullptr) {
209             freeGlobalCallback_(reference_);
210         }
211     }
212 
CallNativeFinalizeCallback()213     void CallNativeFinalizeCallback()
214     {
215         if (nativeFinalizeCallback_ != nullptr) {
216             nativeFinalizeCallback_(reference_);
217         }
218     }
219 private:
220     void *reference_ {nullptr};
221     WeakClearCallback freeGlobalCallback_ {nullptr};
222     WeakClearCallback nativeFinalizeCallback_ {nullptr};
223 };
224 
225 template<typename T>
226 class NodeList {
227 public:
NodeList()228     NodeList()
229     {
230         bool isWeak = std::is_same<T, WeakNode>::value;
231         for (uint32_t i = 0; i < GLOBAL_BLOCK_SIZE; i++) {
232             nodeList_[i].SetIndex(i);
233             nodeList_[i].SetWeak(isWeak);
234         }
235     }
236     ~NodeList() = default;
237 
NodeToNodeList(T * node)238     inline static NodeList<T> *NodeToNodeList(T *node)
239     {
240         uintptr_t ptr = ToUintPtr(node) - node->GetIndex() * sizeof(T);
241         return reinterpret_cast<NodeList<T> *>(ptr);
242     }
243 
NewNode(JSThread * thread,JSTaggedType value)244     inline T *NewNode(JSThread *thread, JSTaggedType value)
245     {
246         if (IsFull()) {
247             return nullptr;
248         }
249         T *node = &nodeList_[index_++];
250         node->Reset(thread, usedList_, value, true);
251         if (usedList_ != nullptr) {
252             usedList_->SetPrev(node);
253         }
254 
255         usedList_ = node;
256         return node;
257     }
258 
GetFreeNode(JSThread * thread,JSTaggedType value)259     inline T *GetFreeNode(JSThread *thread, JSTaggedType value)
260     {
261         T *node = freeList_;
262         if (node != nullptr) {
263             freeList_ = reinterpret_cast<T *>(node->GetNext());
264             node->Reset(thread, usedList_, value, true);
265             if (usedList_ != nullptr) {
266                 usedList_->SetPrev(node);
267             }
268             usedList_ = node;
269         }
270         return node;
271     }
272 
FreeNode(JSThread * thread,T * node)273     inline void FreeNode(JSThread *thread, T *node)
274     {
275         if (node->GetPrev() != nullptr) {
276             node->GetPrev()->SetNext(node->GetNext());
277         }
278         if (node->GetNext() != nullptr) {
279             node->GetNext()->SetPrev(node->GetPrev());
280         }
281         if (node == usedList_) {
282             usedList_ = reinterpret_cast<T *>(node->GetNext());
283         }
284         node->Reset(thread, freeList_, JSTaggedValue::Undefined().GetRawData(), false);
285         if (node->IsWeak()) {
286             reinterpret_cast<WeakNode *>(node)->SetReference(nullptr);
287             reinterpret_cast<WeakNode *>(node)->SetFreeGlobalCallback(nullptr);
288             reinterpret_cast<WeakNode *>(node)->SetNativeFinalizeCallback(nullptr);
289         }
290         if (freeList_ != nullptr) {
291             freeList_->SetPrev(node);
292         }
293         freeList_ = node;
294     }
295 
LinkTo(NodeList<T> * prev)296     inline void LinkTo(NodeList<T> *prev)
297     {
298         next_ = nullptr;
299         prev_ = prev;
300         prev_->next_ = this;
301     }
RemoveList()302     inline void RemoveList()
303     {
304         if (next_ != nullptr) {
305             next_->SetPrev(prev_);
306         }
307         if (prev_ != nullptr) {
308             prev_->SetNext(next_);
309         }
310         if (freeNext_ != nullptr) {
311             freeNext_->SetFreePrev(freePrev_);
312         }
313         if (freePrev_ != nullptr) {
314             freePrev_->SetFreeNext(freeNext_);
315         }
316     }
317 
IsFull()318     inline bool IsFull()
319     {
320         return index_ >= GLOBAL_BLOCK_SIZE;
321     }
322 
HasFreeNode()323     inline bool HasFreeNode()
324     {
325         return freeList_ != nullptr;
326     }
327 
HasUsagedNode()328     inline bool HasUsagedNode()
329     {
330         return !IsFull() || usedList_ != nullptr;
331     }
332 
SetNext(NodeList * next)333     inline void SetNext(NodeList *next)
334     {
335         next_ = next;
336     }
GetNext()337     inline NodeList<T> *GetNext() const
338     {
339         return next_;
340     }
341 
SetPrev(NodeList<T> * prev)342     inline void SetPrev(NodeList<T> *prev)
343     {
344         prev_ = prev;
345     }
GetPrev()346     inline NodeList<T> *GetPrev() const
347     {
348         return prev_;
349     }
350 
SetFreeNext(NodeList<T> * next)351     inline void SetFreeNext(NodeList<T> *next)
352     {
353         freeNext_ = next;
354     }
GetFreeNext()355     inline NodeList<T> *GetFreeNext() const
356     {
357         return freeNext_;
358     }
359 
SetFreePrev(NodeList<T> * prev)360     inline void SetFreePrev(NodeList<T> *prev)
361     {
362         freePrev_ = prev;
363     }
GetFreePrev()364     inline NodeList<T> *GetFreePrev() const
365     {
366         return freePrev_;
367     }
368 
369     template<class Callback>
IterateUsageGlobal(Callback callback)370     inline void IterateUsageGlobal(Callback callback)
371     {
372         T *next = usedList_;
373         T *current = nullptr;
374         while (next != nullptr) {
375             current = next;
376             next = reinterpret_cast<T *>(current->GetNext());
377             ASSERT(current != next);
378             callback(current);
379         }
380     }
381 
382 private:
383     static const uint32_t GLOBAL_BLOCK_SIZE = 256;
384     T nodeList_[GLOBAL_BLOCK_SIZE];  // all
385     T *freeList_ {nullptr};  // dispose node
386     T *usedList_ {nullptr};  // usage node
387     uint32_t index_ {0};
388     NodeList<T> *next_ {nullptr};
389     NodeList<T> *prev_ {nullptr};
390     NodeList<T> *freeNext_ {nullptr};
391     NodeList<T> *freePrev_ {nullptr};
392 };
393 
394 enum class NodeKind : uint8_t {
395     NORMAL_NODE,
396     UNIFIED_NODE,
397 };
398 
399 template<typename T>
400 class EcmaGlobalStorage {
401 public:
402     using WeakClearCallback = void (*)(void *);
403 
EcmaGlobalStorage(JSThread * thread,NativeAreaAllocator * allocator)404     EcmaGlobalStorage(JSThread *thread, NativeAreaAllocator *allocator)
405         : thread_(thread), allocator_(allocator)
406     {
407         topGlobalNodes_ = lastGlobalNodes_ = allocator_->New<NodeList<T>>();
408         topXRefGlobalNodes_ = lastXRefGlobalNodes_ = allocator_->New<NodeList<T>>();
409         topWeakGlobalNodes_ = lastWeakGlobalNodes_ = allocator_->New<NodeList<WeakNode>>();
410     }
411 
~EcmaGlobalStorage()412     ~EcmaGlobalStorage()
413     {
414         auto clearWeakNodeCallback = [] (WeakNode *node) {
415             node->SetUsing(false);
416             node->SetObject(JSTaggedValue::Undefined().GetRawData());
417             node->CallFreeGlobalCallback();
418             node->CallNativeFinalizeCallback();
419         };
420         auto clearNodeCallback = [] (T *node) {
421             node->SetUsing(false);
422             node->SetObject(JSTaggedValue::Undefined().GetRawData());
423         };
424 
425         IterateNodeList<decltype(clearWeakNodeCallback), WeakNode>(clearWeakNodeCallback, topWeakGlobalNodes_);
426         IterateNodeList<decltype(clearNodeCallback), T>(clearNodeCallback, topXRefGlobalNodes_);
427         IterateNodeList<decltype(clearNodeCallback), T>(clearNodeCallback, topGlobalNodes_);
428     }
429 
430     template<class Callback, class S>
IterateNodeList(Callback callback,NodeList<S> * nodeList)431     inline void IterateNodeList(Callback callback, NodeList<S> *nodeList)
432     {
433         auto *next = nodeList;
434         NodeList<S> *current = nullptr;
435         while (next != nullptr) {
436             current = next;
437             next = current->GetNext();
438             ASSERT(current != next);
439             current->IterateUsageGlobal(callback);
440         }
441     }
442 
443     template<NodeKind nodeKind>
NewGlobalHandle(JSTaggedType value)444     inline uintptr_t NewGlobalHandle(JSTaggedType value)
445     {
446         if constexpr(nodeKind == NodeKind::NORMAL_NODE) {
447             return NewGlobalHandleImplement(&lastGlobalNodes_, &freeListNodes_, value);
448         } else if constexpr(nodeKind == NodeKind::UNIFIED_NODE) {
449             return NewGlobalHandleImplement(&lastXRefGlobalNodes_, &xRefFreeListNodes_, value);
450         }
451     }
452 
453     template<NodeKind nodeKind>
DisposeGlobalHandle(uintptr_t nodeAddr)454     inline void DisposeGlobalHandle(uintptr_t nodeAddr)
455     {
456         T *node = reinterpret_cast<T *>(nodeAddr);
457         if (!node->IsUsing()) {
458             return;
459         }
460         if constexpr(nodeKind == NodeKind::UNIFIED_NODE) {
461             DisposeGlobalHandleInner(node, &xRefFreeListNodes_, &topXRefGlobalNodes_,
462                 &lastXRefGlobalNodes_);
463         } else if constexpr(nodeKind == NodeKind::NORMAL_NODE) {
464             if (node->IsWeak()) {
465                 DisposeGlobalHandleInner(reinterpret_cast<WeakNode *>(node), &weakFreeListNodes_, &topWeakGlobalNodes_,
466                     &lastWeakGlobalNodes_);
467             } else {
468                 DisposeGlobalHandleInner(node, &freeListNodes_, &topGlobalNodes_, &lastGlobalNodes_);
469             }
470         }
471     }
472 
473     inline uintptr_t SetWeak(uintptr_t nodeAddr, void *ref = nullptr, WeakClearCallback freeGlobalCallback = nullptr,
474                              WeakClearCallback nativeFinalizeCallback = nullptr)
475     {
476         auto value = reinterpret_cast<T *>(nodeAddr)->GetObject();
477         DisposeGlobalHandle<NodeKind::NORMAL_NODE>(nodeAddr);
478         uintptr_t addr = NewGlobalHandleImplement(&lastWeakGlobalNodes_, &weakFreeListNodes_, value);
479         WeakNode *node = reinterpret_cast<WeakNode *>(addr);
480         node->SetReference(ref);
481         node->SetFreeGlobalCallback(freeGlobalCallback);
482         node->SetNativeFinalizeCallback(nativeFinalizeCallback);
483         return addr;
484     }
485 
ClearWeak(uintptr_t nodeAddr)486     inline uintptr_t ClearWeak(uintptr_t nodeAddr)
487     {
488         auto value = reinterpret_cast<T *>(nodeAddr)->GetObject();
489         DisposeGlobalHandle<NodeKind::NORMAL_NODE>(nodeAddr);
490         uintptr_t ret = NewGlobalHandleImplement(&lastGlobalNodes_, &freeListNodes_, value);
491         return ret;
492     }
493 
IsWeak(uintptr_t addr)494     inline bool IsWeak(uintptr_t addr) const
495     {
496         T *node = reinterpret_cast<T *>(addr);
497         return node->IsWeak();
498     }
499 
500     template<class Callback>
IterateUsageGlobal(Callback && callback)501     void IterateUsageGlobal(Callback &&callback)
502     {
503         IterateNodeList<Callback, T>(callback, topGlobalNodes_);
504         if (nodeKind_ == NodeKind::UNIFIED_NODE) {
505             return;
506         }
507         IterateNodeList<Callback, T>(callback, topXRefGlobalNodes_);
508     }
509 
510     template<class Callback>
IterateWeakUsageGlobal(Callback callback)511     void IterateWeakUsageGlobal(Callback callback)
512     {
513         IterateNodeList<Callback, WeakNode>(callback, topWeakGlobalNodes_);
514     }
515 
516     ECMAGLOBALSTORAGE_PUBLIC_HYBRID_EXTENSION()
517 private:
518     NO_COPY_SEMANTIC(EcmaGlobalStorage);
519     NO_MOVE_SEMANTIC(EcmaGlobalStorage);
520     template<typename S>
DisposeGlobalHandleInner(S * node,NodeList<S> ** freeList,NodeList<S> ** topNodes,NodeList<S> ** lastNodes)521     inline void DisposeGlobalHandleInner(S *node, NodeList<S> **freeList, NodeList<S> **topNodes,
522                                     NodeList<S> **lastNodes)
523     {
524         NodeList<S> *list = NodeList<S>::NodeToNodeList(node);
525         list->FreeNode(thread_, node);
526 
527         // If NodeList has no usage node, then delete NodeList
528         if (!list->HasUsagedNode() && (*topNodes != *lastNodes)) {
529             list->RemoveList();
530             if (*freeList == list) {
531                 *freeList = list->GetFreeNext();
532             }
533             if (*topNodes == list) {
534                 *topNodes = list->GetNext();
535             }
536             if (*lastNodes == list) {
537                 *lastNodes = list->GetPrev();
538             }
539             allocator_->Delete(list);
540         } else {
541             // Add to freeList
542             if (list != *freeList && list->GetFreeNext() == nullptr && list->GetFreePrev() == nullptr) {
543                 list->SetFreeNext(*freeList);
544                 if (*freeList != nullptr) {
545                     (*freeList)->SetFreePrev(list);
546                 }
547                 *freeList = list;
548             }
549         }
550     }
551 
552     template<typename S>
NewGlobalHandleImplement(NodeList<S> ** storage,NodeList<S> ** freeList,JSTaggedType value)553     inline uintptr_t NewGlobalHandleImplement(NodeList<S> **storage, NodeList<S> **freeList, JSTaggedType value)
554     {
555 #if ECMASCRIPT_ENABLE_NEW_HANDLE_CHECK
556         thread_->CheckJSTaggedType(value);
557 #endif
558         if (!(*storage)->IsFull()) {
559             // alloc new block
560             S *node = (*storage)->NewNode(thread_, value);
561             ASSERT(node != nullptr);
562             return node->GetObjectAddress();
563         }
564         if (*freeList != nullptr) {
565             // use free_list node
566             S *node = (*freeList)->GetFreeNode(thread_, value);
567             ASSERT(node != nullptr);
568             if (!(*freeList)->HasFreeNode()) {
569                 auto next = (*freeList)->GetFreeNext();
570                 (*freeList)->SetFreeNext(nullptr);
571                 (*freeList)->SetFreePrev(nullptr);
572                 if (next != nullptr) {
573                     next->SetFreePrev(nullptr);
574                 }
575                 *freeList = next;
576             }
577             return node->GetObjectAddress();
578         }
579         auto block = allocator_->New<NodeList<S>>();
580         if (block == nullptr) {
581             LOG_ECMA(FATAL) << "NewGlobalHandleImplement:block is nullptr";
582         }
583         block->LinkTo(*storage);
584         *storage = block;
585 
586         // use node in block finally
587         S *node = (*storage)->NewNode(thread_, value);
588         ASSERT(node != nullptr);
589         return node->GetObjectAddress();
590     }
591 
592     [[maybe_unused]] JSThread *thread_ {nullptr};
593     NativeAreaAllocator *allocator_ {nullptr};
594     NodeList<T> *topGlobalNodes_ {nullptr};
595     NodeList<T> *lastGlobalNodes_ {nullptr};
596     NodeList<T> *freeListNodes_ {nullptr};
597 
598     NodeList<WeakNode> *topWeakGlobalNodes_ {nullptr};
599     NodeList<WeakNode> *lastWeakGlobalNodes_ {nullptr};
600     NodeList<WeakNode> *weakFreeListNodes_ {nullptr};
601     ECMAGLOBALSTORAGE_PRIVATE_HYBRID_EXTENSION();
602 };
603 }  // namespace panda::ecmascript
604 #endif  // ECMASCRIPT_ECMA_GLOBAL_STORAGE_H
605