1 // Copyright 2009 the V8 project authors. All rights reserved. 2 // Redistribution and use in source and binary forms, with or without 3 // modification, are permitted provided that the following conditions are 4 // met: 5 // 6 // * Redistributions of source code must retain the above copyright 7 // notice, this list of conditions and the following disclaimer. 8 // * Redistributions in binary form must reproduce the above 9 // copyright notice, this list of conditions and the following 10 // disclaimer in the documentation and/or other materials provided 11 // with the distribution. 12 // * Neither the name of Google Inc. nor the names of its 13 // contributors may be used to endorse or promote products derived 14 // from this software without specific prior written permission. 15 // 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28 #include "v8.h" 29 30 #include "api.h" 31 #include "global-handles.h" 32 33 #include "vm-state-inl.h" 34 35 namespace v8 { 36 namespace internal { 37 38 39 ObjectGroup::~ObjectGroup() { 40 if (info_ != NULL) info_->Dispose(); 41 } 42 43 44 class GlobalHandles::Node { 45 public: 46 // State transition diagram: 47 // FREE -> NORMAL <-> WEAK -> PENDING -> NEAR_DEATH -> { NORMAL, WEAK, FREE } 48 enum State { 49 FREE, 50 NORMAL, // Normal global handle. 51 WEAK, // Flagged as weak but not yet finalized. 52 PENDING, // Has been recognized as only reachable by weak handles. 53 NEAR_DEATH // Callback has informed the handle is near death. 54 }; 55 56 // Maps handle location (slot) to the containing node. 57 static Node* FromLocation(Object** location) { 58 ASSERT(OFFSET_OF(Node, object_) == 0); 59 return reinterpret_cast<Node*>(location); 60 } 61 62 Node() {} 63 64 #ifdef DEBUG 65 ~Node() { 66 // TODO(1428): if it's a weak handle we should have invoked its callback. 67 // Zap the values for eager trapping. 68 object_ = NULL; 69 class_id_ = v8::HeapProfiler::kPersistentHandleNoClassId; 70 index_ = 0; 71 independent_ = false; 72 in_new_space_list_ = false; 73 parameter_or_next_free_.next_free = NULL; 74 callback_ = NULL; 75 } 76 #endif 77 78 void Initialize(int index, Node** first_free) { 79 index_ = static_cast<uint8_t>(index); 80 ASSERT(static_cast<int>(index_) == index); 81 state_ = FREE; 82 in_new_space_list_ = false; 83 parameter_or_next_free_.next_free = *first_free; 84 *first_free = this; 85 } 86 87 void Acquire(Object* object, GlobalHandles* global_handles) { 88 ASSERT(state_ == FREE); 89 object_ = object; 90 class_id_ = v8::HeapProfiler::kPersistentHandleNoClassId; 91 independent_ = false; 92 state_ = NORMAL; 93 parameter_or_next_free_.parameter = NULL; 94 callback_ = NULL; 95 IncreaseBlockUses(global_handles); 96 } 97 98 void Release(GlobalHandles* global_handles) { 99 ASSERT(state_ != FREE); 100 if (IsWeakRetainer()) { 101 global_handles->number_of_weak_handles_--; 102 if (object_->IsJSGlobalObject()) { 103 global_handles->number_of_global_object_weak_handles_--; 104 } 105 } 106 state_ = FREE; 107 parameter_or_next_free_.next_free = global_handles->first_free_; 108 global_handles->first_free_ = this; 109 DecreaseBlockUses(global_handles); 110 } 111 112 // Object slot accessors. 113 Object* object() const { return object_; } 114 Object** location() { return &object_; } 115 Handle<Object> handle() { return Handle<Object>(location()); } 116 117 // Wrapper class ID accessors. 118 bool has_wrapper_class_id() const { 119 return class_id_ != v8::HeapProfiler::kPersistentHandleNoClassId; 120 } 121 uint16_t wrapper_class_id() const { return class_id_; } 122 void set_wrapper_class_id(uint16_t class_id) { 123 class_id_ = class_id; 124 } 125 126 // State accessors. 127 128 State state() const { return state_; } 129 130 bool IsNearDeath() const { 131 // Check for PENDING to ensure correct answer when processing callbacks. 132 return state_ == PENDING || state_ == NEAR_DEATH; 133 } 134 135 bool IsWeak() const { return state_ == WEAK; } 136 137 bool IsRetainer() const { return state_ != FREE; } 138 139 bool IsStrongRetainer() const { return state_ == NORMAL; } 140 141 bool IsWeakRetainer() const { 142 return state_ == WEAK || state_ == PENDING || state_ == NEAR_DEATH; 143 } 144 145 void MarkPending() { 146 ASSERT(state_ == WEAK); 147 state_ = PENDING; 148 } 149 150 // Independent flag accessors. 151 void MarkIndependent() { 152 ASSERT(state_ != FREE); 153 independent_ = true; 154 } 155 bool is_independent() const { return independent_; } 156 157 // In-new-space-list flag accessors. 158 void set_in_new_space_list(bool v) { in_new_space_list_ = v; } 159 bool is_in_new_space_list() const { return in_new_space_list_; } 160 161 // Callback accessor. 162 WeakReferenceCallback callback() { return callback_; } 163 164 // Callback parameter accessors. 165 void set_parameter(void* parameter) { 166 ASSERT(state_ != FREE); 167 parameter_or_next_free_.parameter = parameter; 168 } 169 void* parameter() const { 170 ASSERT(state_ != FREE); 171 return parameter_or_next_free_.parameter; 172 } 173 174 // Accessors for next free node in the free list. 175 Node* next_free() { 176 ASSERT(state_ == FREE); 177 return parameter_or_next_free_.next_free; 178 } 179 void set_next_free(Node* value) { 180 ASSERT(state_ == FREE); 181 parameter_or_next_free_.next_free = value; 182 } 183 184 void MakeWeak(GlobalHandles* global_handles, 185 void* parameter, 186 WeakReferenceCallback callback) { 187 ASSERT(state_ != FREE); 188 if (!IsWeakRetainer()) { 189 global_handles->number_of_weak_handles_++; 190 if (object_->IsJSGlobalObject()) { 191 global_handles->number_of_global_object_weak_handles_++; 192 } 193 } 194 state_ = WEAK; 195 set_parameter(parameter); 196 callback_ = callback; 197 } 198 199 void ClearWeakness(GlobalHandles* global_handles) { 200 ASSERT(state_ != FREE); 201 if (IsWeakRetainer()) { 202 global_handles->number_of_weak_handles_--; 203 if (object_->IsJSGlobalObject()) { 204 global_handles->number_of_global_object_weak_handles_--; 205 } 206 } 207 state_ = NORMAL; 208 set_parameter(NULL); 209 } 210 211 bool PostGarbageCollectionProcessing(Isolate* isolate, 212 GlobalHandles* global_handles) { 213 if (state_ != Node::PENDING) return false; 214 WeakReferenceCallback func = callback(); 215 if (func == NULL) { 216 Release(global_handles); 217 return false; 218 } 219 void* par = parameter(); 220 state_ = NEAR_DEATH; 221 set_parameter(NULL); 222 223 v8::Persistent<v8::Object> object = ToApi<v8::Object>(handle()); 224 { 225 // Check that we are not passing a finalized external string to 226 // the callback. 227 ASSERT(!object_->IsExternalAsciiString() || 228 ExternalAsciiString::cast(object_)->resource() != NULL); 229 ASSERT(!object_->IsExternalTwoByteString() || 230 ExternalTwoByteString::cast(object_)->resource() != NULL); 231 // Leaving V8. 232 VMState state(isolate, EXTERNAL); 233 func(object, par); 234 } 235 // Absence of explicit cleanup or revival of weak handle 236 // in most of the cases would lead to memory leak. 237 ASSERT(state_ != NEAR_DEATH); 238 return true; 239 } 240 241 private: 242 inline NodeBlock* FindBlock(); 243 inline void IncreaseBlockUses(GlobalHandles* global_handles); 244 inline void DecreaseBlockUses(GlobalHandles* global_handles); 245 246 // Storage for object pointer. 247 // Placed first to avoid offset computation. 248 Object* object_; 249 250 // Next word stores class_id, index, state, and independent. 251 // Note: the most aligned fields should go first. 252 253 // Wrapper class ID. 254 uint16_t class_id_; 255 256 // Index in the containing handle block. 257 uint8_t index_; 258 259 // Need one more bit for MSVC as it treats enums as signed. 260 State state_ : 4; 261 262 bool independent_ : 1; 263 bool in_new_space_list_ : 1; 264 265 // Handle specific callback. 266 WeakReferenceCallback callback_; 267 268 // Provided data for callback. In FREE state, this is used for 269 // the free list link. 270 union { 271 void* parameter; 272 Node* next_free; 273 } parameter_or_next_free_; 274 275 DISALLOW_COPY_AND_ASSIGN(Node); 276 }; 277 278 279 class GlobalHandles::NodeBlock { 280 public: 281 static const int kSize = 256; 282 283 explicit NodeBlock(NodeBlock* next) 284 : next_(next), used_nodes_(0), next_used_(NULL), prev_used_(NULL) {} 285 286 void PutNodesOnFreeList(Node** first_free) { 287 for (int i = kSize - 1; i >= 0; --i) { 288 nodes_[i].Initialize(i, first_free); 289 } 290 } 291 292 Node* node_at(int index) { 293 ASSERT(0 <= index && index < kSize); 294 return &nodes_[index]; 295 } 296 297 void IncreaseUses(GlobalHandles* global_handles) { 298 ASSERT(used_nodes_ < kSize); 299 if (used_nodes_++ == 0) { 300 NodeBlock* old_first = global_handles->first_used_block_; 301 global_handles->first_used_block_ = this; 302 next_used_ = old_first; 303 prev_used_ = NULL; 304 if (old_first == NULL) return; 305 old_first->prev_used_ = this; 306 } 307 } 308 309 void DecreaseUses(GlobalHandles* global_handles) { 310 ASSERT(used_nodes_ > 0); 311 if (--used_nodes_ == 0) { 312 if (next_used_ != NULL) next_used_->prev_used_ = prev_used_; 313 if (prev_used_ != NULL) prev_used_->next_used_ = next_used_; 314 if (this == global_handles->first_used_block_) { 315 global_handles->first_used_block_ = next_used_; 316 } 317 } 318 } 319 320 // Next block in the list of all blocks. 321 NodeBlock* next() const { return next_; } 322 323 // Next/previous block in the list of blocks with used nodes. 324 NodeBlock* next_used() const { return next_used_; } 325 NodeBlock* prev_used() const { return prev_used_; } 326 327 private: 328 Node nodes_[kSize]; 329 NodeBlock* const next_; 330 int used_nodes_; 331 NodeBlock* next_used_; 332 NodeBlock* prev_used_; 333 }; 334 335 336 GlobalHandles::NodeBlock* GlobalHandles::Node::FindBlock() { 337 intptr_t ptr = reinterpret_cast<intptr_t>(this); 338 ptr = ptr - index_ * sizeof(Node); 339 NodeBlock* block = reinterpret_cast<NodeBlock*>(ptr); 340 ASSERT(block->node_at(index_) == this); 341 return block; 342 } 343 344 345 void GlobalHandles::Node::IncreaseBlockUses(GlobalHandles* global_handles) { 346 FindBlock()->IncreaseUses(global_handles); 347 } 348 349 350 void GlobalHandles::Node::DecreaseBlockUses(GlobalHandles* global_handles) { 351 FindBlock()->DecreaseUses(global_handles); 352 } 353 354 355 class GlobalHandles::NodeIterator { 356 public: 357 explicit NodeIterator(GlobalHandles* global_handles) 358 : block_(global_handles->first_used_block_), 359 index_(0) {} 360 361 bool done() const { return block_ == NULL; } 362 363 Node* node() const { 364 ASSERT(!done()); 365 return block_->node_at(index_); 366 } 367 368 void Advance() { 369 ASSERT(!done()); 370 if (++index_ < NodeBlock::kSize) return; 371 index_ = 0; 372 block_ = block_->next_used(); 373 } 374 375 private: 376 NodeBlock* block_; 377 int index_; 378 379 DISALLOW_COPY_AND_ASSIGN(NodeIterator); 380 }; 381 382 383 GlobalHandles::GlobalHandles(Isolate* isolate) 384 : isolate_(isolate), 385 number_of_weak_handles_(0), 386 number_of_global_object_weak_handles_(0), 387 number_of_global_handles_(0), 388 first_block_(NULL), 389 first_used_block_(NULL), 390 first_free_(NULL), 391 post_gc_processing_count_(0) {} 392 393 394 GlobalHandles::~GlobalHandles() { 395 NodeBlock* block = first_block_; 396 while (block != NULL) { 397 NodeBlock* tmp = block->next(); 398 delete block; 399 block = tmp; 400 } 401 first_block_ = NULL; 402 } 403 404 405 Handle<Object> GlobalHandles::Create(Object* value) { 406 isolate_->counters()->global_handles()->Increment(); 407 number_of_global_handles_++; 408 if (first_free_ == NULL) { 409 first_block_ = new NodeBlock(first_block_); 410 first_block_->PutNodesOnFreeList(&first_free_); 411 } 412 ASSERT(first_free_ != NULL); 413 // Take the first node in the free list. 414 Node* result = first_free_; 415 first_free_ = result->next_free(); 416 result->Acquire(value, this); 417 if (isolate_->heap()->InNewSpace(value) && 418 !result->is_in_new_space_list()) { 419 new_space_nodes_.Add(result); 420 result->set_in_new_space_list(true); 421 } 422 return result->handle(); 423 } 424 425 426 void GlobalHandles::Destroy(Object** location) { 427 isolate_->counters()->global_handles()->Decrement(); 428 number_of_global_handles_--; 429 if (location == NULL) return; 430 Node::FromLocation(location)->Release(this); 431 } 432 433 434 void GlobalHandles::MakeWeak(Object** location, void* parameter, 435 WeakReferenceCallback callback) { 436 ASSERT(callback != NULL); 437 Node::FromLocation(location)->MakeWeak(this, parameter, callback); 438 } 439 440 441 void GlobalHandles::ClearWeakness(Object** location) { 442 Node::FromLocation(location)->ClearWeakness(this); 443 } 444 445 446 void GlobalHandles::MarkIndependent(Object** location) { 447 Node::FromLocation(location)->MarkIndependent(); 448 } 449 450 451 bool GlobalHandles::IsNearDeath(Object** location) { 452 return Node::FromLocation(location)->IsNearDeath(); 453 } 454 455 456 bool GlobalHandles::IsWeak(Object** location) { 457 return Node::FromLocation(location)->IsWeak(); 458 } 459 460 461 void GlobalHandles::SetWrapperClassId(Object** location, uint16_t class_id) { 462 Node::FromLocation(location)->set_wrapper_class_id(class_id); 463 } 464 465 466 void GlobalHandles::IterateWeakRoots(ObjectVisitor* v) { 467 for (NodeIterator it(this); !it.done(); it.Advance()) { 468 if (it.node()->IsWeakRetainer()) v->VisitPointer(it.node()->location()); 469 } 470 } 471 472 473 void GlobalHandles::IterateWeakRoots(WeakReferenceGuest f, 474 WeakReferenceCallback callback) { 475 for (NodeIterator it(this); !it.done(); it.Advance()) { 476 if (it.node()->IsWeak() && it.node()->callback() == callback) { 477 f(it.node()->object(), it.node()->parameter()); 478 } 479 } 480 } 481 482 483 void GlobalHandles::IdentifyWeakHandles(WeakSlotCallback f) { 484 for (NodeIterator it(this); !it.done(); it.Advance()) { 485 if (it.node()->IsWeak() && f(it.node()->location())) { 486 it.node()->MarkPending(); 487 } 488 } 489 } 490 491 492 void GlobalHandles::IterateNewSpaceStrongAndDependentRoots(ObjectVisitor* v) { 493 for (int i = 0; i < new_space_nodes_.length(); ++i) { 494 Node* node = new_space_nodes_[i]; 495 if (node->IsStrongRetainer() || 496 (node->IsWeakRetainer() && !node->is_independent())) { 497 v->VisitPointer(node->location()); 498 } 499 } 500 } 501 502 503 void GlobalHandles::IdentifyNewSpaceWeakIndependentHandles( 504 WeakSlotCallbackWithHeap f) { 505 for (int i = 0; i < new_space_nodes_.length(); ++i) { 506 Node* node = new_space_nodes_[i]; 507 ASSERT(node->is_in_new_space_list()); 508 if (node->is_independent() && node->IsWeak() && 509 f(isolate_->heap(), node->location())) { 510 node->MarkPending(); 511 } 512 } 513 } 514 515 516 void GlobalHandles::IterateNewSpaceWeakIndependentRoots(ObjectVisitor* v) { 517 for (int i = 0; i < new_space_nodes_.length(); ++i) { 518 Node* node = new_space_nodes_[i]; 519 ASSERT(node->is_in_new_space_list()); 520 if (node->is_independent() && node->IsWeakRetainer()) { 521 v->VisitPointer(node->location()); 522 } 523 } 524 } 525 526 527 bool GlobalHandles::PostGarbageCollectionProcessing( 528 GarbageCollector collector) { 529 // Process weak global handle callbacks. This must be done after the 530 // GC is completely done, because the callbacks may invoke arbitrary 531 // API functions. 532 ASSERT(isolate_->heap()->gc_state() == Heap::NOT_IN_GC); 533 const int initial_post_gc_processing_count = ++post_gc_processing_count_; 534 bool next_gc_likely_to_collect_more = false; 535 if (collector == SCAVENGER) { 536 for (int i = 0; i < new_space_nodes_.length(); ++i) { 537 Node* node = new_space_nodes_[i]; 538 ASSERT(node->is_in_new_space_list()); 539 // Skip dependent handles. Their weak callbacks might expect to be 540 // called between two global garbage collection callbacks which 541 // are not called for minor collections. 542 if (!node->is_independent()) continue; 543 if (node->PostGarbageCollectionProcessing(isolate_, this)) { 544 if (initial_post_gc_processing_count != post_gc_processing_count_) { 545 // Weak callback triggered another GC and another round of 546 // PostGarbageCollection processing. The current node might 547 // have been deleted in that round, so we need to bail out (or 548 // restart the processing). 549 return next_gc_likely_to_collect_more; 550 } 551 } 552 if (!node->IsRetainer()) { 553 next_gc_likely_to_collect_more = true; 554 } 555 } 556 } else { 557 for (NodeIterator it(this); !it.done(); it.Advance()) { 558 if (it.node()->PostGarbageCollectionProcessing(isolate_, this)) { 559 if (initial_post_gc_processing_count != post_gc_processing_count_) { 560 // See the comment above. 561 return next_gc_likely_to_collect_more; 562 } 563 } 564 if (!it.node()->IsRetainer()) { 565 next_gc_likely_to_collect_more = true; 566 } 567 } 568 } 569 // Update the list of new space nodes. 570 int last = 0; 571 for (int i = 0; i < new_space_nodes_.length(); ++i) { 572 Node* node = new_space_nodes_[i]; 573 ASSERT(node->is_in_new_space_list()); 574 if (node->IsRetainer() && isolate_->heap()->InNewSpace(node->object())) { 575 new_space_nodes_[last++] = node; 576 } else { 577 node->set_in_new_space_list(false); 578 } 579 } 580 new_space_nodes_.Rewind(last); 581 return next_gc_likely_to_collect_more; 582 } 583 584 585 void GlobalHandles::IterateStrongRoots(ObjectVisitor* v) { 586 for (NodeIterator it(this); !it.done(); it.Advance()) { 587 if (it.node()->IsStrongRetainer()) { 588 v->VisitPointer(it.node()->location()); 589 } 590 } 591 } 592 593 594 void GlobalHandles::IterateAllRoots(ObjectVisitor* v) { 595 for (NodeIterator it(this); !it.done(); it.Advance()) { 596 if (it.node()->IsRetainer()) { 597 v->VisitPointer(it.node()->location()); 598 } 599 } 600 } 601 602 603 void GlobalHandles::IterateAllRootsWithClassIds(ObjectVisitor* v) { 604 for (NodeIterator it(this); !it.done(); it.Advance()) { 605 if (it.node()->has_wrapper_class_id() && it.node()->IsRetainer()) { 606 v->VisitEmbedderReference(it.node()->location(), 607 it.node()->wrapper_class_id()); 608 } 609 } 610 } 611 612 613 void GlobalHandles::RecordStats(HeapStats* stats) { 614 *stats->global_handle_count = 0; 615 *stats->weak_global_handle_count = 0; 616 *stats->pending_global_handle_count = 0; 617 *stats->near_death_global_handle_count = 0; 618 *stats->free_global_handle_count = 0; 619 for (NodeIterator it(this); !it.done(); it.Advance()) { 620 *stats->global_handle_count += 1; 621 if (it.node()->state() == Node::WEAK) { 622 *stats->weak_global_handle_count += 1; 623 } else if (it.node()->state() == Node::PENDING) { 624 *stats->pending_global_handle_count += 1; 625 } else if (it.node()->state() == Node::NEAR_DEATH) { 626 *stats->near_death_global_handle_count += 1; 627 } else if (it.node()->state() == Node::FREE) { 628 *stats->free_global_handle_count += 1; 629 } 630 } 631 } 632 633 #ifdef DEBUG 634 635 void GlobalHandles::PrintStats() { 636 int total = 0; 637 int weak = 0; 638 int pending = 0; 639 int near_death = 0; 640 int destroyed = 0; 641 642 for (NodeIterator it(this); !it.done(); it.Advance()) { 643 total++; 644 if (it.node()->state() == Node::WEAK) weak++; 645 if (it.node()->state() == Node::PENDING) pending++; 646 if (it.node()->state() == Node::NEAR_DEATH) near_death++; 647 if (it.node()->state() == Node::FREE) destroyed++; 648 } 649 650 PrintF("Global Handle Statistics:\n"); 651 PrintF(" allocated memory = %" V8_PTR_PREFIX "dB\n", sizeof(Node) * total); 652 PrintF(" # weak = %d\n", weak); 653 PrintF(" # pending = %d\n", pending); 654 PrintF(" # near_death = %d\n", near_death); 655 PrintF(" # free = %d\n", destroyed); 656 PrintF(" # total = %d\n", total); 657 } 658 659 void GlobalHandles::Print() { 660 PrintF("Global handles:\n"); 661 for (NodeIterator it(this); !it.done(); it.Advance()) { 662 PrintF(" handle %p to %p%s\n", 663 reinterpret_cast<void*>(it.node()->location()), 664 reinterpret_cast<void*>(it.node()->object()), 665 it.node()->IsWeak() ? " (weak)" : ""); 666 } 667 } 668 669 #endif 670 671 672 673 void GlobalHandles::AddObjectGroup(Object*** handles, 674 size_t length, 675 v8::RetainedObjectInfo* info) { 676 #ifdef DEBUG 677 for (size_t i = 0; i < length; ++i) { 678 ASSERT(!Node::FromLocation(handles[i])->is_independent()); 679 } 680 #endif 681 if (length == 0) { 682 if (info != NULL) info->Dispose(); 683 return; 684 } 685 object_groups_.Add(ObjectGroup::New(handles, length, info)); 686 } 687 688 689 void GlobalHandles::AddImplicitReferences(HeapObject** parent, 690 Object*** children, 691 size_t length) { 692 #ifdef DEBUG 693 ASSERT(!Node::FromLocation(BitCast<Object**>(parent))->is_independent()); 694 for (size_t i = 0; i < length; ++i) { 695 ASSERT(!Node::FromLocation(children[i])->is_independent()); 696 } 697 #endif 698 if (length == 0) return; 699 implicit_ref_groups_.Add(ImplicitRefGroup::New(parent, children, length)); 700 } 701 702 703 void GlobalHandles::RemoveObjectGroups() { 704 for (int i = 0; i < object_groups_.length(); i++) { 705 object_groups_.at(i)->Dispose(); 706 } 707 object_groups_.Clear(); 708 } 709 710 711 void GlobalHandles::RemoveImplicitRefGroups() { 712 for (int i = 0; i < implicit_ref_groups_.length(); i++) { 713 implicit_ref_groups_.at(i)->Dispose(); 714 } 715 implicit_ref_groups_.Clear(); 716 } 717 718 719 void GlobalHandles::TearDown() { 720 // TODO(1428): invoke weak callbacks. 721 } 722 723 724 } } // namespace v8::internal 725