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