Home | History | Annotate | Download | only in src
      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