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