1 // Copyright 2009 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "src/global-handles.h" 6 7 #include "src/api-inl.h" 8 #include "src/cancelable-task.h" 9 #include "src/objects-inl.h" 10 #include "src/v8.h" 11 #include "src/visitors.h" 12 #include "src/vm-state-inl.h" 13 14 namespace v8 { 15 namespace internal { 16 17 class GlobalHandles::Node { 18 public: 19 // State transition diagram: 20 // FREE -> NORMAL <-> WEAK -> PENDING -> NEAR_DEATH -> { NORMAL, WEAK, FREE } 21 enum State { 22 FREE = 0, 23 NORMAL, // Normal global handle. 24 WEAK, // Flagged as weak but not yet finalized. 25 PENDING, // Has been recognized as only reachable by weak handles. 26 NEAR_DEATH, // Callback has informed the handle is near death. 27 NUMBER_OF_NODE_STATES 28 }; 29 30 // Maps handle location (slot) to the containing node. 31 static Node* FromLocation(Object** location) { 32 DCHECK_EQ(offsetof(Node, object_), 0); 33 return reinterpret_cast<Node*>(location); 34 } 35 36 Node() { 37 DCHECK_EQ(offsetof(Node, class_id_), Internals::kNodeClassIdOffset); 38 DCHECK_EQ(offsetof(Node, flags_), Internals::kNodeFlagsOffset); 39 STATIC_ASSERT(static_cast<int>(NodeState::kMask) == 40 Internals::kNodeStateMask); 41 STATIC_ASSERT(WEAK == Internals::kNodeStateIsWeakValue); 42 STATIC_ASSERT(PENDING == Internals::kNodeStateIsPendingValue); 43 STATIC_ASSERT(NEAR_DEATH == Internals::kNodeStateIsNearDeathValue); 44 STATIC_ASSERT(static_cast<int>(IsIndependent::kShift) == 45 Internals::kNodeIsIndependentShift); 46 STATIC_ASSERT(static_cast<int>(IsActive::kShift) == 47 Internals::kNodeIsActiveShift); 48 } 49 50 #ifdef ENABLE_HANDLE_ZAPPING 51 ~Node() { 52 // TODO(1428): if it's a weak handle we should have invoked its callback. 53 // Zap the values for eager trapping. 54 object_ = reinterpret_cast<Object*>(kGlobalHandleZapValue); 55 class_id_ = v8::HeapProfiler::kPersistentHandleNoClassId; 56 index_ = 0; 57 set_independent(false); 58 set_active(false); 59 set_in_new_space_list(false); 60 data_.next_free = nullptr; 61 weak_callback_ = nullptr; 62 } 63 #endif 64 65 void Initialize(int index, Node** first_free) { 66 object_ = reinterpret_cast<Object*>(kGlobalHandleZapValue); 67 index_ = static_cast<uint8_t>(index); 68 DCHECK(static_cast<int>(index_) == index); 69 set_state(FREE); 70 set_in_new_space_list(false); 71 data_.next_free = *first_free; 72 *first_free = this; 73 } 74 75 void Acquire(Object* object) { 76 DCHECK(state() == FREE); 77 object_ = object; 78 class_id_ = v8::HeapProfiler::kPersistentHandleNoClassId; 79 set_independent(false); 80 set_active(false); 81 set_state(NORMAL); 82 data_.parameter = nullptr; 83 weak_callback_ = nullptr; 84 IncreaseBlockUses(); 85 } 86 87 void Zap() { 88 DCHECK(IsInUse()); 89 // Zap the values for eager trapping. 90 object_ = reinterpret_cast<Object*>(kGlobalHandleZapValue); 91 } 92 93 void Release() { 94 DCHECK(IsInUse()); 95 set_state(FREE); 96 // Zap the values for eager trapping. 97 object_ = reinterpret_cast<Object*>(kGlobalHandleZapValue); 98 class_id_ = v8::HeapProfiler::kPersistentHandleNoClassId; 99 set_independent(false); 100 set_active(false); 101 weak_callback_ = nullptr; 102 DecreaseBlockUses(); 103 } 104 105 // Object slot accessors. 106 Object* object() const { return object_; } 107 Object** location() { return &object_; } 108 const char* label() { return state() == NORMAL ? data_.label : nullptr; } 109 Handle<Object> handle() { return Handle<Object>(location()); } 110 111 // Wrapper class ID accessors. 112 bool has_wrapper_class_id() const { 113 return class_id_ != v8::HeapProfiler::kPersistentHandleNoClassId; 114 } 115 116 uint16_t wrapper_class_id() const { return class_id_; } 117 118 // State and flag accessors. 119 120 State state() const { 121 return NodeState::decode(flags_); 122 } 123 void set_state(State state) { 124 flags_ = NodeState::update(flags_, state); 125 } 126 127 bool is_independent() { return IsIndependent::decode(flags_); } 128 void set_independent(bool v) { flags_ = IsIndependent::update(flags_, v); } 129 130 bool is_active() { 131 return IsActive::decode(flags_); 132 } 133 void set_active(bool v) { 134 flags_ = IsActive::update(flags_, v); 135 } 136 137 bool is_in_new_space_list() { 138 return IsInNewSpaceList::decode(flags_); 139 } 140 void set_in_new_space_list(bool v) { 141 flags_ = IsInNewSpaceList::update(flags_, v); 142 } 143 144 WeaknessType weakness_type() const { 145 return NodeWeaknessType::decode(flags_); 146 } 147 void set_weakness_type(WeaknessType weakness_type) { 148 flags_ = NodeWeaknessType::update(flags_, weakness_type); 149 } 150 151 bool IsNearDeath() const { 152 // Check for PENDING to ensure correct answer when processing callbacks. 153 return state() == PENDING || state() == NEAR_DEATH; 154 } 155 156 bool IsWeak() const { return state() == WEAK; } 157 158 bool IsInUse() const { return state() != FREE; } 159 160 bool IsPhantomCallback() const { 161 return weakness_type() == PHANTOM_WEAK || 162 weakness_type() == PHANTOM_WEAK_2_EMBEDDER_FIELDS; 163 } 164 165 bool IsPhantomResetHandle() const { 166 return weakness_type() == PHANTOM_WEAK_RESET_HANDLE; 167 } 168 169 bool IsPendingPhantomCallback() const { 170 return state() == PENDING && IsPhantomCallback(); 171 } 172 173 bool IsPendingPhantomResetHandle() const { 174 return state() == PENDING && IsPhantomResetHandle(); 175 } 176 177 bool IsRetainer() const { 178 return state() != FREE && 179 !(state() == NEAR_DEATH && weakness_type() != FINALIZER_WEAK); 180 } 181 182 bool IsStrongRetainer() const { return state() == NORMAL; } 183 184 bool IsWeakRetainer() const { 185 return state() == WEAK || state() == PENDING || 186 (state() == NEAR_DEATH && weakness_type() == FINALIZER_WEAK); 187 } 188 189 void MarkPending() { 190 DCHECK(state() == WEAK); 191 set_state(PENDING); 192 } 193 194 // Callback parameter accessors. 195 void set_parameter(void* parameter) { 196 DCHECK(IsInUse()); 197 data_.parameter = parameter; 198 } 199 void* parameter() const { 200 DCHECK(IsInUse()); 201 return data_.parameter; 202 } 203 204 // Accessors for next free node in the free list. 205 Node* next_free() { 206 DCHECK(state() == FREE); 207 return data_.next_free; 208 } 209 void set_next_free(Node* value) { 210 DCHECK(state() == FREE); 211 data_.next_free = value; 212 } 213 214 void MakeWeak(void* parameter, 215 WeakCallbackInfo<void>::Callback phantom_callback, 216 v8::WeakCallbackType type) { 217 DCHECK_NOT_NULL(phantom_callback); 218 DCHECK(IsInUse()); 219 CHECK_NE(object_, reinterpret_cast<Object*>(kGlobalHandleZapValue)); 220 set_state(WEAK); 221 switch (type) { 222 case v8::WeakCallbackType::kParameter: 223 set_weakness_type(PHANTOM_WEAK); 224 break; 225 case v8::WeakCallbackType::kInternalFields: 226 set_weakness_type(PHANTOM_WEAK_2_EMBEDDER_FIELDS); 227 break; 228 case v8::WeakCallbackType::kFinalizer: 229 set_weakness_type(FINALIZER_WEAK); 230 break; 231 } 232 set_parameter(parameter); 233 weak_callback_ = phantom_callback; 234 } 235 236 void MakeWeak(Object*** location_addr) { 237 DCHECK(IsInUse()); 238 CHECK_NE(object_, reinterpret_cast<Object*>(kGlobalHandleZapValue)); 239 set_state(WEAK); 240 set_weakness_type(PHANTOM_WEAK_RESET_HANDLE); 241 set_parameter(location_addr); 242 weak_callback_ = nullptr; 243 } 244 245 void* ClearWeakness() { 246 DCHECK(IsInUse()); 247 void* p = parameter(); 248 set_state(NORMAL); 249 set_parameter(nullptr); 250 return p; 251 } 252 253 void AnnotateStrongRetainer(const char* label) { 254 DCHECK_EQ(state(), NORMAL); 255 data_.label = label; 256 } 257 258 void CollectPhantomCallbackData( 259 260 std::vector<PendingPhantomCallback>* pending_phantom_callbacks) { 261 DCHECK(weakness_type() == PHANTOM_WEAK || 262 weakness_type() == PHANTOM_WEAK_2_EMBEDDER_FIELDS); 263 DCHECK(state() == PENDING); 264 DCHECK_NOT_NULL(weak_callback_); 265 266 void* embedder_fields[v8::kEmbedderFieldsInWeakCallback] = {nullptr, 267 nullptr}; 268 if (weakness_type() != PHANTOM_WEAK && object()->IsJSObject()) { 269 auto jsobject = JSObject::cast(object()); 270 int field_count = jsobject->GetEmbedderFieldCount(); 271 for (int i = 0; i < v8::kEmbedderFieldsInWeakCallback; ++i) { 272 if (field_count == i) break; 273 auto field = jsobject->GetEmbedderField(i); 274 if (field->IsSmi()) embedder_fields[i] = field; 275 } 276 } 277 278 // Zap with something dangerous. 279 *location() = reinterpret_cast<Object*>(0x6057CA11); 280 281 pending_phantom_callbacks->push_back(PendingPhantomCallback( 282 this, weak_callback_, parameter(), embedder_fields)); 283 DCHECK(IsInUse()); 284 set_state(NEAR_DEATH); 285 } 286 287 void ResetPhantomHandle() { 288 DCHECK(weakness_type() == PHANTOM_WEAK_RESET_HANDLE); 289 DCHECK(state() == PENDING); 290 DCHECK_NULL(weak_callback_); 291 Object*** handle = reinterpret_cast<Object***>(parameter()); 292 *handle = nullptr; 293 Release(); 294 } 295 296 bool PostGarbageCollectionProcessing(Isolate* isolate) { 297 // Handles only weak handles (not phantom) that are dying. 298 if (state() != Node::PENDING) return false; 299 if (weak_callback_ == nullptr) { 300 Release(); 301 return false; 302 } 303 set_state(NEAR_DEATH); 304 305 // Check that we are not passing a finalized external string to 306 // the callback. 307 DCHECK(!object_->IsExternalOneByteString() || 308 ExternalOneByteString::cast(object_)->resource() != nullptr); 309 DCHECK(!object_->IsExternalTwoByteString() || 310 ExternalTwoByteString::cast(object_)->resource() != nullptr); 311 if (weakness_type() != FINALIZER_WEAK) { 312 return false; 313 } 314 315 // Leaving V8. 316 VMState<EXTERNAL> vmstate(isolate); 317 HandleScope handle_scope(isolate); 318 void* embedder_fields[v8::kEmbedderFieldsInWeakCallback] = {nullptr, 319 nullptr}; 320 v8::WeakCallbackInfo<void> data(reinterpret_cast<v8::Isolate*>(isolate), 321 parameter(), embedder_fields, nullptr); 322 weak_callback_(data); 323 324 // Absence of explicit cleanup or revival of weak handle 325 // in most of the cases would lead to memory leak. 326 CHECK(state() != NEAR_DEATH); 327 return true; 328 } 329 330 inline GlobalHandles* GetGlobalHandles(); 331 332 private: 333 inline NodeBlock* FindBlock(); 334 inline void IncreaseBlockUses(); 335 inline void DecreaseBlockUses(); 336 337 // Storage for object pointer. 338 // Placed first to avoid offset computation. 339 Object* object_; 340 341 // Next word stores class_id, index, state, and independent. 342 // Note: the most aligned fields should go first. 343 344 // Wrapper class ID. 345 uint16_t class_id_; 346 347 // Index in the containing handle block. 348 uint8_t index_; 349 350 // This stores three flags (independent, partially_dependent and 351 // in_new_space_list) and a State. 352 class NodeState : public BitField<State, 0, 3> {}; 353 class IsIndependent : public BitField<bool, 3, 1> {}; 354 // The following two fields are mutually exclusive 355 class IsActive : public BitField<bool, 4, 1> {}; 356 class IsInNewSpaceList : public BitField<bool, 5, 1> {}; 357 class NodeWeaknessType : public BitField<WeaknessType, 6, 2> {}; 358 359 uint8_t flags_; 360 361 // Handle specific callback - might be a weak reference in disguise. 362 WeakCallbackInfo<void>::Callback weak_callback_; 363 364 // The meaning of this field depends on node state: 365 // state == FREE: it stores the next free node pointer. 366 // state == NORMAL: it stores the strong retainer label. 367 // otherwise: it stores the parameter for the weak callback. 368 union { 369 Node* next_free; 370 const char* label; 371 void* parameter; 372 } data_; 373 374 DISALLOW_COPY_AND_ASSIGN(Node); 375 }; 376 377 378 class GlobalHandles::NodeBlock { 379 public: 380 static const int kSize = 256; 381 382 explicit NodeBlock(GlobalHandles* global_handles, NodeBlock* next) 383 : next_(next), 384 used_nodes_(0), 385 next_used_(nullptr), 386 prev_used_(nullptr), 387 global_handles_(global_handles) {} 388 389 void PutNodesOnFreeList(Node** first_free) { 390 for (int i = kSize - 1; i >= 0; --i) { 391 nodes_[i].Initialize(i, first_free); 392 } 393 } 394 395 Node* node_at(int index) { 396 DCHECK(0 <= index && index < kSize); 397 return &nodes_[index]; 398 } 399 400 void IncreaseUses() { 401 DCHECK_LT(used_nodes_, kSize); 402 if (used_nodes_++ == 0) { 403 NodeBlock* old_first = global_handles_->first_used_block_; 404 global_handles_->first_used_block_ = this; 405 next_used_ = old_first; 406 prev_used_ = nullptr; 407 if (old_first == nullptr) return; 408 old_first->prev_used_ = this; 409 } 410 } 411 412 void DecreaseUses() { 413 DCHECK_GT(used_nodes_, 0); 414 if (--used_nodes_ == 0) { 415 if (next_used_ != nullptr) next_used_->prev_used_ = prev_used_; 416 if (prev_used_ != nullptr) prev_used_->next_used_ = next_used_; 417 if (this == global_handles_->first_used_block_) { 418 global_handles_->first_used_block_ = next_used_; 419 } 420 } 421 } 422 423 GlobalHandles* global_handles() { return global_handles_; } 424 425 // Next block in the list of all blocks. 426 NodeBlock* next() const { return next_; } 427 428 // Next/previous block in the list of blocks with used nodes. 429 NodeBlock* next_used() const { return next_used_; } 430 NodeBlock* prev_used() const { return prev_used_; } 431 432 private: 433 Node nodes_[kSize]; 434 NodeBlock* const next_; 435 int used_nodes_; 436 NodeBlock* next_used_; 437 NodeBlock* prev_used_; 438 GlobalHandles* global_handles_; 439 }; 440 441 442 GlobalHandles* GlobalHandles::Node::GetGlobalHandles() { 443 return FindBlock()->global_handles(); 444 } 445 446 447 GlobalHandles::NodeBlock* GlobalHandles::Node::FindBlock() { 448 intptr_t ptr = reinterpret_cast<intptr_t>(this); 449 ptr = ptr - index_ * sizeof(Node); 450 NodeBlock* block = reinterpret_cast<NodeBlock*>(ptr); 451 DCHECK(block->node_at(index_) == this); 452 return block; 453 } 454 455 456 void GlobalHandles::Node::IncreaseBlockUses() { 457 NodeBlock* node_block = FindBlock(); 458 node_block->IncreaseUses(); 459 GlobalHandles* global_handles = node_block->global_handles(); 460 global_handles->isolate()->counters()->global_handles()->Increment(); 461 global_handles->number_of_global_handles_++; 462 } 463 464 465 void GlobalHandles::Node::DecreaseBlockUses() { 466 NodeBlock* node_block = FindBlock(); 467 GlobalHandles* global_handles = node_block->global_handles(); 468 data_.next_free = global_handles->first_free_; 469 global_handles->first_free_ = this; 470 node_block->DecreaseUses(); 471 global_handles->isolate()->counters()->global_handles()->Decrement(); 472 global_handles->number_of_global_handles_--; 473 } 474 475 476 class GlobalHandles::NodeIterator { 477 public: 478 explicit NodeIterator(GlobalHandles* global_handles) 479 : block_(global_handles->first_used_block_), 480 index_(0) {} 481 482 bool done() const { return block_ == nullptr; } 483 484 Node* node() const { 485 DCHECK(!done()); 486 return block_->node_at(index_); 487 } 488 489 void Advance() { 490 DCHECK(!done()); 491 if (++index_ < NodeBlock::kSize) return; 492 index_ = 0; 493 block_ = block_->next_used(); 494 } 495 496 private: 497 NodeBlock* block_; 498 int index_; 499 500 DISALLOW_COPY_AND_ASSIGN(NodeIterator); 501 }; 502 503 class GlobalHandles::PendingPhantomCallbacksSecondPassTask 504 : public v8::internal::CancelableTask { 505 public: 506 PendingPhantomCallbacksSecondPassTask(GlobalHandles* global_handles, 507 Isolate* isolate) 508 : CancelableTask(isolate), global_handles_(global_handles) {} 509 510 void RunInternal() override { 511 global_handles_->InvokeSecondPassPhantomCallbacksFromTask(); 512 } 513 514 private: 515 GlobalHandles* global_handles_; 516 DISALLOW_COPY_AND_ASSIGN(PendingPhantomCallbacksSecondPassTask); 517 }; 518 519 GlobalHandles::GlobalHandles(Isolate* isolate) 520 : isolate_(isolate), 521 number_of_global_handles_(0), 522 first_block_(nullptr), 523 first_used_block_(nullptr), 524 first_free_(nullptr), 525 post_gc_processing_count_(0), 526 number_of_phantom_handle_resets_(0) {} 527 528 GlobalHandles::~GlobalHandles() { 529 NodeBlock* block = first_block_; 530 while (block != nullptr) { 531 NodeBlock* tmp = block->next(); 532 delete block; 533 block = tmp; 534 } 535 first_block_ = nullptr; 536 } 537 538 539 Handle<Object> GlobalHandles::Create(Object* value) { 540 if (first_free_ == nullptr) { 541 first_block_ = new NodeBlock(this, first_block_); 542 first_block_->PutNodesOnFreeList(&first_free_); 543 } 544 DCHECK_NOT_NULL(first_free_); 545 // Take the first node in the free list. 546 Node* result = first_free_; 547 first_free_ = result->next_free(); 548 result->Acquire(value); 549 if (Heap::InNewSpace(value) && !result->is_in_new_space_list()) { 550 new_space_nodes_.push_back(result); 551 result->set_in_new_space_list(true); 552 } 553 return result->handle(); 554 } 555 556 557 Handle<Object> GlobalHandles::CopyGlobal(Object** location) { 558 DCHECK_NOT_NULL(location); 559 GlobalHandles* global_handles = 560 Node::FromLocation(location)->GetGlobalHandles(); 561 #ifdef VERIFY_HEAP 562 if (i::FLAG_verify_heap) { 563 (*location)->ObjectVerify(global_handles->isolate()); 564 } 565 #endif // VERIFY_HEAP 566 return global_handles->Create(*location); 567 } 568 569 570 void GlobalHandles::Destroy(Object** location) { 571 if (location != nullptr) Node::FromLocation(location)->Release(); 572 } 573 574 575 typedef v8::WeakCallbackInfo<void>::Callback GenericCallback; 576 577 578 void GlobalHandles::MakeWeak(Object** location, void* parameter, 579 GenericCallback phantom_callback, 580 v8::WeakCallbackType type) { 581 Node::FromLocation(location)->MakeWeak(parameter, phantom_callback, type); 582 } 583 584 void GlobalHandles::MakeWeak(Object*** location_addr) { 585 Node::FromLocation(*location_addr)->MakeWeak(location_addr); 586 } 587 588 void* GlobalHandles::ClearWeakness(Object** location) { 589 return Node::FromLocation(location)->ClearWeakness(); 590 } 591 592 void GlobalHandles::AnnotateStrongRetainer(Object** location, 593 const char* label) { 594 Node::FromLocation(location)->AnnotateStrongRetainer(label); 595 } 596 597 bool GlobalHandles::IsNearDeath(Object** location) { 598 return Node::FromLocation(location)->IsNearDeath(); 599 } 600 601 602 bool GlobalHandles::IsWeak(Object** location) { 603 return Node::FromLocation(location)->IsWeak(); 604 } 605 606 DISABLE_CFI_PERF 607 void GlobalHandles::IterateWeakRootsForFinalizers(RootVisitor* v) { 608 for (NodeIterator it(this); !it.done(); it.Advance()) { 609 Node* node = it.node(); 610 if (node->IsWeakRetainer() && node->state() == Node::PENDING) { 611 DCHECK(!node->IsPhantomCallback()); 612 DCHECK(!node->IsPhantomResetHandle()); 613 // Finalizers need to survive. 614 v->VisitRootPointer(Root::kGlobalHandles, node->label(), 615 node->location()); 616 } 617 } 618 } 619 620 DISABLE_CFI_PERF 621 void GlobalHandles::IterateWeakRootsForPhantomHandles( 622 WeakSlotCallbackWithHeap should_reset_handle) { 623 for (NodeIterator it(this); !it.done(); it.Advance()) { 624 Node* node = it.node(); 625 if (node->IsWeakRetainer() && 626 should_reset_handle(isolate()->heap(), node->location())) { 627 if (node->IsPhantomResetHandle()) { 628 node->MarkPending(); 629 node->ResetPhantomHandle(); 630 ++number_of_phantom_handle_resets_; 631 } else if (node->IsPhantomCallback()) { 632 node->MarkPending(); 633 node->CollectPhantomCallbackData(&pending_phantom_callbacks_); 634 } 635 } 636 } 637 } 638 639 void GlobalHandles::IdentifyWeakHandles( 640 WeakSlotCallbackWithHeap should_reset_handle) { 641 for (NodeIterator it(this); !it.done(); it.Advance()) { 642 Node* node = it.node(); 643 if (node->IsWeak() && 644 should_reset_handle(isolate()->heap(), node->location())) { 645 if (!node->IsPhantomCallback() && !node->IsPhantomResetHandle()) { 646 node->MarkPending(); 647 } 648 } 649 } 650 } 651 652 void GlobalHandles::IterateNewSpaceStrongAndDependentRoots(RootVisitor* v) { 653 for (Node* node : new_space_nodes_) { 654 if (node->IsStrongRetainer() || 655 (node->IsWeakRetainer() && !node->is_independent() && 656 node->is_active())) { 657 v->VisitRootPointer(Root::kGlobalHandles, node->label(), 658 node->location()); 659 } 660 } 661 } 662 663 void GlobalHandles::IterateNewSpaceStrongAndDependentRootsAndIdentifyUnmodified( 664 RootVisitor* v, size_t start, size_t end) { 665 for (size_t i = start; i < end; ++i) { 666 Node* node = new_space_nodes_[i]; 667 if (node->IsWeak() && !JSObject::IsUnmodifiedApiObject(node->location())) { 668 node->set_active(true); 669 } 670 if (node->IsStrongRetainer() || 671 (node->IsWeakRetainer() && !node->is_independent() && 672 node->is_active())) { 673 v->VisitRootPointer(Root::kGlobalHandles, node->label(), 674 node->location()); 675 } 676 } 677 } 678 679 void GlobalHandles::IdentifyWeakUnmodifiedObjects( 680 WeakSlotCallback is_unmodified) { 681 for (Node* node : new_space_nodes_) { 682 if (node->IsWeak() && !is_unmodified(node->location())) { 683 node->set_active(true); 684 } 685 } 686 } 687 688 void GlobalHandles::MarkNewSpaceWeakUnmodifiedObjectsPending( 689 WeakSlotCallbackWithHeap is_dead) { 690 for (Node* node : new_space_nodes_) { 691 DCHECK(node->is_in_new_space_list()); 692 if ((node->is_independent() || !node->is_active()) && node->IsWeak() && 693 is_dead(isolate_->heap(), node->location())) { 694 if (!node->IsPhantomCallback() && !node->IsPhantomResetHandle()) { 695 node->MarkPending(); 696 } 697 } 698 } 699 } 700 701 void GlobalHandles::IterateNewSpaceWeakUnmodifiedRootsForFinalizers( 702 RootVisitor* v) { 703 for (Node* node : new_space_nodes_) { 704 DCHECK(node->is_in_new_space_list()); 705 if ((node->is_independent() || !node->is_active()) && 706 node->IsWeakRetainer() && (node->state() == Node::PENDING)) { 707 DCHECK(!node->IsPhantomCallback()); 708 DCHECK(!node->IsPhantomResetHandle()); 709 // Finalizers need to survive. 710 v->VisitRootPointer(Root::kGlobalHandles, node->label(), 711 node->location()); 712 } 713 } 714 } 715 716 void GlobalHandles::IterateNewSpaceWeakUnmodifiedRootsForPhantomHandles( 717 RootVisitor* v, WeakSlotCallbackWithHeap should_reset_handle) { 718 for (Node* node : new_space_nodes_) { 719 DCHECK(node->is_in_new_space_list()); 720 if ((node->is_independent() || !node->is_active()) && 721 node->IsWeakRetainer() && (node->state() != Node::PENDING)) { 722 DCHECK(node->IsPhantomResetHandle() || node->IsPhantomCallback()); 723 if (should_reset_handle(isolate_->heap(), node->location())) { 724 if (node->IsPhantomResetHandle()) { 725 node->MarkPending(); 726 node->ResetPhantomHandle(); 727 ++number_of_phantom_handle_resets_; 728 729 } else if (node->IsPhantomCallback()) { 730 node->MarkPending(); 731 node->CollectPhantomCallbackData(&pending_phantom_callbacks_); 732 } else { 733 UNREACHABLE(); 734 } 735 } else { 736 // Node survived and needs to be visited. 737 v->VisitRootPointer(Root::kGlobalHandles, node->label(), 738 node->location()); 739 } 740 } 741 } 742 } 743 744 void GlobalHandles::InvokeSecondPassPhantomCallbacksFromTask() { 745 DCHECK(second_pass_callbacks_task_posted_); 746 second_pass_callbacks_task_posted_ = false; 747 TRACE_EVENT0("v8", "V8.GCPhantomHandleProcessingCallback"); 748 isolate()->heap()->CallGCPrologueCallbacks( 749 GCType::kGCTypeProcessWeakCallbacks, kNoGCCallbackFlags); 750 InvokeSecondPassPhantomCallbacks(); 751 isolate()->heap()->CallGCEpilogueCallbacks( 752 GCType::kGCTypeProcessWeakCallbacks, kNoGCCallbackFlags); 753 } 754 755 void GlobalHandles::InvokeSecondPassPhantomCallbacks() { 756 while (!second_pass_callbacks_.empty()) { 757 auto callback = second_pass_callbacks_.back(); 758 second_pass_callbacks_.pop_back(); 759 DCHECK_NULL(callback.node()); 760 // Fire second pass callback 761 callback.Invoke(isolate()); 762 } 763 } 764 765 int GlobalHandles::PostScavengeProcessing( 766 const int initial_post_gc_processing_count) { 767 int freed_nodes = 0; 768 for (Node* node : new_space_nodes_) { 769 DCHECK(node->is_in_new_space_list()); 770 if (!node->IsRetainer()) { 771 // Free nodes do not have weak callbacks. Do not use them to compute 772 // the freed_nodes. 773 continue; 774 } 775 // Skip dependent or unmodified handles. Their weak callbacks might expect 776 // to be 777 // called between two global garbage collection callbacks which 778 // are not called for minor collections. 779 if (!node->is_independent() && (node->is_active())) { 780 node->set_active(false); 781 continue; 782 } 783 node->set_active(false); 784 785 if (node->PostGarbageCollectionProcessing(isolate_)) { 786 if (initial_post_gc_processing_count != post_gc_processing_count_) { 787 // Weak callback triggered another GC and another round of 788 // PostGarbageCollection processing. The current node might 789 // have been deleted in that round, so we need to bail out (or 790 // restart the processing). 791 return freed_nodes; 792 } 793 } 794 if (!node->IsRetainer()) { 795 freed_nodes++; 796 } 797 } 798 return freed_nodes; 799 } 800 801 802 int GlobalHandles::PostMarkSweepProcessing( 803 const int initial_post_gc_processing_count) { 804 int freed_nodes = 0; 805 for (NodeIterator it(this); !it.done(); it.Advance()) { 806 if (!it.node()->IsRetainer()) { 807 // Free nodes do not have weak callbacks. Do not use them to compute 808 // the freed_nodes. 809 continue; 810 } 811 it.node()->set_active(false); 812 if (it.node()->PostGarbageCollectionProcessing(isolate_)) { 813 if (initial_post_gc_processing_count != post_gc_processing_count_) { 814 // See the comment above. 815 return freed_nodes; 816 } 817 } 818 if (!it.node()->IsRetainer()) { 819 freed_nodes++; 820 } 821 } 822 return freed_nodes; 823 } 824 825 826 void GlobalHandles::UpdateListOfNewSpaceNodes() { 827 size_t last = 0; 828 for (Node* node : new_space_nodes_) { 829 DCHECK(node->is_in_new_space_list()); 830 if (node->IsRetainer()) { 831 if (Heap::InNewSpace(node->object())) { 832 new_space_nodes_[last++] = node; 833 isolate_->heap()->IncrementNodesCopiedInNewSpace(); 834 } else { 835 node->set_in_new_space_list(false); 836 isolate_->heap()->IncrementNodesPromoted(); 837 } 838 } else { 839 node->set_in_new_space_list(false); 840 isolate_->heap()->IncrementNodesDiedInNewSpace(); 841 } 842 } 843 DCHECK_LE(last, new_space_nodes_.size()); 844 new_space_nodes_.resize(last); 845 new_space_nodes_.shrink_to_fit(); 846 } 847 848 849 int GlobalHandles::DispatchPendingPhantomCallbacks( 850 bool synchronous_second_pass) { 851 int freed_nodes = 0; 852 // Protect against callback modifying pending_phantom_callbacks_. 853 std::vector<PendingPhantomCallback> pending_phantom_callbacks; 854 pending_phantom_callbacks.swap(pending_phantom_callbacks_); 855 { 856 // The initial pass callbacks must simply clear the nodes. 857 for (auto callback : pending_phantom_callbacks) { 858 // Skip callbacks that have already been processed once. 859 if (callback.node() == nullptr) continue; 860 callback.Invoke(isolate()); 861 if (callback.callback()) second_pass_callbacks_.push_back(callback); 862 freed_nodes++; 863 } 864 } 865 if (!second_pass_callbacks_.empty()) { 866 if (FLAG_optimize_for_size || FLAG_predictable || synchronous_second_pass) { 867 isolate()->heap()->CallGCPrologueCallbacks( 868 GCType::kGCTypeProcessWeakCallbacks, kNoGCCallbackFlags); 869 InvokeSecondPassPhantomCallbacks(); 870 isolate()->heap()->CallGCEpilogueCallbacks( 871 GCType::kGCTypeProcessWeakCallbacks, kNoGCCallbackFlags); 872 } else if (!second_pass_callbacks_task_posted_) { 873 second_pass_callbacks_task_posted_ = true; 874 auto task = new PendingPhantomCallbacksSecondPassTask(this, isolate()); 875 V8::GetCurrentPlatform()->CallOnForegroundThread( 876 reinterpret_cast<v8::Isolate*>(isolate()), task); 877 } 878 } 879 return freed_nodes; 880 } 881 882 883 void GlobalHandles::PendingPhantomCallback::Invoke(Isolate* isolate) { 884 Data::Callback* callback_addr = nullptr; 885 if (node_ != nullptr) { 886 // Initialize for first pass callback. 887 DCHECK(node_->state() == Node::NEAR_DEATH); 888 callback_addr = &callback_; 889 } 890 Data data(reinterpret_cast<v8::Isolate*>(isolate), parameter_, 891 embedder_fields_, callback_addr); 892 Data::Callback callback = callback_; 893 callback_ = nullptr; 894 callback(data); 895 if (node_ != nullptr) { 896 // Transition to second pass. It is required that the first pass callback 897 // resets the handle using |v8::PersistentBase::Reset|. Also see comments on 898 // |v8::WeakCallbackInfo|. 899 CHECK_WITH_MSG(Node::FREE == node_->state(), 900 "Handle not reset in first callback. See comments on " 901 "|v8::WeakCallbackInfo|."); 902 node_ = nullptr; 903 } 904 } 905 906 907 int GlobalHandles::PostGarbageCollectionProcessing( 908 GarbageCollector collector, const v8::GCCallbackFlags gc_callback_flags) { 909 // Process weak global handle callbacks. This must be done after the 910 // GC is completely done, because the callbacks may invoke arbitrary 911 // API functions. 912 DCHECK(isolate_->heap()->gc_state() == Heap::NOT_IN_GC); 913 const int initial_post_gc_processing_count = ++post_gc_processing_count_; 914 int freed_nodes = 0; 915 bool synchronous_second_pass = 916 (gc_callback_flags & 917 (kGCCallbackFlagForced | kGCCallbackFlagCollectAllAvailableGarbage | 918 kGCCallbackFlagSynchronousPhantomCallbackProcessing)) != 0; 919 freed_nodes += DispatchPendingPhantomCallbacks(synchronous_second_pass); 920 if (initial_post_gc_processing_count != post_gc_processing_count_) { 921 // If the callbacks caused a nested GC, then return. See comment in 922 // PostScavengeProcessing. 923 return freed_nodes; 924 } 925 if (Heap::IsYoungGenerationCollector(collector)) { 926 freed_nodes += PostScavengeProcessing(initial_post_gc_processing_count); 927 } else { 928 freed_nodes += PostMarkSweepProcessing(initial_post_gc_processing_count); 929 } 930 if (initial_post_gc_processing_count != post_gc_processing_count_) { 931 // If the callbacks caused a nested GC, then return. See comment in 932 // PostScavengeProcessing. 933 return freed_nodes; 934 } 935 if (initial_post_gc_processing_count == post_gc_processing_count_) { 936 UpdateListOfNewSpaceNodes(); 937 } 938 return freed_nodes; 939 } 940 941 void GlobalHandles::IterateStrongRoots(RootVisitor* v) { 942 for (NodeIterator it(this); !it.done(); it.Advance()) { 943 if (it.node()->IsStrongRetainer()) { 944 v->VisitRootPointer(Root::kGlobalHandles, it.node()->label(), 945 it.node()->location()); 946 } 947 } 948 } 949 950 void GlobalHandles::IterateWeakRoots(RootVisitor* v) { 951 for (NodeIterator it(this); !it.done(); it.Advance()) { 952 if (it.node()->IsWeak()) { 953 v->VisitRootPointer(Root::kGlobalHandles, it.node()->label(), 954 it.node()->location()); 955 } 956 } 957 } 958 959 DISABLE_CFI_PERF 960 void GlobalHandles::IterateAllRoots(RootVisitor* v) { 961 for (NodeIterator it(this); !it.done(); it.Advance()) { 962 if (it.node()->IsRetainer()) { 963 v->VisitRootPointer(Root::kGlobalHandles, it.node()->label(), 964 it.node()->location()); 965 } 966 } 967 } 968 969 DISABLE_CFI_PERF 970 void GlobalHandles::IterateAllNewSpaceRoots(RootVisitor* v) { 971 for (Node* node : new_space_nodes_) { 972 if (node->IsRetainer()) { 973 v->VisitRootPointer(Root::kGlobalHandles, node->label(), 974 node->location()); 975 } 976 } 977 } 978 979 DISABLE_CFI_PERF 980 void GlobalHandles::IterateNewSpaceRoots(RootVisitor* v, size_t start, 981 size_t end) { 982 for (size_t i = start; i < end; ++i) { 983 Node* node = new_space_nodes_[i]; 984 if (node->IsRetainer()) { 985 v->VisitRootPointer(Root::kGlobalHandles, node->label(), 986 node->location()); 987 } 988 } 989 } 990 991 DISABLE_CFI_PERF 992 void GlobalHandles::ApplyPersistentHandleVisitor( 993 v8::PersistentHandleVisitor* visitor, GlobalHandles::Node* node) { 994 v8::Value* value = ToApi<v8::Value>(Handle<Object>(node->location())); 995 visitor->VisitPersistentHandle( 996 reinterpret_cast<v8::Persistent<v8::Value>*>(&value), 997 node->wrapper_class_id()); 998 } 999 1000 DISABLE_CFI_PERF 1001 void GlobalHandles::IterateAllRootsWithClassIds( 1002 v8::PersistentHandleVisitor* visitor) { 1003 for (NodeIterator it(this); !it.done(); it.Advance()) { 1004 if (it.node()->IsRetainer() && it.node()->has_wrapper_class_id()) { 1005 ApplyPersistentHandleVisitor(visitor, it.node()); 1006 } 1007 } 1008 } 1009 1010 1011 DISABLE_CFI_PERF 1012 void GlobalHandles::IterateAllRootsInNewSpaceWithClassIds( 1013 v8::PersistentHandleVisitor* visitor) { 1014 for (Node* node : new_space_nodes_) { 1015 if (node->IsRetainer() && node->has_wrapper_class_id()) { 1016 ApplyPersistentHandleVisitor(visitor, node); 1017 } 1018 } 1019 } 1020 1021 1022 DISABLE_CFI_PERF 1023 void GlobalHandles::IterateWeakRootsInNewSpaceWithClassIds( 1024 v8::PersistentHandleVisitor* visitor) { 1025 for (Node* node : new_space_nodes_) { 1026 if (node->has_wrapper_class_id() && node->IsWeak()) { 1027 ApplyPersistentHandleVisitor(visitor, node); 1028 } 1029 } 1030 } 1031 1032 void GlobalHandles::RecordStats(HeapStats* stats) { 1033 *stats->global_handle_count = 0; 1034 *stats->weak_global_handle_count = 0; 1035 *stats->pending_global_handle_count = 0; 1036 *stats->near_death_global_handle_count = 0; 1037 *stats->free_global_handle_count = 0; 1038 for (NodeIterator it(this); !it.done(); it.Advance()) { 1039 *stats->global_handle_count += 1; 1040 if (it.node()->state() == Node::WEAK) { 1041 *stats->weak_global_handle_count += 1; 1042 } else if (it.node()->state() == Node::PENDING) { 1043 *stats->pending_global_handle_count += 1; 1044 } else if (it.node()->state() == Node::NEAR_DEATH) { 1045 *stats->near_death_global_handle_count += 1; 1046 } else if (it.node()->state() == Node::FREE) { 1047 *stats->free_global_handle_count += 1; 1048 } 1049 } 1050 } 1051 1052 #ifdef DEBUG 1053 1054 void GlobalHandles::PrintStats() { 1055 int total = 0; 1056 int weak = 0; 1057 int pending = 0; 1058 int near_death = 0; 1059 int destroyed = 0; 1060 1061 for (NodeIterator it(this); !it.done(); it.Advance()) { 1062 total++; 1063 if (it.node()->state() == Node::WEAK) weak++; 1064 if (it.node()->state() == Node::PENDING) pending++; 1065 if (it.node()->state() == Node::NEAR_DEATH) near_death++; 1066 if (it.node()->state() == Node::FREE) destroyed++; 1067 } 1068 1069 PrintF("Global Handle Statistics:\n"); 1070 PrintF(" allocated memory = %" PRIuS "B\n", total * sizeof(Node)); 1071 PrintF(" # weak = %d\n", weak); 1072 PrintF(" # pending = %d\n", pending); 1073 PrintF(" # near_death = %d\n", near_death); 1074 PrintF(" # free = %d\n", destroyed); 1075 PrintF(" # total = %d\n", total); 1076 } 1077 1078 1079 void GlobalHandles::Print() { 1080 PrintF("Global handles:\n"); 1081 for (NodeIterator it(this); !it.done(); it.Advance()) { 1082 PrintF(" handle %p to %p%s\n", 1083 reinterpret_cast<void*>(it.node()->location()), 1084 reinterpret_cast<void*>(it.node()->object()), 1085 it.node()->IsWeak() ? " (weak)" : ""); 1086 } 1087 } 1088 1089 #endif 1090 1091 void GlobalHandles::TearDown() {} 1092 1093 EternalHandles::EternalHandles() : size_(0) { 1094 for (unsigned i = 0; i < arraysize(singleton_handles_); i++) { 1095 singleton_handles_[i] = kInvalidIndex; 1096 } 1097 } 1098 1099 1100 EternalHandles::~EternalHandles() { 1101 for (Object** block : blocks_) delete[] block; 1102 } 1103 1104 void EternalHandles::IterateAllRoots(RootVisitor* visitor) { 1105 int limit = size_; 1106 for (Object** block : blocks_) { 1107 DCHECK_GT(limit, 0); 1108 visitor->VisitRootPointers(Root::kEternalHandles, nullptr, block, 1109 block + Min(limit, kSize)); 1110 limit -= kSize; 1111 } 1112 } 1113 1114 void EternalHandles::IterateNewSpaceRoots(RootVisitor* visitor) { 1115 for (int index : new_space_indices_) { 1116 visitor->VisitRootPointer(Root::kEternalHandles, nullptr, 1117 GetLocation(index)); 1118 } 1119 } 1120 1121 void EternalHandles::PostGarbageCollectionProcessing() { 1122 size_t last = 0; 1123 for (int index : new_space_indices_) { 1124 if (Heap::InNewSpace(*GetLocation(index))) { 1125 new_space_indices_[last++] = index; 1126 } 1127 } 1128 DCHECK_LE(last, new_space_indices_.size()); 1129 new_space_indices_.resize(last); 1130 } 1131 1132 1133 void EternalHandles::Create(Isolate* isolate, Object* object, int* index) { 1134 DCHECK_EQ(kInvalidIndex, *index); 1135 if (object == nullptr) return; 1136 Object* the_hole = ReadOnlyRoots(isolate).the_hole_value(); 1137 DCHECK_NE(the_hole, object); 1138 int block = size_ >> kShift; 1139 int offset = size_ & kMask; 1140 // need to resize 1141 if (offset == 0) { 1142 Object** next_block = new Object*[kSize]; 1143 MemsetPointer(next_block, the_hole, kSize); 1144 blocks_.push_back(next_block); 1145 } 1146 DCHECK_EQ(the_hole, blocks_[block][offset]); 1147 blocks_[block][offset] = object; 1148 if (Heap::InNewSpace(object)) { 1149 new_space_indices_.push_back(size_); 1150 } 1151 *index = size_++; 1152 } 1153 1154 1155 } // namespace internal 1156 } // namespace v8 1157