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